mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2024-12-21 19:08:24 +01:00
Solved type issues in traverseOperations etc. Changed functions to take a object single parameter. Condensed endpoints into a function call
This commit is contained in:
parent
77274e6117
commit
9956367384
@ -1,93 +1,50 @@
|
||||
|
||||
import Operations from '../../utils/pdf-operations';
|
||||
import { respondWithPdfFile, response_mustHaveExactlyOneFile } from '../../utils/endpoint-utils';
|
||||
import { PdfFile, fromMulterFile } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile'
|
||||
import { PdfFile, PdfFileSchema, fromMulterFile, fromMulterFiles } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile'
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import express, { Request, Response, RequestHandler } from 'express';
|
||||
const router = express.Router();
|
||||
import multer from 'multer';
|
||||
const upload = multer();
|
||||
import Joi from 'joi';
|
||||
import Joi, { array } from 'joi';
|
||||
|
||||
router.post('/merge-pdfs', upload.single("pdfFile"), async function(req: Request, res: Response) {
|
||||
const schema = Joi.object({
|
||||
deleteAll: Joi.string(),
|
||||
author: Joi.string(),
|
||||
creationDate: Joi.string(),
|
||||
creator: Joi.string(),
|
||||
keywords: Joi.string(),
|
||||
modificationDate: Joi.string(),
|
||||
producer: Joi.string(),
|
||||
subject: Joi.string(),
|
||||
title: Joi.string(),
|
||||
trapped: Joi.string(),
|
||||
allRequestParams: Joi.object().pattern(Joi.string(), Joi.string()),
|
||||
}).required();
|
||||
const { error, value } = schema.validate(req.body);
|
||||
if (error) {
|
||||
res.status(400).send(error.details);
|
||||
return;
|
||||
}
|
||||
if (!req.file) {
|
||||
response_mustHaveExactlyOneFile(res);
|
||||
return;
|
||||
}
|
||||
function registerEndpoint(endpoint: string,
|
||||
nameToAppend: string,
|
||||
fileHandler: RequestHandler,
|
||||
operationFunction: (params: any) => Promise<PdfFile|PdfFile[]>,
|
||||
joiSchema: Joi.ObjectSchema<any>
|
||||
): void {
|
||||
router.post(endpoint, fileHandler, async function(req: Request, res: Response) {
|
||||
const body = req.body;
|
||||
if (req.file) {
|
||||
body.file = fromMulterFile(req.file);
|
||||
}
|
||||
if (req.files) {
|
||||
if (Array.isArray(req.files))
|
||||
body.files = fromMulterFiles(req.files);
|
||||
else {
|
||||
const flattenedFiles = Object.values(req.files).flatMap(va => va);
|
||||
body.files = fromMulterFiles(flattenedFiles);
|
||||
}
|
||||
}
|
||||
|
||||
const arrayFile = fromMulterFile(req.file);
|
||||
const processed = await Operations.updateMetadata(arrayFile, value)
|
||||
const newFilename = appendToFilename(req.file.originalname, '_edited-metadata');
|
||||
respondWithPdfFile(res, processed);
|
||||
});
|
||||
|
||||
router.post('/rotate-pdf', upload.single("pdfFile"), async function(req: Request, res: Response) {
|
||||
const schema = Joi.object({
|
||||
angle: Joi.number().required()
|
||||
console.log(req.body)
|
||||
const { error, value } = joiSchema.validate(req.body);
|
||||
if (error) {
|
||||
res.status(400).send(error.details);
|
||||
return;
|
||||
}
|
||||
|
||||
const processed = await operationFunction(value)
|
||||
if (Array.isArray(processed)) {
|
||||
// TODO zip multiple files
|
||||
} else {
|
||||
processed.filename = appendToFilename(processed.filename, nameToAppend);
|
||||
respondWithPdfFile(res, processed);
|
||||
}
|
||||
});
|
||||
const { error, value } = schema.validate(req.body);
|
||||
if (error) {
|
||||
res.status(400).send(error.details);
|
||||
return;
|
||||
}
|
||||
if (!req.file) {
|
||||
response_mustHaveExactlyOneFile(res);
|
||||
return;
|
||||
}
|
||||
|
||||
const arrayFile = fromMulterFile(req.file);
|
||||
const rotated = await Operations.rotatePages(arrayFile, value.angle)
|
||||
rotated.filename = appendToFilename(arrayFile.filename, '_rotated');
|
||||
respondWithPdfFile(res, rotated);
|
||||
});
|
||||
|
||||
router.post('/update-metadata', upload.single("pdfFile"), async function(req: Request, res: Response) {
|
||||
const schema = Joi.object({
|
||||
deleteAll: Joi.string(),
|
||||
author: Joi.string(),
|
||||
creationDate: Joi.string(),
|
||||
creator: Joi.string(),
|
||||
keywords: Joi.string(),
|
||||
modificationDate: Joi.string(),
|
||||
producer: Joi.string(),
|
||||
subject: Joi.string(),
|
||||
title: Joi.string(),
|
||||
trapped: Joi.string(),
|
||||
allRequestParams: Joi.object().pattern(Joi.string(), Joi.string()),
|
||||
}).required();
|
||||
const { error, value } = schema.validate(req.body);
|
||||
if (error) {
|
||||
res.status(400).send(error.details);
|
||||
return;
|
||||
}
|
||||
if (!req.file) {
|
||||
response_mustHaveExactlyOneFile(res);
|
||||
return;
|
||||
}
|
||||
|
||||
const arrayFile = fromMulterFile(req.file);
|
||||
const processed = await Operations.updateMetadata(arrayFile, value)
|
||||
processed.filename = appendToFilename(arrayFile.filename, '_edited-metadata');
|
||||
respondWithPdfFile(res, processed);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* appends a string before the last '.' of the given filename
|
||||
@ -96,4 +53,28 @@ function appendToFilename(filename: string, str: string) {
|
||||
return filename.replace(/(\.[^.]+)$/, str+'$1')
|
||||
}
|
||||
|
||||
registerEndpoint("/merge-pdfs", "_merged", upload.single("file"), Operations.mergePDFs, Joi.object({
|
||||
files: Joi.array().items(PdfFileSchema).required(),
|
||||
}).required())
|
||||
|
||||
registerEndpoint("/rotate-pdf", "_rotated", upload.single("file"), Operations.rotatePages, Joi.object({
|
||||
file: PdfFileSchema.required(),
|
||||
rotation: Joi.alternatives().try(Joi.number(), Joi.array().items(Joi.number())).required(),
|
||||
}).required())
|
||||
|
||||
registerEndpoint("/update-metadata", "_edited-metadata", upload.single("file"), Operations.updateMetadata, Joi.object({
|
||||
file: PdfFileSchema.required(),
|
||||
deleteAll: Joi.string(),
|
||||
author: Joi.string(),
|
||||
creationDate: Joi.string(),
|
||||
creator: Joi.string(),
|
||||
keywords: Joi.string(),
|
||||
modificationDate: Joi.string(),
|
||||
producer: Joi.string(),
|
||||
subject: Joi.string(),
|
||||
title: Joi.string(),
|
||||
trapped: Joi.string(),
|
||||
allRequestParams: Joi.object().pattern(Joi.string(), Joi.string()),
|
||||
}).required())
|
||||
|
||||
export default router;
|
@ -1,14 +1,17 @@
|
||||
|
||||
import SharedOperations, { OperationsUseages } from "@stirling-pdf/shared-operations/src";
|
||||
import SharedOperations, { OperationsType } from "@stirling-pdf/shared-operations/src";
|
||||
import { ImposeParamsType } from '@stirling-pdf/shared-operations/src/functions/impose'
|
||||
import { PdfFile } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile";
|
||||
|
||||
// Import injected libraries here!
|
||||
//import * as pdfcpuWrapper from "@stirling-pdf/shared-operations/wasm/pdfcpu/pdfcpu-wrapper-node.js";
|
||||
|
||||
async function impose(snapshot: any, nup: number, format: string) {
|
||||
return SharedOperations.impose(snapshot, nup, format, null); // TODO change null to pdfcpuWrapper
|
||||
async function impose(params: ImposeParamsType): Promise<PdfFile> {
|
||||
const paramsToUse = { ...params, pdfcpuWrapper: null }; // TODO change null to pdfcpuWrapper
|
||||
return SharedOperations.impose(paramsToUse);
|
||||
}
|
||||
|
||||
const toExport: OperationsUseages = {
|
||||
const toExport: OperationsType = {
|
||||
...SharedOperations,
|
||||
impose,
|
||||
}
|
||||
|
5
shared-operations/declarations/Action.d.ts
vendored
Normal file
5
shared-operations/declarations/Action.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
export interface Action {
|
||||
values: any;
|
||||
type: string;
|
||||
actions?: Action[];
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export interface Operation {
|
||||
values: {id:any};
|
||||
type: string;
|
||||
operations?: Operation[];
|
||||
}
|
5
shared-operations/declarations/TypeScriptUtils.d.ts
vendored
Normal file
5
shared-operations/declarations/TypeScriptUtils.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
export type ValuesType<T> = T[keyof T];
|
||||
|
||||
// https://dev.to/vborodulin/ts-how-to-override-properties-with-type-intersection-554l
|
||||
export type Override<T1, T2> = Omit<T1, keyof T2> & T2;
|
@ -1,12 +1,25 @@
|
||||
export async function impose(snapshot: any, nup: number, format: string, pdfcpuWrapper: any) {
|
||||
return await pdfcpuWrapper.oneToOne([
|
||||
|
||||
import { PdfFile, fromPdfLib } from '../wrappers/PdfFile';
|
||||
export type ImposeParamsType = {
|
||||
file: any;
|
||||
nup: number;
|
||||
format: string;
|
||||
}
|
||||
export type ImposeParamsBaseType = {
|
||||
file: any;
|
||||
nup: number;
|
||||
format: string;
|
||||
pdfcpuWrapper: any;
|
||||
}
|
||||
export async function impose(params: ImposeParamsBaseType) {
|
||||
return await params.pdfcpuWrapper.oneToOne([
|
||||
"pdfcpu.wasm",
|
||||
"nup",
|
||||
"-c",
|
||||
"disable",
|
||||
'f:' + format,
|
||||
'f:' + params.format,
|
||||
"/output.pdf",
|
||||
String(nup),
|
||||
String(params.nup),
|
||||
"input.pdf",
|
||||
], snapshot);
|
||||
], params.file);
|
||||
}
|
@ -1,10 +1,15 @@
|
||||
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { PdfFile, convertAllToPdfLibFile, fromPdfLib } from '../wrappers/PdfFile';
|
||||
import Joi from 'joi';
|
||||
|
||||
export async function mergePDFs(files: PdfFile[]): Promise<PdfFile> {
|
||||
export type MergeParamsType = {
|
||||
files: PdfFile[];
|
||||
}
|
||||
|
||||
const pdfLibFiles = await convertAllToPdfLibFile(files);
|
||||
export async function mergePDFs(params: MergeParamsType): Promise<PdfFile> {
|
||||
|
||||
const pdfLibFiles = await convertAllToPdfLibFile(params.files);
|
||||
|
||||
const mergedPdf = await PDFDocument.create();
|
||||
|
||||
@ -14,5 +19,5 @@ export async function mergePDFs(files: PdfFile[]): Promise<PdfFile> {
|
||||
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
||||
}
|
||||
|
||||
return fromPdfLib(mergedPdf, files[0].filename);
|
||||
return fromPdfLib(mergedPdf, params.files[0].filename);
|
||||
};
|
@ -2,7 +2,14 @@
|
||||
import { degrees } from 'pdf-lib';
|
||||
import { PdfFile, fromPdfLib } from '../wrappers/PdfFile';
|
||||
|
||||
export async function rotatePages(file: PdfFile, rotation: number|number[]): Promise<PdfFile> {
|
||||
export type RotateParamsType = {
|
||||
file: PdfFile;
|
||||
rotation: number|number[];
|
||||
}
|
||||
|
||||
export async function rotatePages(params: RotateParamsType): Promise<PdfFile> {
|
||||
const { file, rotation } = params;
|
||||
|
||||
const pdfDoc = await file.getAsPdfLib();
|
||||
const pages = pdfDoc.getPages();
|
||||
|
||||
|
@ -2,7 +2,14 @@
|
||||
import { PDFPage } from 'pdf-lib';
|
||||
import { PdfFile, fromPdfLib } from '../wrappers/PdfFile';
|
||||
|
||||
export async function scaleContent(file: PdfFile, scaleFactor: number|number[]): Promise<PdfFile> {
|
||||
export type ScaleContentParamsType = {
|
||||
file: PdfFile;
|
||||
scaleFactor: number|number[];
|
||||
}
|
||||
|
||||
export async function scaleContent(params: ScaleContentParamsType): Promise<PdfFile> {
|
||||
const { file, scaleFactor } = params;
|
||||
|
||||
const pdfDoc = await file.getAsPdfLib();
|
||||
const pages = pdfDoc.getPages();
|
||||
|
||||
|
@ -2,7 +2,14 @@
|
||||
import { PDFPage } from 'pdf-lib';
|
||||
import { PdfFile, fromPdfLib } from '../wrappers/PdfFile';
|
||||
|
||||
export async function scalePage(file: PdfFile, pageSize: {width?:number,height?:number}|{width?:number,height?:number}[]): Promise<PdfFile> {
|
||||
export type ScalePageParamsType = {
|
||||
file: PdfFile;
|
||||
pageSize: { width?:number,height?:number }|{ width?:number,height?:number }[];
|
||||
}
|
||||
|
||||
export async function scalePage(params: ScalePageParamsType): Promise<PdfFile> {
|
||||
const { file, pageSize } = params;
|
||||
|
||||
const pdfDoc = await file.getAsPdfLib();
|
||||
const pages = pdfDoc.getPages();
|
||||
|
||||
|
@ -6,10 +6,15 @@ import { getImagesOnPage } from "./common/getImagesOnPage.js";
|
||||
import { selectPages } from "./subDocumentFunctions";
|
||||
import { PdfFile } from '../wrappers/PdfFile.js';
|
||||
|
||||
export async function splitOn(
|
||||
file: PdfFile,
|
||||
type: "BAR_CODE"|"QR_CODE"|"BLANK_PAGE",
|
||||
whiteThreashold: number) {
|
||||
export type SplitOnParamsType = {
|
||||
file: PdfFile;
|
||||
type: "BAR_CODE"|"QR_CODE"|"BLANK_PAGE";
|
||||
whiteThreashold: number;
|
||||
}
|
||||
|
||||
export async function splitOn(params: SplitOnParamsType) {
|
||||
const { file, type, whiteThreashold } = params;
|
||||
|
||||
let splitAtPages: number[] = [];
|
||||
|
||||
switch (type) {
|
||||
@ -43,7 +48,7 @@ export async function splitOn(
|
||||
console.log(i);
|
||||
if(i == splitAfter) {
|
||||
if(pagesArray.length > 0) {
|
||||
subDocuments.push(await selectPages(file, pagesArray));
|
||||
subDocuments.push(await selectPages({file, pagesToExtractArray:pagesArray}));
|
||||
pagesArray = [];
|
||||
}
|
||||
splitAfter = splitAtPages.shift();
|
||||
@ -54,7 +59,7 @@ export async function splitOn(
|
||||
}
|
||||
}
|
||||
if(pagesArray.length > 0) {
|
||||
subDocuments.push(await selectPages(file, pagesArray));
|
||||
subDocuments.push(await selectPages({file, pagesToExtractArray:pagesArray}));
|
||||
}
|
||||
pagesArray = [];
|
||||
|
||||
|
@ -2,7 +2,14 @@
|
||||
import { selectPages } from "./subDocumentFunctions";
|
||||
import { PdfFile } from '../wrappers/PdfFile';
|
||||
|
||||
export async function splitPDF(file: PdfFile, splitAfterPageArray: number[]): Promise<PdfFile[]> {
|
||||
export type SplitPdfParamsType = {
|
||||
file: PdfFile;
|
||||
splitAfterPageArray: number[];
|
||||
}
|
||||
|
||||
export async function splitPDF(params: SplitPdfParamsType): Promise<PdfFile[]> {
|
||||
const { file, splitAfterPageArray } = params;
|
||||
|
||||
const byteFile = await file.convertToPdfLibFile();
|
||||
if (!byteFile?.pdfLib) return [];
|
||||
|
||||
@ -14,13 +21,13 @@ export async function splitPDF(file: PdfFile, splitAfterPageArray: number[]): Pr
|
||||
|
||||
for (let i = 0; i < numberOfPages; i++) {
|
||||
if(splitAfter && i > splitAfter && pagesArray.length > 0) {
|
||||
subDocuments.push(await selectPages(byteFile, pagesArray));
|
||||
subDocuments.push(await selectPages({file:byteFile, pagesToExtractArray:pagesArray}));
|
||||
splitAfter = splitAfterPageArray.shift();
|
||||
pagesArray = [];
|
||||
}
|
||||
pagesArray.push(i);
|
||||
}
|
||||
subDocuments.push(await selectPages(byteFile, pagesArray));
|
||||
subDocuments.push(await selectPages({file:byteFile, pagesToExtractArray:pagesArray}));
|
||||
pagesArray = [];
|
||||
|
||||
return subDocuments;
|
||||
|
@ -4,9 +4,16 @@ import { PdfFile, fromPdfLib } from '../wrappers/PdfFile.js';
|
||||
import { detectEmptyPages } from "./common/detectEmptyPages.js";
|
||||
|
||||
|
||||
export async function sortPagesWithPreset(file: PdfFile, sortPreset: string, fancyPageSelector: string) {
|
||||
export type SortPagesWithPresetParamsType = {
|
||||
file: PdfFile;
|
||||
sortPreset: string;
|
||||
fancyPageSelector: string;
|
||||
}
|
||||
export async function sortPagesWithPreset(params: SortPagesWithPresetParamsType) {
|
||||
const { file, sortPreset } = params;
|
||||
|
||||
if (sortPreset === "CUSTOM_PAGE_ORDER") {
|
||||
return rearrangePages(file, fancyPageSelector);
|
||||
return rearrangePages(params); // fancyPageSelector passed down with params
|
||||
}
|
||||
|
||||
const sortFunction = sorts[sortPreset];
|
||||
@ -19,19 +26,32 @@ export async function sortPagesWithPreset(file: PdfFile, sortPreset: string, fan
|
||||
|
||||
const pageCount = byteFile.pdfLib.getPageCount();
|
||||
const sortIndecies = sortFunction(pageCount);
|
||||
return selectPages(byteFile, sortIndecies);
|
||||
return selectPages({file:byteFile, pagesToExtractArray:sortIndecies});
|
||||
}
|
||||
|
||||
export async function rearrangePages(file: PdfFile, fancyPageSelector: string): Promise<PdfFile> {
|
||||
export type RearrangePagesParamsType = {
|
||||
file: PdfFile;
|
||||
sortPreset: string;
|
||||
fancyPageSelector: string;
|
||||
}
|
||||
export async function rearrangePages(params: RearrangePagesParamsType): Promise<PdfFile> {
|
||||
const { file, fancyPageSelector } = params;
|
||||
|
||||
const byteFile = await file.convertToPdfLibFile();
|
||||
if (!byteFile?.pdfLib) return byteFile;
|
||||
|
||||
const pagesToExtractArray = parseFancyPageSelector(fancyPageSelector, byteFile.pdfLib.getPageCount());
|
||||
const newDocument = selectPages(byteFile, pagesToExtractArray);
|
||||
const newDocument = selectPages({file:byteFile, pagesToExtractArray});
|
||||
return newDocument;
|
||||
};
|
||||
|
||||
export async function selectPages(file: PdfFile, pagesToExtractArray: number[]): Promise<PdfFile> {
|
||||
export type SelectPagesParamsType = {
|
||||
file: PdfFile;
|
||||
pagesToExtractArray: number[];
|
||||
}
|
||||
export async function selectPages(params: SelectPagesParamsType): Promise<PdfFile> {
|
||||
const { file, pagesToExtractArray } = params;
|
||||
|
||||
const byteFile = await file.convertToPdfLibFile();
|
||||
if (!byteFile?.pdfLib) return byteFile;
|
||||
|
||||
@ -51,18 +71,30 @@ export async function selectPages(file: PdfFile, pagesToExtractArray: number[]):
|
||||
return fromPdfLib(subDocument, file.filename);
|
||||
}
|
||||
|
||||
export async function removePages(file: PdfFile, pagesToRemoveArray: number[]): Promise<PdfFile> {
|
||||
export type RemovePagesParamsType = {
|
||||
file: PdfFile;
|
||||
pagesToRemoveArray: number[];
|
||||
}
|
||||
export async function removePages(params: RemovePagesParamsType): Promise<PdfFile> {
|
||||
const { file, pagesToRemoveArray } = params;
|
||||
|
||||
const byteFile = await file.convertToPdfLibFile();
|
||||
if (!byteFile?.pdfLib) return byteFile;
|
||||
|
||||
const pagesToExtractArray = invertSelection(pagesToRemoveArray, byteFile.pdfLib.getPageIndices())
|
||||
return selectPages(byteFile, pagesToExtractArray);
|
||||
return selectPages({file:byteFile, pagesToExtractArray});
|
||||
}
|
||||
|
||||
export async function removeBlankPages(file: PdfFile, whiteThreashold: number) {
|
||||
export type RemoveBlankPagesParamsType = {
|
||||
file: PdfFile;
|
||||
whiteThreashold: number;
|
||||
}
|
||||
export async function removeBlankPages(params: RemoveBlankPagesParamsType) {
|
||||
const { file, whiteThreashold } = params;
|
||||
|
||||
const emptyPages = await detectEmptyPages(file, whiteThreashold);
|
||||
console.log("Empty Pages: ", emptyPages);
|
||||
return removePages(file, emptyPages);
|
||||
return removePages({file, pagesToRemoveArray:emptyPages});
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
|
||||
import { PdfFile, fromPdfLib } from '../wrappers/PdfFile';
|
||||
|
||||
export type Metadata = {
|
||||
export type UpdateMetadataParams = {
|
||||
file: PdfFile,
|
||||
deleteAll?: boolean, // Delete all metadata if set to true
|
||||
author?: string, // The author of the document
|
||||
creationDate?: Date, // The creation date of the document (format: yyyy/MM/dd HH:mm:ss)
|
||||
@ -15,10 +16,10 @@ export type Metadata = {
|
||||
//allRequestParams?: {[key: string]: [key: string]}, // Map list of key and value of custom parameters. Note these must start with customKey and customValue if they are non-standard
|
||||
}
|
||||
|
||||
export async function updateMetadata(file: PdfFile, metadata: Metadata|null): Promise<PdfFile> {
|
||||
const pdfDoc = await file.getAsPdfLib();
|
||||
export async function updateMetadata(params: UpdateMetadataParams): Promise<PdfFile> {
|
||||
const pdfDoc = await params.file.getAsPdfLib();
|
||||
|
||||
if (!metadata || metadata.deleteAll) {
|
||||
if (params.deleteAll) {
|
||||
pdfDoc.setAuthor("");
|
||||
pdfDoc.setCreationDate(new Date(0))
|
||||
pdfDoc.setCreator("")
|
||||
@ -28,28 +29,25 @@ export async function updateMetadata(file: PdfFile, metadata: Metadata|null): Pr
|
||||
pdfDoc.setSubject("")
|
||||
pdfDoc.setTitle("")
|
||||
}
|
||||
if (!metadata) {
|
||||
return fromPdfLib(pdfDoc, file.filename);
|
||||
}
|
||||
|
||||
if(metadata.author)
|
||||
pdfDoc.setAuthor(metadata.author);
|
||||
if(metadata.creationDate)
|
||||
pdfDoc.setCreationDate(metadata.creationDate)
|
||||
if(metadata.creator)
|
||||
pdfDoc.setCreator(metadata.creator)
|
||||
if(metadata.keywords)
|
||||
pdfDoc.setKeywords(metadata.keywords.split(","))
|
||||
if(metadata.modificationDate)
|
||||
pdfDoc.setModificationDate(metadata.modificationDate)
|
||||
if(metadata.producer)
|
||||
pdfDoc.setProducer(metadata.producer)
|
||||
if(metadata.subject)
|
||||
pdfDoc.setSubject(metadata.subject)
|
||||
if(metadata.title)
|
||||
pdfDoc.setTitle(metadata.title)
|
||||
if(params.author)
|
||||
pdfDoc.setAuthor(params.author);
|
||||
if(params.creationDate)
|
||||
pdfDoc.setCreationDate(params.creationDate)
|
||||
if(params.creator)
|
||||
pdfDoc.setCreator(params.creator)
|
||||
if(params.keywords)
|
||||
pdfDoc.setKeywords(params.keywords.split(","))
|
||||
if(params.modificationDate)
|
||||
pdfDoc.setModificationDate(params.modificationDate)
|
||||
if(params.producer)
|
||||
pdfDoc.setProducer(params.producer)
|
||||
if(params.subject)
|
||||
pdfDoc.setSubject(params.subject)
|
||||
if(params.title)
|
||||
pdfDoc.setTitle(params.title)
|
||||
|
||||
// TODO add trapped and custom metadata. May need another library
|
||||
|
||||
return fromPdfLib(pdfDoc, file.filename);
|
||||
return fromPdfLib(pdfDoc, params.file.filename);
|
||||
};
|
||||
|
@ -1,16 +1,25 @@
|
||||
|
||||
import {
|
||||
sortPagesWithPreset, SortPagesWithPresetParamsType,
|
||||
rearrangePages, RearrangePagesParamsType,
|
||||
selectPages, SelectPagesParamsType,
|
||||
removePages, RemovePagesParamsType,
|
||||
removeBlankPages, RemoveBlankPagesParamsType
|
||||
} from "./functions/subDocumentFunctions";
|
||||
import { impose, ImposeParamsBaseType, ImposeParamsType } from "./functions/impose";
|
||||
import { mergePDFs, MergeParamsType } from './functions/mergePDFs';
|
||||
import { rotatePages, RotateParamsType } from './functions/rotatePages';
|
||||
import { scaleContent, ScaleContentParamsType} from './functions/scaleContent';
|
||||
import { scalePage, ScalePageParamsType } from './functions/scalePage';
|
||||
import { splitOn, SplitOnParamsType } from './functions/splitOn';
|
||||
import { splitPDF, SplitPdfParamsType } from './functions/splitPDF';
|
||||
import { updateMetadata, UpdateMetadataParams } from "./functions/updateMetadata";
|
||||
import { PdfFile } from "./wrappers/PdfFile";
|
||||
|
||||
import { Override } from '../declarations/TypeScriptUtils'
|
||||
|
||||
// Import injected libraries here!
|
||||
|
||||
import { sortPagesWithPreset, rearrangePages, selectPages, removePages, removeBlankPages } from "./functions/subDocumentFunctions";
|
||||
import { impose } from "./functions/impose";
|
||||
import { mergePDFs } from './functions/mergePDFs';
|
||||
import { rotatePages } from './functions/rotatePages';
|
||||
import { scaleContent} from './functions/scaleContent';
|
||||
import { scalePage } from './functions/scalePage';
|
||||
import { splitOn } from './functions/splitOn';
|
||||
import { splitPDF } from './functions/splitPDF';
|
||||
import { updateMetadata } from "./functions/updateMetadata";
|
||||
|
||||
const toExport = {
|
||||
sortPagesWithPreset, rearrangePages, selectPages, removePages, removeBlankPages,
|
||||
impose,
|
||||
@ -24,9 +33,29 @@ const toExport = {
|
||||
}
|
||||
export default toExport;
|
||||
|
||||
export type OperationsParametersBaseType = {
|
||||
sortPagesWithPreset: SortPagesWithPresetParamsType;
|
||||
rearrangePages: RearrangePagesParamsType;
|
||||
selectPages: SelectPagesParamsType;
|
||||
removePages: RemovePagesParamsType;
|
||||
removeBlankPages: RemoveBlankPagesParamsType;
|
||||
impose: ImposeParamsBaseType;
|
||||
mergePDFs: MergeParamsType;
|
||||
rotatePages: RotateParamsType;
|
||||
scaleContent: ScaleContentParamsType;
|
||||
scalePage: ScalePageParamsType;
|
||||
splitOn: SplitOnParamsType;
|
||||
splitPDF: SplitPdfParamsType;
|
||||
updateMetadata: UpdateMetadataParams;
|
||||
}
|
||||
|
||||
export type OperationsBaseType = typeof toExport;
|
||||
|
||||
// Overide fields in the type of toExport, with the given fields and types. This seems to magically work!
|
||||
// https://dev.to/vborodulin/ts-how-to-override-properties-with-type-intersection-554l
|
||||
type Override<T1, T2> = Omit<T1, keyof T2> & T2;
|
||||
export type OperationsUseages = Override<typeof toExport, {
|
||||
impose: (snapshot: any, nup: number, format: string) => any;
|
||||
}>;
|
||||
export type OperationsType = Override<OperationsBaseType, {
|
||||
impose: (params: ImposeParamsType) => Promise<PdfFile>;
|
||||
}>;
|
||||
|
||||
export type OperationsParametersType = Override<OperationsParametersBaseType, {
|
||||
impose: ImposeParamsType;
|
||||
}>;
|
||||
|
@ -1,37 +1,38 @@
|
||||
import { Operation } from "../../declarations/Operation";
|
||||
import { Action } from "../../declarations/Action";
|
||||
import { PdfFile } from "../wrappers/PdfFile";
|
||||
|
||||
export function organizeWaitOperations(operations: Operation[]) {
|
||||
export function organizeWaitOperations(actions: Action[]) {
|
||||
|
||||
// Initialize an object to store the counts and associated "done" operations
|
||||
const waitCounts = {};
|
||||
const doneOperations = {};
|
||||
const waitCounts: {[key: string]: number} = {};
|
||||
const doneOperations: {[key: string]: Action} = {};
|
||||
|
||||
// Function to count "type: wait" operations and associate "done" operations per id
|
||||
function countWaitOperationsAndDone(operations: Operation[]) {
|
||||
for (const operation of operations) {
|
||||
if (operation.type === "wait") {
|
||||
const id = operation.values.id;
|
||||
function countWaitOperationsAndDone(actions: Action[]) {
|
||||
for (const action of actions) {
|
||||
if (action.type === "wait") {
|
||||
const id = action.values.id;
|
||||
if (id in waitCounts) {
|
||||
waitCounts[id]++;
|
||||
} else {
|
||||
waitCounts[id] = 1;
|
||||
}
|
||||
}
|
||||
if (operation.type === "done") {
|
||||
const id = operation.values.id;
|
||||
doneOperations[id] = operation;
|
||||
if (action.type === "done") {
|
||||
const id = action.values.id;
|
||||
doneOperations[id] = action;
|
||||
}
|
||||
if (operation.operations) {
|
||||
countWaitOperationsAndDone(operation.operations);
|
||||
if (action.actions) {
|
||||
countWaitOperationsAndDone(action.actions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start counting and associating from the root operations
|
||||
countWaitOperationsAndDone(operations);
|
||||
countWaitOperationsAndDone(actions);
|
||||
|
||||
// Combine counts and associated "done" operations
|
||||
const result = {};
|
||||
const result: ResultType = {};
|
||||
for (const id in waitCounts) {
|
||||
result[id] = {
|
||||
waitCount: waitCounts[id],
|
||||
@ -42,3 +43,10 @@ export function organizeWaitOperations(operations: Operation[]) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export type ResultType = {
|
||||
[key: string]: {
|
||||
waitCount: number,
|
||||
doneOperation: Action,
|
||||
input: PdfFile[]
|
||||
}
|
||||
}
|
@ -1,39 +1,42 @@
|
||||
import { organizeWaitOperations } from "./organizeWaitOperations.js";
|
||||
import { Operation } from "../../declarations/Operation.js";
|
||||
import { PDF } from "../../declarations/PDF.js";
|
||||
import { organizeWaitOperations } from "./organizeWaitOperations";
|
||||
import { Action } from "../../declarations/Action";
|
||||
import { OperationsType } from "../../src/index";
|
||||
import { PdfFile } from "../wrappers/PdfFile";
|
||||
|
||||
export async function * traverseOperations(operations: Operation[], input: PDF[] | PDF, Operations: AllOperations) {
|
||||
import { ValuesType } from "../../declarations/TypeScriptUtils"
|
||||
|
||||
export async function * traverseOperations(operations: Action[], input: PdfFile[] | PdfFile, Operations: OperationsType) {
|
||||
const waitOperations = organizeWaitOperations(operations);
|
||||
let results: PDF[] = [];
|
||||
let results: PdfFile[] = [];
|
||||
yield* nextOperation(operations, input);
|
||||
return results;
|
||||
|
||||
async function * nextOperation(operations: Operation[], input: PDF[] | PDF) {
|
||||
if(Array.isArray(operations) && operations.length == 0) { // isEmpty
|
||||
async function * nextOperation(actions: Action[], input: PdfFile[] | PdfFile): AsyncGenerator<any, any, unknown> {
|
||||
if(Array.isArray(actions) && actions.length == 0) { // isEmpty
|
||||
if(Array.isArray(input)) {
|
||||
console.log("operation done: " + input[0].fileName + (input.length > 1 ? "+" : ""));
|
||||
console.log("operation done: " + input[0].filename + (input.length > 1 ? "+" : ""));
|
||||
results = results.concat(input);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
console.log("operation done: " + input.fileName);
|
||||
console.log("operation done: " + input.filename);
|
||||
results.push(input);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < operations.length; i++) {
|
||||
yield* computeOperation(operations[i], structuredClone(input));
|
||||
for (let i = 0; i < actions.length; i++) {
|
||||
yield* computeOperation(actions[i], structuredClone(input));
|
||||
}
|
||||
}
|
||||
|
||||
async function * computeOperation(operation: Operation, input: PDF|PDF[]) {
|
||||
yield "Starting: " + operation.type;
|
||||
switch (operation.type) {
|
||||
async function * computeOperation(action: Action, input: PdfFile|PdfFile[]) {
|
||||
yield "Starting: " + action.type;
|
||||
switch (action.type) {
|
||||
case "done": // Skip this, because it is a valid node.
|
||||
break;
|
||||
case "wait":
|
||||
const waitOperation = waitOperations[operation.values.id];
|
||||
const waitOperation = waitOperations[action.values.id];
|
||||
|
||||
if(Array.isArray(input)) {
|
||||
waitOperation.input.concat(input); // TODO: May have unexpected concequences. Needs further testing!
|
||||
@ -43,141 +46,138 @@ export async function * traverseOperations(operations: Operation[], input: PDF[]
|
||||
}
|
||||
|
||||
waitOperation.waitCount--;
|
||||
if(waitOperation.waitCount == 0) {
|
||||
yield* nextOperation(waitOperation.doneOperation.operations, waitOperation.input);
|
||||
if(waitOperation.waitCount == 0 && waitOperation.doneOperation.actions) {
|
||||
yield* nextOperation(waitOperation.doneOperation.actions, waitOperation.input);
|
||||
}
|
||||
break;
|
||||
case "extract":
|
||||
yield* nToN(input, operation, async (input) => {
|
||||
input.fileName += "_extractedPages";
|
||||
input.buffer = await Operations.extractPages(input.buffer, operation.values["pagesToExtractArray"]);
|
||||
yield* nToN(input, action, async (input) => {
|
||||
const newPdf = await Operations.selectPages({file: input, pagesToExtractArray: action.values["pagesToExtractArray"]});
|
||||
newPdf.filename += "_extractedPages";
|
||||
return newPdf;
|
||||
});
|
||||
break;
|
||||
case "impose":
|
||||
yield* nToN(input, operation, async (input) => {
|
||||
input.fileName += "_imposed";
|
||||
input.buffer = await Operations.impose(input.buffer, operation.values["nup"], operation.values["format"]);
|
||||
yield* nToN(input, action, async (input) => {
|
||||
const newPdf = await Operations.impose({file: input, nup: action.values["nup"], format: action.values["format"]});
|
||||
newPdf.filename += "_imposed";
|
||||
return newPdf;
|
||||
});
|
||||
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 Operations.mergePDFs(inputs.map(input => input.buffer))
|
||||
}
|
||||
yield* nToOne(input, action, async (inputs) => {
|
||||
const newPdf = await Operations.mergePDFs({files: inputs});
|
||||
newPdf.filename = inputs.map(input => input.filename).join("_and_") + "_merged";
|
||||
return newPdf;
|
||||
});
|
||||
break;
|
||||
case "rotate":
|
||||
yield* nToN(input, operation, async (input) => {
|
||||
input.fileName += "_turned";
|
||||
input.buffer = await Operations.rotatePages(input.buffer, operation.values["rotation"]);
|
||||
yield* nToN(input, action, async (input) => {
|
||||
const newPdf = await Operations.rotatePages({file: input, rotation: action.values["rotation"]});
|
||||
newPdf.filename += "_turned";
|
||||
return newPdf;
|
||||
});
|
||||
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 Operations.splitPDF(input.buffer, operation.values["pagesToSplitAfterArray"]);
|
||||
|
||||
const splits = [];
|
||||
yield* oneToN(input, action, async (input) => {
|
||||
const splitResult = await Operations.splitPDF({file: input, splitAfterPageArray: action.values["splitAfterPageArray"]});
|
||||
for (let j = 0; j < splitResult.length; j++) {
|
||||
splits.push({
|
||||
originalFileName: input.originalFileName,
|
||||
fileName: input.fileName + "_split" + j,
|
||||
buffer: splitResult[j]
|
||||
})
|
||||
splitResult[j].filename = splitResult[j].filename + "_split" + j;
|
||||
}
|
||||
return splits;
|
||||
return splitResult;
|
||||
});
|
||||
break;
|
||||
case "updateMetadata":
|
||||
yield* nToN(input, operation, async (input) => {
|
||||
input.fileName += "_metadataEdited";
|
||||
input.buffer = await Operations.updateMetadata(input.buffer, operation.values["metadata"]);
|
||||
yield* nToN(input, action, async (input) => {
|
||||
const newPdf = await Operations.updateMetadata({file: input, ...action.values["metadata"]});
|
||||
newPdf.filename += "_metadataEdited";
|
||||
return newPdf;
|
||||
});
|
||||
break;
|
||||
case "organizePages":
|
||||
yield* nToN(input, operation, async (input) => {
|
||||
input.fileName += "_pagesOrganized";
|
||||
input.buffer = await Operations.organizePages(input.buffer, operation.values["operation"], operation.values["customOrderString"]);
|
||||
case "sortPagesWithPreset":
|
||||
yield* nToN(input, action, async (input) => {
|
||||
const newPdf = await Operations.sortPagesWithPreset({file: input, sortPreset: action.values["sortPreset"], fancyPageSelector: action.values["fancyPageSelector"]});
|
||||
newPdf.filename += "_pagesOrganized";
|
||||
return newPdf;
|
||||
});
|
||||
break;
|
||||
case "removeBlankPages":
|
||||
yield* nToN(input, operation, async (input) => {
|
||||
input.fileName += "_removedBlanks";
|
||||
input.buffer = await Operations.removeBlankPages(input.buffer, operation.values["whiteThreashold"]);
|
||||
yield* nToN(input, action, async (input) => {
|
||||
const newPdf = await Operations.removeBlankPages({file: input, whiteThreashold: action.values["whiteThreashold"]});
|
||||
newPdf.filename += "_removedBlanks";
|
||||
return newPdf;
|
||||
});
|
||||
break;
|
||||
case "splitOn":
|
||||
yield* oneToN(input, operation, async (input) => {
|
||||
const splitResult = await Operations.splitOn(input.buffer, operation.values["type"], operation.values["whiteThreashold"]);
|
||||
const splits = [];
|
||||
yield* oneToN(input, action, async (input) => {
|
||||
const splitResult = await Operations.splitOn({file: input, type: action.values["type"], whiteThreashold: action.values["whiteThreashold"]});
|
||||
for (let j = 0; j < splitResult.length; j++) {
|
||||
splits.push({
|
||||
originalFileName: input.originalFileName,
|
||||
fileName: input.fileName + "_split" + j,
|
||||
buffer: splitResult[j]
|
||||
})
|
||||
splitResult[j].filename = splitResult[j].filename + "_split" + j;
|
||||
}
|
||||
|
||||
return splits;
|
||||
return splitResult;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new Error(`${operation.type} not implemented yet.`);
|
||||
throw new Error(`${action.type} not implemented yet.`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PDF|PDF[]} input
|
||||
* @param {JSON} operation
|
||||
* @param {PdfFile|PdfFile[]} input
|
||||
* @param {JSON} action
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * nToOne(inputs, operation, callback) {
|
||||
inputs = Array.from(inputs); // Convert single values to array, keep arrays as is.
|
||||
async function * nToOne(inputs: PdfFile|PdfFile[], action: Action, callback: (pdf: PdfFile[]) => Promise<PdfFile>): AsyncGenerator<any, any, unknown> {
|
||||
const input = Array.isArray(inputs) ? inputs : [inputs]; // Convert single values to array, keep arrays as is.
|
||||
|
||||
inputs = await callback(inputs);
|
||||
yield* nextOperation(operation.operations, inputs);
|
||||
const newInputs = await callback(input);
|
||||
if (action.actions) {
|
||||
yield* nextOperation(action.actions, newInputs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PDF|PDF[]} input
|
||||
* @param {JSON} operation
|
||||
* @param {PdfFile|PdfFile[]} input
|
||||
* @param {JSON} action
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * oneToN(input, operation, callback) {
|
||||
async function * oneToN(input: PdfFile|PdfFile[], action: Action, callback: (pdf: PdfFile) => Promise<PdfFile[]>): AsyncGenerator<any, any, unknown> {
|
||||
if(Array.isArray(input)) {
|
||||
let output = [];
|
||||
let output: PdfFile[] = [];
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output = output.concat(await callback(input[i]));
|
||||
}
|
||||
yield* nextOperation(operation.operations, output);
|
||||
if (action.actions) {
|
||||
yield* nextOperation(action.actions, output);
|
||||
}
|
||||
}
|
||||
else {
|
||||
input = await callback(input);
|
||||
yield* nextOperation(operation.operations, input);
|
||||
const nextInput = await callback(input);
|
||||
if (action.actions) {
|
||||
yield* nextOperation(action.actions, nextInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PDF|PDF[]} input
|
||||
* @param {JSON} operation
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function * nToN(input, operation, callback) {
|
||||
async function * nToN(input: PdfFile|PdfFile[], action: Action, callback: (pdf: PdfFile) => Promise<PdfFile>): AsyncGenerator<any, any, unknown> {
|
||||
if(Array.isArray(input)) {
|
||||
const nextInputs: PdfFile[] = []
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
await callback(input[i]);
|
||||
nextInputs.concat(await callback(input[i]));
|
||||
}
|
||||
if (action.actions) {
|
||||
yield* nextOperation(action.actions, nextInputs);
|
||||
}
|
||||
yield* nextOperation(operation.operations, input);
|
||||
}
|
||||
else {
|
||||
await callback(input);
|
||||
yield* nextOperation(operation.operations, input);
|
||||
const nextInput = await callback(input);
|
||||
if (action.actions) {
|
||||
yield* nextOperation(action.actions, nextInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import * as PDFJS from 'pdfjs-dist';
|
||||
import { PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api';
|
||||
import Joi from 'joi';
|
||||
|
||||
export class PdfFile {
|
||||
byteArray: Uint8Array | null;
|
||||
@ -57,10 +58,19 @@ export class PdfFile {
|
||||
return file.pdfJs!;
|
||||
}
|
||||
}
|
||||
export const PdfFileSchema = Joi.any().custom((value, helpers) => {
|
||||
if (!(value instanceof PdfFile)) {
|
||||
throw new Error('value is not a PdfFile');
|
||||
}
|
||||
return value;
|
||||
}, "PdfFile validation");
|
||||
|
||||
export function fromMulterFile(value: Express.Multer.File): PdfFile {
|
||||
return fromUint8Array(value.buffer, value.originalname)
|
||||
}
|
||||
export function fromMulterFiles(values: Express.Multer.File[]): PdfFile[] {
|
||||
return values.map(v => fromUint8Array(v.buffer, v.originalname));
|
||||
}
|
||||
export function fromUint8Array(value: Uint8Array, filename: string): PdfFile {
|
||||
const out = new PdfFile();
|
||||
out.byteArray = value;
|
||||
|
Loading…
Reference in New Issue
Block a user