mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-05-28 01:17:03 +02:00
Merge branch 'stirling-pdf-rewrite' into version-2
This commit is contained in:
commit
0a43660e55
@ -5,6 +5,8 @@ This file should introduce you with the concepts and tools used in this project.
|
||||
## PDF Library Docs
|
||||
- [pdf-lib](https://pdf-lib.js.org) - js
|
||||
- [pdfcpu](https://pdfcpu.io) - go-wasm
|
||||
- [opencv-wasm](https://www.npmjs.com/package/opencv-wasm) - ?-wasm
|
||||
- [pdfjs](https://www.npmjs.com/package/pdfjs-dist) - js
|
||||
|
||||
## Adding a PDF Operation
|
||||
StirlingPDF aims to support as many types of operations as possible, including some that cannot be executed in the client. Because of this, we have decided to move some of the shared functionality into it's own node module so that it can be shared by both client and server.
|
||||
|
67
README.md
67
README.md
@ -88,22 +88,25 @@ If you are interested in learning about this, take a look at the Example workflo
|
||||
|
||||
Current functions of spdf and their progress in this repo.
|
||||
|
||||
| Status | Feature | Description |
|
||||
| ------ | ---------------------- | ----------- |
|
||||
| ✔️ | Merge | |
|
||||
| ✔️ | Split | |
|
||||
| ✔️ | Rotate | |
|
||||
| ✔️ | Multi-Page-Layout | |
|
||||
| ✔️ | Adjust page size/scale | |
|
||||
| 🚧 | Organize | |
|
||||
| ✔️ | Change Metadata | |
|
||||
| ❌ | Add Watermark | |
|
||||
| Status | Feature | Description |
|
||||
| ------ | ------------------------ | ----------- |
|
||||
| ✔️ | Merge | |
|
||||
| ✔️ | Split | |
|
||||
| ✔️ | Rotate | |
|
||||
| ✔️ | Multi-Page-Layout | |
|
||||
| ✔️ | Adjust page size/scale | |
|
||||
| ✔️ | Organize | |
|
||||
| ✔️ | Change Metadata | |
|
||||
| ✔️ | Auto Rename | |
|
||||
| ❌ | Add Watermark | |
|
||||
| ❌ | PDF to Single large Page | |
|
||||
| ❌ | Auto Redact | |
|
||||
| ❌ | Remove Pages | |
|
||||
|
||||
| Status | Feature | Description |
|
||||
| ------ | --------------------------- | ----------- |
|
||||
| ❌ | Remove Pages | |
|
||||
| ❌ | Remove Blank Pages | |
|
||||
| ❌ | Detect/Split Scanned photos | |
|
||||
| Status | Feature | Description |
|
||||
| ------ | ------------------ | ----------- |
|
||||
| ✔️ | Remove Blank Pages | |
|
||||
| ✔️ | Auto Split Pages | |
|
||||
|
||||
| Status | Feature | Description |
|
||||
| ------ | ------------ | ----------- |
|
||||
@ -111,6 +114,9 @@ Current functions of spdf and their progress in this repo.
|
||||
| ❌ | Compress | |
|
||||
| ❌ | Flatten | |
|
||||
| ❌ | Compare/Diff | |
|
||||
| ❌ | Sanitize | |
|
||||
| ❌ | Get info | |
|
||||
| ❌ | Show JS | |
|
||||
|
||||
| Status | Feature | Description |
|
||||
| ------ | --------------------- | ----------- |
|
||||
@ -120,20 +126,25 @@ Current functions of spdf and their progress in this repo.
|
||||
| ❌ | Remove Password | |
|
||||
| ❌ | Change Permissions | |
|
||||
|
||||
| Status | Feature | Description |
|
||||
| ------ | -------------- | ----------- |
|
||||
| ❌ | Image to PDF | |
|
||||
| ❌ | Add image | |
|
||||
| ❌ | Extract Images | |
|
||||
| ❌ | PDF to Image | |
|
||||
| ❌ | OCR | |
|
||||
| Status | Feature | Description |
|
||||
| ------ | --------------------------- | ----------- |
|
||||
| ❌ | Image to PDF | |
|
||||
| ❌ | Add image | |
|
||||
| ❌ | Extract Images | |
|
||||
| ❌ | PDF to Image | |
|
||||
| ❌ | OCR | |
|
||||
| ❌ | Detect/Split Scanned photos | |
|
||||
|
||||
| Status | Feature | Description |
|
||||
| ------ | ------------------------ | ----------- |
|
||||
| ❌ | Convert file to PDF | |
|
||||
| ❌ | PDF to Text/RTF | |
|
||||
| ❌ | PDF to HTML | |
|
||||
| ❌ | PDF to XML | |
|
||||
| ❌ | URL to PDF | |
|
||||
| ❌ | HTML to PDF | |
|
||||
| ❌ | Markdown to PDF | |
|
||||
|
||||
| Status | Feature | Description |
|
||||
| ------ | ------------------- | ----------- |
|
||||
| ❌ | Convert file to PDF | |
|
||||
| ❌ | PDF to Text/RTF | |
|
||||
| ❌ | PDF to HTML | |
|
||||
| ❌ | PDF to XML | |
|
||||
|
||||
✔️: Done, 🚧: Started Developement, ❌: Planned Feature
|
||||
|
||||
|
167
client-vanilla/dep/downloadjs_1.4.7.js
Normal file
167
client-vanilla/dep/downloadjs_1.4.7.js
Normal file
@ -0,0 +1,167 @@
|
||||
//download.js v4.2, by dandavis; 2008-2016. [MIT] see http://danml.com/download.html for tests/usage
|
||||
// v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime
|
||||
// v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs
|
||||
// v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support. 3.1 improved safari handling.
|
||||
// v4 adds AMD/UMD, commonJS, and plain browser support
|
||||
// v4.1 adds url download capability via solo URL argument (same domain/CORS only)
|
||||
// v4.2 adds semantic variable names, long (over 2MB) dataURL support, and hidden by default temp anchors
|
||||
// https://github.com/rndme/download
|
||||
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define([], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node. Does not work with strict CommonJS, but
|
||||
// only CommonJS-like environments that support module.exports,
|
||||
// like Node.
|
||||
module.exports = factory();
|
||||
} else {
|
||||
// Browser globals (root is window)
|
||||
root.download = factory();
|
||||
}
|
||||
}(this, function () {
|
||||
|
||||
return function download(data, strFileName, strMimeType) {
|
||||
|
||||
var self = window, // this script is only for browsers anyway...
|
||||
defaultMime = "application/octet-stream", // this default mime also triggers iframe downloads
|
||||
mimeType = strMimeType || defaultMime,
|
||||
payload = data,
|
||||
url = !strFileName && !strMimeType && payload,
|
||||
anchor = document.createElement("a"),
|
||||
toString = function(a){return String(a);},
|
||||
myBlob = (self.Blob || self.MozBlob || self.WebKitBlob || toString),
|
||||
fileName = strFileName || "download",
|
||||
blob,
|
||||
reader;
|
||||
myBlob= myBlob.call ? myBlob.bind(self) : Blob ;
|
||||
|
||||
if(String(this)==="true"){ //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
|
||||
payload=[payload, mimeType];
|
||||
mimeType=payload[0];
|
||||
payload=payload[1];
|
||||
}
|
||||
|
||||
|
||||
if(url && url.length< 2048){ // if no filename and no mime, assume a url was passed as the only argument
|
||||
fileName = url.split("/").pop().split("?")[0];
|
||||
anchor.href = url; // assign href prop to temp anchor
|
||||
if(anchor.href.indexOf(url) !== -1){ // if the browser determines that it's a potentially valid url path:
|
||||
var ajax=new XMLHttpRequest();
|
||||
ajax.open( "GET", url, true);
|
||||
ajax.responseType = 'blob';
|
||||
ajax.onload= function(e){
|
||||
download(e.target.response, fileName, defaultMime);
|
||||
};
|
||||
setTimeout(function(){ ajax.send();}, 0); // allows setting custom ajax headers using the return:
|
||||
return ajax;
|
||||
} // end if valid url?
|
||||
} // end if url?
|
||||
|
||||
|
||||
//go ahead and download dataURLs right away
|
||||
if(/^data:([\w+-]+\/[\w+.-]+)?[,;]/.test(payload)){
|
||||
|
||||
if(payload.length > (1024*1024*1.999) && myBlob !== toString ){
|
||||
payload=dataUrlToBlob(payload);
|
||||
mimeType=payload.type || defaultMime;
|
||||
}else{
|
||||
return navigator.msSaveBlob ? // IE10 can't do a[download], only Blobs:
|
||||
navigator.msSaveBlob(dataUrlToBlob(payload), fileName) :
|
||||
saver(payload) ; // everyone else can save dataURLs un-processed
|
||||
}
|
||||
|
||||
}else{//not data url, is it a string with special needs?
|
||||
if(/([\x80-\xff])/.test(payload)){
|
||||
var i=0, tempUiArr= new Uint8Array(payload.length), mx=tempUiArr.length;
|
||||
for(i;i<mx;++i) tempUiArr[i]= payload.charCodeAt(i);
|
||||
payload=new myBlob([tempUiArr], {type: mimeType});
|
||||
}
|
||||
}
|
||||
blob = payload instanceof myBlob ?
|
||||
payload :
|
||||
new myBlob([payload], {type: mimeType}) ;
|
||||
|
||||
|
||||
function dataUrlToBlob(strUrl) {
|
||||
var parts= strUrl.split(/[:;,]/),
|
||||
type= parts[1],
|
||||
decoder= parts[2] == "base64" ? atob : decodeURIComponent,
|
||||
binData= decoder( parts.pop() ),
|
||||
mx= binData.length,
|
||||
i= 0,
|
||||
uiArr= new Uint8Array(mx);
|
||||
|
||||
for(i;i<mx;++i) uiArr[i]= binData.charCodeAt(i);
|
||||
|
||||
return new myBlob([uiArr], {type: type});
|
||||
}
|
||||
|
||||
function saver(url, winMode){
|
||||
|
||||
if ('download' in anchor) { //html5 A[download]
|
||||
anchor.href = url;
|
||||
anchor.setAttribute("download", fileName);
|
||||
anchor.className = "download-js-link";
|
||||
anchor.innerHTML = "downloading...";
|
||||
anchor.style.display = "none";
|
||||
document.body.appendChild(anchor);
|
||||
setTimeout(function() {
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
if(winMode===true){setTimeout(function(){ self.URL.revokeObjectURL(anchor.href);}, 250 );}
|
||||
}, 66);
|
||||
return true;
|
||||
}
|
||||
|
||||
// handle non-a[download] safari as best we can:
|
||||
if(/(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(navigator.userAgent)) {
|
||||
if(/^data:/.test(url)) url="data:"+url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
|
||||
if(!window.open(url)){ // popup blocked, offer direct download:
|
||||
if(confirm("Displaying New Document\n\nUse Save As... to download, then click back to return to this page.")){ location.href=url; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//do iframe dataURL download (old ch+FF):
|
||||
var f = document.createElement("iframe");
|
||||
document.body.appendChild(f);
|
||||
|
||||
if(!winMode && /^data:/.test(url)){ // force a mime that will download:
|
||||
url="data:"+url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
|
||||
}
|
||||
f.src=url;
|
||||
setTimeout(function(){ document.body.removeChild(f); }, 333);
|
||||
|
||||
}//end saver
|
||||
|
||||
|
||||
|
||||
|
||||
if (navigator.msSaveBlob) { // IE10+ : (has Blob, but not a[download] or URL)
|
||||
return navigator.msSaveBlob(blob, fileName);
|
||||
}
|
||||
|
||||
if(self.URL){ // simple fast and modern way using Blob and URL:
|
||||
saver(self.URL.createObjectURL(blob), true);
|
||||
}else{
|
||||
// handle non-Blob()+non-URL browsers:
|
||||
if(typeof blob === "string" || blob.constructor===toString ){
|
||||
try{
|
||||
return saver( "data:" + mimeType + ";base64," + self.btoa(blob) );
|
||||
}catch(y){
|
||||
return saver( "data:" + mimeType + "," + encodeURIComponent(blob) );
|
||||
}
|
||||
}
|
||||
|
||||
// Blob but not URL support:
|
||||
reader=new FileReader();
|
||||
reader.onload=function(e){
|
||||
saver(this.result);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
return true;
|
||||
}; /* end download() */
|
||||
}));
|
10100
client-vanilla/dep/jsQR.js
Normal file
10100
client-vanilla/dep/jsQR.js
Normal file
File diff suppressed because it is too large
Load Diff
16
client-vanilla/dep/pdf-lib.min.js
vendored
Normal file
16
client-vanilla/dep/pdf-lib.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9995
client-vanilla/dep/pdf.min.js
vendored
Normal file
9995
client-vanilla/dep/pdf.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22
client-vanilla/dep/pdf.worker.min.js
vendored
Normal file
22
client-vanilla/dep/pdf.worker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
165
client-vanilla/exampleWorkflows.js
Normal file
165
client-vanilla/exampleWorkflows.js
Normal file
@ -0,0 +1,165 @@
|
||||
// JSON Representation of this Node Tree:
|
||||
// https://discord.com/channels/1068636748814483718/1099390571493195898/1118192754103693483
|
||||
// https://cdn.discordapp.com/attachments/1099390571493195898/1118192753759764520/image.png?ex=6537dba7&is=652566a7&hm=dc46820ef7c34bc37424794966c5f66f93ba0e15a740742c364d47d31ea119a9&
|
||||
export const discordWorkflow = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "extract",
|
||||
values: { "index": "1" },
|
||||
operations: [
|
||||
{
|
||||
type: "removeObjects",
|
||||
values: { "objectNames": "photo, josh" },
|
||||
operations: [
|
||||
{
|
||||
type: "wait",
|
||||
values: { "id": 1 }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "extract",
|
||||
values: { "index": "2-5" },
|
||||
operations: [
|
||||
{
|
||||
type: "fillField",
|
||||
values: { "objectName": "name", "inputValue": "Josh" },
|
||||
operations: [
|
||||
{
|
||||
type: "wait",
|
||||
values: { "id": 1 }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "done", // This gets called when the other merge-ops with the same id finish.
|
||||
values: { "id": 1 },
|
||||
operations: [
|
||||
{
|
||||
type: "merge",
|
||||
values: {},
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "extractImages",
|
||||
values: {},
|
||||
operations: []
|
||||
},
|
||||
{
|
||||
type: "merge",
|
||||
values: {},
|
||||
operations: [
|
||||
{
|
||||
type: "transform",
|
||||
values: { "scale": "2x", "rotation": "90deg" },
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// This will merge all input files into one giant document
|
||||
export const mergeOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "merge",
|
||||
values: {},
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Extract Pages and store them in a new document
|
||||
export const extractOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "extract",
|
||||
values: { "pagesToExtractArray": [0, 2] },
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Split a document up into multiple documents
|
||||
export const splitOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "split",
|
||||
values: { "pagesToSplitAfterArray": [2, 10] },
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const rotateOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "rotate",
|
||||
values: { "rotation": -90 },
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const imposeOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "impose",
|
||||
values: { "nup": 2, "format": "A4L" },
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const removeBlankPagesOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "removeBlankPages",
|
||||
values: { "whiteThreashold": 10 },
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const splitOnQR = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "splitOn",
|
||||
values: {
|
||||
type: "QR_CODE"
|
||||
},
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
65
client-vanilla/functions.js
Normal file
65
client-vanilla/functions.js
Normal file
@ -0,0 +1,65 @@
|
||||
// PDFLib gets imported via index.html script-tag
|
||||
// PDFJS as pdfjsLib via index.html script-tag
|
||||
// jsQR via index.html script-tag
|
||||
|
||||
import * as pdfcpuWraopper from "./wasm/pdfcpu/pdfcpu-wrapper-browser.js";
|
||||
|
||||
const OpenCV = { cv: cv } // OPENCV gets importet as cv via index.html script-tag
|
||||
|
||||
import { extractPages as dependantExtractPages } from "./functions/extractPages.js";
|
||||
import { impose as dependantImpose } from './functions/impose.js';
|
||||
import { mergePDFs as dependantMergePDFs } from './functions/mergePDFs.js';
|
||||
import { rotatePages as dependantRotatePages } from './functions/rotatePages.js';
|
||||
import { scaleContent as dependantScaleContent} from './functions/scaleContent.js';
|
||||
import { scalePage as dependantScalePage } from './functions/scalePage.js';
|
||||
import { splitPDF as dependantSplitPDF } from './functions/splitPDF.js';
|
||||
import { editMetadata as dependantEditMetadata} from "./functions/editMetadata.js";
|
||||
import { organizePages as dependantOrganizePages} from "./functions/organizePages.js";
|
||||
import { removeBlankPages as dependantRemoveBlankPages} from "./functions/removeBlankPages.js";
|
||||
import { splitOn as dependantSplitOn } from "./functions/splitOn.js";
|
||||
|
||||
// TODO: Dynamic loading & undloading of libraries.
|
||||
|
||||
export async function extractPages(snapshot, pagesToExtractArray) {
|
||||
return dependantExtractPages(snapshot, pagesToExtractArray, PDFLib);
|
||||
}
|
||||
|
||||
export async function impose(snapshot, nup, format) {
|
||||
return dependantImpose(snapshot, nup, format, pdfcpuWraopper);
|
||||
}
|
||||
|
||||
export async function mergePDFs(snapshots) {
|
||||
return dependantMergePDFs(snapshots, PDFLib);
|
||||
}
|
||||
|
||||
export async function rotatePages(snapshot, rotation) {
|
||||
return dependantRotatePages(snapshot, rotation, PDFLib);
|
||||
}
|
||||
|
||||
export async function scaleContent(snapshot, scaleFactor) {
|
||||
return dependantScaleContent(snapshot, scaleFactor, PDFLib);
|
||||
}
|
||||
|
||||
export async function scalePage(snapshot, pageSize) {
|
||||
return dependantScalePage(snapshot, pageSize, PDFLib);
|
||||
}
|
||||
|
||||
export async function splitPDF(snapshot, splitAfterPageArray) {
|
||||
return dependantSplitPDF(snapshot, splitAfterPageArray, PDFLib);
|
||||
}
|
||||
|
||||
export async function editMetadata(snapshot, metadata) {
|
||||
return dependantEditMetadata(snapshot, metadata, PDFLib);
|
||||
}
|
||||
|
||||
export async function organizePages(snapshot, operation, customOrderString) {
|
||||
return dependantOrganizePages(snapshot, operation, customOrderString, PDFLib);
|
||||
}
|
||||
|
||||
export async function removeBlankPages(snapshot, whiteThreashold) {
|
||||
return dependantRemoveBlankPages(snapshot, whiteThreashold, pdfjsLib, OpenCV, PDFLib);
|
||||
}
|
||||
|
||||
export async function splitOn(snapshot, type, whiteThreashold) {
|
||||
return dependantSplitOn(snapshot, type, whiteThreashold, pdfjsLib, OpenCV, PDFLib, jsQR);
|
||||
}
|
0
client-vanilla/index.css
Normal file
0
client-vanilla/index.css
Normal file
33
client-vanilla/index.html
Normal file
33
client-vanilla/index.html
Normal file
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
|
||||
<script src="/dep/pdf-lib.min.js"></script>
|
||||
<script src="/dep/downloadjs_1.4.7.js"></script>
|
||||
<script src="/dep/pdf.min.js"></script>
|
||||
<script src="/dep/jsQR.js"></script>
|
||||
|
||||
<script src="/wasm/browserfs.min.js"></script>
|
||||
<script src="/wasm/opencv/opencv_3_4_custom_O3.js"></script>
|
||||
|
||||
<script src="index.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<input type="file" id="pdfFile" accept=".pdf" multiple>
|
||||
<br>
|
||||
|
||||
<br>
|
||||
<textarea name="workflow" id="workflow" cols="120" rows="20"></textarea>
|
||||
<br>
|
||||
<select id="pdfOptions">
|
||||
</select>
|
||||
<button id="loadButton">Load</button>
|
||||
<br>
|
||||
|
||||
<br>
|
||||
<button id="doneButton">Done</button>
|
||||
</body>
|
||||
</html>
|
65
client-vanilla/index.js
Normal file
65
client-vanilla/index.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { scaleContent } from "./functions/scaleContent.js";
|
||||
import { scalePage, PageSize } from "./functions/scalePage.js";
|
||||
import * as exampleWorkflows from "./exampleWorkflows.js";
|
||||
import { traverseOperations } from "./traverseOperations.js";
|
||||
import * as Functions from "./functions.js";
|
||||
|
||||
(async () => {
|
||||
const workflowField = document.getElementById("workflow");
|
||||
|
||||
const dropdown = document.getElementById("pdfOptions");
|
||||
// Clear existing options (if any)
|
||||
dropdown.innerHTML = '';
|
||||
|
||||
// Iterate over the keys of the object and create an option for each key
|
||||
for (const key in exampleWorkflows) {
|
||||
const option = document.createElement('option');
|
||||
option.value = key;
|
||||
option.text = key;
|
||||
dropdown.appendChild(option);
|
||||
}
|
||||
|
||||
const loadButton = document.getElementById("loadButton");
|
||||
loadButton.addEventListener("click", (e) => {
|
||||
workflowField.value = JSON.stringify(exampleWorkflows[dropdown.value], null, 2);
|
||||
});
|
||||
loadButton.click();
|
||||
|
||||
const pdfFileInput = document.getElementById('pdfFile');
|
||||
const doneButton = document.getElementById("doneButton");
|
||||
|
||||
doneButton.addEventListener('click', async (e) => {
|
||||
console.log("Starting...");
|
||||
|
||||
const files = Array.from(pdfFileInput.files);
|
||||
const inputs = await Promise.all(files.map(async file => {
|
||||
return {
|
||||
originalFileName: file.name.replace(/\.[^/.]+$/, ""),
|
||||
fileName: file.name.replace(/\.[^/.]+$/, ""),
|
||||
buffer: new Uint8Array(await file.arrayBuffer())
|
||||
}
|
||||
}));
|
||||
console.log(inputs);
|
||||
|
||||
const workflow = JSON.parse(workflowField.value);
|
||||
console.log(workflow);
|
||||
const traverse = traverseOperations(workflow.operations, inputs, Functions);
|
||||
|
||||
let pdfResults;
|
||||
let iteration;
|
||||
while (true) {
|
||||
iteration = await traverse.next();
|
||||
if (iteration.done) {
|
||||
pdfResults = iteration.value;
|
||||
console.log(`data: processing done\n\n`);
|
||||
break;
|
||||
}
|
||||
console.log(`data: ${iteration.value}\n\n`);
|
||||
}
|
||||
|
||||
// TODO: Zip if wanted
|
||||
pdfResults.forEach(result => {
|
||||
download(result.buffer, result.fileName, "application/pdf");
|
||||
});
|
||||
});
|
||||
})();
|
396
package-lock.json
generated
396
package-lock.json
generated
@ -3624,6 +3624,59 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
||||
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.0",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"make-dir": "^3.1.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"nopt": "^5.0.0",
|
||||
"npmlog": "^5.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.5",
|
||||
"tar": "^6.1.11"
|
||||
},
|
||||
"bin": {
|
||||
"node-pre-gyp": "bin/node-pre-gyp"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@ -4485,6 +4538,12 @@
|
||||
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@ -4531,7 +4590,7 @@
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
},
|
||||
@ -4543,7 +4602,7 @@
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
@ -4560,7 +4619,7 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/aggregate-error": {
|
||||
"version": "3.1.0",
|
||||
@ -4619,7 +4678,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@ -4644,6 +4703,12 @@
|
||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
||||
},
|
||||
"node_modules/aproba": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/arch": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
|
||||
@ -4707,6 +4772,19 @@
|
||||
"streamx": "^2.15.0"
|
||||
}
|
||||
},
|
||||
"node_modules/are-we-there-yet": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
|
||||
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
@ -5261,6 +5339,21 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/canvas": {
|
||||
"version": "2.11.2",
|
||||
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
|
||||
"integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.0",
|
||||
"nan": "^2.17.0",
|
||||
"simple-get": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
@ -5326,7 +5419,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
@ -5435,6 +5528,15 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/color-support": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
||||
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"color-support": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
@ -5489,7 +5591,7 @@
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "1.6.2",
|
||||
@ -5574,6 +5676,12 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
@ -5901,6 +6009,18 @@
|
||||
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/decompress-response": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
||||
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"mimic-response": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-eql": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
|
||||
@ -6011,6 +6131,12 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
@ -6036,6 +6162,15 @@
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
|
||||
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/diff-sequences": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
|
||||
@ -6141,7 +6276,7 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
@ -7118,7 +7253,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0"
|
||||
},
|
||||
@ -7130,7 +7265,7 @@
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@ -7142,7 +7277,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/fs-monkey": {
|
||||
"version": "0.3.3",
|
||||
@ -7203,6 +7338,26 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gauge": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
||||
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"aproba": "^1.0.3 || ^2.0.0",
|
||||
"color-support": "^1.1.2",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.1",
|
||||
"object-assign": "^4.1.1",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^4.2.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wide-align": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@ -7456,6 +7611,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
||||
@ -7583,7 +7744,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
@ -7596,7 +7757,7 @@
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
@ -7613,7 +7774,7 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/human-signals": {
|
||||
"version": "1.1.1",
|
||||
@ -7958,7 +8119,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@ -8979,6 +9140,21 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"semver": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@ -9076,6 +9252,18 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-response": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/min-indent": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
||||
@ -9108,7 +9296,7 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@ -9117,7 +9305,7 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
@ -9130,7 +9318,7 @@
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@ -9142,7 +9330,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.5.6",
|
||||
@ -9189,6 +9377,12 @@
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
|
||||
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||
@ -9284,12 +9478,69 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
||||
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"abbrev": "1"
|
||||
},
|
||||
"bin": {
|
||||
"nopt": "bin/nopt.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@ -9310,6 +9561,18 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/npmlog": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
|
||||
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"are-we-there-yet": "^2.0.0",
|
||||
"console-control-strings": "^1.1.0",
|
||||
"gauge": "^3.0.0",
|
||||
"set-blocking": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nwsapi": {
|
||||
"version": "2.2.7",
|
||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
|
||||
@ -9610,7 +9873,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -9660,6 +9923,15 @@
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/path2d-polyfill": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz",
|
||||
"integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz",
|
||||
@ -9686,6 +9958,18 @@
|
||||
"tslib": "^1.11.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pdfjs-dist": {
|
||||
"version": "4.0.189",
|
||||
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.0.189.tgz",
|
||||
"integrity": "sha512-5IWbLRJjQJhk3cu3nNFAvIYoSzT8xRYlRkFCIV1tn7hK1eq9H+6vOP0ytJhZz9YI35IXlu33uQspvmYO6Oir4g==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"canvas": "^2.11.2",
|
||||
"path2d-polyfill": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
@ -10420,7 +10704,7 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
@ -10435,7 +10719,7 @@
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -10445,7 +10729,7 @@
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@ -10465,7 +10749,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@ -10625,7 +10909,7 @@
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@ -10681,6 +10965,12 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
|
||||
@ -10767,7 +11057,38 @@
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/simple-concat": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/simple-get": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"decompress-response": "^4.2.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sisteransi": {
|
||||
"version": "1.0.5",
|
||||
@ -10955,7 +11276,7 @@
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
@ -11048,7 +11369,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
@ -11150,7 +11471,7 @@
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
|
||||
"integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
@ -11182,7 +11503,7 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
@ -11194,7 +11515,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/temp-dir": {
|
||||
"version": "2.0.0",
|
||||
@ -12246,6 +12567,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wide-align": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
||||
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||
}
|
||||
},
|
||||
"node_modules/workbox-background-sync": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.0.0.tgz",
|
||||
@ -12859,7 +13189,11 @@
|
||||
"shared-operations": {
|
||||
"name": "@stirling-pdf/shared-operations",
|
||||
"version": "0.0.0",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pdfjs-dist": "^4.0.189"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
"devDependencies": {
|
||||
"concurrently": "^8.2.2"
|
||||
},
|
||||
"engines" : {
|
||||
"engines" : {
|
||||
"npm" : ">=7.24.2"
|
||||
},
|
||||
"engineStrict" : true
|
||||
|
63
server-node/functions.js
Normal file
63
server-node/functions.js
Normal file
@ -0,0 +1,63 @@
|
||||
import PDFLib from 'pdf-lib';
|
||||
import PDFJS from "pdfjs-dist";
|
||||
import jsQR from "jsqr";
|
||||
|
||||
delete global.crypto; // TODO: I hate to do this, but the new node version forces me to, if anyone finds a better solution, please tell me!
|
||||
import * as pdfcpuWraopper from "../shared-operations/wasm/pdfcpu/pdfcpu-wrapper-node.js";
|
||||
import OpenCV from 'opencv-wasm';
|
||||
|
||||
import { extractPages as dependantExtractPages } from "../shared-operations/functions/extractPages.js";
|
||||
import { impose as dependantImpose } from '../shared-operations/functions/impose.js';
|
||||
import { mergePDFs as dependantMergePDFs } from '../shared-operations/functions/mergePDFs.js';
|
||||
import { rotatePages as dependantRotatePages } from '../shared-operations/functions/rotatePages.js';
|
||||
import { scaleContent as dependantScaleContent} from '../shared-operations/functions/scaleContent.js';
|
||||
import { scalePage as dependantScalePage } from '../shared-operations/functions/scalePage.js';
|
||||
import { splitPDF as dependantSplitPDF } from '../shared-operations/functions/splitPDF.js';
|
||||
import { editMetadata as dependantEditMetadata } from '../shared-operations/functions/editMetadata.js';
|
||||
import { organizePages as dependantOrganizePages } from '../shared-operations/functions/organizePages.js';
|
||||
import { removeBlankPages as dependantRemoveBlankPages} from '../shared-operations/functions/removeBlankPages.js';
|
||||
import { splitOn as dependantSplitOn } from "../shared-operations/functions/splitOn.js";
|
||||
|
||||
export async function extractPages(snapshot, pagesToExtractArray) {
|
||||
return dependantExtractPages(snapshot, pagesToExtractArray, PDFLib);
|
||||
}
|
||||
|
||||
export async function impose(snapshot, nup, format) {
|
||||
return dependantImpose(snapshot, nup, format, pdfcpuWraopper);
|
||||
}
|
||||
|
||||
export async function mergePDFs(snapshots) {
|
||||
return dependantMergePDFs(snapshots, PDFLib);
|
||||
}
|
||||
|
||||
export async function rotatePages(snapshot, rotation) {
|
||||
return dependantRotatePages(snapshot, rotation, PDFLib);
|
||||
}
|
||||
|
||||
export async function scaleContent(snapshot, scaleFactor) {
|
||||
return dependantScaleContent(snapshot, scaleFactor, PDFLib);
|
||||
}
|
||||
|
||||
export async function scalePage(snapshot, pageSize) {
|
||||
return dependantScalePage(snapshot, pageSize, PDFLib);
|
||||
}
|
||||
|
||||
export async function splitPDF(snapshot, splitAfterPageArray) {
|
||||
return dependantSplitPDF(snapshot, splitAfterPageArray, PDFLib);
|
||||
}
|
||||
|
||||
export async function editMetadata(snapshot, metadata) {
|
||||
return dependantEditMetadata(snapshot, metadata, PDFLib);
|
||||
}
|
||||
|
||||
export async function organizePages(snapshot, operation, customOrderString) {
|
||||
return dependantOrganizePages(snapshot, operation, customOrderString, PDFLib);
|
||||
}
|
||||
|
||||
export async function removeBlankPages(snapshot, whiteThreashold) {
|
||||
return dependantRemoveBlankPages(snapshot, whiteThreashold, PDFJS, OpenCV, PDFLib);
|
||||
}
|
||||
|
||||
export async function splitOn(snapshot, type, whiteThreashold) {
|
||||
return dependantSplitOn(snapshot, type, whiteThreashold, PDFJS, OpenCV, PDFLib, jsQR);
|
||||
}
|
@ -16,6 +16,15 @@ app.get('/', function (req, res, next) { // TODO: Use EJS?
|
||||
app.use("/api/operations", operations);
|
||||
//app.use("/api/workflow", workflow);
|
||||
|
||||
// server-node: backend api
|
||||
import api from './server-node/routes/api/index.js';
|
||||
app.use("/api/", api);
|
||||
|
||||
// client-vanilla: frontend
|
||||
app.use(express.static('./client-vanilla'));
|
||||
app.use(express.static('./shared-operations'));
|
||||
|
||||
// serve
|
||||
app.listen(PORT, function (err) {
|
||||
if (err) console.log(err);
|
||||
console.log(`http://localhost:${PORT}`);
|
||||
|
@ -110,7 +110,6 @@ export const splitOnly = {
|
||||
]
|
||||
}
|
||||
|
||||
// Split a document up into multiple documents
|
||||
export const rotateOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
@ -124,7 +123,6 @@ export const rotateOnly = {
|
||||
]
|
||||
}
|
||||
|
||||
// Split a document up into multiple documents
|
||||
export const imposeOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
@ -136,4 +134,32 @@ export const imposeOnly = {
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const removeBlankPagesOnly = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "removeBlankPages",
|
||||
values: { "whiteThreashold": 10 },
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const splitOnQR = {
|
||||
outputOptions: {
|
||||
zip: false
|
||||
},
|
||||
operations: [
|
||||
{
|
||||
type: "splitOn",
|
||||
values: {
|
||||
type: "QR_CODE"
|
||||
},
|
||||
operations: []
|
||||
}
|
||||
]
|
||||
}
|
15
server-node/routes/api/index.js
Normal file
15
server-node/routes/api/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
import express from 'express';
|
||||
import workflow from './workflow.js';
|
||||
import fileUpload from 'express-fileupload';
|
||||
|
||||
const router = express.Router();
|
||||
router.use(fileUpload());
|
||||
|
||||
router.get("/", function (req, res, next) {
|
||||
// TODO: Implement root api endpoint
|
||||
res.status(501).json({"Error": "Unfinished Endpoint. This sould probably send some api docs?"});
|
||||
});
|
||||
|
||||
router.use("/workflow", workflow);
|
||||
|
||||
export default router;
|
@ -5,8 +5,8 @@ import Archiver from 'archiver';
|
||||
import multer from 'multer'
|
||||
const upload = multer();
|
||||
|
||||
import * as Functions from "../../src/pdf-operations.js";
|
||||
import { traverseOperations } from "../../public/traverseOperations.js";
|
||||
import * as Functions from "../../functions.js";
|
||||
import { traverseOperations } from "../../../shared-operations/traverseOperations.js";
|
||||
|
||||
const activeWorkflows = {};
|
||||
|
||||
@ -83,7 +83,7 @@ router.post("/:workflowUuid?", [
|
||||
}
|
||||
});
|
||||
|
||||
const traverse = traverseOperations(workflow.operations, inputs);
|
||||
const traverse = traverseOperations(workflow.operations, inputs, Functions);
|
||||
|
||||
let pdfResults;
|
||||
let iteration;
|
||||
|
19
shared-operations/functions/createSubDocument.js
Normal file
19
shared-operations/functions/createSubDocument.js
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
|
||||
export async function createSubDocument(pdfDoc, pagesToExtractArray) {
|
||||
const subDocument = await PDFDocument.create();
|
||||
|
||||
// Check that array max number is not larger pdf pages number
|
||||
if(Math.max(...pagesToExtractArray) >= pdfDoc.getPageCount()) {
|
||||
throw new Error(`The PDF document only has ${pdfDoc.getPageCount()} pages and you tried to extract page ${Math.max(...pagesToExtractArray)}`);
|
||||
}
|
||||
|
||||
const copiedPages = await subDocument.copyPages(pdfDoc, pagesToExtractArray);
|
||||
|
||||
for (let i = 0; i < copiedPages.length; i++) {
|
||||
subDocument.addPage(copiedPages[i]);
|
||||
}
|
||||
|
||||
return subDocument.save();
|
||||
}
|
62
shared-operations/functions/detectEmptyPages.js
Normal file
62
shared-operations/functions/detectEmptyPages.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { getImagesOnPage } from "./getImagesOnPage.js";
|
||||
import PDFJS from 'pdfjs-dist';
|
||||
|
||||
export async function detectEmptyPages(snapshot, whiteThreashold, OpenCV) {
|
||||
const pdfDoc = await PDFJS.getDocument(snapshot).promise;
|
||||
|
||||
const emptyPages = [];
|
||||
for (let i = 1; i <= pdfDoc.numPages; i++) {
|
||||
const page = await pdfDoc.getPage(i);
|
||||
console.log("Checking page " + i);
|
||||
|
||||
if(!await hasText(page)) {
|
||||
console.log(`Found text on Page ${i}, page is not empty`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!await areImagesBlank(page, whiteThreashold)) {
|
||||
console.log(`Found non white image on Page ${i}, page is not empty`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Page ${i} is empty.`);
|
||||
emptyPages.push(i - 1);
|
||||
}
|
||||
return emptyPages;
|
||||
|
||||
async function hasText(page) {
|
||||
const textContent = await page.getTextContent();
|
||||
return textContent.items.length === 0;
|
||||
}
|
||||
|
||||
async function areImagesBlank(page, threshold) {
|
||||
const images = await getImagesOnPage(page);
|
||||
for (const image of images) {
|
||||
if(!isImageBlank(image, threshold))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isImageBlank(image, threshold) {
|
||||
const src = new OpenCV.cv.Mat(image.width, image.height, OpenCV.cv.CV_8UC4);
|
||||
src.data.set(image.data);
|
||||
// Convert the image to grayscale
|
||||
const gray = new OpenCV.cv.Mat();
|
||||
OpenCV.cv.cvtColor(src, gray, OpenCV.cv.COLOR_RGBA2GRAY);
|
||||
|
||||
// Calculate the mean value of the grayscale image
|
||||
const meanValue = OpenCV.cv.mean(gray);
|
||||
|
||||
// Free memory
|
||||
src.delete();
|
||||
gray.delete();
|
||||
|
||||
// Check if the mean value is below the threshold
|
||||
if (meanValue[0] <= threshold) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { createSubDocument } from './createSubDocument';
|
||||
|
||||
export async function extractPages(snapshot, pagesToExtractArray) {
|
||||
const pdfDoc = await PDFDocument.load(snapshot)
|
||||
@ -7,20 +8,3 @@ export async function extractPages(snapshot, pagesToExtractArray) {
|
||||
// TODO: invent a better format for pagesToExtractArray and convert it.
|
||||
return createSubDocument(pdfDoc, pagesToExtractArray);
|
||||
};
|
||||
|
||||
export async function createSubDocument(pdfDoc, pagesToExtractArray) {
|
||||
const subDocument = await PDFDocument.create();
|
||||
|
||||
// Check that array max number is not larger pdf pages number
|
||||
if(Math.max(...pagesToExtractArray) >= pdfDoc.getPageCount()) {
|
||||
throw new Error(`The PDF document only has ${pdfDoc.getPageCount()} pages and you tried to extract page ${Math.max(...pagesToExtractArray)}`);
|
||||
}
|
||||
|
||||
const copiedPages = await subDocument.copyPages(pdfDoc, pagesToExtractArray);
|
||||
|
||||
for (let i = 0; i < copiedPages.length; i++) {
|
||||
subDocument.addPage(copiedPages[i]);
|
||||
}
|
||||
|
||||
return subDocument.save();
|
||||
}
|
14
shared-operations/functions/getImagesOnPage.js
Normal file
14
shared-operations/functions/getImagesOnPage.js
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
import PDFJS from 'pdfjs-dist';
|
||||
|
||||
export async function getImagesOnPage(page) {
|
||||
const ops = await page.getOperatorList();
|
||||
const images = [];
|
||||
for (var j=0; j < ops.fnArray.length; j++) {
|
||||
if (ops.fnArray[j] == PDFJS.OPS.paintJpegXObject || ops.fnArray[j] == PDFJS.OPS.paintImageXObject) {
|
||||
const image = page.objs.get(ops.argsArray[j][0]);
|
||||
images.push(image);
|
||||
}
|
||||
}
|
||||
return images;
|
||||
}
|
12
shared-operations/functions/impose.js
Normal file
12
shared-operations/functions/impose.js
Normal file
@ -0,0 +1,12 @@
|
||||
export async function impose(snapshot, nup, format, pdfcpuWraopper) {
|
||||
return await pdfcpuWraopper.oneToOne([
|
||||
"pdfcpu.wasm",
|
||||
"nup",
|
||||
"-c",
|
||||
"disable",
|
||||
'f:' + format,
|
||||
"/output.pdf",
|
||||
String(nup),
|
||||
"input.pdf",
|
||||
], snapshot);
|
||||
}
|
18
shared-operations/functions/removeBlankPages.js
Normal file
18
shared-operations/functions/removeBlankPages.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { detectEmptyPages } from "./detectEmptyPages.js";
|
||||
|
||||
export async function removeBlankPages(snapshot, whiteThreashold, OpenCV) {
|
||||
|
||||
const emptyPages = await detectEmptyPages(snapshot, whiteThreashold, OpenCV);
|
||||
|
||||
console.log("Empty Pages: ", emptyPages);
|
||||
|
||||
const pdfDoc = await PDFDocument.load(snapshot);
|
||||
|
||||
// Reverse the array before looping in order to keep the indecies at the right pages. E.g. if you delete page 5 page 7 becomes page 6, if you delete page 7 page 5 remains page 5
|
||||
emptyPages.reverse().forEach(pageIndex => {
|
||||
pdfDoc.removePage(pageIndex);
|
||||
})
|
||||
|
||||
return pdfDoc.save();
|
||||
};
|
121
shared-operations/functions/splitOn.js
Normal file
121
shared-operations/functions/splitOn.js
Normal file
@ -0,0 +1,121 @@
|
||||
import { detectEmptyPages } from "./shared/detectEmptyPages.js";
|
||||
import { getImagesOnPage } from "./shared/getImagesOnPage.js";
|
||||
import { createSubDocument } from "./shared/createSubDocument.js";
|
||||
import PDFJS from 'pdfjs-dist';
|
||||
|
||||
/**
|
||||
* @typedef {"BAR_CODE"|"QR_CODE"|"BLANK_PAGE"} SplitType
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Uint16Array} snapshot
|
||||
* @param {SplitType} type
|
||||
* @param {} PDFJS
|
||||
* @param {import('opencv-wasm')} OpenCV
|
||||
* @param {} PDFLib
|
||||
* @returns
|
||||
*/
|
||||
export async function splitOn(snapshot, type, whiteThreashold, OpenCV, PDFLib, jsQR) {
|
||||
|
||||
let splitAtPages = [];
|
||||
|
||||
switch (type) {
|
||||
case "BAR_CODE":
|
||||
// TODO: Implement
|
||||
throw new Error("This split-type has not been implemented yet");
|
||||
break;
|
||||
|
||||
case "QR_CODE":
|
||||
splitAtPages = await getPagesWithQRCode(snapshot);
|
||||
break;
|
||||
|
||||
case "BLANK_PAGE":
|
||||
splitAtPages = await detectEmptyPages(snapshot, whiteThreashold, OpenCV);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("An invalid split-type was provided.")
|
||||
break;
|
||||
}
|
||||
|
||||
console.log("Split At Pages: ", splitAtPages);
|
||||
|
||||
// Remove detected Pages & Split
|
||||
const pdfDoc = await PDFLib.PDFDocument.load(snapshot);
|
||||
|
||||
const numberOfPages = pdfDoc.getPages().length;
|
||||
|
||||
let pagesArray = [];
|
||||
let splitAfter = splitAtPages.shift();
|
||||
const subDocuments = [];
|
||||
|
||||
for (let i = 0; i < numberOfPages; i++) {
|
||||
console.log(i);
|
||||
if(i == splitAfter) {
|
||||
if(pagesArray.length > 0) {
|
||||
subDocuments.push(await createSubDocument(pdfDoc, pagesArray, PDFLib));
|
||||
pagesArray = [];
|
||||
}
|
||||
splitAfter = splitAtPages.shift();
|
||||
}
|
||||
else { // Skip splitAtPage
|
||||
console.log("PagesArray")
|
||||
pagesArray.push(i);
|
||||
}
|
||||
}
|
||||
if(pagesArray.length > 0) {
|
||||
subDocuments.push(await createSubDocument(pdfDoc, pagesArray, PDFLib));
|
||||
}
|
||||
pagesArray = [];
|
||||
|
||||
return subDocuments;
|
||||
|
||||
async function getPagesWithQRCode(snapshot) {
|
||||
const pdfDoc = await PDFJS.getDocument(snapshot).promise;
|
||||
|
||||
const pagesWithQR = [];
|
||||
for (let i = 0; i < pdfDoc.numPages; i++) {
|
||||
console.log("Page:", i, "/", pdfDoc.numPages);
|
||||
const page = await pdfDoc.getPage(i + 1);
|
||||
|
||||
const images = await getImagesOnPage(page);
|
||||
console.log("images:", images);
|
||||
for (const image of images) {
|
||||
const data = await checkForQROnImage(image);
|
||||
if(data == "https://github.com/Frooodle/Stirling-PDF") {
|
||||
pagesWithQR.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pagesWithQR.length == 0) {
|
||||
console.warn("Could not find any QR Codes in the provided PDF.")
|
||||
}
|
||||
return pagesWithQR;
|
||||
}
|
||||
|
||||
async function checkForQROnImage(image) {
|
||||
// TODO: There is an issue with the jsQR package (The package expects rgba but sometimes we have rgb), and the package seems to be stale, we could create a fork and fix the issue. In the meanwhile we just force rgba:
|
||||
// Check for rgb and convert to rgba
|
||||
|
||||
if(image.data.length == image.width * image.height * 3) {
|
||||
const tmpArray = new Uint8ClampedArray(image.width * image.height * 4);
|
||||
|
||||
// Iterate through the original array and add an alpha channel
|
||||
for (let i = 0, j = 0; i < image.data.length; i += 3, j += 4) {
|
||||
tmpArray[j] = image.data[i]; // Red channel
|
||||
tmpArray[j + 1] = image.data[i + 1]; // Green channel
|
||||
tmpArray[j + 2] = image.data[i + 2]; // Blue channel
|
||||
tmpArray[j + 3] = 255; // Alpha channel (fully opaque)
|
||||
}
|
||||
|
||||
image.data = tmpArray;
|
||||
}
|
||||
|
||||
const code = jsQR(image.data, image.width, image.height);
|
||||
if(code)
|
||||
return code.data;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
|
||||
import { createSubDocument } from "./extractPages.js";
|
||||
import { createSubDocument } from "./shared/extractPages.js";
|
||||
|
||||
export async function splitPDF(snapshot, splitAfterPageArray) {
|
||||
const pdfDoc = await PDFDocument.load(snapshot)
|
||||
@ -18,7 +18,7 @@ export async function splitPDF(snapshot, splitAfterPageArray) {
|
||||
splitAfter = splitAfterPageArray.shift();
|
||||
pagesArray = [];
|
||||
}
|
||||
pagesArray.push(i);
|
||||
pagesArray.push(i);
|
||||
}
|
||||
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
|
||||
pagesArray = [];
|
||||
|
@ -7,5 +7,9 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pdfjs-dist": "^4.0.189"
|
||||
}
|
||||
}
|
||||
|
49
shared-operations/wasm/opencv/opencv_3_4_custom_O3.js
Normal file
49
shared-operations/wasm/opencv/opencv_3_4_custom_O3.js
Normal file
File diff suppressed because one or more lines are too long
104
shared-operations/wasm/pdfcpu/pdfcpu-wrapper-browser.js
Normal file
104
shared-operations/wasm/pdfcpu/pdfcpu-wrapper-browser.js
Normal file
@ -0,0 +1,104 @@
|
||||
// imports browserfs via index.html script-tag
|
||||
|
||||
let wasmLocation = "/wasm/pdfcpu/";
|
||||
|
||||
let fs;
|
||||
let Buffer;
|
||||
|
||||
// TODO: This can later be defered to load asynchronously
|
||||
configureFs();
|
||||
loadWasm();
|
||||
|
||||
function configureFs() {
|
||||
BrowserFS.configure(
|
||||
{
|
||||
fs: "InMemory",
|
||||
},
|
||||
function (e) {
|
||||
if (e) {
|
||||
// An error happened!
|
||||
throw e;
|
||||
}
|
||||
fs = BrowserFS.BFSRequire("fs");
|
||||
Buffer = BrowserFS.BFSRequire("buffer").Buffer;
|
||||
|
||||
window.fs = fs;
|
||||
window.Buffer = Buffer;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function loadWasm() {
|
||||
const script = document.createElement("script");
|
||||
script.src = wasmLocation + "/wasm_exec.js";
|
||||
script.async = true;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
const runWasm = async (param) => {
|
||||
if (window.cachedWasmResponse === undefined) {
|
||||
const response = await fetch(wasmLocation + "/pdfcpu.wasm");
|
||||
const buffer = await response.arrayBuffer();
|
||||
window.cachedWasmResponse = buffer;
|
||||
window.go = new Go();
|
||||
}
|
||||
const { instance } = await WebAssembly.instantiate(
|
||||
window.cachedWasmResponse,
|
||||
window.go.importObject
|
||||
);
|
||||
window.go.argv = param;
|
||||
await window.go.run(instance);
|
||||
return window.go.exitCode;
|
||||
};
|
||||
|
||||
async function loadFileAsync(data) {
|
||||
console.log(`Writing file to MemoryFS`);
|
||||
await fs.writeFile(`/input.pdf`, data);
|
||||
console.log(`Write done. Validating...`);
|
||||
let exitcode = await runWasm([
|
||||
"pdfcpu.wasm",
|
||||
"validate",
|
||||
"-c",
|
||||
"disable",
|
||||
`/input.pdf`,
|
||||
]);
|
||||
|
||||
if (exitcode !== 0)
|
||||
throw new Error("There was an error validating your PDFs");
|
||||
|
||||
console.log(`File is Valid`);
|
||||
}
|
||||
|
||||
export async function impose(snapshot, nup, format) {
|
||||
|
||||
};
|
||||
|
||||
export async function oneToOne(wasmArray, snapshot) {
|
||||
await loadFileAsync(Buffer.from(snapshot));
|
||||
|
||||
console.log("Nuping File");
|
||||
let exitcode = await runWasm(wasmArray);
|
||||
|
||||
if (exitcode !== 0) {
|
||||
console.error("There was an error nuping your PDFs");
|
||||
return;
|
||||
}
|
||||
|
||||
await fs.unlink("input.pdf");
|
||||
const contents = fs.readFileSync("output.pdf");
|
||||
fs.unlink("output.pdf");
|
||||
console.log("Your File ist Ready!");
|
||||
return new Uint8Array(contents);
|
||||
}
|
||||
|
||||
export async function manyToOne() {
|
||||
//TODO: Do this of neccesary for some operations
|
||||
}
|
||||
|
||||
export async function oneToMany() {
|
||||
//TODO: Do this of neccesary for some operations
|
||||
}
|
||||
|
||||
export async function manyToMany() {
|
||||
//TODO: Do this of neccesary for some operations
|
||||
}
|
145
shared-operations/wasm/pdfcpu/pdfcpu-wrapper-node.js
Normal file
145
shared-operations/wasm/pdfcpu/pdfcpu-wrapper-node.js
Normal file
@ -0,0 +1,145 @@
|
||||
import { WasmFs } from '@wasmer/wasmfs';
|
||||
import path from "path";
|
||||
|
||||
let webWasmLocation = "/wasm/";
|
||||
let nodeWasmLocation = "./public/wasm/";
|
||||
|
||||
let fs;
|
||||
const wasmfs = new WasmFs();
|
||||
|
||||
// TODO: This can later be defered to load asynchronously
|
||||
(async () => {
|
||||
await loadWasm();
|
||||
await configureFs();
|
||||
})();
|
||||
|
||||
async function configureFs() {
|
||||
// Can't use BrowserFS: https://github.com/jvilk/BrowserFS/issues/271
|
||||
fs = wasmfs.fs;
|
||||
global.fs = fs;
|
||||
|
||||
console.log("InMemoryFs configured");
|
||||
}
|
||||
|
||||
async function loadWasm() {
|
||||
global.crypto = (await import("crypto")).webcrypto; // wasm dependecy
|
||||
await import("./wasm_exec.js");
|
||||
}
|
||||
|
||||
const runWasm = async (param) => {
|
||||
if (global.cachedWasmResponse === undefined) {
|
||||
const buffer = (await import("fs")).readFileSync(nodeWasmLocation + "/pdfcpu.wasm");
|
||||
global.cachedWasmResponse = buffer;
|
||||
global.go = new Go();
|
||||
}
|
||||
const { instance } = await WebAssembly.instantiate(
|
||||
global.cachedWasmResponse,
|
||||
global.go.importObject
|
||||
);
|
||||
global.go.argv = param;
|
||||
await global.go.run(instance);
|
||||
return global.go.exitCode;
|
||||
};
|
||||
|
||||
async function loadFileAsync(data) {
|
||||
console.log(`Writing file to Disk`);
|
||||
fs.writeFileSync(`input.pdf`, data);
|
||||
console.log(`Write done. Validating...`);
|
||||
let exitcode = await runWasm([
|
||||
"pdfcpu.wasm",
|
||||
"validate",
|
||||
"-c",
|
||||
"disable",
|
||||
`input.pdf`,
|
||||
]);
|
||||
if (exitcode !== 0)
|
||||
throw new Error("There was an error validating your PDFs");
|
||||
|
||||
// // Get logs of command
|
||||
// wasmfs.getStdOut().then(response => {
|
||||
// console.log(response);
|
||||
// });
|
||||
|
||||
console.log(`File is Valid`);
|
||||
}
|
||||
|
||||
export async function oneToOne(wasmArray, snapshot) {
|
||||
await loadFileAsync(Buffer.from(snapshot));
|
||||
|
||||
console.log("Nuping File");
|
||||
|
||||
let exitcode = await runWasm(wasmArray);
|
||||
if (exitcode !== 0) {
|
||||
console.error("There was an error nuping your PDFs");
|
||||
return;
|
||||
}
|
||||
console.log("Nuping Done");
|
||||
|
||||
await checkExistsWithTimeout("/output.pdf", 1000);
|
||||
console.log("Write started...");
|
||||
|
||||
|
||||
// TODO: Make this more elegant, this waits for the write to finish.
|
||||
// Maybe replace wasmfs with https://github.com/streamich/memfs
|
||||
let fileSize;
|
||||
while (true) {
|
||||
fileSize = fs.statSync("/output.pdf").size;
|
||||
await new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 50);
|
||||
});
|
||||
if(fileSize > 0 && fileSize == fs.statSync("/output.pdf").size) // Wait for file Size not changing anymore.
|
||||
break;
|
||||
}
|
||||
|
||||
console.log("Could be done?");
|
||||
|
||||
fs.unlinkSync("input.pdf");
|
||||
|
||||
const data = fs.readFileSync("/output.pdf");
|
||||
if(data.length == 0) {
|
||||
throw Error("File Size 0 that should not happen. The write probably didn't finish in time.");
|
||||
}
|
||||
fs.unlinkSync("output.pdf");
|
||||
console.log("Your File ist Ready!");
|
||||
return new Uint8Array(data);
|
||||
}
|
||||
|
||||
export async function manyToOne() {
|
||||
//TODO: Do this if necessary for some pdfcpu operations
|
||||
}
|
||||
|
||||
export async function oneToMany() {
|
||||
//TODO: Do this if necessary for some pdfcpu operations
|
||||
}
|
||||
|
||||
export async function manyToMany() {
|
||||
//TODO: Do this if necessary for some pdfcpu operations
|
||||
}
|
||||
|
||||
// THX: https://stackoverflow.com/questions/26165725/nodejs-check-file-exists-if-not-wait-till-it-exist
|
||||
function checkExistsWithTimeout(filePath, timeout) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
var timer = setTimeout(function () {
|
||||
watcher.close();
|
||||
reject(new Error('File did not exists and was not created during the timeout.'));
|
||||
}, timeout);
|
||||
|
||||
fs.access(filePath, fs.constants.R_OK, function (err) {
|
||||
if (!err) {
|
||||
clearTimeout(timer);
|
||||
watcher.close();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
var dir = path.dirname(filePath);
|
||||
var watcher = fs.watch(dir, function (eventType, filename) {
|
||||
clearTimeout(timer);
|
||||
watcher.close();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
BIN
shared-operations/wasm/pdfcpu/pdfcpu.wasm
Normal file
BIN
shared-operations/wasm/pdfcpu/pdfcpu.wasm
Normal file
Binary file not shown.
872
shared-operations/wasm/pdfcpu/wasm_exec.js
Normal file
872
shared-operations/wasm/pdfcpu/wasm_exec.js
Normal file
@ -0,0 +1,872 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
(() => {
|
||||
// Map multiple JavaScript environments to a single common API,
|
||||
// preferring web standards over Node.js API.
|
||||
//
|
||||
// Environments considered:
|
||||
// - Browsers
|
||||
// - Node.js
|
||||
// - Electron
|
||||
// - Parcel
|
||||
// - Webpack
|
||||
|
||||
console.log("imported")
|
||||
if (typeof global !== "undefined") {
|
||||
// global already exists
|
||||
} else if (typeof window !== "undefined") {
|
||||
window.global = window;
|
||||
} else if (typeof self !== "undefined") {
|
||||
self.global = self;
|
||||
} else {
|
||||
throw new Error("cannot export Go (neither global, window nor self is defined)");
|
||||
}
|
||||
|
||||
let logFS = false
|
||||
var handler = {
|
||||
get: function (target, property) {
|
||||
if (property in target && target[property] instanceof Function) {
|
||||
return function () {
|
||||
if (logFS) {
|
||||
console.log(property, 'called', arguments);
|
||||
}
|
||||
// 将callback替换
|
||||
if (arguments[arguments.length - 1] instanceof Function) {
|
||||
var origCB = arguments[arguments.length - 1];
|
||||
var newCB = function () {
|
||||
if (logFS) {
|
||||
console.log('callback for', property, 'get called with args:', arguments);
|
||||
}
|
||||
return Reflect.apply(origCB, arguments.callee, arguments);
|
||||
}
|
||||
arguments[arguments.length - 1] = newCB;
|
||||
}
|
||||
return Reflect.apply(target[property], target, arguments);
|
||||
}
|
||||
} else {
|
||||
return target[property]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!global.require && typeof require !== "undefined") {
|
||||
global.require = require;
|
||||
}
|
||||
|
||||
|
||||
if (!global.fs && global.require) {
|
||||
|
||||
//const fs = require("fs");
|
||||
if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
|
||||
global.fs = fs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const enosys = () => {
|
||||
const err = new Error("not implemented");
|
||||
err.code = "ENOSYS";
|
||||
return err;
|
||||
};
|
||||
|
||||
if (!global.fs) {
|
||||
let outputBuf = "";
|
||||
global.fs = {
|
||||
constants: {
|
||||
O_WRONLY: -1,
|
||||
O_RDWR: -1,
|
||||
O_CREAT: -1,
|
||||
O_TRUNC: -1,
|
||||
O_APPEND: -1,
|
||||
O_EXCL: -1
|
||||
}, // unused
|
||||
writeSync(fd, buf) {
|
||||
outputBuf += decoder.decode(buf);
|
||||
const nl = outputBuf.lastIndexOf("\n");
|
||||
if (nl != -1) {
|
||||
console.log(outputBuf.substr(0, nl));
|
||||
outputBuf = outputBuf.substr(nl + 1);
|
||||
}
|
||||
return buf.length;
|
||||
},
|
||||
write(fd, buf, offset, length, position, callback) {
|
||||
if (offset !== 0 || length !== buf.length || position !== null) {
|
||||
callback(enosys());
|
||||
return;
|
||||
}
|
||||
const n = this.writeSync(fd, buf);
|
||||
callback(null, n);
|
||||
},
|
||||
chmod(path, mode, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
chown(path, uid, gid, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
close(fd, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
fchmod(fd, mode, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
fchown(fd, uid, gid, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
fstat(fd, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
fsync(fd, callback) {
|
||||
callback(null);
|
||||
},
|
||||
ftruncate(fd, length, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
lchown(path, uid, gid, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
link(path, link, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
lstat(path, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
mkdir(path, perm, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
open(path, flags, mode, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
read(fd, buffer, offset, length, position, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
readdir(path, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
readlink(path, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
rename(from, to, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
rmdir(path, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
stat(path, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
symlink(path, link, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
truncate(path, length, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
unlink(path, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
utimes(path, atime, mtime, callback) {
|
||||
callback(enosys());
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.process) {
|
||||
global.process = {
|
||||
getuid() {
|
||||
return -1;
|
||||
},
|
||||
getgid() {
|
||||
return -1;
|
||||
},
|
||||
geteuid() {
|
||||
return -1;
|
||||
},
|
||||
getegid() {
|
||||
return -1;
|
||||
},
|
||||
getgroups() {
|
||||
throw enosys();
|
||||
},
|
||||
pid: -1,
|
||||
ppid: -1,
|
||||
umask() {
|
||||
throw enosys();
|
||||
},
|
||||
cwd() {
|
||||
throw enosys();
|
||||
},
|
||||
chdir() {
|
||||
throw enosys();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (!global.crypto && global.require) {
|
||||
const nodeCrypto = require("crypto");
|
||||
global.crypto = {
|
||||
getRandomValues(b) {
|
||||
nodeCrypto.randomFillSync(b);
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!global.crypto) {
|
||||
throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
|
||||
}
|
||||
|
||||
if (!global.performance) {
|
||||
global.performance = {
|
||||
now() {
|
||||
const [sec, nsec] = process.hrtime();
|
||||
return sec * 1000 + nsec / 1000000;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.TextEncoder && global.require) {
|
||||
global.TextEncoder = require("util").TextEncoder;
|
||||
}
|
||||
if (!global.TextEncoder) {
|
||||
throw new Error("global.TextEncoder is not available, polyfill required");
|
||||
}
|
||||
|
||||
if (!global.TextDecoder && global.require) {
|
||||
global.TextDecoder = require("util").TextDecoder;
|
||||
}
|
||||
if (!global.TextDecoder) {
|
||||
throw new Error("global.TextDecoder is not available, polyfill required");
|
||||
}
|
||||
|
||||
|
||||
const isNodeJS = global.process && global.process.title === "node";
|
||||
|
||||
if (!isNodeJS) {
|
||||
// console.log("ini browser fs")
|
||||
// var myfs = global.BrowserFS.BFSRequire('fs');
|
||||
// global.Buffer = global.BrowserFS.BFSRequire('buffer').Buffer;
|
||||
// global.fs = myfs;
|
||||
|
||||
global.fs.constants = {
|
||||
O_RDONLY: 0,
|
||||
O_WRONLY: 1,
|
||||
O_RDWR: 2,
|
||||
O_CREAT: 64,
|
||||
O_CREATE: 64,
|
||||
O_EXCL: 128,
|
||||
O_NOCTTY: 256,
|
||||
O_TRUNC: 512,
|
||||
O_APPEND: 1024,
|
||||
O_DIRECTORY: 65536,
|
||||
O_NOATIME: 262144,
|
||||
O_NOFOLLOW: 131072,
|
||||
O_SYNC: 1052672,
|
||||
O_DIRECT: 16384,
|
||||
O_NONBLOCK: 2048,
|
||||
};
|
||||
|
||||
let outputBuf = "";
|
||||
|
||||
global.fs.writeSyncOriginal = global.fs.writeSync
|
||||
global.fs.writeSync = function (fd, buf) {
|
||||
if (fd === 1 || fd === 2) {
|
||||
outputBuf += decoder.decode(buf);
|
||||
const nl = outputBuf.lastIndexOf("\n");
|
||||
if (nl != -1) {
|
||||
console.log(outputBuf.substr(0, nl));
|
||||
outputBuf = outputBuf.substr(nl + 1);
|
||||
}
|
||||
return buf.length;
|
||||
} else {
|
||||
return global.fs.writeSyncOriginal(...arguments);
|
||||
}
|
||||
};
|
||||
|
||||
global.fs.writeOriginal = global.fs.write
|
||||
global.fs.write = function (fd, buf, offset, length, position, callback) {
|
||||
// (corresponding to STDOUT/STDERR)
|
||||
if (fd === 1 || fd === 2) {
|
||||
if (offset !== 0 || length !== buf.length || position !== null) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
const n = this.writeSync(fd, buf);
|
||||
callback(null, n, buf);
|
||||
} else {
|
||||
// buf: read buf first
|
||||
arguments[1] = global.Buffer.from(arguments[1]);
|
||||
return global.fs.writeOriginal(...arguments);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
global.fs.openOriginal = global.fs.open
|
||||
global.fs.open = function (path, flags, mode, callback) {
|
||||
var myflags = 'r';
|
||||
var O = global.fs.constants;
|
||||
|
||||
// Convert numeric flags to string flags
|
||||
// FIXME: maybe wrong...
|
||||
console.log("open dir?", path, 'flag', flags, myflags)
|
||||
if (flags & O.O_WRONLY) { // 'w'
|
||||
myflags = 'w';
|
||||
if (flags & O.O_EXCL) {
|
||||
myflags = 'wx';
|
||||
}
|
||||
} else if (flags & O.O_RDWR) { // 'r+' or 'w+'
|
||||
if (flags & O.O_CREAT && flags & O.O_TRUNC) { // w+
|
||||
if (flags & O.O_EXCL) {
|
||||
myflags = 'wx+';
|
||||
} else {
|
||||
myflags = 'w+';
|
||||
}
|
||||
} else { // r+
|
||||
myflags = 'r+';
|
||||
}
|
||||
} else if (flags & O.O_APPEND) { // 'a'
|
||||
console.log("append error")
|
||||
throw new Error("Not implmented");
|
||||
} else {
|
||||
// 打开文件
|
||||
myflags = 'r+';
|
||||
console.log("open dir?", path, 'flag', flags, myflags)
|
||||
}
|
||||
|
||||
|
||||
return global.fs.openOriginal(path, myflags, mode, callback);
|
||||
};
|
||||
|
||||
global.fs.fstatOriginal = global.fs.fstat;
|
||||
global.fs.fstat = function (fd, callback) {
|
||||
return global.fs.fstatOriginal(fd, function () {
|
||||
var retStat = arguments[1];
|
||||
delete retStat['fileData'];
|
||||
retStat.atimeMs = retStat.atime.getTime();
|
||||
retStat.mtimeMs = retStat.mtime.getTime();
|
||||
retStat.ctimeMs = retStat.ctime.getTime();
|
||||
retStat.birthtimeMs = retStat.birthtime.getTime();
|
||||
return callback(arguments[0], retStat);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
global.fs.closeOriginal = global.fs.close;
|
||||
global.fs.close = function (fd, callback) {
|
||||
return global.fs.closeOriginal(fd, function () {
|
||||
if (typeof arguments[0] === 'undefined') arguments[0] = null;
|
||||
return callback(...arguments);
|
||||
});
|
||||
}
|
||||
|
||||
// global.fs.renameOriginal = global.fs.rename
|
||||
// global.fs.rename = function (from, to, callback) {
|
||||
// console.log("rename a0", arguments[0])
|
||||
// global.fs.renameOriginal(from, to);
|
||||
// callback(arguments[0])
|
||||
// }
|
||||
|
||||
// global.fs.renameSyncOriginal = global.fs.renameSync
|
||||
// global.fs.renameSync = function(fd, options) {
|
||||
// console.log("Sync")
|
||||
// }
|
||||
|
||||
|
||||
global.fs = new Proxy(global.fs, handler);
|
||||
}
|
||||
|
||||
// End of polyfills for common API.
|
||||
|
||||
const encoder = new TextEncoder("utf-8");
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
|
||||
global.Go = class {
|
||||
constructor() {
|
||||
this.argv = ["js"];
|
||||
this.env = {};
|
||||
this.exit = (code) => {
|
||||
this.exitCode = code;
|
||||
if (code !== 0) {
|
||||
console.warn("exit code:", code);
|
||||
}
|
||||
};
|
||||
this._exitPromise = new Promise((resolve) => {
|
||||
this._resolveExitPromise = resolve;
|
||||
});
|
||||
this._pendingEvent = null;
|
||||
this._scheduledTimeouts = new Map();
|
||||
this._nextCallbackTimeoutID = 1;
|
||||
|
||||
const setInt64 = (addr, v) => {
|
||||
this.mem.setUint32(addr + 0, v, true);
|
||||
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
|
||||
}
|
||||
|
||||
const getInt64 = (addr) => {
|
||||
const low = this.mem.getUint32(addr + 0, true);
|
||||
const high = this.mem.getInt32(addr + 4, true);
|
||||
return low + high * 4294967296;
|
||||
}
|
||||
|
||||
const loadValue = (addr) => {
|
||||
const f = this.mem.getFloat64(addr, true);
|
||||
if (f === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (!isNaN(f)) {
|
||||
return f;
|
||||
}
|
||||
|
||||
const id = this.mem.getUint32(addr, true);
|
||||
return this._values[id];
|
||||
}
|
||||
|
||||
const storeValue = (addr, v) => {
|
||||
const nanHead = 0x7FF80000;
|
||||
|
||||
if (typeof v === "number" && v !== 0) {
|
||||
if (isNaN(v)) {
|
||||
this.mem.setUint32(addr + 4, nanHead, true);
|
||||
this.mem.setUint32(addr, 0, true);
|
||||
return;
|
||||
}
|
||||
this.mem.setFloat64(addr, v, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (v === undefined) {
|
||||
this.mem.setFloat64(addr, 0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
let id = this._ids.get(v);
|
||||
if (id === undefined) {
|
||||
id = this._idPool.pop();
|
||||
if (id === undefined) {
|
||||
id = this._values.length;
|
||||
}
|
||||
this._values[id] = v;
|
||||
this._goRefCounts[id] = 0;
|
||||
this._ids.set(v, id);
|
||||
}
|
||||
this._goRefCounts[id]++;
|
||||
let typeFlag = 0;
|
||||
switch (typeof v) {
|
||||
case "object":
|
||||
if (v !== null) {
|
||||
typeFlag = 1;
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
typeFlag = 2;
|
||||
break;
|
||||
case "symbol":
|
||||
typeFlag = 3;
|
||||
break;
|
||||
case "function":
|
||||
typeFlag = 4;
|
||||
break;
|
||||
}
|
||||
this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
|
||||
this.mem.setUint32(addr, id, true);
|
||||
}
|
||||
|
||||
const loadSlice = (addr) => {
|
||||
const array = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
|
||||
}
|
||||
|
||||
const loadSliceOfValues = (addr) => {
|
||||
const array = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
const a = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
a[i] = loadValue(array + i * 8);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
const loadString = (addr) => {
|
||||
const saddr = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
|
||||
}
|
||||
|
||||
const timeOrigin = Date.now() - performance.now();
|
||||
this.importObject = {
|
||||
go: {
|
||||
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
|
||||
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
|
||||
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
|
||||
// This changes the SP, thus we have to update the SP used by the imported function.
|
||||
|
||||
// func wasmExit(code int32)
|
||||
"runtime.wasmExit": (sp) => {
|
||||
sp >>>= 0;
|
||||
const code = this.mem.getInt32(sp + 8, true);
|
||||
this.exited = true;
|
||||
delete this._inst;
|
||||
delete this._values;
|
||||
delete this._goRefCounts;
|
||||
delete this._ids;
|
||||
delete this._idPool;
|
||||
this.exit(code);
|
||||
},
|
||||
|
||||
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
|
||||
"runtime.wasmWrite": (sp) => {
|
||||
sp >>>= 0;
|
||||
const fd = getInt64(sp + 8);
|
||||
const p = getInt64(sp + 16);
|
||||
const n = this.mem.getInt32(sp + 24, true);
|
||||
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
|
||||
},
|
||||
|
||||
// func resetMemoryDataView()
|
||||
"runtime.resetMemoryDataView": (sp) => {
|
||||
sp >>>= 0;
|
||||
this.mem = new DataView(this._inst.exports.mem.buffer);
|
||||
},
|
||||
|
||||
// func nanotime1() int64
|
||||
"runtime.nanotime1": (sp) => {
|
||||
sp >>>= 0;
|
||||
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
|
||||
},
|
||||
|
||||
// func walltime1() (sec int64, nsec int32)
|
||||
"runtime.walltime1": (sp) => {
|
||||
sp >>>= 0;
|
||||
const msec = (new Date).getTime();
|
||||
setInt64(sp + 8, msec / 1000);
|
||||
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
|
||||
},
|
||||
|
||||
// func scheduleTimeoutEvent(delay int64) int32
|
||||
"runtime.scheduleTimeoutEvent": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this._nextCallbackTimeoutID;
|
||||
this._nextCallbackTimeoutID++;
|
||||
this._scheduledTimeouts.set(id, setTimeout(
|
||||
() => {
|
||||
this._resume();
|
||||
while (this._scheduledTimeouts.has(id)) {
|
||||
// for some reason Go failed to register the timeout event, log and try again
|
||||
// (temporary workaround for https://github.com/golang/go/issues/28975)
|
||||
console.warn("scheduleTimeoutEvent: missed timeout event");
|
||||
this._resume();
|
||||
}
|
||||
},
|
||||
getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
|
||||
));
|
||||
this.mem.setInt32(sp + 16, id, true);
|
||||
},
|
||||
|
||||
// func clearTimeoutEvent(id int32)
|
||||
"runtime.clearTimeoutEvent": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this.mem.getInt32(sp + 8, true);
|
||||
clearTimeout(this._scheduledTimeouts.get(id));
|
||||
this._scheduledTimeouts.delete(id);
|
||||
},
|
||||
|
||||
// func getRandomData(r []byte)
|
||||
"runtime.getRandomData": (sp) => {
|
||||
sp >>>= 0;
|
||||
crypto.getRandomValues(loadSlice(sp + 8));
|
||||
},
|
||||
|
||||
// func finalizeRef(v ref)
|
||||
"syscall/js.finalizeRef": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this.mem.getUint32(sp + 8, true);
|
||||
this._goRefCounts[id]--;
|
||||
if (this._goRefCounts[id] === 0) {
|
||||
const v = this._values[id];
|
||||
this._values[id] = null;
|
||||
this._ids.delete(v);
|
||||
this._idPool.push(id);
|
||||
}
|
||||
},
|
||||
|
||||
// func stringVal(value string) ref
|
||||
"syscall/js.stringVal": (sp) => {
|
||||
sp >>>= 0;
|
||||
storeValue(sp + 24, loadString(sp + 8));
|
||||
},
|
||||
|
||||
// func valueGet(v ref, p string) ref
|
||||
"syscall/js.valueGet": (sp) => {
|
||||
sp >>>= 0;
|
||||
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 32, result);
|
||||
},
|
||||
|
||||
// func valueSet(v ref, p string, x ref)
|
||||
"syscall/js.valueSet": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
|
||||
},
|
||||
|
||||
// func valueDelete(v ref, p string)
|
||||
"syscall/js.valueDelete": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
|
||||
},
|
||||
|
||||
// func valueIndex(v ref, i int) ref
|
||||
"syscall/js.valueIndex": (sp) => {
|
||||
sp >>>= 0;
|
||||
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
||||
},
|
||||
|
||||
// valueSetIndex(v ref, i int, x ref)
|
||||
"syscall/js.valueSetIndex": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
|
||||
},
|
||||
|
||||
// func valueCall(v ref, m string, args []ref) (ref, bool)
|
||||
"syscall/js.valueCall": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const m = Reflect.get(v, loadString(sp + 16));
|
||||
const args = loadSliceOfValues(sp + 32);
|
||||
const result = Reflect.apply(m, v, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 56, result);
|
||||
this.mem.setUint8(sp + 64, 1);
|
||||
} catch (err) {
|
||||
storeValue(sp + 56, err);
|
||||
this.mem.setUint8(sp + 64, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueInvoke(v ref, args []ref) (ref, bool)
|
||||
"syscall/js.valueInvoke": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const args = loadSliceOfValues(sp + 16);
|
||||
const result = Reflect.apply(v, undefined, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, result);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
} catch (err) {
|
||||
storeValue(sp + 40, err);
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueNew(v ref, args []ref) (ref, bool)
|
||||
"syscall/js.valueNew": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const args = loadSliceOfValues(sp + 16);
|
||||
const result = Reflect.construct(v, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, result);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
} catch (err) {
|
||||
storeValue(sp + 40, err);
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueLength(v ref) int
|
||||
"syscall/js.valueLength": (sp) => {
|
||||
sp >>>= 0;
|
||||
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
||||
},
|
||||
|
||||
// valuePrepareString(v ref) (ref, int)
|
||||
"syscall/js.valuePrepareString": (sp) => {
|
||||
sp >>>= 0;
|
||||
const str = encoder.encode(String(loadValue(sp + 8)));
|
||||
storeValue(sp + 16, str);
|
||||
setInt64(sp + 24, str.length);
|
||||
},
|
||||
|
||||
// valueLoadString(v ref, b []byte)
|
||||
"syscall/js.valueLoadString": (sp) => {
|
||||
sp >>>= 0;
|
||||
const str = loadValue(sp + 8);
|
||||
loadSlice(sp + 16).set(str);
|
||||
},
|
||||
|
||||
// func valueInstanceOf(v ref, t ref) bool
|
||||
"syscall/js.valueInstanceOf": (sp) => {
|
||||
sp >>>= 0;
|
||||
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
|
||||
},
|
||||
|
||||
// func copyBytesToGo(dst []byte, src ref) (int, bool)
|
||||
"syscall/js.copyBytesToGo": (sp) => {
|
||||
sp >>>= 0;
|
||||
const dst = loadSlice(sp + 8);
|
||||
const src = loadValue(sp + 32);
|
||||
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
return;
|
||||
}
|
||||
const toCopy = src.subarray(0, dst.length);
|
||||
dst.set(toCopy);
|
||||
setInt64(sp + 40, toCopy.length);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
},
|
||||
|
||||
// func copyBytesToJS(dst ref, src []byte) (int, bool)
|
||||
"syscall/js.copyBytesToJS": (sp) => {
|
||||
sp >>>= 0;
|
||||
const dst = loadValue(sp + 8);
|
||||
const src = loadSlice(sp + 16);
|
||||
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
return;
|
||||
}
|
||||
const toCopy = src.subarray(0, dst.length);
|
||||
dst.set(toCopy);
|
||||
setInt64(sp + 40, toCopy.length);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
},
|
||||
|
||||
"debug": (value) => {
|
||||
console.log(value);
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async run(instance) {
|
||||
if (!(instance instanceof WebAssembly.Instance)) {
|
||||
throw new Error("Go.run: WebAssembly.Instance expected");
|
||||
}
|
||||
this._inst = instance;
|
||||
this.mem = new DataView(this._inst.exports.mem.buffer);
|
||||
this._values = [ // JS values that Go currently has references to, indexed by reference id
|
||||
NaN,
|
||||
0,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
global,
|
||||
this,
|
||||
];
|
||||
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
|
||||
this._ids = new Map([ // mapping from JS values to reference ids
|
||||
[0, 1],
|
||||
[null, 2],
|
||||
[true, 3],
|
||||
[false, 4],
|
||||
[global, 5],
|
||||
[this, 6],
|
||||
]);
|
||||
this._idPool = []; // unused ids that have been garbage collected
|
||||
this.exited = false; // whether the Go program has exited
|
||||
|
||||
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
|
||||
let offset = 4096;
|
||||
|
||||
const strPtr = (str) => {
|
||||
const ptr = offset;
|
||||
const bytes = encoder.encode(str + "\0");
|
||||
new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
|
||||
offset += bytes.length;
|
||||
if (offset % 8 !== 0) {
|
||||
offset += 8 - (offset % 8);
|
||||
}
|
||||
return ptr;
|
||||
};
|
||||
|
||||
const argc = this.argv.length;
|
||||
|
||||
const argvPtrs = [];
|
||||
this.argv.forEach((arg) => {
|
||||
argvPtrs.push(strPtr(arg));
|
||||
});
|
||||
argvPtrs.push(0);
|
||||
|
||||
const keys = Object.keys(this.env).sort();
|
||||
keys.forEach((key) => {
|
||||
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
|
||||
});
|
||||
argvPtrs.push(0);
|
||||
|
||||
const argv = offset;
|
||||
argvPtrs.forEach((ptr) => {
|
||||
this.mem.setUint32(offset, ptr, true);
|
||||
this.mem.setUint32(offset + 4, 0, true);
|
||||
offset += 8;
|
||||
});
|
||||
|
||||
this._inst.exports.run(argc, argv);
|
||||
if (this.exited) {
|
||||
this._resolveExitPromise();
|
||||
}
|
||||
await this._exitPromise;
|
||||
}
|
||||
|
||||
_resume() {
|
||||
if (this.exited) {
|
||||
throw new Error("Go program has already exited");
|
||||
}
|
||||
this._inst.exports.resume();
|
||||
if (this.exited) {
|
||||
this._resolveExitPromise();
|
||||
}
|
||||
}
|
||||
|
||||
_makeFuncWrapper(id) {
|
||||
const go = this;
|
||||
return function () {
|
||||
const event = {
|
||||
id: id,
|
||||
this: this,
|
||||
args: arguments
|
||||
};
|
||||
go._pendingEvent = event;
|
||||
go._resume();
|
||||
return event.result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
typeof module !== "undefined" &&
|
||||
global.require &&
|
||||
global.require.main === module &&
|
||||
global.process &&
|
||||
global.process.versions &&
|
||||
!global.process.versions.electron
|
||||
) {
|
||||
if (process.argv.length < 3) {
|
||||
console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const go = new Go();
|
||||
go.argv = process.argv.slice(2);
|
||||
go.env = Object.assign({
|
||||
TMPDIR: require("os").tmpdir()
|
||||
}, process.env);
|
||||
go.exit = process.exit;
|
||||
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
|
||||
process.on("exit", (code) => { // Node.js exits if no event handler is pending
|
||||
if (code === 0 && !go.exited) {
|
||||
// deadlock, make Go print error and stack traces
|
||||
go._pendingEvent = {
|
||||
id: 0
|
||||
};
|
||||
go._resume();
|
||||
}
|
||||
});
|
||||
return go.run(result.instance);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
})();
|
@ -1,19 +1,31 @@
|
||||
import { organizeWaitOperations } from "./organizeWaitOperations.js";
|
||||
|
||||
/**
|
||||
* @typedef PDF
|
||||
* @property {string} originalFileName
|
||||
* @property {string} fileName
|
||||
* @property {Uint8Array} buffer
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} operations
|
||||
* @param {*} input
|
||||
* @param {import('../src/pdf-operations.js')} Functions
|
||||
* @returns
|
||||
* @param {JSON} operations
|
||||
* @param {PDF|PDF[]} input
|
||||
* @param {import('./functions.js')} Functions
|
||||
* @returns {}
|
||||
*/
|
||||
export async function * traverseOperations(operations, input, Functions) {
|
||||
const waitOperations = organizeWaitOperations(operations);
|
||||
let results = [];
|
||||
/** @type {PDF[]} */ let results = [];
|
||||
yield* nextOperation(operations, input);
|
||||
console.log("Done2");
|
||||
return results;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {JSON} operations
|
||||
* @param {PDF|PDF[]} input
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * nextOperation(operations, input) {
|
||||
if(Array.isArray(operations) && operations.length == 0) { // isEmpty
|
||||
if(Array.isArray(input)) {
|
||||
@ -33,6 +45,12 @@ export async function * traverseOperations(operations, input, Functions) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {JSON} operation
|
||||
* @param {PDF|PDF[]} input
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * computeOperation(operation, input) {
|
||||
yield "Starting: " + operation.type;
|
||||
switch (operation.type) {
|
||||
@ -93,8 +111,7 @@ export async function * traverseOperations(operations, input, Functions) {
|
||||
buffer: splitResult[j]
|
||||
})
|
||||
}
|
||||
|
||||
input = splits;
|
||||
return splits;
|
||||
});
|
||||
break;
|
||||
case "editMetadata":
|
||||
@ -109,34 +126,72 @@ export async function * traverseOperations(operations, input, Functions) {
|
||||
input.buffer = await Functions.organizePages(input.buffer, operation.values["operation"], operation.values["customOrderString"]);
|
||||
});
|
||||
break;
|
||||
case "removeBlankPages":
|
||||
yield* nToN(input, operation, async (input) => {
|
||||
input.fileName += "_removedBlanks";
|
||||
input.buffer = await Functions.removeBlankPages(input.buffer, operation.values["whiteThreashold"]);
|
||||
});
|
||||
break;
|
||||
case "splitOn":
|
||||
yield* oneToN(input, operation, async (input) => {
|
||||
const splitResult = await Functions.splitOn(input.buffer, operation.values["type"], operation.values["whiteThreashold"]);
|
||||
const splits = [];
|
||||
for (let j = 0; j < splitResult.length; j++) {
|
||||
splits.push({
|
||||
originalFileName: input.originalFileName,
|
||||
fileName: input.fileName + "_split" + j,
|
||||
buffer: splitResult[j]
|
||||
})
|
||||
}
|
||||
|
||||
return splits;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new Error(`${operation.type} not implemented yet.`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PDF|PDF[]} input
|
||||
* @param {JSON} operation
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * nToOne(inputs, operation, callback) {
|
||||
if(!Array.isArray(inputs)) {
|
||||
inputs = [inputs];
|
||||
}
|
||||
inputs = Array.from(inputs); // Convert single values to array, keep arrays as is.
|
||||
|
||||
inputs = await callback(inputs);
|
||||
yield* nextOperation(operation.operations, inputs);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PDF|PDF[]} input
|
||||
* @param {JSON} operation
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * oneToN(input, operation, callback) {
|
||||
if(Array.isArray(input)) {
|
||||
let output = [];
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
await callback(input[i]);
|
||||
output = output.concat(await callback(input[i]));
|
||||
}
|
||||
yield* nextOperation(operation.operations, input);
|
||||
yield* nextOperation(operation.operations, output);
|
||||
}
|
||||
else {
|
||||
await callback(input);
|
||||
input = await callback(input);
|
||||
yield* nextOperation(operation.operations, input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PDF|PDF[]} input
|
||||
* @param {JSON} operation
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * nToN(input, operation, callback) {
|
||||
if(Array.isArray(input)) {
|
||||
for (let i = 0; i < input.length; i++) {
|
Loading…
Reference in New Issue
Block a user