Add document splitting functionality to the multi-tools page (#1808)
* Add a split button on top of the insert button in multitool viewer. * Add placeholder splitFileButtonCallback method. * Remove unused splitFileButtonContainer element. * Add this binding to setActions for splitFileButtonCallback * Add test log for adding separators. * Add test log for adding separators. * Remove test logs and add visual indicators to separators instead. * Add splitting functionality to multi-tools. * Prevent trying to split from index 0. * Hide the split button for the first page to avoid confusion. * Change the class name 'cutBefore' to 'split-before' to fall mroe in line with already existing classes. * Add dummy methods for splitting and compressing documents. * Remove form submission, begin work on client side splitting. * Add client side document splitting. * Add client side archiving for the split documents. * Fix a bug that adds an empty page to splitted documents due to a sorting error. * Add a 'Split All' button and the relevant functionality. --------- Co-authored-by: kazandaki <ahmetfiratusta@gmail.com> Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,9 @@ class PdfContainer {
|
||||
this.setDownloadAttribute = this.setDownloadAttribute.bind(this);
|
||||
this.preventIllegalChars = this.preventIllegalChars.bind(this);
|
||||
this.addImageFile = this.addImageFile.bind(this);
|
||||
this.nameAndArchiveFiles = this.nameAndArchiveFiles.bind(this);
|
||||
this.splitPDF = this.splitPDF.bind(this);
|
||||
this.splitAll = this.splitAll.bind(this);
|
||||
|
||||
this.pdfAdapters = pdfAdapters;
|
||||
|
||||
@@ -34,6 +37,7 @@ class PdfContainer {
|
||||
window.addFiles = this.addFiles;
|
||||
window.exportPdf = this.exportPdf;
|
||||
window.rotateAll = this.rotateAll;
|
||||
window.splitAll = this.splitAll;
|
||||
|
||||
const filenameInput = document.getElementById("filename-input");
|
||||
const downloadBtn = document.getElementById("export-button");
|
||||
@@ -212,6 +216,61 @@ class PdfContainer {
|
||||
}
|
||||
}
|
||||
|
||||
splitAll() {
|
||||
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
||||
if (this.pagesContainer.querySelectorAll(".split-before").length > 0) {
|
||||
allPages.forEach(page => {
|
||||
page.classList.remove("split-before");
|
||||
});
|
||||
} else {
|
||||
allPages.forEach(page => {
|
||||
page.classList.add("split-before");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async splitPDF(baseDocBytes, splitters) {
|
||||
const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes);
|
||||
const pageNum = baseDocument.getPages().length;
|
||||
|
||||
splitters.sort((a, b) => a - b);; // We'll sort the separator indexes just in case querySelectorAll does something funny.
|
||||
splitters.push(pageNum); // We'll also add a faux separator at the end in order to get the pages after the last separator.
|
||||
|
||||
const splitDocuments = [];
|
||||
for (const splitterPosition of splitters) {
|
||||
const subDocument = await PDFLib.PDFDocument.create();
|
||||
|
||||
const splitterIndex = splitters.indexOf(splitterPosition);
|
||||
|
||||
let firstPage = splitterIndex === 0 ? 0 : splitters[splitterIndex - 1];
|
||||
|
||||
const pageIndices = Array.from({ length: splitterPosition - firstPage }, (value, key) => firstPage + key);
|
||||
|
||||
const copiedPages = await subDocument.copyPages(baseDocument, pageIndices);
|
||||
|
||||
copiedPages.forEach(copiedPage => {
|
||||
subDocument.addPage(copiedPage);
|
||||
});
|
||||
|
||||
const subDocumentBytes = await subDocument.save();
|
||||
|
||||
splitDocuments.push(subDocumentBytes);
|
||||
};
|
||||
|
||||
return splitDocuments;
|
||||
}
|
||||
|
||||
async nameAndArchiveFiles(pdfBytesArray, baseNameString) {
|
||||
const zip = new JSZip();
|
||||
|
||||
for (let i = 0; i < pdfBytesArray.length; i++) {
|
||||
const documentBlob = new Blob([pdfBytesArray[i]], { type: "application/pdf" });
|
||||
zip.file(baseNameString + "-" + (i + 1) + ".pdf", documentBlob);
|
||||
}
|
||||
|
||||
return zip;
|
||||
}
|
||||
|
||||
async exportPdf() {
|
||||
const pdfDoc = await PDFLib.PDFDocument.create();
|
||||
const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements
|
||||
@@ -262,8 +321,6 @@ class PdfContainer {
|
||||
}
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" });
|
||||
const url = URL.createObjectURL(pdfBlob);
|
||||
const downloadOption = localStorage.getItem("downloadOption");
|
||||
|
||||
const filenameInput = document.getElementById("filename-input");
|
||||
|
||||
@@ -280,28 +337,60 @@ class PdfContainer {
|
||||
this.fileName = filenameInput.value;
|
||||
}
|
||||
|
||||
if (!filenameInput.value.includes(".pdf")) {
|
||||
filenameInput.value = filenameInput.value + ".pdf";
|
||||
this.fileName = filenameInput.value;
|
||||
}
|
||||
const separators = this.pagesContainer.querySelectorAll(".split-before");
|
||||
if (separators.length !== 0) { // Split the pdf if there are separators.
|
||||
const baseName = this.fileName ? this.fileName : "managed";
|
||||
|
||||
if (downloadOption === "sameWindow") {
|
||||
// Open the file in the same window
|
||||
window.location.href = url;
|
||||
} else if (downloadOption === "newWindow") {
|
||||
// Open the file in a new window
|
||||
window.open(url, "_blank");
|
||||
} else {
|
||||
// Download the file
|
||||
this.downloadLink = document.createElement("a");
|
||||
this.downloadLink.id = "download-link";
|
||||
this.downloadLink.href = url;
|
||||
// downloadLink.download = this.fileName ? this.fileName : 'managed.pdf';
|
||||
// downloadLink.download = this.fileName;
|
||||
this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
|
||||
this.downloadLink.setAttribute("target", "_blank");
|
||||
this.downloadLink.onclick = this.setDownloadAttribute;
|
||||
this.downloadLink.click();
|
||||
const pagesArray = Array.from(this.pagesContainer.children);
|
||||
const splitters = [];
|
||||
separators.forEach(page => {
|
||||
const pageIndex = pagesArray.indexOf(page);
|
||||
if (pageIndex !== 0) {
|
||||
splitters.push(pageIndex);
|
||||
}
|
||||
});
|
||||
|
||||
const splitDocuments = await this.splitPDF(pdfBytes, splitters);
|
||||
const archivedDocuments = await this.nameAndArchiveFiles(splitDocuments, baseName);
|
||||
|
||||
const self = this;
|
||||
archivedDocuments.generateAsync({ type: "base64" }).then(function (base64) {
|
||||
const url = "data:application/zip;base64," + base64;
|
||||
self.downloadLink = document.createElement("a");
|
||||
self.downloadLink.href = url;
|
||||
self.downloadLink.setAttribute("download", baseName + ".zip");
|
||||
self.downloadLink.setAttribute("target", "_blank");
|
||||
self.downloadLink.click();
|
||||
});
|
||||
|
||||
} else { // Continue normally if there are no separators
|
||||
|
||||
const url = URL.createObjectURL(pdfBlob);
|
||||
const downloadOption = localStorage.getItem("downloadOption");
|
||||
|
||||
if (!filenameInput.value.includes(".pdf")) {
|
||||
filenameInput.value = filenameInput.value + ".pdf";
|
||||
this.fileName = filenameInput.value;
|
||||
}
|
||||
|
||||
if (downloadOption === "sameWindow") {
|
||||
// Open the file in the same window
|
||||
window.location.href = url;
|
||||
} else if (downloadOption === "newWindow") {
|
||||
// Open the file in a new window
|
||||
window.open(url, "_blank");
|
||||
} else {
|
||||
// Download the file
|
||||
this.downloadLink = document.createElement("a");
|
||||
this.downloadLink.id = "download-link";
|
||||
this.downloadLink.href = url;
|
||||
// downloadLink.download = this.fileName ? this.fileName : 'managed.pdf';
|
||||
// downloadLink.download = this.fileName;
|
||||
this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
|
||||
this.downloadLink.setAttribute("target", "_blank");
|
||||
this.downloadLink.onclick = this.setDownloadAttribute;
|
||||
this.downloadLink.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +439,7 @@ function detectImageType(uint8Array) {
|
||||
|
||||
// Check for TIFF signature (little-endian and big-endian)
|
||||
if ((uint8Array[0] === 73 && uint8Array[1] === 73 && uint8Array[2] === 42 && uint8Array[3] === 0) ||
|
||||
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)) {
|
||||
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)) {
|
||||
return 'TIFF';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user