update #5
5 changed files with 159 additions and 30 deletions
|
@ -127,6 +127,19 @@ label {
|
||||||
margin-bottom: 16px;
|
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 */
|
/* Pushes the last item to the left */
|
||||||
.page-container:last-child {
|
.page-container:last-child {
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
@ -171,6 +184,7 @@ label {
|
||||||
.page-container:last-child:lang(nqo),
|
.page-container:last-child:lang(nqo),
|
||||||
/* N'Ko */
|
/* N'Ko */
|
||||||
.page-container:last-child:lang(bqi)
|
.page-container:last-child:lang(bqi)
|
||||||
|
|
||||||
/* Bakhtiari */
|
/* Bakhtiari */
|
||||||
{
|
{
|
||||||
margin-left: auto !important;
|
margin-left: auto !important;
|
||||||
|
|
|
@ -117,3 +117,12 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pdf-actions_split-file-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 25%;
|
||||||
|
right: 50%;
|
||||||
|
translate: 0 -50%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 100px;
|
||||||
|
}
|
||||||
|
|
|
@ -73,7 +73,12 @@ class PdfActionsManager {
|
||||||
this.addFiles(imgContainer);
|
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.movePageTo = movePageTo;
|
||||||
this.addFiles = addFiles;
|
this.addFiles = addFiles;
|
||||||
this.rotateElement = rotateElement;
|
this.rotateElement = rotateElement;
|
||||||
|
@ -84,6 +89,7 @@ class PdfActionsManager {
|
||||||
this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this);
|
this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this);
|
||||||
this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this);
|
this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this);
|
||||||
this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this);
|
this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this);
|
||||||
|
this.splitFileButtonCallback = this.splitFileButtonCallback.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
adapt(div) {
|
adapt(div) {
|
||||||
|
@ -140,6 +146,12 @@ class PdfActionsManager {
|
||||||
insertFileButton.onclick = this.insertFileButtonCallback;
|
insertFileButton.onclick = this.insertFileButtonCallback;
|
||||||
insertFileButtonContainer.appendChild(insertFileButton);
|
insertFileButtonContainer.appendChild(insertFileButton);
|
||||||
|
|
||||||
|
const splitFileButton = document.createElement("button");
|
||||||
|
splitFileButton.classList.add("btn", "btn-primary", "pdf-actions_split-file-button");
|
||||||
|
splitFileButton.innerHTML = `<span class="material-symbols-rounded">cut</span>`;
|
||||||
|
splitFileButton.onclick = this.splitFileButtonCallback;
|
||||||
|
insertFileButtonContainer.appendChild(splitFileButton);
|
||||||
|
|
||||||
div.appendChild(insertFileButtonContainer);
|
div.appendChild(insertFileButtonContainer);
|
||||||
|
|
||||||
// add this button to every element, but only show it on the last one :D
|
// add this button to every element, but only show it on the last one :D
|
||||||
|
|
|
@ -19,6 +19,9 @@ class PdfContainer {
|
||||||
this.setDownloadAttribute = this.setDownloadAttribute.bind(this);
|
this.setDownloadAttribute = this.setDownloadAttribute.bind(this);
|
||||||
this.preventIllegalChars = this.preventIllegalChars.bind(this);
|
this.preventIllegalChars = this.preventIllegalChars.bind(this);
|
||||||
this.addImageFile = this.addImageFile.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;
|
this.pdfAdapters = pdfAdapters;
|
||||||
|
|
||||||
|
@ -34,6 +37,7 @@ class PdfContainer {
|
||||||
window.addFiles = this.addFiles;
|
window.addFiles = this.addFiles;
|
||||||
window.exportPdf = this.exportPdf;
|
window.exportPdf = this.exportPdf;
|
||||||
window.rotateAll = this.rotateAll;
|
window.rotateAll = this.rotateAll;
|
||||||
|
window.splitAll = this.splitAll;
|
||||||
|
|
||||||
const filenameInput = document.getElementById("filename-input");
|
const filenameInput = document.getElementById("filename-input");
|
||||||
const downloadBtn = document.getElementById("export-button");
|
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() {
|
async exportPdf() {
|
||||||
const pdfDoc = await PDFLib.PDFDocument.create();
|
const pdfDoc = await PDFLib.PDFDocument.create();
|
||||||
const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements
|
const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements
|
||||||
|
@ -262,8 +321,6 @@ class PdfContainer {
|
||||||
}
|
}
|
||||||
const pdfBytes = await pdfDoc.save();
|
const pdfBytes = await pdfDoc.save();
|
||||||
const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" });
|
const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" });
|
||||||
const url = URL.createObjectURL(pdfBlob);
|
|
||||||
const downloadOption = localStorage.getItem("downloadOption");
|
|
||||||
|
|
||||||
const filenameInput = document.getElementById("filename-input");
|
const filenameInput = document.getElementById("filename-input");
|
||||||
|
|
||||||
|
@ -280,6 +337,37 @@ class PdfContainer {
|
||||||
this.fileName = filenameInput.value;
|
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";
|
||||||
|
|
||||||
|
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")) {
|
if (!filenameInput.value.includes(".pdf")) {
|
||||||
filenameInput.value = filenameInput.value + ".pdf";
|
filenameInput.value = filenameInput.value + ".pdf";
|
||||||
this.fileName = filenameInput.value;
|
this.fileName = filenameInput.value;
|
||||||
|
@ -304,6 +392,7 @@ class PdfContainer {
|
||||||
this.downloadLink.click();
|
this.downloadLink.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
setDownloadAttribute() {
|
setDownloadAttribute() {
|
||||||
|
|
|
@ -42,6 +42,11 @@
|
||||||
rotate_right
|
rotate_right
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-secondary enable-on-file" onclick="splitAll()" disabled>
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
cut
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf()" disabled>
|
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf()" disabled>
|
||||||
<span class="material-symbols-rounded">
|
<span class="material-symbols-rounded">
|
||||||
download
|
download
|
||||||
|
|
Loading…
Reference in a new issue