From 1006927b0cdb5106d062ade283357d95fa7a2a6c Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Wed, 28 Aug 2024 22:29:55 +0200 Subject: [PATCH] Add image support to multi-tool page Related to #278 --- .../static/js/multitool/ImageHighlighter.js | 23 +++++++- .../static/js/multitool/PdfContainer.js | 58 +++++++++++++++---- .../static/js/multitool/fileInput.js | 19 ++++++ src/main/resources/templates/multi-tool.html | 14 ++--- 4 files changed, 95 insertions(+), 19 deletions(-) diff --git a/src/main/resources/static/js/multitool/ImageHighlighter.js b/src/main/resources/static/js/multitool/ImageHighlighter.js index b72df3bbed8..7fc53209ed8 100644 --- a/src/main/resources/static/js/multitool/ImageHighlighter.js +++ b/src/main/resources/static/js/multitool/ImageHighlighter.js @@ -1,4 +1,4 @@ -class ImageHiglighter { +class ImageHighlighter { imageHighlighter; constructor(id) { this.imageHighlighter = document.getElementById(id); @@ -41,6 +41,25 @@ class ImageHiglighter { img.addEventListener("click", this.imageHighlightCallback); return div; } + + async addImageFile(file, nextSiblingElement) { + const div = document.createElement("div"); + div.classList.add("page-container"); + + var img = document.createElement("img"); + img.classList.add("page-image"); + img.src = URL.createObjectURL(file); + div.appendChild(img); + + this.pdfAdapters.forEach((adapter) => { + adapter.adapt?.(div); + }); + if (nextSiblingElement) { + this.pagesContainer.insertBefore(div, nextSiblingElement); + } else { + this.pagesContainer.appendChild(div); + } + } } -export default ImageHiglighter; +export default ImageHighlighter; diff --git a/src/main/resources/static/js/multitool/PdfContainer.js b/src/main/resources/static/js/multitool/PdfContainer.js index 0b11658d541..a2182324758 100644 --- a/src/main/resources/static/js/multitool/PdfContainer.js +++ b/src/main/resources/static/js/multitool/PdfContainer.js @@ -18,6 +18,7 @@ class PdfContainer { this.updateFilename = this.updateFilename.bind(this); this.setDownloadAttribute = this.setDownloadAttribute.bind(this); this.preventIllegalChars = this.preventIllegalChars.bind(this); + this.addImageFile = this.addImageFile.bind(this); this.pdfAdapters = pdfAdapters; @@ -69,7 +70,7 @@ class PdfContainer { var input = document.createElement("input"); input.type = "file"; input.multiple = true; - input.setAttribute("accept", "application/pdf"); + input.setAttribute("accept", "application/pdf,image/*"); input.onchange = async (e) => { const files = e.target.files; @@ -83,7 +84,12 @@ class PdfContainer { async addPdfsFromFiles(files, nextSiblingElement) { this.fileName = files[0].name; for (var i = 0; i < files.length; i++) { - await this.addPdfFile(files[i], nextSiblingElement); + const file = files[i]; + if (file.type === "application/pdf") { + await this.addPdfFile(file, nextSiblingElement); + } else if (file.type.startsWith("image/")) { + await this.addImageFile(file, nextSiblingElement); + } } document.querySelectorAll(".enable-on-file").forEach((element) => { @@ -130,6 +136,25 @@ class PdfContainer { } } + async addImageFile(file, nextSiblingElement) { + const div = document.createElement("div"); + div.classList.add("page-container"); + + var img = document.createElement("img"); + img.classList.add("page-image"); + img.src = URL.createObjectURL(file); + div.appendChild(img); + + this.pdfAdapters.forEach((adapter) => { + adapter.adapt?.(div); + }); + if (nextSiblingElement) { + this.pagesContainer.insertBefore(div, nextSiblingElement); + } else { + this.pagesContainer.appendChild(div); + } + } + async loadFile(file) { var objectUrl = URL.createObjectURL(file); var pdfDocument = await this.toPdfLib(objectUrl); @@ -193,16 +218,29 @@ class PdfContainer { for (var i = 0; i < pageContainers.length; i++) { const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container if (!img) continue; - const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]); - const page = pages[0]; - const rotation = img.style.rotate; - if (rotation) { - const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, "")); - page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle)); - } + if (img.doc) { + const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]); + const page = pages[0]; - pdfDoc.addPage(page); + const rotation = img.style.rotate; + if (rotation) { + const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, "")); + page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle)); + } + + pdfDoc.addPage(page); + } else { + const page = pdfDoc.addPage([img.naturalWidth, img.naturalHeight]); + const imageBytes = await fetch(img.src).then((res) => res.arrayBuffer()); + const image = await pdfDoc.embedPng(imageBytes); + page.drawImage(image, { + x: 0, + y: 0, + width: img.naturalWidth, + height: img.naturalHeight, + }); + } } const pdfBytes = await pdfDoc.save(); const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" }); diff --git a/src/main/resources/static/js/multitool/fileInput.js b/src/main/resources/static/js/multitool/fileInput.js index 77455c06b46..9efa8eb2a9e 100644 --- a/src/main/resources/static/js/multitool/fileInput.js +++ b/src/main/resources/static/js/multitool/fileInput.js @@ -90,6 +90,25 @@ class FileDragManager { this.updateFilename(files ? files[0].name : ""); }); } + + async addImageFile(file, nextSiblingElement) { + const div = document.createElement("div"); + div.classList.add("page-container"); + + var img = document.createElement("img"); + img.classList.add("page-image"); + img.src = URL.createObjectURL(file); + div.appendChild(img); + + this.pdfAdapters.forEach((adapter) => { + adapter.adapt?.(div); + }); + if (nextSiblingElement) { + this.pagesContainer.insertBefore(div, nextSiblingElement); + } else { + this.pagesContainer.appendChild(div); + } + } } export default FileDragManager; diff --git a/src/main/resources/templates/multi-tool.html b/src/main/resources/templates/multi-tool.html index b578d594261..2bbbf08681b 100644 --- a/src/main/resources/templates/multi-tool.html +++ b/src/main/resources/templates/multi-tool.html @@ -27,7 +27,7 @@ th:placeholder="#{multiTool.uploadPrompts}">
-