0

I have even added some delay on mobile devices to give time to process, change the image format to reduce processing effort and resolution size, I have change the viewport and 10% of the times works and the rest 90% doesn't work, as you can see I have added console.logs to debug and usually the last log is right prior to do the canvas capture or while is doing the capture, here is my code, any suggestion?

<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

<script>
document.getElementById('generate-pdf').addEventListener('click', function() {
  const isMobile = screen.width < 1024;  // Check if the device is mobile

  if (isMobile) {
    // Temporarily change the viewport for mobile devices to width=800
    const originalViewportContent = document.querySelector('meta[name="viewport"]').getAttribute('content');
    document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=800');
    console.log("Viewport temporarily set to width=800 for mobile.");
  }

  console.log("Starting PDF generation...");

  const { jsPDF } = window.jspdf;
  const doc = new jsPDF('p', 'pt', 'a4'); // A4 size PDF

  // Select the div content
  const content = document.getElementById('pdf-content');
  console.log("PDF content selected.");

  // Get all elements you want to exclude (e.g., buttons with class 'exclude')
  const elementsToExclude = content.querySelectorAll('.exclude');
  console.log("Elements to exclude:", elementsToExclude);

  // Temporarily hide the excluded elements
  elementsToExclude.forEach(function(el) {
    el.style.display = 'none';
  });
  console.log("Excluded elements hidden.");

  // Get the company name from the page (you can change the selector to match the correct element)
  const companyName = document.querySelector('.company-name').innerText.trim();
  console.log("Company name found:", companyName);

  // Default to 'Contract' if no company name is found
  const fileName = companyName ? `${companyName} Contract.pdf` : 'Contract.pdf';
  console.log("Generated filename:", fileName);

  // Set lower scale for mobile devices to reduce file size
  const scale = isMobile ? 1 : 2;  // Lower scale on mobile to reduce file size
  console.log("Using scale:", scale);

  // If mobile, delay the html2canvas capture and image processing by 500ms
  const delay = isMobile ? 500 : 0;

  setTimeout(function() {
    console.log("Starting html2canvas capture...");

    // Start html2canvas capture (without options)
    html2canvas(content).then(function(canvas) {
      console.log("Canvas captured.");

      // Determine the image format based on device type (PNG for desktop, JPEG for mobile)
      const imgFormat = isMobile ? 'image/jpeg' : 'image/png';
      console.log("Using image format:", imgFormat);

      // Convert the canvas to a base64 image (JPEG for mobile, PNG for desktop)
      setTimeout(function() {
        const imgData = canvas.toDataURL(imgFormat); // Switch format based on device type
        console.log("Image data created.");

        // Get the dimensions of the canvas
        const imgWidth = canvas.width;
        const imgHeight = canvas.height;
        console.log("Canvas dimensions:", imgWidth, imgHeight);

        // A4 page dimensions in points
        const pageWidth = 595.28;
        const pageHeight = 841.89;

        // Calculate scaling for A4 page
        const scaleX = pageWidth / imgWidth;
        const scaleY = pageHeight / imgHeight;
        const finalScale = Math.min(scaleX, scaleY);
        console.log("Calculated scaling:", finalScale);

        // Center the image on the PDF
        const xOffset = (pageWidth - imgWidth * finalScale) / 2;
        const yOffset = (pageHeight - imgHeight * finalScale) / 2;
        console.log("Image positioning: xOffset:", xOffset, "yOffset:", yOffset);

        // Delay before adding the image to the PDF (500ms for mobile only)
        setTimeout(function() {
          doc.addImage(imgData, imgFormat.toUpperCase(), xOffset, yOffset, imgWidth * finalScale, imgHeight * finalScale, undefined, 'FAST');
          console.log("Image added to PDF.");

          // Delay before saving the PDF (500ms for mobile only)
          setTimeout(function() {
            // Save the generated PDF with the dynamically created filename
            doc.save(fileName);
            console.log("PDF saved:", fileName);

            // Restore the visibility of the excluded elements
            elementsToExclude.forEach(function(el) {
              el.style.display = '';  // Restore original display
            });
            console.log("Excluded elements restored.");

            // Ensure the viewport is restored after all steps are complete
            if (isMobile) {
              const viewportMeta = document.querySelector('meta[name="viewport"]');
              if (viewportMeta) {
                viewportMeta.setAttribute('content', originalViewportContent);
                console.log("Viewport restored to original settings.");
              } else {
                console.error("Error: Viewport meta tag not found.");
              }
            }
          }, 500); // Delay before saving PDF (500ms for mobile)
        }, 500); // Delay before adding image to PDF (500ms)
      }, 500); // Delay before converting canvas to image data (500ms)

    }).catch(function(error) {
      console.error('Error during capture: ', error);

      // Restore the visibility of the excluded elements in case of error
      elementsToExclude.forEach(function(el) {
        el.style.display = '';  // Restore original display
      });

      // Restore the original viewport settings
      if (isMobile) {
        const viewportMeta = document.querySelector('meta[name="viewport"]');
        if (viewportMeta) {
          viewportMeta.setAttribute('content', originalViewportContent);
          console.log("Viewport restored to original settings.");
        } else {
          console.error("Error: Viewport meta tag not found.");
        }
      }
    });
  }, delay); // Only apply the delay for mobile
});
</script>
0

1 Answer 1

0

Finally made it work with different tips that other people provided in different posts, for example change all the images on the website from lazy to eager while doing the capture, also change the viewport widht and height, also adding some delay in the whole process to give time to do it correctly, and this is how the final code look like.

<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

<script>
document.getElementById('generate-pdf').addEventListener('click', function() {
  const isMobile = screen.width < 1024;  // Check if the device is mobile

  let lazyImagesChanged = false;  // Flag to track if lazy loading was changed

  // Only modify the viewport and images if it's a mobile device
  if (isMobile) {
    const viewportMeta = document.querySelector('meta[name="viewport"]');
    
    if (!viewportMeta) {
      console.error("Viewport meta tag not found.");
      return;
    }

    const originalViewportContent = viewportMeta.getAttribute('content'); // Store the original viewport content
    viewportMeta.setAttribute('content', 'width=800'); // Temporarily change the viewport for mobile
    console.log("Viewport temporarily set to width=800 for mobile.");

    // Change lazy-loaded images to eager load before starting the capture
    const lazyImages = document.querySelectorAll('img[loading="lazy"]');
    if (lazyImages.length > 0) {
      lazyImages.forEach(img => {
        img.setAttribute('loading', 'eager');
      });
      console.log("Lazy-loaded images set to eager load for capture.");
      lazyImagesChanged = true;  // Mark that images were changed
    }

    // Start the PDF generation process after changing the viewport
    generatePDF(originalViewportContent, viewportMeta, isMobile, lazyImagesChanged);  // Pass the flag to the function
  } else {
    // For desktop devices, directly start the process without modifying the viewport
    console.log("Desktop device detected, starting PDF generation...");
    generatePDF(null, null, isMobile, false);  // No need to change or pass the viewport meta for desktop
  }
});

// Function to generate the PDF
function generatePDF(originalViewportContent, viewportMeta, isMobile, lazyImagesChanged) {
  console.log("Starting PDF generation...");

  const { jsPDF } = window.jspdf;
  const doc = new jsPDF('p', 'pt', 'a4'); // A4 size PDF

  // Select the div content
  const content = document.getElementById('pdf-content');
  console.log("PDF content selected.");

  // Get all elements you want to exclude (e.g., buttons with class 'exclude')
  const elementsToExclude = content.querySelectorAll('.exclude');
  console.log("Elements to exclude:", elementsToExclude);

  // Temporarily hide the excluded elements
  elementsToExclude.forEach(function(el) {
    el.style.display = 'none';
  });
  console.log("Excluded elements hidden.");

  // Get the company name from the page (you can change the selector to match the correct element)
  const companyName = document.querySelector('.company-name').innerText.trim();
  console.log("Company name found:", companyName);

  // Default to 'Contract' if no company name is found
  const fileName = companyName ? `${companyName} Contract.pdf` : 'Contract.pdf';
  console.log("Generated filename:", fileName);

  // Set lower scale for mobile devices to reduce file size
  const scale = isMobile ? 1 : 2;  // Lower scale on mobile to reduce file size
  console.log("Using scale:", scale);

  // If mobile, delay the html2canvas capture and image processing by 500ms
  const delay = isMobile ? 500 : 0;

  setTimeout(function() {
    console.log("Starting html2canvas capture...");

    // Start html2canvas capture (without options)
    html2canvas(content).then(function(canvas) {
      console.log("Canvas captured.");

      // Determine the image format based on device type (PNG for desktop, JPEG for mobile)
      const imgFormat = isMobile ? 'image/jpeg' : 'image/png';
      console.log("Using image format:", imgFormat);

      // Convert the canvas to a base64 image (JPEG for mobile, PNG for desktop)
      setTimeout(function() {
        const imgData = canvas.toDataURL(imgFormat); // Switch format based on device type
        console.log("Image data created.");

        // Get the dimensions of the canvas
        const imgWidth = canvas.width;
        const imgHeight = canvas.height;
        console.log("Canvas dimensions:", imgWidth, imgHeight);

        // A4 page dimensions in points
        const pageWidth = 595.28;
        const pageHeight = 841.89;

        // Calculate scaling for A4 page
        const scaleX = pageWidth / imgWidth;
        const scaleY = pageHeight / imgHeight;
        const finalScale = Math.min(scaleX, scaleY);
        console.log("Calculated scaling:", finalScale);

        // Center the image on the PDF
        const xOffset = (pageWidth - imgWidth * finalScale) / 2;
        const yOffset = (pageHeight - imgHeight * finalScale) / 2;
        console.log("Image positioning: xOffset:", xOffset, "yOffset:", yOffset);

        // Delay before adding the image to the PDF (500ms for mobile only)
        setTimeout(function() {
          doc.addImage(imgData, imgFormat.toUpperCase(), xOffset, yOffset, imgWidth * finalScale, imgHeight * finalScale, undefined, 'FAST');
          console.log("Image added to PDF.");

          // Delay before saving the PDF (500ms for mobile only)
          setTimeout(function() {
            // Save the generated PDF with the dynamically created filename
            doc.save(fileName);
            console.log("PDF saved:", fileName);

            // Restore the visibility of the excluded elements
            elementsToExclude.forEach(function(el) {
              el.style.display = '';  // Restore original display
            });
            console.log("Excluded elements restored.");

            // Ensure the viewport is restored after all steps are complete
            if (originalViewportContent && viewportMeta) {
              viewportMeta.setAttribute('content', originalViewportContent);
              console.log("Viewport restored to original settings.");
            }

            // Restore the images back to lazy loading only if they were changed at the beginning
            if (lazyImagesChanged) {
              const lazyImages = document.querySelectorAll('img[loading="eager"]');
              lazyImages.forEach(img => {
                img.setAttribute('loading', 'lazy');
              });
              console.log("Images restored to lazy loading.");
            }

            // Reload the page after restoring the viewport and images, but only if it's mobile
            if (isMobile) {
              setTimeout(function() {
                window.location.reload(); // Reload the page after 500ms delay
                console.log("Page reloaded.");
              }, 500); // Delay to ensure everything is restored
            }
          }, 500); // Delay before saving PDF (500ms for mobile)
        }, 500); // Delay before adding image to PDF (500ms)
      }, 500); // Delay before converting canvas to image data (500ms)

    }).catch(function(error) {
      console.error('Error during capture: ', error);

      // Restore the visibility of the excluded elements in case of error
      elementsToExclude.forEach(function(el) {
        el.style.display = '';  // Restore original display
      });

      // Restore the original viewport settings
      if (originalViewportContent && viewportMeta) {
        viewportMeta.setAttribute('content', originalViewportContent);
        console.log("Viewport restored to original settings.");
      }

      // Restore the images back to lazy loading only if they were changed at the beginning
      if (lazyImagesChanged) {
        const lazyImages = document.querySelectorAll('img[loading="eager"]');
        lazyImages.forEach(img => {
          img.setAttribute('loading', 'lazy');
        });
        console.log("Images restored to lazy loading.");
      }

      // Reload the page after restoring the viewport and images, but only if it's mobile
      if (isMobile) {
        setTimeout(function() {
          window.location.reload(); // Reload the page after 500ms delay
          console.log("Page reloaded.");
        }, 500); // Delay to ensure everything is restored
      }
    });
  }, delay); // Only apply the delay for mobile
}
</script>
Sign up to request clarification or add additional context in comments.

Comments

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.