mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-11-01 01:21:18 +01:00 
			
		
		
		
	Add image support to multi-tool page (#1769)
* Add image support to multi-tool page Related to #278 * changes to support image types * final touches --------- Co-authored-by: a <a>
This commit is contained in:
		
							parent
							
								
									316021453f
								
							
						
					
					
						commit
						68bc4d82de
					
				@ -1,4 +1,4 @@
 | 
				
			|||||||
class ImageHiglighter {
 | 
					class ImageHighlighter {
 | 
				
			||||||
  imageHighlighter;
 | 
					  imageHighlighter;
 | 
				
			||||||
  constructor(id) {
 | 
					  constructor(id) {
 | 
				
			||||||
    this.imageHighlighter = document.getElementById(id);
 | 
					    this.imageHighlighter = document.getElementById(id);
 | 
				
			||||||
@ -41,6 +41,25 @@ class ImageHiglighter {
 | 
				
			|||||||
    img.addEventListener("click", this.imageHighlightCallback);
 | 
					    img.addEventListener("click", this.imageHighlightCallback);
 | 
				
			||||||
    return div;
 | 
					    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;
 | 
				
			||||||
 | 
				
			|||||||
@ -10,27 +10,28 @@ class PdfContainer {
 | 
				
			|||||||
    this.pagesContainerWrapper = document.getElementById(wrapperId);
 | 
					    this.pagesContainerWrapper = document.getElementById(wrapperId);
 | 
				
			||||||
    this.downloadLink = null;
 | 
					    this.downloadLink = null;
 | 
				
			||||||
    this.movePageTo = this.movePageTo.bind(this);
 | 
					    this.movePageTo = this.movePageTo.bind(this);
 | 
				
			||||||
    this.addPdfs = this.addPdfs.bind(this);
 | 
					    this.addFiles = this.addFiles.bind(this);
 | 
				
			||||||
    this.addPdfsFromFiles = this.addPdfsFromFiles.bind(this);
 | 
					    this.addFilesFromFiles = this.addFilesFromFiles.bind(this);
 | 
				
			||||||
    this.rotateElement = this.rotateElement.bind(this);
 | 
					    this.rotateElement = this.rotateElement.bind(this);
 | 
				
			||||||
    this.rotateAll = this.rotateAll.bind(this);
 | 
					    this.rotateAll = this.rotateAll.bind(this);
 | 
				
			||||||
    this.exportPdf = this.exportPdf.bind(this);
 | 
					    this.exportPdf = this.exportPdf.bind(this);
 | 
				
			||||||
    this.updateFilename = this.updateFilename.bind(this);
 | 
					    this.updateFilename = this.updateFilename.bind(this);
 | 
				
			||||||
    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.pdfAdapters = pdfAdapters;
 | 
					    this.pdfAdapters = pdfAdapters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.pdfAdapters.forEach((adapter) => {
 | 
					    this.pdfAdapters.forEach((adapter) => {
 | 
				
			||||||
      adapter.setActions({
 | 
					      adapter.setActions({
 | 
				
			||||||
        movePageTo: this.movePageTo,
 | 
					        movePageTo: this.movePageTo,
 | 
				
			||||||
        addPdfs: this.addPdfs,
 | 
					        addFiles: this.addFiles,
 | 
				
			||||||
        rotateElement: this.rotateElement,
 | 
					        rotateElement: this.rotateElement,
 | 
				
			||||||
        updateFilename: this.updateFilename,
 | 
					        updateFilename: this.updateFilename,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    window.addPdfs = this.addPdfs;
 | 
					    window.addFiles = this.addFiles;
 | 
				
			||||||
    window.exportPdf = this.exportPdf;
 | 
					    window.exportPdf = this.exportPdf;
 | 
				
			||||||
    window.rotateAll = this.rotateAll;
 | 
					    window.rotateAll = this.rotateAll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -65,25 +66,30 @@ class PdfContainer {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addPdfs(nextSiblingElement) {
 | 
					  addFiles(nextSiblingElement) {
 | 
				
			||||||
    var input = document.createElement("input");
 | 
					    var input = document.createElement("input");
 | 
				
			||||||
    input.type = "file";
 | 
					    input.type = "file";
 | 
				
			||||||
    input.multiple = true;
 | 
					    input.multiple = true;
 | 
				
			||||||
    input.setAttribute("accept", "application/pdf");
 | 
					    input.setAttribute("accept", "application/pdf,image/*");
 | 
				
			||||||
    input.onchange = async (e) => {
 | 
					    input.onchange = async (e) => {
 | 
				
			||||||
      const files = e.target.files;
 | 
					      const files = e.target.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.addPdfsFromFiles(files, nextSiblingElement);
 | 
					      this.addFilesFromFiles(files, nextSiblingElement);
 | 
				
			||||||
      this.updateFilename(files ? files[0].name : "");
 | 
					      this.updateFilename(files ? files[0].name : "");
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    input.click();
 | 
					    input.click();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async addPdfsFromFiles(files, nextSiblingElement) {
 | 
					  async addFilesFromFiles(files, nextSiblingElement) {
 | 
				
			||||||
    this.fileName = files[0].name;
 | 
					    this.fileName = files[0].name;
 | 
				
			||||||
    for (var i = 0; i < files.length; i++) {
 | 
					    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) => {
 | 
					    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) {
 | 
					  async loadFile(file) {
 | 
				
			||||||
    var objectUrl = URL.createObjectURL(file);
 | 
					    var objectUrl = URL.createObjectURL(file);
 | 
				
			||||||
    var pdfDocument = await this.toPdfLib(objectUrl);
 | 
					    var pdfDocument = await this.toPdfLib(objectUrl);
 | 
				
			||||||
@ -193,16 +218,47 @@ class PdfContainer {
 | 
				
			|||||||
    for (var i = 0; i < pageContainers.length; i++) {
 | 
					    for (var i = 0; i < pageContainers.length; i++) {
 | 
				
			||||||
      const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
 | 
					      const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
 | 
				
			||||||
      if (!img) continue;
 | 
					      if (!img) continue;
 | 
				
			||||||
      const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]);
 | 
					      let page;
 | 
				
			||||||
      const page = pages[0];
 | 
					      if (img.doc) {
 | 
				
			||||||
 | 
					        const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]);
 | 
				
			||||||
 | 
					        page = pages[0];
 | 
				
			||||||
 | 
					        pdfDoc.addPage(page);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        page = pdfDoc.addPage([img.naturalWidth, img.naturalHeight]);
 | 
				
			||||||
 | 
					        const imageBytes = await fetch(img.src).then((res) => res.arrayBuffer());
 | 
				
			||||||
 | 
					        const uint8Array = new Uint8Array(imageBytes);
 | 
				
			||||||
 | 
					        const imageType = detectImageType(uint8Array);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let image;
 | 
				
			||||||
 | 
					        switch (imageType) {
 | 
				
			||||||
 | 
					          case 'PNG':
 | 
				
			||||||
 | 
					            image = await pdfDoc.embedPng(imageBytes);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 'JPEG':
 | 
				
			||||||
 | 
					            image = await pdfDoc.embedJpg(imageBytes);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 'TIFF':
 | 
				
			||||||
 | 
					            image = await pdfDoc.embedTiff(imageBytes);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 'GIF':
 | 
				
			||||||
 | 
					            console.warn(`Unsupported image type: ${imageType}`);
 | 
				
			||||||
 | 
					            continue; // Skip this image
 | 
				
			||||||
 | 
					          default:
 | 
				
			||||||
 | 
					            console.warn(`Unsupported image type: ${imageType}`);
 | 
				
			||||||
 | 
					            continue; // Skip this image
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        page.drawImage(image, {
 | 
				
			||||||
 | 
					          x: 0,
 | 
				
			||||||
 | 
					          y: 0,
 | 
				
			||||||
 | 
					          width: img.naturalWidth,
 | 
				
			||||||
 | 
					          height: img.naturalHeight,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      const rotation = img.style.rotate;
 | 
					      const rotation = img.style.rotate;
 | 
				
			||||||
      if (rotation) {
 | 
					      if (rotation) {
 | 
				
			||||||
        const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
 | 
					        const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
 | 
				
			||||||
        page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
 | 
					        page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
      pdfDoc.addPage(page);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    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" });
 | 
				
			||||||
@ -249,6 +305,7 @@ class PdfContainer {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setDownloadAttribute() {
 | 
					  setDownloadAttribute() {
 | 
				
			||||||
    this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
 | 
					    this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -280,5 +337,28 @@ class PdfContainer {
 | 
				
			|||||||
    // }
 | 
					    // }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					function detectImageType(uint8Array) {
 | 
				
			||||||
 | 
					  // Check for PNG signature
 | 
				
			||||||
 | 
					  if (uint8Array[0] === 137 && uint8Array[1] === 80 && uint8Array[2] === 78 && uint8Array[3] === 71) {
 | 
				
			||||||
 | 
					    return 'PNG';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check for JPEG signature
 | 
				
			||||||
 | 
					  if (uint8Array[0] === 255 && uint8Array[1] === 216 && uint8Array[2] === 255) {
 | 
				
			||||||
 | 
					    return 'JPEG';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 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)) {
 | 
				
			||||||
 | 
					    return 'TIFF';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check for GIF signature
 | 
				
			||||||
 | 
					  if (uint8Array[0] === 71 && uint8Array[1] === 73 && uint8Array[2] === 70) {
 | 
				
			||||||
 | 
					    return 'GIF';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 'UNKNOWN';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
export default PdfContainer;
 | 
					export default PdfContainer;
 | 
				
			||||||
 | 
				
			|||||||
@ -90,6 +90,25 @@ class FileDragManager {
 | 
				
			|||||||
        this.updateFilename(files ? files[0].name : "");
 | 
					        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;
 | 
					export default FileDragManager;
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,7 @@
 | 
				
			|||||||
                    th:placeholder="#{multiTool.uploadPrompts}">
 | 
					                    th:placeholder="#{multiTool.uploadPrompts}">
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="mt-action-btn">
 | 
					                <div class="mt-action-btn">
 | 
				
			||||||
                  <button class="btn btn-primary" onclick="addPdfs()">
 | 
					                  <button class="btn btn-primary" onclick="addFiles()">
 | 
				
			||||||
                    <span class="material-symbols-rounded">
 | 
					                    <span class="material-symbols-rounded">
 | 
				
			||||||
                      add
 | 
					                      add
 | 
				
			||||||
                    </span>
 | 
					                    </span>
 | 
				
			||||||
@ -52,12 +52,12 @@
 | 
				
			|||||||
              <div class="multi-tool-container">
 | 
					              <div class="multi-tool-container">
 | 
				
			||||||
                <div class="d-flex flex-wrap" id="pages-container-wrapper">
 | 
					                <div class="d-flex flex-wrap" id="pages-container-wrapper">
 | 
				
			||||||
                  <div id="pages-container">
 | 
					                  <div id="pages-container">
 | 
				
			||||||
                    <div class="page-container" th:each="pdf, status: ${pdfList}"
 | 
					                    <div class="page-container" th:each="file, status: ${fileList}"
 | 
				
			||||||
                      th:id="'page-container-' + ${status.index}">
 | 
					                      th:id="'page-container-' + ${status.index}">
 | 
				
			||||||
                      <div class="page-number-container">
 | 
					                      <div class="page-number-container">
 | 
				
			||||||
                        <span th:text="${status.index + 1}"></span>
 | 
					                        <span th:text="${status.index + 1}"></span>
 | 
				
			||||||
                      </div>
 | 
					                      </div>
 | 
				
			||||||
                      <img th:src="${pdf.imageUrl}" alt="PDF Page">
 | 
					                      <img th:src="${file.imageUrl}" alt="File Page">
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
@ -82,14 +82,14 @@
 | 
				
			|||||||
    const dragDropManager = new DragDropManager('drag-container', 'pages-container');
 | 
					    const dragDropManager = new DragDropManager('drag-container', 'pages-container');
 | 
				
			||||||
    // enables image highlight on click
 | 
					    // enables image highlight on click
 | 
				
			||||||
    const imageHighlighter = new ImageHighlighter('image-highlighter');
 | 
					    const imageHighlighter = new ImageHighlighter('image-highlighter');
 | 
				
			||||||
    // enables the default action buttons on each pdf
 | 
					    // enables the default action buttons on each file
 | 
				
			||||||
    const pdfActionsManager = new PdfActionsManager('pages-container');
 | 
					    const pdfActionsManager = new PdfActionsManager('pages-container');
 | 
				
			||||||
    const fileDragManager = new FileDragManager();
 | 
					    const fileDragManager = new FileDragManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Scroll the wrapper horizontally
 | 
					    // Scroll the wrapper horizontally
 | 
				
			||||||
    scrollDivHorizontally('pages-container-wrapper');
 | 
					    scrollDivHorizontally('pages-container-wrapper');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Automatically exposes rotateAll, addPdfs and exportPdf to the window for the global buttons.
 | 
					    // Automatically exposes rotateAll, addFiles and exportPdf to the window for the global buttons.
 | 
				
			||||||
    const pdfContainer = new PdfContainer(
 | 
					    const pdfContainer = new PdfContainer(
 | 
				
			||||||
      'pages-container',
 | 
					      'pages-container',
 | 
				
			||||||
      'pages-container-wrapper',
 | 
					      'pages-container-wrapper',
 | 
				
			||||||
@ -101,7 +101,7 @@
 | 
				
			|||||||
      ]
 | 
					      ]
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fileDragManager.setCallback(async (files) => pdfContainer.addPdfsFromFiles(files));
 | 
					    fileDragManager.setCallback(async (files) => pdfContainer.addFilesFromFiles(files));
 | 
				
			||||||
  </script>
 | 
					  </script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user