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;
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
@ -10,27 +10,28 @@ class PdfContainer {
 | 
			
		||||
    this.pagesContainerWrapper = document.getElementById(wrapperId);
 | 
			
		||||
    this.downloadLink = null;
 | 
			
		||||
    this.movePageTo = this.movePageTo.bind(this);
 | 
			
		||||
    this.addPdfs = this.addPdfs.bind(this);
 | 
			
		||||
    this.addPdfsFromFiles = this.addPdfsFromFiles.bind(this);
 | 
			
		||||
    this.addFiles = this.addFiles.bind(this);
 | 
			
		||||
    this.addFilesFromFiles = this.addFilesFromFiles.bind(this);
 | 
			
		||||
    this.rotateElement = this.rotateElement.bind(this);
 | 
			
		||||
    this.rotateAll = this.rotateAll.bind(this);
 | 
			
		||||
    this.exportPdf = this.exportPdf.bind(this);
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    this.pdfAdapters.forEach((adapter) => {
 | 
			
		||||
      adapter.setActions({
 | 
			
		||||
        movePageTo: this.movePageTo,
 | 
			
		||||
        addPdfs: this.addPdfs,
 | 
			
		||||
        addFiles: this.addFiles,
 | 
			
		||||
        rotateElement: this.rotateElement,
 | 
			
		||||
        updateFilename: this.updateFilename,
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    window.addPdfs = this.addPdfs;
 | 
			
		||||
    window.addFiles = this.addFiles;
 | 
			
		||||
    window.exportPdf = this.exportPdf;
 | 
			
		||||
    window.rotateAll = this.rotateAll;
 | 
			
		||||
 | 
			
		||||
@ -65,25 +66,30 @@ class PdfContainer {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addPdfs(nextSiblingElement) {
 | 
			
		||||
  addFiles(nextSiblingElement) {
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
      this.addPdfsFromFiles(files, nextSiblingElement);
 | 
			
		||||
      this.addFilesFromFiles(files, nextSiblingElement);
 | 
			
		||||
      this.updateFilename(files ? files[0].name : "");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    input.click();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async addPdfsFromFiles(files, nextSiblingElement) {
 | 
			
		||||
  async addFilesFromFiles(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,47 @@ 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;
 | 
			
		||||
      let page;
 | 
			
		||||
      if (img.doc) {
 | 
			
		||||
        const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]);
 | 
			
		||||
      const page = pages[0];
 | 
			
		||||
        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;
 | 
			
		||||
      if (rotation) {
 | 
			
		||||
        const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
 | 
			
		||||
        page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      pdfDoc.addPage(page);
 | 
			
		||||
    }
 | 
			
		||||
    const pdfBytes = await pdfDoc.save();
 | 
			
		||||
    const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" });
 | 
			
		||||
@ -249,6 +305,7 @@ class PdfContainer {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  setDownloadAttribute() {
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@
 | 
			
		||||
                    th:placeholder="#{multiTool.uploadPrompts}">
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="mt-action-btn">
 | 
			
		||||
                  <button class="btn btn-primary" onclick="addPdfs()">
 | 
			
		||||
                  <button class="btn btn-primary" onclick="addFiles()">
 | 
			
		||||
                    <span class="material-symbols-rounded">
 | 
			
		||||
                      add
 | 
			
		||||
                    </span>
 | 
			
		||||
@ -52,12 +52,12 @@
 | 
			
		||||
              <div class="multi-tool-container">
 | 
			
		||||
                <div class="d-flex flex-wrap" id="pages-container-wrapper">
 | 
			
		||||
                  <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}">
 | 
			
		||||
                      <div class="page-number-container">
 | 
			
		||||
                        <span th:text="${status.index + 1}"></span>
 | 
			
		||||
                      </div>
 | 
			
		||||
                      <img th:src="${pdf.imageUrl}" alt="PDF Page">
 | 
			
		||||
                      <img th:src="${file.imageUrl}" alt="File Page">
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
@ -82,14 +82,14 @@
 | 
			
		||||
    const dragDropManager = new DragDropManager('drag-container', 'pages-container');
 | 
			
		||||
    // enables image highlight on click
 | 
			
		||||
    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 fileDragManager = new FileDragManager();
 | 
			
		||||
 | 
			
		||||
    // Scroll the wrapper horizontally
 | 
			
		||||
    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(
 | 
			
		||||
      'pages-container',
 | 
			
		||||
      'pages-container-wrapper',
 | 
			
		||||
@ -101,7 +101,7 @@
 | 
			
		||||
      ]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    fileDragManager.setCallback(async (files) => pdfContainer.addPdfsFromFiles(files));
 | 
			
		||||
    fileDragManager.setCallback(async (files) => pdfContainer.addFilesFromFiles(files));
 | 
			
		||||
  </script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user