puppeteer icon indicating copy to clipboard operation
puppeteer copied to clipboard

Can not render a WebGL image for PDF -- get a black box instead

Open onamission opened this issue 7 years ago • 33 comments

Summary:

I am trying to get a PDF of a page that has a panoramic image on it using Puppeteer. The panoramic image is rendered using WebGL, which I suspect to be the issue. All I get is a black box instead of the image (see the bottom of this post for screen image). But, I can use Puppeteer to get a screenshot of that same page and the panorama looks great. I am not sure why it won't render when I attempt getting a PDF, but works for screenshot.

Steps to reproduce

Tell us about your environment:

  • Puppeteer version: [email protected]
  • Platform / OS version: Linux c1301da96bb5 4.9.49-moby running in a Docker container
  • URLs (if applicable):
  • Node.js version: 8.8.1 (NOTE: the code below was originally developed in 6.1, but we now run 8.8.1)

What steps will reproduce the problem?

Please include code that reproduces the issue.

  1. Using this code:
var page, browser;
return Puppeteer.launch({args: options.chromeArgs})
    .then(res => {
        if (!res) {
            return reject(new Error("No browser to open"));
        }
        browser = res;
        return browser.newPage();
    })
    .then(res => {
        if (!res) {
            throw new Error("No browswer available");
         }
        page = res;
        return page.setViewport({
            height: 720,
            width: 1280,
            isLandscape: true,
        ));
    })
    .then(() => {
        return page.goto(url,{"waitUntil":"networkidle0"});
    })
    .then(() => {
        return page.pdf(options);
    })
    .then(buffer => {
       if (browser) {
           browser.close();
       }
       return resolve(buffer);
    })
    .catch(err => {
        if (browser) {
            browser.close();
        }
        return reject(err);
    });
  1. I have tried these options:
            "pageTimeout": 120000,
            "chromeArgs": ["--disable-gpu","--disable-setuid-sandbox","--no-sandbox"],
            "headless": true,
            "printBackground": true,
            "displayHeaderFooter": true,
            "landscape": true,
            "margin": {
                "top": 0,
                "bottom": 0,
                "left": 0,
                "right": 0
            }
        }
  1. I have also tried adding these the the chromeArgs: --use-gl=swiftshader --use-gl=osmesa --use-gl=swiftshader-webgl

What is the expected result? I would expect the PDF to look like the screenshot -- like this 004

Here is the screenshot from Puppeteer (NOTE: the html page is formatted slightly differently for PDF rendering, but the panoramic image is the same) 0002

What happens instead? The PDF has a black box where the panoramic image should be. 003

onamission avatar Jan 05 '18 18:01 onamission

What happens if you try to print the page with chrome?

JoelEinbinder avatar Jan 05 '18 21:01 JoelEinbinder

@onamission I see you try to create pdf right after the networkidle0 event. I'd expect the page with WebGL context to do some async rendering using requestAnimationFrame, so it might be that you need to wait a bit before attempting to pdf the page.

aslushnikov avatar Jan 09 '18 02:01 aslushnikov

Curious if anyone has solved this since I've ran into a similar issue. Image/PNG generation works just fine but PDF's are showing blanks for a canvas-based map.

I can post repo steps here if it makes debugging easier.

joelgriffith avatar Jan 29 '18 15:01 joelgriffith

Same problem here so I'll add examples plus link to the actual page in case this would be helpful.

The following page renders just fine as screenshot, but the map canvas (Mapbox-gl WebGL) is blank when rendering as PDF: https://dashboards.cluvio.com/dashboards/wx7y-53qr-pv5o/shared?sharingToken=759d96a5-da83-493c-833e-6b8f694a6189

dashboard

dashboard.pdf

ianformanek avatar Jan 31 '18 15:01 ianformanek

FWIW I think this might be a problem deeper downstream since this issue is present in a few libraries. I've created a post on the headless dev group here https://groups.google.com/a/chromium.org/forum/#!topic/headless-dev/BVpTJ4JIR8Q to try and see if it's deeper than just this library

joelgriffith avatar Jan 31 '18 15:01 joelgriffith

Some follow up: I've been instructed to create a bug in the Chromium tracker, which is can be found here.

I'll keep this thread up-to-date as its status changes.

joelgriffith avatar Feb 05 '18 15:02 joelgriffith

I'm having a similar issue except both screenshot and pdf both output as white. If I turn off headless then the screenshot renders as expected and the pdf crashes with the following.

(node:53832) UnhandledPromiseRejectionWarning: Error: Protocol error (Page.printToPDF): PrintToPDF is not implemented undefined at Promise (/Users/matt/Projects/test/node_modules/puppeteer/lib/Connection.js:196:56) at new Promise (<anonymous>) at CDPSession.send (/Users/matt/Projects/test/node_modules/puppeteer/lib/Connection.js:195:12) at Page.pdf (/Users/matt/Projects/test/node_modules/puppeteer/lib/Page.js:771:39) at /Users/matt/Projects/test/index.js:21:14 at <anonymous> (node:53832) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:53832) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

antiquechrono avatar Feb 26 '18 23:02 antiquechrono

@joelgriffith in your Google group comment, you mention that screenshot works - how were you able to at least get screenshot working? I am also trying to get this to work with MapboxGL

allthesignals avatar Mar 08 '18 23:03 allthesignals

I had better luck with this solution: https://github.com/GoogleChrome/puppeteer/issues/1260#issuecomment-348878456, specifically for MapboxGL.

allthesignals avatar Mar 19 '18 18:03 allthesignals

@allthesignals Hi, I'm trying to run headless with MapboxGL , it works fine in all distributions ,but only in Centos it doesnt work. I'm using centos 7.4. do you know anything about it?

ronalb avatar Mar 27 '18 08:03 ronalb

me too.

ubuntu server 16.04 (X)

Linux mint 18.3 with Cinnamon (√)

zuohuadong avatar Apr 23 '18 09:04 zuohuadong

someone have solved this problem?

fhvknb avatar May 03 '18 08:05 fhvknb

Hello, had similar issue running headless chromium on lambda for PDF generation (screenshots were working fine with WebGL but PDF not), solved it by rolling back to 64.0.3282.167 (instead of latest stable 67.0.3396.79 or dev 69.0.3452.0), hope it might help someone.

mvoropaiev avatar Jun 12 '18 05:06 mvoropaiev

also on windows platform, puppeteer version 1.5 doesn't render webgl, puppeteer version 1.3 works fine.

ronalb avatar Jun 13 '18 13:06 ronalb

Also with Mac 1.5 webgl in pdf is blank.

nuthinking avatar Jun 14 '18 09:06 nuthinking

I'm experiencing the same issues in regard to manipulating and outputting a WebGL context via PDF. The process I use it to render a WebGL scene in Chrome headless, export a PNG snapshot using JavaScript, inject that image into the DOM, and then save as both a screenshot and PDF. The whole process is run using a single page within Puppeteer.

Here are results from the most recent Puppeteer versions (on Windows):

1.6.1 (HeadlessChrome/69.0.3494.0 - Revision 575458)

  • WebGL context does not output in PDF
  • Image snapshot of WebGL context does not output in PDF

1.6.0 (HeadlessChrome/69.0.3477.0 - Revision 571375)

  • WebGL context does not output in PDF
  • Image snapshot of WebGL context does not output in PDF

1.5.0 (HeadlessChrome/69.0.3452.0 - Revision 564778)

  • WebGL context does not output in PDF
  • Image snapshot of WebGL context does not output in PDF

1.4.0 (HeadlessChrome/68.0.3419.0 - Revision 555668)

  • WebGL context does not output in PDF
  • Image snapshot of WebGL context does output in PDF

As you can see, something happened between 1.4.0 and 1.5.0 to break the WebGL context in Puppeteer PDF exports.

robhawkes avatar Jul 27 '18 09:07 robhawkes

@robhawkes I had to downgrade to 1.4 because of this issue. I'm rendering mapbox webgl maps in headless chrome.

ronalb avatar Aug 09 '18 14:08 ronalb

@ronalb Coincidentally, that's exactly what I'm doing too! The 1.4.0 downgrade has been workable for me so far, though it's frustrating that it's not possible to use the latest version as things stand.

robhawkes avatar Aug 09 '18 14:08 robhawkes

@robhawkes yeah ,very frustrating. as from 1.6 some race conditions bugs were fixed.

ronalb avatar Aug 09 '18 14:08 ronalb

@robhawkes just checked on puppeteer 1.7 and it seems to be fixed !

ronalb avatar Aug 13 '18 13:08 ronalb

@ronalb You got me excited but it still doesn't seem to work for me (1.7.0 using HeadlessChrome/70.0.3508.0), the WebGL context is still blank. Did you do anything different to get it working?

robhawkes avatar Aug 13 '18 14:08 robhawkes

@robhawkes no I didn't do anything ,but maybe my scenario is different. I also see that you wrote in version 1.4 that it doesnt work for you in PDF. for me it worked.

some more info: I think I know the difference , I am not really using the PDF feature , what I do is take an image snapshot,(I had to use some timeout because of mapbox doesn't let me know when the map is loaded) and put it on a new generated html, then I export the new html to pdf.

ronalb avatar Aug 13 '18 14:08 ronalb

I haven't seen a lot of movement on the Chromium side (https://bugs.chromium.org/p/chromium/issues/detail?id=809065), is there any sense on how/when this could be fixed?

joelgriffith avatar Dec 10 '18 17:12 joelgriffith

@ianformanek Make sure you're using preserveDrawingBuffer: true.

andrewharvey avatar Dec 16 '18 01:12 andrewharvey

Yeah, this is it! Creating the mapbox-gl instances like this:

      map = new mapboxgl.Map({
        container: '...',
        ...,
        preserveDrawingBuffer: true
      });

fixes the issue when the map would be empty when using puppeteer to render pdf (probably limited to rendering on server without a GPU).

It is still interesting that pdf and image behave differently in this regard, but probably has to do with timing. See also https://github.com/mapbox/mapbox-gl-js/issues/6448

Thanks!

ianformanek avatar Dec 16 '18 09:12 ianformanek

Any update on this thread: The issue is still coming for "puppeteer": "^2.0.0". With headless mode, on taking the screenshot of a canvas element it's getting the blank output, but on running the same code with headlfull mode it's working just fine.

anurasin avatar Jan 21 '20 16:01 anurasin

I'm seeing this issue when I try to render a PDF containing a mapbox-gl-js map on the latest release of Puppeteer (5.3.1) on Ubuntu server 18.04 LTS.

I've tried using the bundled Chromium version and latest releases of Chrome and Chrome Beta - all give the same result.

I've tried setting the preserveDrawingBuffer: true option on the Mapbox map.

If I set dumpio: true when launching Puppeteer, I do see this error message:

[1019/145021.772786:ERROR:command_buffer_proxy_impl.cc(122)] ContextResult::kTransientFailure: Failed to send GpuChannelMsg_CreateCommandBuffer.

Screenshot 2020-10-19 at 15 48 51

gbuckingham89 avatar Oct 19 '20 14:10 gbuckingham89

I had the same issue with Three.js. A workaround is to use the already-suggested preserveDrawingBuffer:

renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });

UPDATE: I ended-up using screenshots instead, since preserveDrawingBuffer was causing unexpected issues with some browsers/OSs when rendering the scene in the browser.

Peque avatar Dec 27 '20 01:12 Peque

For some reason the mapbox issue affects pdf but not screenshots, so my workaround was to replace canvases with screenshots:

let canvases = await page.$$('canvas')
for(let canvas of canvases){
    let str = await canvas.screenshot({ encoding: "base64" })
    let dataUrl = 'data:image/png;base64,' + str
    await canvas.evaluate((canvas, dataUrl) => {
      const newDiv = document.createElement('div')
      newDiv.innerHTML = '<img src="' + dataUrl + '">'
      canvas.parentNode.replaceChild(newDiv, canvas)
    }, dataUrl)
}

pguardiario avatar Jun 19 '21 02:06 pguardiario

Any updates? I am still experiencing this on puppeteer: "13.0.0. screenshot works. but pdf doesnt work. I have also noticed that on threejs, when there is no light added to the scene, and the material uses MeshNormalMaterial() it works.

EDIT: @pguardiario answer worked for me

YosanAI avatar Dec 24 '21 13:12 YosanAI