Canvas drawing not loading in puppeteer - javascript

I need to generate snapshots for seo.
I am using puppeteer(headless chrome) for this purpose.
On main page i have a canvas, on which i start to draw once the component has mounted (my main site is in react).
Issue is that when i get the html from puppeteer, the drawing on the canvas is not there.
In puppeteer code i wait till the content is not loaded.
html = await page.content()
How can i make puppeteer wait till the point canvas is not painted.

page.content will only return the HTML representation of the DOM. To get the actual image of a canvas inside the DOM, you can use the function toDataURL. This will return the image that is shown in a base64-encoded string.
Code sample
const dataUrl = await page.evaluate(() => {
const canvas = document.querySelector("#canvas-selector");
return canvas.toDataURL();
});
// dataUrl looks like this: "data:image/png;base64,iVBORw..."
const base64String = dataUrl.substr(dataUrl.indexOf(',') + 1); // get everything after the comma
const imgBuffer = Buffer.from(base64String, 'base64'); //
fs.writeFileSync('image.png', imgBuffer);
The evaluate call will return the base64 encoded buffer of the image. You need to first remove the "data:...," from that and then you can put that into a buffer. The buffer can then be saved (or handled in any other way).

Related

Javascript - imageCapture.takePhoto() function to take pictures

I am building an web application for my experiment purpose. The aim here is to capture ~15-20 frames per second from the webcam and send it to the server. Once the frame is captured, it is converted to base64 and added to an array. After certain time, it is sent back to the server. Currently I am using imageCapture.takePhoto() to achieve this functionality. I get blob as a result which is then changed to base64. The application runs for ~5 seconds and during this time, frames are captured and sent to the server.
What are the more efficient ways to capture the frames through webcam to achieve this?
You can capture still images directly from the <video> element used to preview the stream from .getUserMedia(). You set up that preview, of course, by doing this sort of thing (pseudocode).
const stream = await navigator.getUserMedia(options)
const videoElement = document.querySelector('video#whateverId')
videoElement.srcObject = stream
videoElement.play()
Next, make yourself a canvas object and a context for it. It doesn't have to be visible.
const scratchCanvas = document.createElement('canvas')
scratchCanvas.width = video.videoWidth
scratchCanvas.height = video.videoHeight
const scratchContext = scratchCanvas.getContext('2d')
Now you can make yourself a function like this.
function stillCapture(video, canvas, context) {
context.drawImage( video, 0, 0, video.videoWidth, video.videoHeight)
canvas.toBlob(
function (jpegBlob) {
/* do something useful with the Blob containing jpeg */
}, 'image/jpeg')
}
A Blob containing a jpeg version of a still capture shows up in the callback. Do with it whatever you need to do.
Then, invoke that function every so often. For example, to get approximately 15fps, do this.
const howOften = 1000.0 / 15.0
setInterval (stillCapture, howOften, videoElement, scratchCanvas, scratchContext)
All this saves you the extra work of using .takePhoto().

Download a 'data:' image/file using puppeteer and node.js

I'm trying to download an image using node.js and puppeteer but I'm running into some issues. I'm using a webscraper to gather the links of the images from the site and then using the https/http package to download the image.
This works for the images using http and https sources but some images have links that look like this (the whole link is very long so I cut the rest):
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAw8AAADGCAYAAACU07w3AAAZuUlEQVR4Ae3df4yU930n8Pcslu1I1PU17okdO1cLrTD+g8rNcvRyti6247K5NG5S5HOl5hA2uZ7du6RJEGYPTFy1Nv4RUJy0cWVkeQ9ErqqriHNrR8niZuVIbntBS886rBZWCGHVsNEFRQ5BloPCzGn2B+yzZMLyaP........
I'm not sure how to handle these links or how to download the image. Any help would be appreciated.
You need to first decode the url from base64 using node.js Buffer.
// the content type image/png has to be removed first
const data = 'iVBORw0KGgoAAAANSUhEUgAAAw8AAADGCAYAAACU07w3AAAZuUlEQVR4Ae3df4yU930n8Pcslu1I1PU17okdO1cLrTD+g8rNcvRyti6247K5NG5S5HOl5hA2uZ7du6RJEGYPTFy1Nv4RUJy0cWVkeQ9ErqqriHNrR8niZuVIbntBS886rBZWCGHVsNEFRQ5BloPCzGn2B+yzZMLyaP';
const buffer = new Buffer(data);
const base64data = buff.toString('base64');
// after this you will get the url string and continue to fetch the image
These are the base64 encoded images (mostly used for icons and small images).
you can ignore it.
if(url.startsWith('data:')){
//base 64 image
} else{
// an image url
}
if you really want to mess with base64 I can give you a workaround.
import { parseDataURI } from 'dauria';
import mimeTypes from 'mime-types';
const fileContent = parseDataURI(file);
// you probably need an extension for that image.
let ext = mimeTypes.extension(fileContent.MIME) || 'bin';
fs.writeFile("a random file"+"."+ext, fileContent.buffer, function (err) {
console.log(err); // writes out file without error, but it's not a valid image
});

Is there a good way to ensure an object gets created before moving through code that uses it?

I have a bit of code that uses fetch() to grab and convert .tiff images to an html5 canvas to be displayed in a browser using tiff.js (https://github.com/seikichi/tiff.js/tree/master). It almost works great, however, I am noticing that sometimes the images don't make it to the browser.
Some images will appear, but occasionally others will not, with the following error message in the browser:
ReferenceError: Tiff is not defined
I need to find out if there is a good way to ensure that these objects get created successfully, and would appreciate any insight I could get into what causes this behavior.
class tiffImage {
constructor() {
this.tiffURL = 'url-to-image';
this.height;
this.width;
this.canvas;
}
async loadImage() {
fetch(this.tiffURL)
// retrieve tiff and convert it to an html5 canvas
let response = await fetch(this.tiffURL);
let buffer = await response.arrayBuffer();
let tiff = new Tiff({buffer: buffer}); // error points to this line
this.canvas = tiff.toCanvas();
/* Parse some data from image and do DOM stuff */
}
}
// retrieve and display boards
let someTiff1 = new tiffImage();
let someTiff2 = new tiffImage();
let someTiff3 = new tiffImage();
let someTiff4 = new tiffImage();
let someTiff5 = new tiffImage();
someTiff1.loadImage();
someTiff2.loadImage();
someTiff3.loadImage();
someTiff4.loadImage();
someTiff5.loadImage();
Sometimes all of the images are loaded probably, and sometimes not. If the page is refreshed enough times it is guaranteed to see some images fail to load. Note that in my actual project I am instantiating and calling loadImage() on 13 objects.
Read up on Promises. A promise will allow you to wait for asynchronous actions to complete before progressing.
loadImage() {
return fetch(this.tiffURL)
.then(response=>{
// retrieve tiff and convert it to an html5 canvas
let buffer = response.arrayBuffer();
let tiff = new Tiff({buffer: buffer}); // error points to this line
this.canvas = tiff.toCanvas();
return this;
/* Parse some data from image and do DOM stuff */
}
}
Promise.all([someTiff1.loadImage(),someTiff2.loadImage()])
.then(results=>{
console.log("My results", results)
})
Don't use async and await in your code. If you're using these, you're using Promises wrong.

How to load an HTML5 Canvas with an image from an image-service?

My web app calls a Web API service, which returns an image. The service returns nothing but an image. Calling the service is little different because there is a function in the routing code that adds the required auth-code and such. Anyway, my point is, I don't have the full URL and even if I did, I wouldn't want to pass it into code in plain-text. So what I have is a response, and that response is an image.
getThumb(filename: string) {
return this.http.get('/Picture/' + filename).subscribe(response => {
return response;
});
}
What I need to do is draw that image on to a canvas. From what I've seen on the internet so far, it looks like I want to create an image element, then assign that element src a URL, then I can add it to the canvas. It's the src part that's perplexing me. All the samples I see are either loading the image from a local filesystem or predefined URL, or from a base64 string, etc. I can't figure out how to just load an image I have as a response from a service. I'm sure I'm overthinking it.
Does anyone have some sample code to illustrate this?
e.g Something like this:
var img = new Image(); // Create new img element
img.src = ... ; // Set source to image
You could convert the image to Base64. In my example, you request the image and convert it to a blob using response.blob(). Once it's a blob, use fileReader.readAsDataURL to get the Base64.
const fileReader = new FileReader();
fetch("image-resource").then((response) => {
if(response.ok) {
return response.blob();
}
}).then((blob) => {
fileReader.readAsDataURL(blob);
fileReader.onloadend = () => {
console.log(fileReader.result);
}
});
References:
readAsDataURL
Blob

Fetching images with JavascriptResponse in CefBrowser (Without downloading twice)

I'm attempting to use CefSharp (Offscreen) to get image information in a webpage. I'd like to avoid downloading the content twice (I'm aware I can pull the src string from an image tag, and then download the image again). Right now, this is my code:
using (var browser = new ChromiumWebBrowser("http://example.com"))
{
//All this does is wait for the entire frame to be loaded.
await LoadPageAsync(browser);
var res1 = await browser.EvaluateScriptAsync("document.getElementsByTagName('img')[0].src");
//res1.Result = the source of the image (A string)
var res2 = await browser.EvaluateScriptAsync("document.getElementsByTagName('img')[0]");
//This causes an APPCRASH on CefSharp.BrowserSubprocess.exe
}
The way I figure it, CefSharp is already downloading these images to render a webpage. I'd like to avoid making a second request to pull these images from the client, and pull them directly from the client. Is this possible? What are the limitations of the JavascriptResponse object, and why is it causing an APPCRASH here?
Some thoughts: I thought about base64 encoding the image and then pulling it out this way, but this would require me to generate a canvas and fill that canvas every time for each image I want, generate a base64 string, bring it to c# as a string, and then decode it back to an image. I don't know how efficient that would be, but I'm hoping there could be a better solution.
This is how I solved it:
result = await browser.EvaluateScriptAsync(#"
;(function() {
var getDataFromImg = function(img) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0 );
var dataURL = canvas.toDataURL('image/png');
return dataURL.replace(/^data:image\/(png|jpg);base64,/, '');
}
var images = document.querySelectorAll('.image');
var finalArray = {};
for ( var i=0; i<images.length; i++ )
{
//I just filled in array. Depending on what you're grabbing, you may want to fill
//This with objects instead with text to identify each image.
finalArray.push(getDataFromDiv(images[i]));
}
return finalArray;
})()");
//Helper function for below
private static string FixBase64ForImage(string image)
{
var sbText = new StringBuilder(image, image.Length);
sbText.Replace("\r\n", string.Empty);
sbText.Replace(" ", string.Empty);
return sbText.ToString();
}
//In c# convert the data to a memory stream, and then load it from that.
var bitmapData = Convert.FromBase64String(FixBase64ForImage(image));
var streamBitmap = new MemoryStream(bitmapData);
var sourceImage = (Bitmap) Image.FromStream(streamBitmap);
Try executing this javascript...
How to get base64 encoded data from html image
CefSharp should have FileReader api.
Then you can have the EvaluateScriptAsync call return the base64 encoded image data.

Categories