mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-06 18:30:57 +00:00
Added support for PDFJS to PdfFile. Migrated some operations to use this new wrapper
This commit is contained in:
parent
3fad22c4fe
commit
0d915fcc33
@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
import { PDFDocument } from 'pdf-lib';
|
|
||||||
|
|
||||||
export async function createSubDocument(pdfDoc: PDFDocument, pagesToExtractArray: number[]): Promise<Uint8Array> {
|
|
||||||
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();
|
|
||||||
}
|
|
@ -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 { Image } from 'image-js';
|
||||||
|
|
||||||
import { getImagesOnPage } from "./getImagesOnPage.js";
|
import { getImagesOnPage } from "./getImagesOnPage.js";
|
||||||
|
|
||||||
export async function detectEmptyPages(snapshot: string | URL | ArrayBuffer | DocumentInitParameters, whiteThreashold: number) {
|
export async function detectEmptyPages(file: PdfFile, whiteThreashold: number): Promise<number[]> {
|
||||||
const pdfDoc = await PDFJS.getDocument(snapshot).promise;
|
const pdfDoc = await file.getAsPdfJs();
|
||||||
|
|
||||||
const emptyPages: number[] = [];
|
const emptyPages: number[] = [];
|
||||||
for (let i = 1; i <= pdfDoc.numPages; i++) {
|
for (let i = 1; i <= pdfDoc.numPages; i++) {
|
||||||
|
47
shared-operations/functions/common/pdf-utils.ts
Normal file
47
shared-operations/functions/common/pdf-utils.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
import { PdfFile, convertAllToPdfLibFile } from '../../wrappers/PdfFile';
|
||||||
|
|
||||||
|
export async function sortPdfs(
|
||||||
|
files: PdfFile[],
|
||||||
|
sortType: "orderProvided"|"byFileName"|"byDateModified"|"byDateCreated"|"byPDFTitle" = "orderProvided"
|
||||||
|
): Promise<PdfFile[]> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@ -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<Uint8Array>{
|
|
||||||
const pdfDoc = await PDFDocument.load(snapshot)
|
|
||||||
|
|
||||||
// TODO: invent a better format for pagesToExtractArray and convert it.
|
|
||||||
return createSubDocument(pdfDoc, pagesToExtractArray);
|
|
||||||
};
|
|
@ -1,20 +1,18 @@
|
|||||||
|
|
||||||
import { PDFDocument } from 'pdf-lib';
|
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<PdfFile> {
|
export async function mergePDFs(files: PdfFile[]): Promise<PdfFile> {
|
||||||
|
|
||||||
await convertAllToLibPdf(files);
|
const pdfLibFiles = await convertAllToPdfLibFile(files);
|
||||||
|
|
||||||
const mergedPdf = await PDFDocument.create();
|
const mergedPdf = await PDFDocument.create();
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < pdfLibFiles.length; i++) {
|
||||||
const pdfToMerge = files[i].pdfLib;
|
const pdfToMerge = await pdfLibFiles[i].getAsPdfLib();
|
||||||
if (!pdfToMerge) continue;
|
|
||||||
|
|
||||||
const copiedPages = await mergedPdf.copyPages(pdfToMerge, pdfToMerge.getPageIndices());
|
const copiedPages = await mergedPdf.copyPages(pdfToMerge, pdfToMerge.getPageIndices());
|
||||||
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
return fromPDFDocument(mergedPdf);
|
return fromPdfLib(mergedPdf);
|
||||||
};
|
};
|
@ -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<Uint8Array> {
|
|
||||||
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;
|
|
||||||
}
|
|
@ -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();
|
|
||||||
};
|
|
@ -5,11 +5,12 @@ import jsQR from "jsqr";
|
|||||||
|
|
||||||
import { detectEmptyPages } from "./common/detectEmptyPages.js";
|
import { detectEmptyPages } from "./common/detectEmptyPages.js";
|
||||||
import { getImagesOnPage } from "./common/getImagesOnPage.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 { TypedArray, DocumentInitParameters } from 'pdfjs-dist/types/src/display/api.js';
|
||||||
|
import { PdfFile } from '../wrappers/PdfFile.js';
|
||||||
|
|
||||||
export async function splitOn(
|
export async function splitOn(
|
||||||
snapshot: string | ArrayBuffer | Uint8Array,
|
file: PdfFile,
|
||||||
type: "BAR_CODE"|"QR_CODE"|"BLANK_PAGE",
|
type: "BAR_CODE"|"QR_CODE"|"BLANK_PAGE",
|
||||||
whiteThreashold: number) {
|
whiteThreashold: number) {
|
||||||
let splitAtPages: number[] = [];
|
let splitAtPages: number[] = [];
|
||||||
@ -20,11 +21,11 @@ export async function splitOn(
|
|||||||
throw new Error("This split-type has not been implemented yet");
|
throw new Error("This split-type has not been implemented yet");
|
||||||
|
|
||||||
case "QR_CODE":
|
case "QR_CODE":
|
||||||
splitAtPages = await getPagesWithQRCode(snapshot);
|
splitAtPages = await getPagesWithQRCode(file);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "BLANK_PAGE":
|
case "BLANK_PAGE":
|
||||||
splitAtPages = await detectEmptyPages(snapshot, whiteThreashold);
|
splitAtPages = await detectEmptyPages(file, whiteThreashold);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -34,19 +35,18 @@ export async function splitOn(
|
|||||||
console.log("Split At Pages: ", splitAtPages);
|
console.log("Split At Pages: ", splitAtPages);
|
||||||
|
|
||||||
// Remove detected Pages & Split
|
// Remove detected Pages & Split
|
||||||
const pdfDoc = await PDFDocument.load(snapshot);
|
const pdfDoc = await file.getAsPdfLib();
|
||||||
|
const numberOfPages = pdfDoc.getPageCount();
|
||||||
const numberOfPages = pdfDoc.getPages().length;
|
|
||||||
|
|
||||||
let pagesArray: number[] = [];
|
let pagesArray: number[] = [];
|
||||||
let splitAfter = splitAtPages.shift();
|
let splitAfter = splitAtPages.shift();
|
||||||
const subDocuments: Uint8Array[] = [];
|
const subDocuments: PdfFile[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < numberOfPages; i++) {
|
for (let i = 0; i < numberOfPages; i++) {
|
||||||
console.log(i);
|
console.log(i);
|
||||||
if(i == splitAfter) {
|
if(i == splitAfter) {
|
||||||
if(pagesArray.length > 0) {
|
if(pagesArray.length > 0) {
|
||||||
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
|
subDocuments.push(await selectPages(file, pagesArray));
|
||||||
pagesArray = [];
|
pagesArray = [];
|
||||||
}
|
}
|
||||||
splitAfter = splitAtPages.shift();
|
splitAfter = splitAtPages.shift();
|
||||||
@ -57,14 +57,14 @@ export async function splitOn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(pagesArray.length > 0) {
|
if(pagesArray.length > 0) {
|
||||||
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
|
subDocuments.push(await selectPages(file, pagesArray));
|
||||||
}
|
}
|
||||||
pagesArray = [];
|
pagesArray = [];
|
||||||
|
|
||||||
return subDocuments;
|
return subDocuments;
|
||||||
|
|
||||||
async function getPagesWithQRCode(snapshot: string | ArrayBuffer | URL | TypedArray | DocumentInitParameters) {
|
async function getPagesWithQRCode(file: PdfFile) {
|
||||||
const pdfDoc = await PDFJS.getDocument(snapshot).promise;
|
const pdfDoc = await file.getAsPdfJs();
|
||||||
|
|
||||||
const pagesWithQR: number[] = [];
|
const pagesWithQR: number[] = [];
|
||||||
for (let i = 0; i < pdfDoc.numPages; i++) {
|
for (let i = 0; i < pdfDoc.numPages; i++) {
|
||||||
|
@ -1,27 +1,28 @@
|
|||||||
|
|
||||||
import { PDFDocument } from 'pdf-lib';
|
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<Uint8Array[]> {
|
export async function splitPDF(file: PdfFile, splitAfterPageArray: number[]): Promise<PdfFile[]> {
|
||||||
|
const byteFile = await file.convertToPdfLibFile();
|
||||||
|
if (!byteFile?.pdfLib) return [];
|
||||||
|
|
||||||
const pdfDoc = await PDFDocument.load(snapshot)
|
const numberOfPages = byteFile.pdfLib.getPages().length;
|
||||||
|
|
||||||
const numberOfPages = pdfDoc.getPages().length;
|
|
||||||
|
|
||||||
let pagesArray: number[] = [];
|
let pagesArray: number[] = [];
|
||||||
let splitAfter = splitAfterPageArray.shift();
|
let splitAfter = splitAfterPageArray.shift();
|
||||||
const subDocuments: Uint8Array[] = [];
|
const subDocuments: PdfFile[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < numberOfPages; i++) {
|
for (let i = 0; i < numberOfPages; i++) {
|
||||||
if(splitAfter && i > splitAfter && pagesArray.length > 0) {
|
if(splitAfter && i > splitAfter && pagesArray.length > 0) {
|
||||||
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
|
subDocuments.push(await selectPages(byteFile, pagesArray));
|
||||||
splitAfter = splitAfterPageArray.shift();
|
splitAfter = splitAfterPageArray.shift();
|
||||||
pagesArray = [];
|
pagesArray = [];
|
||||||
}
|
}
|
||||||
pagesArray.push(i);
|
pagesArray.push(i);
|
||||||
}
|
}
|
||||||
subDocuments.push(await createSubDocument(pdfDoc, pagesArray));
|
subDocuments.push(await selectPages(byteFile, pagesArray));
|
||||||
pagesArray = [];
|
pagesArray = [];
|
||||||
|
|
||||||
return subDocuments;
|
return subDocuments;
|
||||||
|
227
shared-operations/functions/subDocumentFunctions.ts
Normal file
227
shared-operations/functions/subDocumentFunctions.ts
Normal file
@ -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<PdfFile> {
|
||||||
|
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<PdfFile> {
|
||||||
|
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<PdfFile> {
|
||||||
|
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,
|
||||||
|
});
|
@ -1,11 +1,9 @@
|
|||||||
|
|
||||||
// Import injected libraries here!
|
// Import injected libraries here!
|
||||||
|
|
||||||
import { extractPages } from "./functions/extractPages";
|
import { sortPagesWithPreset, rearrangePages, selectPages, removePages, removeBlankPages } from "./functions/subDocumentFunctions";
|
||||||
import { impose } from "./functions/impose";
|
import { impose } from "./functions/impose";
|
||||||
import { mergePDFs } from './functions/mergePDFs';
|
import { mergePDFs } from './functions/mergePDFs';
|
||||||
import { organizePages } from './functions/organizePages';
|
|
||||||
import { removeBlankPages } from './functions/removeBlankPages';
|
|
||||||
import { rotatePages } from './functions/rotatePages';
|
import { rotatePages } from './functions/rotatePages';
|
||||||
import { scaleContent} from './functions/scaleContent';
|
import { scaleContent} from './functions/scaleContent';
|
||||||
import { scalePage } from './functions/scalePage';
|
import { scalePage } from './functions/scalePage';
|
||||||
@ -14,11 +12,9 @@ import { splitPDF } from './functions/splitPDF';
|
|||||||
import { updateMetadata } from "./functions/updateMetadata";
|
import { updateMetadata } from "./functions/updateMetadata";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
extractPages,
|
sortPagesWithPreset, rearrangePages, selectPages, removePages, removeBlankPages,
|
||||||
impose,
|
impose,
|
||||||
mergePDFs,
|
mergePDFs,
|
||||||
organizePages,
|
|
||||||
removeBlankPages,
|
|
||||||
rotatePages,
|
rotatePages,
|
||||||
scaleContent,
|
scaleContent,
|
||||||
scalePage,
|
scalePage,
|
||||||
|
@ -1,56 +1,95 @@
|
|||||||
|
|
||||||
import { PDFDocument } from 'pdf-lib';
|
import { PDFDocument } from 'pdf-lib';
|
||||||
|
import * as PDFJS from 'pdfjs-dist';
|
||||||
|
import { PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api';
|
||||||
|
|
||||||
export class PdfFile {
|
export class PdfFile {
|
||||||
byteArray: Uint8Array | null;
|
byteArray: Uint8Array | null;
|
||||||
pdfLib: PDFDocument | null;
|
pdfLib: PDFDocument | null;
|
||||||
|
pdfJs: PDFDocumentProxy | null;
|
||||||
filename?: string;
|
filename?: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.byteArray = null;
|
this.byteArray = null;
|
||||||
this.pdfLib = null;
|
this.pdfLib = null;
|
||||||
|
this.pdfJs = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async convertToByteArray(): Promise<void> {
|
async convertToByteArrayFile(): Promise<PdfFile> {
|
||||||
|
if (this.byteArray) return this;
|
||||||
|
|
||||||
|
var byteArray: Uint8Array|null = null;
|
||||||
if (this.pdfLib) {
|
if (this.pdfLib) {
|
||||||
this.byteArray = await this.pdfLib.save();
|
byteArray = await this.pdfLib.save();
|
||||||
this.pdfLib = null;
|
} else if (this.pdfJs) {
|
||||||
|
byteArray = await this.pdfJs.getData();
|
||||||
}
|
}
|
||||||
|
return fromUint8Array(byteArray!, this.filename);
|
||||||
}
|
}
|
||||||
async convertToLibPdf(): Promise<void> {
|
async convertToPdfLibFile(): Promise<PdfFile> {
|
||||||
if (this.byteArray) {
|
if (this.pdfLib) return this;
|
||||||
this.pdfLib = await PDFDocument.load(this.byteArray, {
|
|
||||||
|
const byteFile = await this.convertToByteArrayFile();
|
||||||
|
const pdfLib = await PDFDocument.load(byteFile.byteArray!, {
|
||||||
updateMetadata: false,
|
updateMetadata: false,
|
||||||
});
|
});
|
||||||
this.byteArray = null;
|
return fromPdfLib(pdfLib, this.filename);
|
||||||
}
|
}
|
||||||
|
async convertToPdfJsFile(): Promise<PdfFile> {
|
||||||
|
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<Uint8Array> {
|
||||||
|
const file = await this.convertToByteArrayFile();
|
||||||
|
return file.byteArray!;
|
||||||
|
}
|
||||||
|
async getAsPdfLib(): Promise<PDFDocument> {
|
||||||
|
const file = await this.convertToPdfLibFile();
|
||||||
|
return file.pdfLib!;
|
||||||
|
}
|
||||||
|
async getAsPdfJs(): Promise<PDFDocumentProxy> {
|
||||||
|
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)
|
return fromUint8Array(value.buffer, filename)
|
||||||
}
|
}
|
||||||
export function fromUint8Array(value: Uint8Array, filename?: string) {
|
export function fromUint8Array(value: Uint8Array, filename?: string): PdfFile {
|
||||||
const out = new PdfFile();
|
const out = new PdfFile();
|
||||||
out.byteArray = value;
|
out.byteArray = value;
|
||||||
out.filename = filename;
|
out.filename = filename;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
export function fromPDFDocument(value: PDFDocument, filename?: string) {
|
export function fromPdfLib(value: PDFDocument, filename?: string): PdfFile {
|
||||||
const out = new PdfFile();
|
const out = new PdfFile();
|
||||||
out.pdfLib = value;
|
out.pdfLib = value;
|
||||||
out.filename = filename;
|
out.filename = filename;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
export function fromPdfJs(value: PDFDocumentProxy, filename?: string): PdfFile {
|
||||||
export async function convertAllToByteArray(files: PdfFile[]): Promise<void> {
|
const out = new PdfFile();
|
||||||
const pdfPromises = files.map(s => s.convertToByteArray());
|
out.pdfJs = value;
|
||||||
await Promise.all(pdfPromises);
|
out.filename = filename;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function convertAllToLibPdf(files: PdfFile[]): Promise<void> {
|
export async function convertAllToByteArrayFile(files: PdfFile[]): Promise<(PdfFile)[]> {
|
||||||
const pdfPromises = files.map(s => s.convertToLibPdf());
|
const pdfPromises = files.map(s => s.convertToByteArrayFile());
|
||||||
await Promise.all(pdfPromises);
|
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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user