Export to PDF the three.js canvas PLUS an html container. How?

Hello guys,

I am struggling to export one page of my web app into PDF. I currently have an html container which contains the three.js canvas and some div with panels and so on. The three.js canvas has four viewports at the same time. I basically have four cameras that are using the same renderer but in different positions and they are being displayed at the same time. And I also have some panels and styles.

I am trying to export to PDF the whole container with the renderers and the panels. But I did not find a way in doing it. If I use html2canvas, only the panel is exported into PDF and no renderer is displayed. And if I do it without html2canvas, only the renderers are exported into PDF.

So, the container is a white box, and inside the container there is the three.js canvas occupying the biggest space of the container, and the panel in the bottom. I would like to keep this structure when exporting to PDF.

So, is there a way to export the whole html container showing the renderers of the three.js canvas? Is there a way to combine both? Or will I need to refactor all my code to add the panel into the three.js canvas? And, if I do so, how do I export as a PDF the canvas containing the panels?

I am a bit lost in this, and I am a beginner in programming. So, thank you so much for your time and help!

Thank you for providing such a detailed explanation of your situation. Exporting a complex web page with Three.js content to PDF can indeed be challenging. Let’s explore a few approaches you could try:

  1. Combine Three.js rendering with HTML2Canvas:
    This approach involves rendering your Three.js scene to an image, then inserting that image into your HTML before using HTML2Canvas.
// Assuming 'container' is your main container element
function exportToPDF() {
  // Render Three.js scene to image
  const imageData = renderer.domElement.toDataURL("image/png");
  
  // Create a temporary img element
  const img = document.createElement('img');
  img.src = imageData;
  
  // Replace Three.js canvas with the image temporarily
  const canvas = document.querySelector('canvas');
  const parent = canvas.parentNode;
  parent.replaceChild(img, canvas);

  // Use html2canvas to capture everything
  html2canvas(container).then(canvas => {
    // Convert to PDF using a library like jsPDF
    const pdf = new jsPDF();
    pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 0, 0);
    pdf.save("output.pdf");

    // Restore the original Three.js canvas
    parent.replaceChild(canvas, img);
  });
}
  1. Manual composition using jsPDF:

This method involves capturing your Three.js render and your HTML elements separately, then composing them manually in a PDF.

function exportToPDF() {
  const pdf = new jsPDF();
  
  // Capture Three.js render
  const imageData = renderer.domElement.toDataURL("image/png");
  
  // Add Three.js render to PDF
  pdf.addImage(imageData, 'PNG', 10, 10, 190, 100);  // Adjust dimensions as needed
  
  // Capture HTML elements (panels)
  html2canvas(document.querySelector('.panels')).then(canvas => {
    const panelImageData = canvas.toDataURL('image/png');
    
    // Add panel image to PDF
    pdf.addImage(panelImageData, 'PNG', 10, 120, 190, 50);  // Adjust as needed
    
    pdf.save("output.pdf");
  });
}

Each of these approaches has its pros and cons:

  1. The first method is client-side but might have issues with complex Three.js scenes.
  2. The second method gives you more control but requires manual positioning.
    If these methods don’t work well with your four-viewport setup, you might need to capture each viewport separately and compose them in the PDF manually.
    Remember, PDF generation in browsers can be tricky and might not always produce perfect results, especially with complex 3D content. You might need to experiment and adjust based on your specific needs.
    I’m not sure this can be helpful to you.
    thanks.

Do you have preserveDrawingBuffer: true
in your renderer constructor?
This can prevent the gl layer from clearing the buffer before your tools can get access to the underlying bitmap…
Probably unrelated but something to be aware of.

Thank you so much for suggesting these approaches! I will test them out now, and I come back to you to say which one worked! Have a goo day

1 Like

I have it set to true. But then only one camera view is printed in the PDF. It seems it captures only the last rendered frame even though I am using the same renderer for each camera. But thanks anyway

Does the page render normally? I.e. you see all 4 images while its running?
Do you explicitly disable renderer.autoClear in between those sub renders to prevent the buffer being cleared?

The 4 images are rendering fine. I will try out the the renderer.autoClear, I havent done this one.

I am able to get the PDF with the html panels and so on, but notthe renderers. This is basically my renderer function:

I tested now, and nothing changed with the autoClear.

Can you save the canvas off with renderer.domElement.toDataUrl()

after you do the render? You might want to try that and see if you get the full image out of it… if so, perhaps you can work around the issue…

1 Like

I could, but I will try it out again to see if I did not mess up something in the code!

Thank you so much for helping me! I appreciate

1 Like

@manthrax @Rex_Dev

I was able to get the images being displayed by the cameras in the PDF by wrapping this:
image
into an requestAnimationFrame(). I was not expecting this solution though. Now, I need to combine with the html container to export both in PDF.

1 Like

Cool… at least you have a workaround. :smiley:
I’m surprised that new WebGLRenderer({preserveDrawingBuffer:true}) didn’t work… but sometimes things like having a css “background” set can interfere with it…