Removed duplicate code of node backend

Frontend traverser needs to be updated
This commit is contained in:
Felix Kaspar 2023-10-22 00:55:28 +02:00
parent b5ba2b25cd
commit 26cdb1d04f
19 changed files with 128 additions and 255 deletions

View File

@ -5,3 +5,14 @@ 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
## Adding a PDF Function
In order to add a PDF-Function there are several files that need to be changed. If the function is on the backend only, or on only on the frontend, you just need to add it to one of the locations. If it is available on both, you need to update both locations.
Dependency Injection is used to accomodate for different imports across platforms.
### Backend
Backend functions can have different implementations than their frontend counterparts if neccesary. Otherwise they can just link to their frontend implementation.
### Frontend

View File

@ -52,7 +52,7 @@ You can also nest workflows like this:
{
"type": "impose",
"values": {
"nup": 2, // 2 pages of the input docuemtn will be put on one page of the output document.
"nup": 2, // 2 pages of the input document will be put on one page of the output document.
"format": "A4L" // A4L -> The page size of the Ouput will be an A4 in Landscape. You can also use other paper formats and "P" for portrait output.
},
"operations": []

View File

@ -1,25 +0,0 @@
import { PDFDocument, ParseSpeeds } from 'pdf-lib'
export const extractPages = async (snapshot, pagesToExtractArray) => {
const pdfDoc = await PDFDocument.load(snapshot)
// 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();
}

View File

@ -1,15 +0,0 @@
import * as pdfcpuWraopper from "../public/wasm/pdfcpu-wrapper-node.js";
export async function impose(snapshot, nup, format) {
return await pdfcpuWraopper.oneToOne([
"pdfcpu.wasm",
"nup",
"-c",
"disable",
'f:' + format,
"/output.pdf",
String(nup),
"input.pdf",
], snapshot);
}

38
functions/index.js Normal file
View File

@ -0,0 +1,38 @@
import PDFLib from 'pdf-lib';
import * as pdfcpuWraopper from "../public/wasm/pdfcpu-wrapper-node.js";
import { extractPages as dependantExtractPages } from "../public/functions/extractPages.js";
import { impose as dependantImpose } from '../public/functions/impose.js';
import { mergePDFs as dependantMergePDFs } from '../public/functions/mergePDFs.js';
import { rotatePages as dependantRotatePages } from '../public/functions/rotatePages.js';
import { scaleContent as dependantScaleContent} from '../public/functions/scaleContent.js';
import { scalePage as dependantScalePage } from '../public/functions/scalePage.js';
import { splitPDF as dependantSplitPDF } from '../public/functions/splitPDF.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);
}

View File

@ -1,15 +0,0 @@
import { PDFDocument, ParseSpeeds } from 'pdf-lib'
export const mergePDFs = async (snapshots) => {
const mergedPdf = await PDFDocument.create();
for (let i = 0; i < snapshots.length; i++) {
const pdfToMerge = await PDFDocument.load(snapshots[i]);
const copiedPages = await mergedPdf.copyPages(pdfToMerge, pdfToMerge.getPageIndices());
copiedPages.forEach((page) => mergedPdf.addPage(page));
}
return mergedPdf.save();
};

View File

@ -1,18 +0,0 @@
import { PDFDocument, ParseSpeeds } from 'pdf-lib'
export const rotatePages = async (snapshot, rotation) => {
// Load the original PDF file
const pdfDoc = await PDFDocument.load(snapshot, {
parseSpeed: ParseSpeeds.Fastest,
});
const pages = pdfDoc.getPages();
pages.forEach(page => {
// Change page size
page.setRotation(degrees(rotation))
});
// Serialize the modified document
return pdfDoc.save();
};

View File

@ -1,29 +0,0 @@
import { PDFDocument, ParseSpeeds } from 'pdf-lib'
export const scaleContent = async (snapshot, scale_factor) => {
// Load the original PDF file
const pdfDoc = await PDFDocument.load(snapshot, {
parseSpeed: ParseSpeeds.Fastest,
});
const pages = pdfDoc.getPages();
pages.forEach(page => {
const width = page.getWidth();
const height = page.getHeight();
// Scale content
page.scaleContent(scale_factor, scale_factor);
const scaled_diff = {
width: Math.round(width - scale_factor * width),
height: Math.round(height - scale_factor * height),
};
// Center content in new page format
page.translateContent(Math.round(scaled_diff.width / 2), Math.round(scaled_diff.height / 2));
});
// Serialize the modified document
return pdfDoc.save();
};

View File

@ -1,31 +0,0 @@
import { PDFDocument, ParseSpeeds } from 'pdf-lib'
export const scalePage = async (snapshot, page_size) => {
// Load the original PDF file
const pdfDoc = await PDFDocument.load(snapshot, {
parseSpeed: ParseSpeeds.Fastest,
});
const new_size = page_size;
const pages = pdfDoc.getPages();
pages.forEach(page => {
// Change page size
page.setSize(new_size.width, new_size.height);
});
// Serialize the modified document
return pdfDoc.save();
};
export const PageSize = {
a4: {
width: 594.96,
height: 841.92
},
letter: {
width: 612,
height: 792
}
};

View File

@ -1,24 +0,0 @@
import { PDFDocument, ParseSpeeds } from 'pdf-lib'
import { createSubDocument } from "./extractPages.js";
export const splitPDF = async (snapshot, splitAfterPageArray) => {
const pdfDoc = await PDFDocument.load(snapshot)
const numberOfPages = pdfDoc.getPages().length;
let pagesArray = [];
let splitAfter = splitAfterPageArray.shift();
const subDocuments = [];
for (let i = 0; i < numberOfPages; i++) {
if(i > splitAfter && pagesArray.length > 0) {
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
splitAfter = splitAfterPageArray.shift();
pagesArray = [];
}
pagesArray.push(i);
}
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
pagesArray = [];
return subDocuments;
};

View File

@ -1,14 +1,12 @@
const { PDFDocument, ParseSpeeds } = PDFLib;
export const extractPages = async (snapshot, pagesToExtractArray) => {
const pdfDoc = await PDFDocument.load(snapshot)
export async function extractPages(snapshot, pagesToExtractArray, PDFLib) {
const pdfDoc = await PDFLib.PDFDocument.load(snapshot)
// TODO: invent a better format for pagesToExtractArray and convert it.
return createSubDocument(pdfDoc, pagesToExtractArray);
return createSubDocument(pdfDoc, pagesToExtractArray, PDFLib);
};
export async function createSubDocument(pdfDoc, pagesToExtractArray) {
const subDocument = await PDFDocument.create();
export async function createSubDocument(pdfDoc, pagesToExtractArray, PDFLib) {
const subDocument = await PDFLib.PDFDocument.create();
// Check that array max number is not larger pdf pages number
if(Math.max(...pagesToExtractArray) >= pdfDoc.getPageCount()) {

View File

@ -1,6 +1,4 @@
import * as pdfcpuWraopper from "../wasm/pdfcpu-wrapper-browser.js";
export async function impose(snapshot, nup, format) {
export async function impose(snapshot, nup, format, pdfcpuWraopper) {
return await pdfcpuWraopper.oneToOne([
"pdfcpu.wasm",
"nup",

View File

@ -1,11 +1,9 @@
const { PDFDocument, ParseSpeeds } = PDFLib;
export const mergePDFs = async (snapshots, PDFLib) => {
export const mergePDFs = async (snapshots) => {
const mergedPdf = await PDFDocument.create();
const mergedPdf = await PDFLib.PDFDocument.create();
for (let i = 0; i < snapshots.length; i++) {
const pdfToMerge = await PDFDocument.load(snapshots[i]);
const pdfToMerge = await PDFLib.PDFDocument.load(snapshots[i]);
const copiedPages = await mergedPdf.copyPages(pdfToMerge, pdfToMerge.getPageIndices());
copiedPages.forEach((page) => mergedPdf.addPage(page));

View File

@ -1,18 +0,0 @@
const { PDFDocument, ParseSpeeds, degrees } = PDFLib;
export const rotatePages = async (snapshot, rotation) => {
// Load the original PDF file
const pdfDoc = await PDFDocument.load(snapshot, {
parseSpeed: ParseSpeeds.Fastest,
});
const pages = pdfDoc.getPages();
pages.forEach(page => {
// Change page size
page.setRotation(degrees(rotation))
});
// Serialize the modified document
return pdfDoc.save();
};

View File

@ -0,0 +1,16 @@
export async function rotatePages (snapshot, rotation, PDFLib) {
// Load the original PDF file
const pdfDoc = await PDFLib.PDFDocument.load(snapshot, {
parseSpeed: PDFLib.ParseSpeeds.Fastest,
});
const pages = pdfDoc.getPages();
pages.forEach(page => {
// Change page size
page.setRotation(PDFLib.degrees(rotation))
});
// Serialize the modified document
return pdfDoc.save();
};

View File

@ -1,9 +1,7 @@
const { PDFDocument, ParseSpeeds } = PDFLib;
export const scaleContent = async (snapshot, scale_factor) => {
export async function scaleContent(snapshot, scaleFactor, PDFLib) {
// Load the original PDF file
const pdfDoc = await PDFDocument.load(snapshot, {
parseSpeed: ParseSpeeds.Fastest,
const pdfDoc = await PDFLib.PDFDocument.load(snapshot, {
parseSpeed: PDFLib.ParseSpeeds.Fastest,
});
const pages = pdfDoc.getPages();
@ -13,10 +11,10 @@ export const scaleContent = async (snapshot, scale_factor) => {
const height = page.getHeight();
// Scale content
page.scaleContent(scale_factor, scale_factor);
page.scaleContent(scaleFactor, scaleFactor);
const scaled_diff = {
width: Math.round(width - scale_factor * width),
height: Math.round(height - scale_factor * height),
width: Math.round(width - scaleFactor * width),
height: Math.round(height - scaleFactor * height),
};
// Center content in new page format

View File

@ -1,12 +1,10 @@
const { PDFDocument, ParseSpeeds } = PDFLib;
export const scalePage = async (snapshot, page_size) => {
export async function scalePage(snapshot, pageSize, PDFLib) {
// Load the original PDF file
const pdfDoc = await PDFDocument.load(snapshot, {
parseSpeed: ParseSpeeds.Fastest,
const pdfDoc = await PDFLib.PDFDocument.load(snapshot, {
parseSpeed: PDFLib.ParseSpeeds.Fastest,
});
const new_size = page_size;
const new_size = pageSize;
const pages = pdfDoc.getPages();

View File

@ -1,9 +1,7 @@
import { createSubDocument } from "./extractPages.js";
const { PDFDocument, ParseSpeeds } = PDFLib;
export const splitPDF = async (snapshot, splitAfterPageArray) => {
const pdfDoc = await PDFDocument.load(snapshot)
export async function splitPDF(snapshot, splitAfterPageArray, PDFLib) {
const pdfDoc = await PDFLib.PDFDocument.load(snapshot)
const numberOfPages = pdfDoc.getPages().length;
@ -13,13 +11,13 @@ export const splitPDF = async (snapshot, splitAfterPageArray) => {
for (let i = 0; i < numberOfPages; i++) {
if(i > splitAfter && pagesArray.length > 0) {
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
subDocuments.push(await createSubDocument(pdfDoc, pagesArray, PDFLib));
splitAfter = splitAfterPageArray.shift();
pagesArray = [];
}
pagesArray.push(i);
}
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
subDocuments.push(await createSubDocument(pdfDoc, pagesArray, PDFLib));
pagesArray = [];
return subDocuments;

View File

@ -1,21 +1,17 @@
import { extractPages } from "./functions/extractPages.js";
import { impose } from "./functions/impose.js";
import { mergePDFs } from "./functions/mergePDFs.js";
import { rotatePages } from "./functions/rotatePDF.js";
import { splitPDF } from "./functions/splitPDF.js";
import * as Functions from "./functions/index.js";
import { organizeWaitOperations } from "./public/organizeWaitOperations.js";
export async function * traverseOperations(operations, input) {
const waitOperations = organizeWaitOperations(operations);
let results = [];
yield* nextOperation(operations, input)
yield* nextOperation(operations, input);
console.log("Done2");
return results;
async function * nextOperation(operations, input) {
console.log(Array.isArray(operations) && operations.length == 0);
if(Array.isArray(operations) && operations.length == 0) { // isEmpty
if(Array.isArray(input)) {
console.log("operation done: " + input[0].fileName + "+");
console.log("operation done: " + input[0].fileName + input.length > 1 ? "+" : "");
results = results.concat(input);
return;
}
@ -34,6 +30,8 @@ export async function * traverseOperations(operations, input) {
async function * computeOperation(operation, input) {
yield "Starting: " + operation.type;
switch (operation.type) {
case "done": // Skip this, because it is a valid node.
break;
case "wait":
const waitOperation = waitOperations[operation.values.id];
@ -52,15 +50,34 @@ export async function * traverseOperations(operations, input) {
case "extract":
yield* nToN(input, operation, async (input) => {
input.fileName += "_extractedPages";
input.buffer = await extractPages(input.buffer, operation.values["pagesToExtractArray"]);
input.buffer = await Functions.extractPages(input.buffer, operation.values["pagesToExtractArray"]);
});
break;
case "impose":
yield* nToN(input, operation, async (input) => {
input.fileName += "_imposed";
input.buffer = await Functions.impose(input.buffer, operation.values["nup"], operation.values["format"]);
});
break;
case "merge":
yield* nToOne(input, operation, async (inputs) => {
return {
originalFileName: inputs.map(input => input.originalFileName).join("_and_"),
fileName: inputs.map(input => input.fileName).join("_and_") + "_merged",
buffer: await Functions.mergePDFs(inputs.map(input => input.buffer))
}
});
break;
case "rotate":
yield* nToN(input, operation, async (input) => {
input.fileName += "_turned";
input.buffer = await Functions.rotatePages(input.buffer, operation.values["rotation"]);
});
break;
case "split":
// TODO: A split might break the done condition, it may count multiple times. Needs further testing!
yield* oneToN(input, operation, async (input) => {
const splitResult = await splitPDF(input.buffer, operation.values["pagesToSplitAfterArray"]);
const splitResult = await Functions.splitPDF(input.buffer, operation.values["pagesToSplitAfterArray"]);
const splits = [];
for (let j = 0; j < splitResult.length; j++) {
@ -74,41 +91,19 @@ export async function * traverseOperations(operations, input) {
input = splits;
});
break;
case "merge":
yield * nToOne(input, operation, async (input) => {
const inputs = input;
input = {
originalFileName: inputs.map(input => input.originalFileName).join("_and_"),
fileName: inputs.map(input => input.fileName).join("_and_") + "_merged",
buffer: await mergePDFs(inputs.map(input => input.buffer))
}
});
break;
case "rotate":
yield * nToN(input, operation, async (input) => {
input.fileName += "_turned";
input.buffer = await rotatePages(input.buffer, operation.values["rotation"]);
});
break;
case "impose":
yield * nToN(input, operation, async (input) => {
input.fileName += "_imposed";
input.buffer = await impose(input.buffer, operation.values["nup"], operation.values["format"]);
});
break;
default:
throw new Error(`${operation.type} not implemented yet.`);
break;
}
}
async function * nToOne(input, operation, callback) {
if(!Array.isArray(input)) {
input = [input];
async function * nToOne(inputs, operation, callback) {
if(!Array.isArray(inputs)) {
inputs = [inputs];
}
await callback(input);
yield* nextOperation(operation.operations, input);
inputs = await callback(inputs);
yield* nextOperation(operation.operations, inputs);
}
async function * oneToN(input, operation, callback) {