Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot render server side image that contains a Picture #80

Closed
timhemel opened this issue Jun 28, 2019 · 4 comments
Closed

cannot render server side image that contains a Picture #80

timhemel opened this issue Jun 28, 2019 · 4 comments
Labels
bug An error in the library, extensions, documentation, or samples

Comments

@timhemel
Copy link

Description

I have followed the example from the documentation to render server side images with puppeteer. I have tried to render both png and svg, but it did not work. I have used the following code:

const puppeteer = require('puppeteer');
const fs = require('fs');

const parseDataUrl = (dataUrl) => {
	const matches = dataUrl.match(/^data:(.+);base64,(.+)$/);
	if (matches.length !== 3) {
		throw new Error('Could not parse data URL.');
	}
	return { mime: matches[1], buffer: Buffer.from(matches[2], 'base64') };
};

(async () => {
	const browser = await puppeteer.launch();
	const page = await browser.newPage();
	page.setContent('<div id="myDiagramDiv"></div>');

	await page.addScriptTag({
		// url: 'https://cdnjs.cloudflare.com/ajax/libs/gojs/1.8.7/go.js'
		path: 'node_modules/gojs/release/go.js'
	});


	const [imagedata, svgdata] = await page.evaluate( () => {
		var $ = go.GraphObject.make;
		var myDiagram = $(go.Diagram, "myDiagramDiv",
			{
				"animationManager.isEnabled": false,
				"undoManager.isEnabled": true  // enable undo & redo
			});

		myDiagram.add(
			$(go.Part,
				$(go.Picture, {
					// with this line, i get an error message
					// desiredSize: new go.Size(100,100),
					source: "sample.png"
				})
			)
		);

		     
		let data = myDiagram.makeImageData();
		let svgdata = myDiagram.makeSVG({ size: new go.Size(800, NaN) });
		let svgstr = new XMLSerializer().serializeToString(svgdata);
		return [ data, svgstr ];
	});

	const { buffer } = parseDataUrl(imagedata);
	fs.writeFileSync('gojs-screenshot.png', buffer, 'base64');
	fs.writeFileSync('gojs.svg', svgdata);
	
	await browser.close();
})();

I am using:

├── gojs@2.0.13
└─┬ puppeteer@1.18.1

Desired behaviour

The two files should be PNG and SVG file containing the picture. If I specify the desiredSize property, the diagram should contain the picture with the right dimensions.

Observerd behaviour

The files do not contain the picture. The SVG file contains:

<svg width="800px" height="0px" viewBox="0 0 800 0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="width: 800px; height: 0px;"><g transform="matrix(1, 0, 0, 1, 0, 0)" clip-path="url(#mainClip396)"><g transform="matrix(Infinity, NaN, NaN, Infinity, 1, 1)"/></g><clipPath id="mainClip396"><rect x="0" y="0" width="800px" height="0px"/></clipPath></svg>

If I specify the desiredSize property, I get the following error message:

(node:7298) UnhandledPromiseRejectionWarning: Error: Evaluation failed: Error: Element has no source ("src") attribute: [object HTMLImageElement]
    at A (node_modules/gojs/release/go.js:12:21)
    at Yj.t.Ei (node_modules/gojs/release/go.js:1270:77)
    at Yj.t.hc (node_modules/gojs/release/go.js:876:106)
    at T.t.Ei (node_modules/gojs/release/go.js:1097:72)
    at T.t.hc (node_modules/gojs/release/go.js:876:106)
    at fi.t.hc (node_modules/gojs/release/go.js:533:390)
    at B (node_modules/gojs/release/go.js:617:280)
    at uj (node_modules/gojs/release/go.js:617:307)
    at sk (node_modules/gojs/release/go.js:705:138)
    at t.xz (node_modules/gojs/release/go.js:701:28)
    at ExecutionContext._evaluateInternal (/home/tim/.../node_modules/puppeteer/lib/ExecutionContext.js:122:13)
    at process._tickCallback (internal/process/next_tick.js:68:7)
  -- ASYNC --
    at ExecutionContext.<anonymous> (/home/tim/.../node_modules/puppeteer/lib/helper.js:111:15)
    at DOMWorld.evaluate (/home/tim/.../node_modules/puppeteer/lib/DOMWorld.js:112:20)
    at process._tickCallback (internal/process/next_tick.js:68:7)
  -- ASYNC --
    at Frame.<anonymous> (/home/tim/.../node_modules/puppeteer/lib/helper.js:111:15)
    at Page.evaluate (/home/tim/.../node_modules/puppeteer/lib/Page.js:782:43)
    at Page.<anonymous> (/home/tim/.../node_modules/puppeteer/lib/helper.js:112:23)
    at /home/tim/.../issue-poc.js:23:42
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:7298) 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:7298) [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.

The issue is possibly related to issue #46.

@simonsarris
Copy link
Collaborator

We'll investigate.

@simonsarris
Copy link
Collaborator

simonsarris commented Jul 9, 2019

It seems that, in puppeteer, the .width and .naturalWidth of HTMLImageElements created in-memory are always zero, even after an image has loaded. This is unexpected and something of a problem. So a desired size may be necessary.

Additionally, it seems in puppeteer that getting the value of HTMLImageElement.src returns an empty string, and you must use getAttribute('src'). This will be fixed in the library, but there may be other changes needed for puppeteer before we make the next release.

@simonsarris
Copy link
Collaborator

This has been fixed for SVG, but not for makeImageData, because makeImageData is still not drawing the image.

This may be a puppeteer issue, where puppeteer needs the images loaded on the page in order to work correctly. From what I've seen so far, images created in JavaScript in Puppeteer, even after loading, always have a width/height/naturalWidth/naturalHeight of 0, which is unfortunate.

@simonsarris
Copy link
Collaborator

We've just released GoJS 2.0.14, which might fix this issue. It at least, I think, fixes the SVG version of the issue. Below is the script I have used to test. If you have an example of puppeteer correctly drawing images to a canvas but not to GoJS, I would be interested to see why, and try to find out what's different.

const puppeteer = require('puppeteer');
const fs = require('fs');

const parseDataUrl = (dataUrl) => {
	const matches = dataUrl.match(/^data:(.+);base64,(.+)$/);
	if (matches.length !== 3) {
		throw new Error('Could not parse data URL.');
	}
	return { mime: matches[1], buffer: Buffer.from(matches[2], 'base64') };
};

(async () => {
	const browser = await puppeteer.launch({
    // headless: false,
    // devtools: true
  });
	const page = await browser.newPage();
	page.setContent('<div id="myDiagramDiv"></div>');

	await page.addScriptTag({
		 url: 'https://gojs.net/2.0.14/release/go.js'
    // path: 'node_modules/gojs/release/go.js'
	});

  const results = await page.evaluate( () => {
		var $ = go.GraphObject.make;
		var myDiagram = $(go.Diagram, "myDiagramDiv",
			{
				"animationManager.isEnabled": false,
				"undoManager.isEnabled": true  // enable undo & redo
			});

		myDiagram.add(
			$(go.Part,
				$(go.Picture, {
					// with this line, i get an error message
           desiredSize: new go.Size(55,55),
				   source: "images/55x55.png"
				})
			)
		);

		myDiagram.makeImageData({
      callback: function(imagedata) {
        const { buffer } = parseDataUrl(imagedata);
        fs.writeFileSync('_gojs-screenshot.png', buffer, 'base64');
    } });

    const getIMG = () => {
      return new Promise((resolve, reject) => {
        myDiagram.makeImageData({
          background: 'red',
          size: new go.Size(300, NaN),
          callback: function(data) {
            resolve(data);
          }
        });
      })
    }

    async function fetchIMG() {
      return await getIMG();
    }

    const getSVG = () => {
        return new Promise((resolve, reject) => {
          myDiagram.makeSVG({
            size: new go.Size(300, NaN),
            callback: function(svgdata) {
              let svgstr = new XMLSerializer().serializeToString(svgdata);
              console.log(svgstr)
              resolve(svgstr)
            }
          });
        })
    }

    async function fetchSVG() {
      return await getSVG();
    }

		// var results =  [fetchIMG(), fetchSVG()];
		 return fetchSVG();
	});

  console.log(results);
  fs.writeFileSync('_gojs.svg', results);
  //const { buffer } = parseDataUrl(imagedata);
  //fs.writeFileSync('_gojs-screenshot.png', buffer, 'base64');

 await browser.close();
})();

@WalterNorthwoods WalterNorthwoods added the bug An error in the library, extensions, documentation, or samples label Jul 31, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug An error in the library, extensions, documentation, or samples
Projects
None yet
Development

No branches or pull requests

3 participants