Search code examples
javascriptreactjscanvasjspdf

canvas2html to jsPDF - Problem with handling the Promise


I am using jsPDF to convert html block into PDF file. I go through my HTML and check if there is a text or an image. I use doc.text for texts and doc.addImage for images. I am having difficulty working with the response from canvasOutput = html2canvas(input) as it is a Promise. My code looks like this:

  for (const element of htmlData) {
    input = document.getElementById(element);
    var typ = input.innerHTML.substring(23, 27);

    if (typ == '<h3>') {
      doc.setFont(fontName, 'bold');
      writeText(input, h3_fontSize, 5);
    } else if (typ == '<h5>') {
      doc.setFont(fontName, 'bold');
      writeText(input, h5_fontSize, 3);
    } else if (typ == '<img') {
      var canvasOutput = html2canvas(input);
      canvasOutput.then((response) => {
        imgData = response.toDataURL('image/jpeg', 1.0);
        doc.addImage(imgData, 'PNG', left_edge_distance, position_mm, 100, 100);
      });
    }
  }

  doc.save('download.pdf');

The function writeText() contains the following code:

function writeText(input, fontSize, lineSpace) {
    doc.setFontSize(fontSize);
    var content = input.innerHTML.substring(27, input.innerHTML.length - 11);
    var splitText = doc.splitTextToSize(content, max_text_width);
    splitText.forEach((element) => {
      doc.text(element, left_edge_distance, position_mm);
      position_mm = position_mm + fontSize * pixelTrans + lineSpace;
    });
  }

The output file does not contain the image but when I copy the doc.save() inside the response function of the promise the image gets into my PDF.

My question is: How can I get the result of html2canvas out of the promise?


Solution

  • var allPromises = [];
    for (const element of htmlData) {
      var input = document.getElementById(element);
      if (input.toLowerCase().startsWith('<img')) {
        allPromises.push(html2canvas(input));
      } else {
        allPromises.push(Promise.resolve(input));
      }
    }
    
    Promise.all(allPromises).then(response => {
      response.forEach(input => {
        if (input instanceof String) {
          doc.setFont(fontName, 'bold');
          var isH3 = input.toLowerCase().startsWith('<h3>');
          writeText(input, isH3 ? h3_fontSize : h5_fontSize, isH3 ? 5 : 3);
        } else {
          imgData = input.toDataURL('image/jpeg', 1.0);
          doc.addImage(imgData, 'PNG', left_edge_distance, position_mm, 100, 100);
        }
      });
      doc.save('download.pdf');
    });
    

    I would modify first block of code to above one and leave the rest as it is. I will explain here what it does:

    1. maintain an array of promises.
    2. loop and check if the input is image tag and store the returned promise to the array from html2Canvas()
    3. otherwise just store the resolved promise returning the input to the array for sake of maintaining order.
    4. run Promise.all() and iterate through each responses from the array of promises.
    5. if it is string, then write text otherwise add Image.
    6. finally, save it.