0

I'm facing a weird error with the File System API. I'm trying to download a PDF generated from a batch of images when the use clicks a button.

Here is my current code:

mainContainer.addEventListener('click', async function (event) {
    if (event.target.id === 'dl-button') {
       const selectedImgs = getSelectedImages();
       let images = await getAllImageDataUrls(selectedImgs);
       await triggerImagesDownload(images);
    }
});

async function embedImage(pdfDoc, base64Image) {
    let base64ImagePart = base64Image.split(';base64,')[1]
    const isJPEG = base64ImagePart.startsWith("/9j/")
    const imageBytes = Uint8Array.from(atob(base64ImagePart), c => c.charCodeAt(0));
    if (isJPEG) {
        return await pdfDoc.embedJpg(imageBytes);
    } else {
        return await pdfDoc.embedPng(imageBytes);
    }
}

async function triggerImagesDownload(base64Images) {
    // Create a new PDF document
    const pdfDoc = await PDFLib.PDFDocument.create();
    const embedImagesPromises = base64Images.map(base64Image => embedImage(pdfDoc, base64Image));
    const images = await Promise.all(embedImagesPromises);
    images.forEach(image => {
        const page = pdfDoc.addPage([image.width, image.height]);
        page.drawImage(image, {
            x: 0,
            y: 0,
            width: image.width,
            height: image.height,
        });
    });

    // Serialize the PDFDocument to bytes
    const pdfBytes = await pdfDoc.save();
    await savePDFFile(new Blob([pdfBytes], {type: 'application/pdf'}));
    chrome.storage.local.set({currentView: "home"}, function() {
        window.location.reload();
    });
}

async function savePDFFile(pdfBlob) {
    let suggestedName = `images.pdf`
    try {
      const newHandle = await window.showSaveFilePicker({
        suggestedName: suggestedName,
        types: [{
          description: 'PDF document from captured slides',
          accept: {'application/pdf': ['.pdf']}
        }],
      });
      const writableStream = await newHandle.createWritable();
      await writableStream.write(pdfBlob);
      await writableStream.close();
      console.log('PDF saved successfully.');
    } catch (err) {
        alert(err)
    }
  }

The problem occurs when the number of images I convert into a PDF. I've tested with a batch of 13 images:

  • if I convert max 5 images and download the generated pdf, I get to use the file picker and basically the try logic inside the savePDFFile function

  • if I try to exceed the 5 images limit I get a SecurityError:

    SecurityError: Failed to execute 'showSaveFilePicker' on 'Window': Must be handling a user gesture to show a file picker.

A useful information is that the pdf generated from 5 images is 7.6MB

Does anyone know what am I doing wrong or what I've missed in the fs api documentation?

2
  • showSaveFilePicker is one of the methods guarded by Transient activation - "a window state that indicates a user has recently pressed a button, moved a mouse, used a menu, or performed some other user interaction. Transient activation expires after a timeout (if not renewed by further interaction)" (bold highlighting by me.) So apparently, your process simply takes too long, when you go over a certain amount of images. Commented Mar 7, 2024 at 8:52
  • Not sure if there is any other workaround, than to let the user know that the PDF generation has finished, and make them explicitly click some button again, to now trigger the download. Commented Mar 7, 2024 at 8:53

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.