diff --git a/server-node/nodemon.json b/server-node/nodemon.json new file mode 100644 index 00000000..cffbb28a --- /dev/null +++ b/server-node/nodemon.json @@ -0,0 +1,6 @@ +{ + "watch": ["src"], + "ext": "ts,json", + "ignore": ["src/**/*.spec.ts"], + "exec": "node --trace-warnings --experimental-specifier-resolution=node --loader ts-node/esm ./src/index.ts" +} \ No newline at end of file diff --git a/server-node/package.json b/server-node/package.json index 1c755037..f9aabe37 100644 --- a/server-node/package.json +++ b/server-node/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "npx tsc", "start": "node dist/index.js", - "dev": "nodemon --watch './**/*.ts' --exec node --experimental-specifier-resolution=node --loader ts-node/esm ./src/index.ts" + "dev": "nodemon" }, "keywords": [], "author": "", diff --git a/server-node/src/routes/api/operations-controller.ts b/server-node/src/routes/api/operations-controller.ts index 6a6b3198..b2450311 100644 --- a/server-node/src/routes/api/operations-controller.ts +++ b/server-node/src/routes/api/operations-controller.ts @@ -1,7 +1,7 @@ import Operations from '../../utils/pdf-operations'; import { respondWithPdfFile, response_mustHaveExactlyOneFile } from '../../utils/endpoint-utils'; -import { PdfFile, PdfFileSchema, fromMulterFile, fromMulterFiles } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile' +import { PdfFile, PdfFileSchema } from '@stirling-pdf/shared-operations/src/wrappers/PdfFile' import express, { Request, Response, RequestHandler } from 'express'; const router = express.Router(); @@ -18,14 +18,14 @@ function registerEndpoint(endpoint: string, router.post(endpoint, fileHandler, async function(req: Request, res: Response) { const body = req.body; if (req.file) { - body.file = fromMulterFile(req.file); + body.file = PdfFile.fromMulterFile(req.file); } if (req.files) { if (Array.isArray(req.files)) - body.files = fromMulterFiles(req.files); + body.files = PdfFile.fromMulterFiles(req.files); else { const flattenedFiles = Object.values(req.files).flatMap(va => va); - body.files = fromMulterFiles(flattenedFiles); + body.files = PdfFile.fromMulterFiles(flattenedFiles); } } diff --git a/server-node/tsconfig.json b/server-node/tsconfig.json index 8a7dca9a..30e670b9 100644 --- a/server-node/tsconfig.json +++ b/server-node/tsconfig.json @@ -111,4 +111,9 @@ "src", "declarations/*.d.ts" ], + "ts-node": { + "experimentalSpecifierResolution": "node", + "transpileOnly": true, + "esm": true, + }, } diff --git a/shared-operations/src/functions/impose.ts b/shared-operations/src/functions/impose.ts index 95002ad6..a011ba3b 100644 --- a/shared-operations/src/functions/impose.ts +++ b/shared-operations/src/functions/impose.ts @@ -1,5 +1,3 @@ - -import { PdfFile, fromPdfLib } from '../wrappers/PdfFile'; export type ImposeParamsType = { file: any; nup: number; diff --git a/shared-operations/src/functions/mergePDFs.ts b/shared-operations/src/functions/mergePDFs.ts index fa750203..980e2bae 100644 --- a/shared-operations/src/functions/mergePDFs.ts +++ b/shared-operations/src/functions/mergePDFs.ts @@ -1,22 +1,19 @@ import { PDFDocument } from 'pdf-lib'; -import { PdfFile, convertAllToPdfLibFile, fromPdfLib } from '../wrappers/PdfFile'; +import { PdfFile } from '../wrappers/PdfFile'; export type MergeParamsType = { files: PdfFile[]; } export async function mergePDFs(params: MergeParamsType): Promise { - - const pdfLibFiles = await convertAllToPdfLibFile(params.files); - const mergedPdf = await PDFDocument.create(); - for (let i = 0; i < pdfLibFiles.length; i++) { - const pdfToMerge = await pdfLibFiles[i].getAsPdfLib(); + for (let i = 0; i < params.files.length; i++) { + const pdfToMerge = await params.files[i].pdflibDocument; const copiedPages = await mergedPdf.copyPages(pdfToMerge, pdfToMerge.getPageIndices()); copiedPages.forEach((page) => mergedPdf.addPage(page)); } - return fromPdfLib(mergedPdf, params.files[0].filename); + return new PdfFile(params.files.map(f => ), mergedPdf, params.files[0].filename); }; \ No newline at end of file diff --git a/shared-operations/src/functions/rotatePages.ts b/shared-operations/src/functions/rotatePages.ts index 5380c41a..936df988 100644 --- a/shared-operations/src/functions/rotatePages.ts +++ b/shared-operations/src/functions/rotatePages.ts @@ -1,6 +1,6 @@ import { degrees } from 'pdf-lib'; -import { PdfFile, fromPdfLib } from '../wrappers/PdfFile'; +import { PdfFile } from '../wrappers/PdfFile'; export type RotateParamsType = { file: PdfFile; @@ -10,8 +10,7 @@ export type RotateParamsType = { export async function rotatePages(params: RotateParamsType): Promise { const { file, rotation } = params; - const pdfDoc = await file.getAsPdfLib(); - const pages = pdfDoc.getPages(); + const pages = (await file.pdflibDocument).getPages(); if (Array.isArray(rotation)) { if (rotation.length != pages.length) { @@ -29,5 +28,5 @@ export async function rotatePages(params: RotateParamsType): Promise { }); } - return fromPdfLib(pdfDoc, file.filename); + return file; }; \ No newline at end of file diff --git a/shared-operations/src/wrappers/PdfFile.ts b/shared-operations/src/wrappers/PdfFile.ts index f3b7cead..b4c0a2dd 100644 --- a/shared-operations/src/wrappers/PdfFile.ts +++ b/shared-operations/src/wrappers/PdfFile.ts @@ -1,106 +1,84 @@ - -import { PDFDocument } from 'pdf-lib'; import * as PDFJS from 'pdfjs-dist'; -import { PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api'; +import { PDFDocumentProxy as PDFJSDocument } from 'pdfjs-dist/types/src/display/api'; +import { PDFDocument as PDFLibDocument } from 'pdf-lib'; + import Joi from 'joi'; export class PdfFile { - byteArray: Uint8Array | null; - pdfLib: PDFDocument | null; - pdfJs: PDFDocumentProxy | null; + private representation: Uint8Array | PDFLibDocument | PDFJSDocument; + originalFilename: string; filename: string; - constructor() { - this.byteArray = null; - this.pdfLib = null; - this.pdfJs = null; - this.filename = ""; - } - - async convertToByteArrayFile(): Promise { - if (this.byteArray) return this; - - var byteArray: Uint8Array|null = null; - if (this.pdfLib) { - byteArray = await this.pdfLib.save(); - } else if (this.pdfJs) { - byteArray = await this.pdfJs.getData(); - } - return fromUint8Array(byteArray!, this.filename); + get uint8Array() : Promise { + switch (this.representation.constructor) { + case Uint8Array: + return new Promise((resolve, reject) => { + resolve(this.representation as Uint8Array); + }); + case PDFLibDocument: + return (this.representation as PDFLibDocument).save(); + case PDFJSDocument: + return (this.representation as PDFJSDocument).getData(); + default: + throw Error("unhandeled PDF type"); + } } - async convertToPdfLibFile(): Promise { - if (this.pdfLib) return this; - - const byteFile = await this.convertToByteArrayFile(); - const pdfLib = await PDFDocument.load(byteFile.byteArray!, { - updateMetadata: false, - }); - return fromPdfLib(pdfLib, this.filename); - } - async convertToPdfJsFile(): Promise { - if (this.pdfJs) return this; - - const byteFile = await this.convertToByteArrayFile(); - const pdfJs = await PDFJS.getDocument(byteFile.byteArray!).promise; - return fromPdfJs(pdfJs, this.filename); + set uint8Array(value: Uint8Array) { + this.representation = value; } - async getAsByteArray(): Promise { - const file = await this.convertToByteArrayFile(); - return file.byteArray!; + get pdflibDocument() : Promise { + switch (this.representation.constructor) { + case PDFLibDocument: // PDFLib + return new Promise((resolve, reject) => { + resolve(this.representation as PDFLibDocument); + }); + default: + return new Promise(async (resolve, reject) => { + resolve(PDFLibDocument.load(await this.uint8Array, { + updateMetadata: false, + })); + }); + } } - async getAsPdfLib(): Promise { - const file = await this.convertToPdfLibFile(); - return file.pdfLib!; + set pdflibDocument(value: PDFLibDocument) { + this.representation = value; } - async getAsPdfJs(): Promise { - const file = await this.convertToPdfJsFile(); - return file.pdfJs!; + + get pdfjsDocuemnt() : Promise { + switch (this.representation.constructor) { + case PDFJSDocument: + return new Promise((resolve, reject) => { + resolve(this.representation as PDFJSDocument); + }); + default: + return new Promise(async (resolve, reject) => { + resolve(await PDFJS.getDocument(await this.uint8Array).promise); + }); + } + } + set pdfjsDocuemnt(value: PDFJSDocument) { + this.representation = value; + } + + constructor(originalFilename: string, representation: Uint8Array | PDFLibDocument | PDFJSDocument, filename?: string) { + this.originalFilename = originalFilename; + this.filename = filename ? filename : originalFilename; + + this.representation = representation; + } + + static fromMulterFile(value: Express.Multer.File): PdfFile { + return new PdfFile(value.originalname, value.buffer as Uint8Array) + } + static fromMulterFiles(values: Express.Multer.File[]): PdfFile[] { + return values.map(v => PdfFile.fromMulterFile(v)); } } + 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; - out.filename = filename; - return out; -} -export function fromPdfLib(value: PDFDocument, filename: string): PdfFile { - const out = new PdfFile(); - out.pdfLib = value; - out.filename = filename; - return out; -} -export function fromPdfJs(value: PDFDocumentProxy, filename: string): PdfFile { - const out = new PdfFile(); - out.pdfJs = value; - out.filename = filename; - return out; -} - -export async function convertAllToByteArrayFile(files: PdfFile[]): Promise<(PdfFile)[]> { - const pdfPromises = files.map(s => s.convertToByteArrayFile()); - return await Promise.all(pdfPromises); -} - -export async function convertAllToPdfLibFile(files: PdfFile[]): Promise<(PdfFile)[]> { - const pdfPromises = files.map(s => s.convertToPdfLibFile()); - return await Promise.all(pdfPromises); -} - -export async function convertAllToPdfJsFile(files: PdfFile[]): Promise<(PdfFile)[]> { - const pdfPromises = files.map(s => s.convertToPdfJsFile()); - return await Promise.all(pdfPromises); -} +}, "PdfFile validation"); \ No newline at end of file