diff --git a/src/main/resources/static/css/multi-tool.css b/src/main/resources/static/css/multi-tool.css
index 0e609652..29565908 100644
--- a/src/main/resources/static/css/multi-tool.css
+++ b/src/main/resources/static/css/multi-tool.css
@@ -20,7 +20,7 @@ label {
display: flex;
gap: 10px;
align-items: start;
- background-color: var(--md-sys-color-surface-5);
+ background-color: var(--md-sys-color-surface-5);
border: none;
backdrop-filter: blur(2px);
top: 10px;
@@ -127,6 +127,19 @@ label {
margin-bottom: 16px;
}
+.page-container.split-before {
+ border-left: 1px dashed var(--md-sys-color-on-surface);
+ padding-left: -1px;
+}
+
+.page-container.split-before:first-child {
+ border-left: none;
+}
+
+.page-container:first-child .pdf-actions_split-file-button {
+ display: none;
+}
+
/* Pushes the last item to the left */
.page-container:last-child {
margin-right: auto;
@@ -171,8 +184,9 @@ label {
.page-container:last-child:lang(nqo),
/* N'Ko */
.page-container:last-child:lang(bqi)
+
/* Bakhtiari */
-{
+ {
margin-left: auto !important;
margin-right: 0 !important;
}
@@ -209,4 +223,4 @@ label {
.tool-header {
margin: 0.5rem 1rem 2rem;
-}
\ No newline at end of file
+}
diff --git a/src/main/resources/static/css/pdfActions.css b/src/main/resources/static/css/pdfActions.css
index e8a51224..b76e45b1 100644
--- a/src/main/resources/static/css/pdfActions.css
+++ b/src/main/resources/static/css/pdfActions.css
@@ -116,4 +116,13 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
translate: 50% -50%;
aspect-ratio: 1;
border-radius: 100px;
-}
\ No newline at end of file
+}
+
+.pdf-actions_split-file-button {
+ position: absolute;
+ top: 25%;
+ right: 50%;
+ translate: 0 -50%;
+ aspect-ratio: 1;
+ border-radius: 100px;
+}
diff --git a/src/main/resources/static/js/multitool/PdfActionsManager.js b/src/main/resources/static/js/multitool/PdfActionsManager.js
index a4e313c7..83768dc0 100644
--- a/src/main/resources/static/js/multitool/PdfActionsManager.js
+++ b/src/main/resources/static/js/multitool/PdfActionsManager.js
@@ -73,7 +73,12 @@ class PdfActionsManager {
this.addFiles(imgContainer);
}
- setActions({ movePageTo, addFiles, rotateElement }) {
+ splitFileButtonCallback(e) {
+ var imgContainer = this.getPageContainer(e.target);
+ imgContainer.classList.toggle("split-before");
+ }
+
+ setActions({ movePageTo, addPdfs, rotateElement }) {
this.movePageTo = movePageTo;
this.addFiles = addFiles;
this.rotateElement = rotateElement;
@@ -84,6 +89,7 @@ class PdfActionsManager {
this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this);
this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this);
this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this);
+ this.splitFileButtonCallback = this.splitFileButtonCallback.bind(this);
}
adapt(div) {
@@ -140,6 +146,12 @@ class PdfActionsManager {
insertFileButton.onclick = this.insertFileButtonCallback;
insertFileButtonContainer.appendChild(insertFileButton);
+ const splitFileButton = document.createElement("button");
+ splitFileButton.classList.add("btn", "btn-primary", "pdf-actions_split-file-button");
+ splitFileButton.innerHTML = `cut`;
+ splitFileButton.onclick = this.splitFileButtonCallback;
+ insertFileButtonContainer.appendChild(splitFileButton);
+
div.appendChild(insertFileButtonContainer);
// add this button to every element, but only show it on the last one :D
diff --git a/src/main/resources/static/js/multitool/PdfContainer.js b/src/main/resources/static/js/multitool/PdfContainer.js
index 7f2ceebf..b55b5699 100644
--- a/src/main/resources/static/js/multitool/PdfContainer.js
+++ b/src/main/resources/static/js/multitool/PdfContainer.js
@@ -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';
}
diff --git a/src/main/resources/templates/multi-tool.html b/src/main/resources/templates/multi-tool.html
index 2bbbf086..9f165014 100644
--- a/src/main/resources/templates/multi-tool.html
+++ b/src/main/resources/templates/multi-tool.html
@@ -42,6 +42,11 @@
rotate_right
+