diff --git a/shared-operations/functions/common/createSubDocument.ts b/shared-operations/functions/common/createSubDocument.ts deleted file mode 100644 index ffe67e36c..000000000 --- a/shared-operations/functions/common/createSubDocument.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import { PDFDocument } from 'pdf-lib'; - -export async function createSubDocument(pdfDoc: PDFDocument, pagesToExtractArray: number[]): Promise { - 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(); -} diff --git a/shared-operations/functions/common/detectEmptyPages.ts b/shared-operations/functions/common/detectEmptyPages.ts index 964b6b4bf..9db14d181 100644 --- a/shared-operations/functions/common/detectEmptyPages.ts +++ b/shared-operations/functions/common/detectEmptyPages.ts @@ -1,11 +1,12 @@ -import { DocumentInitParameters, PDFPageProxy } from "pdfjs-dist/types/src/display/api.js"; -import * as PDFJS from 'pdfjs-dist'; + +import { PdfFile } from '../../wrappers/PdfFile'; +import { PDFPageProxy } from "pdfjs-dist/types/src/display/api.js"; import { Image } from 'image-js'; import { getImagesOnPage } from "./getImagesOnPage.js"; -export async function detectEmptyPages(snapshot: string | URL | ArrayBuffer | DocumentInitParameters, whiteThreashold: number) { - const pdfDoc = await PDFJS.getDocument(snapshot).promise; +export async function detectEmptyPages(file: PdfFile, whiteThreashold: number): Promise { + const pdfDoc = await file.getAsPdfJs(); const emptyPages: number[] = []; for (let i = 1; i <= pdfDoc.numPages; i++) { diff --git a/shared-operations/functions/common/pdf-utils.ts b/shared-operations/functions/common/pdf-utils.ts new file mode 100644 index 000000000..632cca16c --- /dev/null +++ b/shared-operations/functions/common/pdf-utils.ts @@ -0,0 +1,47 @@ + +import { PdfFile, convertAllToPdfLibFile } from '../../wrappers/PdfFile'; + +export async function sortPdfs( + files: PdfFile[], + sortType: "orderProvided"|"byFileName"|"byDateModified"|"byDateCreated"|"byPDFTitle" = "orderProvided" + ): Promise { + + const pdfLibFiles = await convertAllToPdfLibFile(files); + + switch(sortType) { + case "byFileName": + pdfLibFiles.sort((a, b) => { + if (!a || !b) return 0; + const ad = a.filename, bd = b.filename; + if (!ad || !bd) return 0; + return ad.localeCompare(bd); + }); + break; + case "byDateModified": + pdfLibFiles.sort((a, b) => { + const ad = a.pdfLib?.getModificationDate()?.getTime(); + const bd = b.pdfLib?.getModificationDate()?.getTime(); + if (!ad || !bd) return 0; + return ad > bd ? 1 : -1 + }); + break; + case "byDateCreated": + pdfLibFiles.sort((a, b) => { + const ad = a.pdfLib?.getCreationDate()?.getTime(); + const bd = b.pdfLib?.getCreationDate()?.getTime(); + if (!ad || !bd) return 0; + return ad > bd ? 1 : -1 + }); + break; + case "byPDFTitle": + pdfLibFiles.sort((a, b) => { + const ad = a.pdfLib?.getTitle(); + const bd = b.pdfLib?.getTitle(); + if (!ad || !bd) return 0; + return ad.localeCompare(bd); + }); + break; + } + + return pdfLibFiles; +} \ No newline at end of file diff --git a/shared-operations/functions/extractPages.ts b/shared-operations/functions/extractPages.ts deleted file mode 100644 index 8fc79ceea..000000000 --- a/shared-operations/functions/extractPages.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import { PDFDocument } from 'pdf-lib'; -import { createSubDocument } from './common/createSubDocument'; - -export async function extractPages(snapshot: string | Uint8Array | ArrayBuffer, pagesToExtractArray: number[]): Promise{ - const pdfDoc = await PDFDocument.load(snapshot) - - // TODO: invent a better format for pagesToExtractArray and convert it. - return createSubDocument(pdfDoc, pagesToExtractArray); -}; diff --git a/shared-operations/functions/mergePDFs.ts b/shared-operations/functions/mergePDFs.ts index 433a2a2e4..883bacfb9 100644 --- a/shared-operations/functions/mergePDFs.ts +++ b/shared-operations/functions/mergePDFs.ts @@ -1,20 +1,18 @@ import { PDFDocument } from 'pdf-lib'; -import { PdfFile, convertAllToLibPdf, fromPDFDocument } from '../wrappers/PdfFile'; +import { PdfFile, convertAllToPdfLibFile, fromPdfLib } from '../wrappers/PdfFile'; export async function mergePDFs(files: PdfFile[]): Promise { - await convertAllToLibPdf(files); + const pdfLibFiles = await convertAllToPdfLibFile(files); const mergedPdf = await PDFDocument.create(); - for (let i = 0; i < files.length; i++) { - const pdfToMerge = files[i].pdfLib; - if (!pdfToMerge) continue; - + for (let i = 0; i < pdfLibFiles.length; i++) { + const pdfToMerge = await pdfLibFiles[i].getAsPdfLib(); const copiedPages = await mergedPdf.copyPages(pdfToMerge, pdfToMerge.getPageIndices()); copiedPages.forEach((page) => mergedPdf.addPage(page)); } - return fromPDFDocument(mergedPdf); + return fromPdfLib(mergedPdf); }; \ No newline at end of file diff --git a/shared-operations/functions/organizePages.ts b/shared-operations/functions/organizePages.ts deleted file mode 100644 index 26a1e5b0b..000000000 --- a/shared-operations/functions/organizePages.ts +++ /dev/null @@ -1,116 +0,0 @@ - -import { PDFDocument, PDFPage } from 'pdf-lib'; - -export async function organizePages( - snapshot: string | Uint8Array | ArrayBuffer, - operation: "CUSTOM_PAGE_ORDER" | - "REVERSE_ORDER" | - "DUPLEX_SORT" | - "BOOKLET_SORT" | - "ODD_EVEN_SPLIT" | - "REMOVE_FIRST" | - "REMOVE_LAST" | - "REMOVE_FIRST_AND_LAST", - customOrderString: string): Promise { - const pdfDoc = await PDFDocument.load(snapshot); - let subDocument = await PDFDocument.create(); - const copiedPages = await subDocument.copyPages(pdfDoc, pdfDoc.getPageIndices()); - - - const pageCount = pdfDoc.getPages().length; - - switch (operation) { - case "CUSTOM_PAGE_ORDER": - console.log("Custom Order"); - const pageOrderArray = parseCustomPageOrder(customOrderString, pageCount); - console.log(pageOrderArray); - - const customOrderedPages = pageOrderArray.map((pageIndex) => copiedPages[pageIndex]); - customOrderedPages.forEach((page) => subDocument.addPage(page)); - break; - case "REVERSE_ORDER": - const reversedPages: PDFPage[] = []; - for (let i = pageCount - 1; i >= 0; i--) { - reversedPages.push(copiedPages[i]); - } - reversedPages.forEach((page) => subDocument.addPage(page)); - break; - case 'DUPLEX_SORT': //TODO: Needs to be checked by someone who knows more about duplex printing. - const duplexPages: PDFPage[] = []; - const half = (pageCount + 1) / 2 - for (let i = 1; i <= half; i++) { - duplexPages.push(copiedPages[i - 1]); - if (i <= pageCount - half) { - duplexPages.push(copiedPages[pageCount - i]); - } - } - duplexPages.forEach((page) => subDocument.addPage(page)); - break; - case 'BOOKLET_SORT': - const bookletPages: PDFPage[] = []; - for (let i = 0; i < pageCount / 2; i++) { - bookletPages.push(copiedPages[i]); - bookletPages.push(copiedPages[pageCount - i - 1]); - } - bookletPages.forEach((page) => subDocument.addPage(page)); - break; - case 'ODD_EVEN_SPLIT': - const oddPages: PDFPage[] = []; - const evenPages: PDFPage[] = []; - for (let i = 0; i < pageCount; i++) { - if (i % 2 === 0) { - evenPages.push(copiedPages[i]); - } else { - oddPages.push(copiedPages[i]); - } - } - oddPages.forEach((page) => subDocument.addPage(page)); - evenPages.forEach((page) => subDocument.addPage(page)); - break; - case 'REMOVE_FIRST': - pdfDoc.removePage(0); - subDocument = pdfDoc; - break; - case 'REMOVE_LAST': - pdfDoc.removePage(pageCount - 1); - subDocument = pdfDoc; - break; - case 'REMOVE_FIRST_AND_LAST': - pdfDoc.removePage(0); - pdfDoc.removePage(pageCount - 2); - subDocument = pdfDoc; - break; - default: - throw new Error("Operation not supported"); - break; - } - - return subDocument.save(); -}; - -function parseCustomPageOrder(customOrder: string, pageCount: number) { - const pageOrderArray: number[] = []; - const ranges = customOrder.split(','); - - ranges.forEach((range) => { - if (range.includes('-')) { - const [start, end] = range.split('-').map(Number); - for (let i = start; i <= end; i++) { - pageOrderArray.push(i - 1); - } - } else if (range.includes('n')) { - const [even, odd] = range.split('n').map(Number); - for (let i = 1; i <= pageCount; i++) { - if (i % 2 === 0) { - pageOrderArray.push((i * even) - 1); - } else { - pageOrderArray.push((i * odd) - 1); - } - } - } else { - pageOrderArray.push(Number(range) - 1); - } - }); - - return pageOrderArray; -} \ No newline at end of file diff --git a/shared-operations/functions/removeBlankPages.ts b/shared-operations/functions/removeBlankPages.ts deleted file mode 100644 index af92d59c0..000000000 --- a/shared-operations/functions/removeBlankPages.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { PDFDocument } from 'pdf-lib'; -import { detectEmptyPages } from "./common/detectEmptyPages.js"; - -export async function removeBlankPages(snapshot: string | ArrayBuffer | Uint8Array, whiteThreashold: number) { - - const emptyPages = await detectEmptyPages(snapshot, whiteThreashold); - - 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(); -}; \ No newline at end of file diff --git a/shared-operations/functions/splitOn.ts b/shared-operations/functions/splitOn.ts index 0e09688bd..9ff8d4303 100644 --- a/shared-operations/functions/splitOn.ts +++ b/shared-operations/functions/splitOn.ts @@ -5,11 +5,12 @@ import jsQR from "jsqr"; import { detectEmptyPages } from "./common/detectEmptyPages.js"; import { getImagesOnPage } from "./common/getImagesOnPage.js"; -import { createSubDocument } from "./common/createSubDocument.js"; +import { selectPages } from "./subDocumentFunctions"; import { TypedArray, DocumentInitParameters } from 'pdfjs-dist/types/src/display/api.js'; +import { PdfFile } from '../wrappers/PdfFile.js'; export async function splitOn( - snapshot: string | ArrayBuffer | Uint8Array, + file: PdfFile, type: "BAR_CODE"|"QR_CODE"|"BLANK_PAGE", whiteThreashold: number) { let splitAtPages: number[] = []; @@ -20,11 +21,11 @@ export async function splitOn( throw new Error("This split-type has not been implemented yet"); case "QR_CODE": - splitAtPages = await getPagesWithQRCode(snapshot); + splitAtPages = await getPagesWithQRCode(file); break; case "BLANK_PAGE": - splitAtPages = await detectEmptyPages(snapshot, whiteThreashold); + splitAtPages = await detectEmptyPages(file, whiteThreashold); break; default: @@ -34,19 +35,18 @@ export async function splitOn( console.log("Split At Pages: ", splitAtPages); // Remove detected Pages & Split - const pdfDoc = await PDFDocument.load(snapshot); - - const numberOfPages = pdfDoc.getPages().length; + const pdfDoc = await file.getAsPdfLib(); + const numberOfPages = pdfDoc.getPageCount(); let pagesArray: number[] = []; let splitAfter = splitAtPages.shift(); - const subDocuments: Uint8Array[] = []; + const subDocuments: PdfFile[] = []; for (let i = 0; i < numberOfPages; i++) { console.log(i); if(i == splitAfter) { if(pagesArray.length > 0) { - subDocuments.push(await createSubDocument(pdfDoc, pagesArray)); + subDocuments.push(await selectPages(file, pagesArray)); pagesArray = []; } splitAfter = splitAtPages.shift(); @@ -57,14 +57,14 @@ export async function splitOn( } } if(pagesArray.length > 0) { - subDocuments.push(await createSubDocument(pdfDoc, pagesArray)); + subDocuments.push(await selectPages(file, pagesArray)); } pagesArray = []; return subDocuments; - async function getPagesWithQRCode(snapshot: string | ArrayBuffer | URL | TypedArray | DocumentInitParameters) { - const pdfDoc = await PDFJS.getDocument(snapshot).promise; + async function getPagesWithQRCode(file: PdfFile) { + const pdfDoc = await file.getAsPdfJs(); const pagesWithQR: number[] = []; for (let i = 0; i < pdfDoc.numPages; i++) { @@ -110,4 +110,4 @@ export async function splitOn( else return null; } -}; \ No newline at end of file +}; diff --git a/shared-operations/functions/splitPDF.ts b/shared-operations/functions/splitPDF.ts index 6f119b48b..edf7597dc 100644 --- a/shared-operations/functions/splitPDF.ts +++ b/shared-operations/functions/splitPDF.ts @@ -1,27 +1,28 @@ import { PDFDocument } from 'pdf-lib'; -import { createSubDocument } from "./common/createSubDocument.js"; +import { selectPages } from "./subDocumentFunctions"; +import { PdfFile } from '../wrappers/PdfFile'; -export async function splitPDF(snapshot: string | Uint8Array | ArrayBuffer, splitAfterPageArray: number[]): Promise { +export async function splitPDF(file: PdfFile, splitAfterPageArray: number[]): Promise { + const byteFile = await file.convertToPdfLibFile(); + if (!byteFile?.pdfLib) return []; - const pdfDoc = await PDFDocument.load(snapshot) - - const numberOfPages = pdfDoc.getPages().length; + const numberOfPages = byteFile.pdfLib.getPages().length; let pagesArray: number[] = []; let splitAfter = splitAfterPageArray.shift(); - const subDocuments: Uint8Array[] = []; + const subDocuments: PdfFile[] = []; for (let i = 0; i < numberOfPages; i++) { if(splitAfter && i > splitAfter && pagesArray.length > 0) { - subDocuments.push(await createSubDocument(pdfDoc, pagesArray)); + subDocuments.push(await selectPages(byteFile, pagesArray)); splitAfter = splitAfterPageArray.shift(); pagesArray = []; } pagesArray.push(i); } - subDocuments.push(await createSubDocument(pdfDoc, pagesArray)); + subDocuments.push(await selectPages(byteFile, pagesArray)); pagesArray = []; return subDocuments; diff --git a/shared-operations/functions/subDocumentFunctions.ts b/shared-operations/functions/subDocumentFunctions.ts new file mode 100644 index 000000000..9c393d428 --- /dev/null +++ b/shared-operations/functions/subDocumentFunctions.ts @@ -0,0 +1,227 @@ + +import { PDFDocument } from 'pdf-lib'; +import { PdfFile, fromPdfLib } from '../wrappers/PdfFile.js'; +import { detectEmptyPages } from "./common/detectEmptyPages.js"; + + +export async function sortPagesWithPreset(file: PdfFile, sortPreset: string, fancyPageSelector: string) { + if (sortPreset === "CUSTOM_PAGE_ORDER") { + return rearrangePages(file, fancyPageSelector); + } + + const sortFunction = sorts[sortPreset]; + if (!sortFunction) { + throw new Error("Operation not supported"); + } + + const byteFile = await file.convertToPdfLibFile(); + if (!byteFile?.pdfLib) return byteFile; + + const pageCount = byteFile.pdfLib.getPageCount(); + const sortIndecies = sortFunction(pageCount); + return selectPages(byteFile, sortIndecies); +} + +export async function rearrangePages(file: PdfFile, fancyPageSelector: string): Promise { + const byteFile = await file.convertToPdfLibFile(); + if (!byteFile?.pdfLib) return byteFile; + + const pagesToExtractArray = parseFancyPageSelector(fancyPageSelector, byteFile.pdfLib.getPageCount()); + const newDocument = selectPages(byteFile, pagesToExtractArray); + return newDocument; +}; + +export async function selectPages(file: PdfFile, pagesToExtractArray: number[]): Promise { + const byteFile = await file.convertToPdfLibFile(); + if (!byteFile?.pdfLib) return byteFile; + + const subDocument = await PDFDocument.create(); + + // Check that array max number is not larger pdf pages number + if(Math.max(...pagesToExtractArray) >= byteFile.pdfLib.getPageCount()) { + throw new Error(`The PDF document only has ${byteFile.pdfLib.getPageCount()} pages and you tried to extract page ${Math.max(...pagesToExtractArray)}`); + } + + const copiedPages = await subDocument.copyPages(byteFile.pdfLib, pagesToExtractArray); + + for (let i = 0; i < copiedPages.length; i++) { + subDocument.addPage(copiedPages[i]); + } + + return fromPdfLib(subDocument); +} + +export async function removePages(file: PdfFile, pagesToRemoveArray: number[]): Promise { + const byteFile = await file.convertToPdfLibFile(); + if (!byteFile?.pdfLib) return byteFile; + + const pagesToExtractArray = invertSelection(pagesToRemoveArray, byteFile.pdfLib.getPageIndices()) + return selectPages(byteFile, pagesToExtractArray); +} + +export async function removeBlankPages(file: PdfFile, whiteThreashold: number) { + const emptyPages = await detectEmptyPages(file, whiteThreashold); + console.log("Empty Pages: ", emptyPages); + return removePages(file, emptyPages); +} + + +/** + * Parse the page selector string used in the 'PDF Page Organizer' + * @param pageOrderArr + * @param totalPages + * @returns + */ +function parseFancyPageSelector(pageNumbers: string, totalPages: number): number[] { + // Translated to JS from the original Java function + const pageOrderArr = pageNumbers.split(",") + const newPageOrder: number[] = []; + + // loop through the page order array + pageOrderArr.forEach(element => { + if (element.toLocaleLowerCase() === "all") { + for (var i = 0; i < totalPages; i++) { + newPageOrder.push(i); + } + // As all pages are already added, no need to check further + return; + } + else if (element.match("\\d*n\\+?-?\\d*|\\d*\\+?n")) { + // Handle page order as a function + var coefficient = 0; + var constant = 0; + var coefficientExists = false; + var constantExists = false; + + if (element.includes("n")) { + var parts = element.split("n"); + if (!parts[0]) { + coefficient = parseInt(parts[0]); + coefficientExists = true; + } + if (parts.length > 1 && parts[1]) { + constant = parseInt(parts[1]); + constantExists = true; + } + } else if (element.includes("+")) { + constant = parseInt(element.replace("+", "")); + constantExists = true; + } + + for (var i = 1; i <= totalPages; i++) { + var pageNum = coefficientExists ? coefficient * i : i; + pageNum += constantExists ? constant : 0; + + if (pageNum <= totalPages && pageNum > 0) { + newPageOrder.push(pageNum - 1); + } + } + } else if (element.includes("-")) { + // split the range into start and end page + const range = element.split("-"); + const start = parseInt(range[0]); + var end = parseInt(range[1]); + // check if the end page is greater than total pages + if (end > totalPages) { + end = totalPages; + } + // loop through the range of pages + for (var j = start; j <= end; j++) { + // print the current index + newPageOrder.push(j - 1); + } + } else { + // if the element is a single page + newPageOrder.push(parseInt(element) - 1); + } + }); + + return newPageOrder; +} + +function invertSelection(selection: number[], pageIndecies: number[]): number[] { + const pageIndeciesCopy = [...pageIndecies]; + return pageIndeciesCopy.filter(x => !selection.includes(x)); +} + +////////////////// +// Page Sorters // +////////////////// +function reverseSort(totalPages: number): number[] { + return [...Array(totalPages).keys()].reverse(); +} + +function duplexSort(totalPages: number): number[] { + // Translated to JS from the original Java function + const newPageOrder: number[] = []; + const half = Math.floor((totalPages + 1) / 2); // This ensures proper behavior with odd numbers of pages + + for (let i = 1; i <= half; i++) { + newPageOrder.push(i - 1); + if (i <= totalPages - half) { + // Avoid going out of bounds + newPageOrder.push(totalPages - i); + } + } + + return newPageOrder; +} + +function bookletSort(totalPages: number): number[] { + const newPageOrder: number[] = []; + for (let i = 0; i < totalPages / 2; i++) { + newPageOrder.push(i); + newPageOrder.push(totalPages - i - 1); + } + return newPageOrder; +} + +function sideStitchBooklet(totalPages: number): number[] { + const newPageOrder: number[] = []; + for (let i = 0; i < (totalPages + 3) / 4; i++) { + const begin = i * 4; + newPageOrder.push(Math.min(begin + 3, totalPages - 1)); + newPageOrder.push(Math.min(begin, totalPages - 1)); + newPageOrder.push(Math.min(begin + 1, totalPages - 1)); + newPageOrder.push(Math.min(begin + 2, totalPages - 1)); + } + return newPageOrder; +} + +function oddEvenSplit(totalPages: number): number[] { + const newPageOrder: number[] = []; + for (let i = 1; i <= totalPages; i += 2) { + newPageOrder.push(i - 1); + } + for (let i = 2; i <= totalPages; i += 2) { + newPageOrder.push(i - 1); + } + return newPageOrder; +} + +function removeFirst(totalPages: number): number[] { + return [...Array(totalPages-1).keys()].map(i => i+1); +} + +function removeLast(totalPages: number): number[] { + return [...Array(totalPages-1).keys()]; +} + +function removeFirstAndLast(totalPages: number): number[] { + return [...Array(totalPages-2).keys()].map(i => i+1); +} + +export type SortFunction = (totalPages: number) => number[]; +type Sorts = { + [key: string]: SortFunction; +}; +export const sorts: Sorts = Object.freeze({ + "REVERSE_ORDER": reverseSort, + "DUPLEX_SORT": duplexSort, + "BOOKLET_SORT": bookletSort, + "SIDE_STITCH_BOOKLET_SORT": sideStitchBooklet, + "ODD_EVEN_SPLIT": oddEvenSplit, + "REMOVE_FIRST": removeFirst, + "REMOVE_LAST": removeLast, + "REMOVE_FIRST_AND_LAST": removeFirstAndLast, +}); diff --git a/shared-operations/index.ts b/shared-operations/index.ts index 1ffa5cd03..bad36c99d 100644 --- a/shared-operations/index.ts +++ b/shared-operations/index.ts @@ -1,11 +1,9 @@ // Import injected libraries here! -import { extractPages } from "./functions/extractPages"; +import { sortPagesWithPreset, rearrangePages, selectPages, removePages, removeBlankPages } from "./functions/subDocumentFunctions"; import { impose } from "./functions/impose"; import { mergePDFs } from './functions/mergePDFs'; -import { organizePages } from './functions/organizePages'; -import { removeBlankPages } from './functions/removeBlankPages'; import { rotatePages } from './functions/rotatePages'; import { scaleContent} from './functions/scaleContent'; import { scalePage } from './functions/scalePage'; @@ -14,11 +12,9 @@ import { splitPDF } from './functions/splitPDF'; import { updateMetadata } from "./functions/updateMetadata"; export default { - extractPages, + sortPagesWithPreset, rearrangePages, selectPages, removePages, removeBlankPages, impose, mergePDFs, - organizePages, - removeBlankPages, rotatePages, scaleContent, scalePage, diff --git a/shared-operations/wrappers/PdfFile.ts b/shared-operations/wrappers/PdfFile.ts index 386170b94..5faa68285 100644 --- a/shared-operations/wrappers/PdfFile.ts +++ b/shared-operations/wrappers/PdfFile.ts @@ -1,56 +1,95 @@ import { PDFDocument } from 'pdf-lib'; +import * as PDFJS from 'pdfjs-dist'; +import { PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api'; export class PdfFile { byteArray: Uint8Array | null; pdfLib: PDFDocument | null; + pdfJs: PDFDocumentProxy | null; filename?: string; constructor() { this.byteArray = null; this.pdfLib = null; + this.pdfJs = null; } - async convertToByteArray(): Promise { + async convertToByteArrayFile(): Promise { + if (this.byteArray) return this; + + var byteArray: Uint8Array|null = null; if (this.pdfLib) { - this.byteArray = await this.pdfLib.save(); - this.pdfLib = null; + byteArray = await this.pdfLib.save(); + } else if (this.pdfJs) { + byteArray = await this.pdfJs.getData(); } + return fromUint8Array(byteArray!, this.filename); } - async convertToLibPdf(): Promise { - if (this.byteArray) { - this.pdfLib = await PDFDocument.load(this.byteArray, { - updateMetadata: false, - }); - this.byteArray = null; - } + 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); + } + + async getAsByteArray(): Promise { + const file = await this.convertToByteArrayFile(); + return file.byteArray!; + } + async getAsPdfLib(): Promise { + const file = await this.convertToPdfLibFile(); + return file.pdfLib!; + } + async getAsPdfJs(): Promise { + const file = await this.convertToPdfJsFile(); + return file.pdfJs!; } } -export function fromMulterFile(value: Express.Multer.File, filename?: string) { +export function fromMulterFile(value: Express.Multer.File, filename?: string): PdfFile { return fromUint8Array(value.buffer, filename) } -export function fromUint8Array(value: Uint8Array, filename?: string) { +export function fromUint8Array(value: Uint8Array, filename?: string): PdfFile { const out = new PdfFile(); out.byteArray = value; out.filename = filename; return out; } -export function fromPDFDocument(value: PDFDocument, filename?: string) { +export function fromPdfLib(value: PDFDocument, filename?: string): PdfFile { const out = new PdfFile(); out.pdfLib = value; out.filename = filename; return out; } - -export async function convertAllToByteArray(files: PdfFile[]): Promise { - const pdfPromises = files.map(s => s.convertToByteArray()); - await Promise.all(pdfPromises); - +export function fromPdfJs(value: PDFDocumentProxy, filename?: string): PdfFile { + const out = new PdfFile(); + out.pdfJs = value; + out.filename = filename; + return out; } -export async function convertAllToLibPdf(files: PdfFile[]): Promise { - const pdfPromises = files.map(s => s.convertToLibPdf()); - await Promise.all(pdfPromises); - +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); }