ChatGPT解决这个技术问题 Extra ChatGPT

Capture HTML Canvas as gif/jpg/png/pdf?

Is it possible to capture or print what's displayed in an html canvas as an image or pdf?

I'd like to generate an image via canvas, and be able to generate a png from that image.

Here's a pythonic solution: stackoverflow.com/questions/19395649/… in addition to answer stackoverflow.com/a/3514404/529442
Example here freakyjolly.com/…
You might find canvas2image library useful for this: github.com/hongru/canvas2image
If someone is looking for an answer related to React, I wrote two tutorials about it yesterday: React Component to Image and React Component to PDF.

d
donohoe

Original answer was specific to a similar question. This has been revised:

const canvas = document.getElementById('mycanvas')
const img    = canvas.toDataURL('image/png')

with the value in IMG you can write it out as a new Image like so:

document.getElementById('existing-image-id').src = img

or

document.write('<img src="'+img+'"/>');

one more question, how can I save the image I got in this tag to server. Any guess??
But if i use var img = canvas.toDataURL("image/jpeg"); am getting the background as complete black. How to rectify that
Oh come on. I answered this in 2009. What do you expect?
@donohoe actually you answered it in August 2010 :)
It would use a lot less memory to do something like var img = new Image(); img.src = canvas.toDataURL(); document.body.appendChild(img);. The document.write code is making the data URL, them making a HTML string, then putting a copy of that string in the DOM, the browser then has to parse that HTML string, put another copy on the image element, then parse it again to turn the data URL into image data, then finally it can show the image. For a screen size image that's a huge amount of memory/copying/parsing. Just a suggestion
P
Phillip

HTML5 provides Canvas.toDataURL(mimetype) which is implemented in Opera, Firefox, and Safari 4 beta. There are a number of security restrictions, however (mostly to do with drawing content from another origin onto the canvas).

So you don't need an additional library.

e.g.

 <canvas id=canvas width=200 height=200></canvas>
 <script>
      window.onload = function() {
          var canvas = document.getElementById("canvas");
          var context = canvas.getContext("2d");
          context.fillStyle = "green";
          context.fillRect(50, 50, 100, 100);
          // no argument defaults to image/png; image/jpeg, etc also work on some
          // implementations -- image/png is the only one that must be supported per spec.
          window.location = canvas.toDataURL("image/png");
      }
 </script>

Theoretically this should create and then navigate to an image with a green square in the middle of it, but I haven't tested.


Where the image will be saved. Location in my local?
the image will be displayed as an image in your browser. You can then save it to disk or whatever. Here's a quick and dirty generic "Canvas2PNG" bookmarklet that converts the first canvas in the page to PNG and displays it in the browser in a new window: javascript:void(window.open().location = document.getElementsByTagName("canvas")[0].toDataURL("image/png"))
If the image is a few MB in size, prepare to crash your browser (I did that a while back in FireFox).
How could this be modified to render multiple images?
Data URIs have max length, so there is upper limit on the size of an image that you can put to a data url.
m
meiamsome

I thought I'd extend the scope of this question a bit, with some useful tidbits on the matter.

In order to get the canvas as an image, you should do the following:

var canvas = document.getElementById("mycanvas");
var image = canvas.toDataURL("image/png");

You can use this to write the image to the page:

document.write('<img src="'+image+'"/>');

Where "image/png" is a mime type (png is the only one that must be supported). If you would like an array of the supported types you can do something along the lines of this:

var imageMimes = ['image/png', 'image/bmp', 'image/gif', 'image/jpeg', 'image/tiff']; //Extend as necessary 
var acceptedMimes = new Array();
for(i = 0; i < imageMimes.length; i++) {
    if(canvas.toDataURL(imageMimes[i]).search(imageMimes[i])>=0) {
        acceptedMimes[acceptedMimes.length] = imageMimes[i];
    }
}

You only need to run this once per page - it should never change through a page's lifecycle.

If you wish to make the user download the file as it is saved you can do the following:

var canvas = document.getElementById("mycanvas");
var image = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); //Convert image to 'octet-stream' (Just a download, really)
window.location.href = image;

If you're using that with different mime types, be sure to change both instances of image/png, but not the image/octet-stream. It is also worth mentioning that if you use any cross-domain resources in rendering your canvas, you will encounter a security error when you try to use the toDataUrl method.


With your solution for downloading the file, I must choose the software I want to use, it's a bit unconvenient... Is there a way to re-replace the mime as png once downloaded?
@So4ne I don't think so, it has to be an image/octet-stream to prompt the user for a download. If you get rid of that line, you'll end up with the page redirecting to the image. I would love to know if someone else knows a way to do this nicely, though.
This is great. Thanks! But what if I want to save to server and not local? Will a form file input accept the img src as something that is uploadable?
d
david.barkhuizen
function exportCanvasAsPNG(id, fileName) {

    var canvasElement = document.getElementById(id);

    var MIME_TYPE = "image/png";

    var imgURL = canvasElement.toDataURL(MIME_TYPE);

    var dlLink = document.createElement('a');
    dlLink.download = fileName;
    dlLink.href = imgURL;
    dlLink.dataset.downloadurl = [MIME_TYPE, dlLink.download, dlLink.href].join(':');

    document.body.appendChild(dlLink);
    dlLink.click();
    document.body.removeChild(dlLink);
}

The saved file stays in .svg format. How to save it as png?
This is a nice solution except that it may not work on IE. Getting error "The data area passed to a system call is too small"
In Chrome it says "Network error", while in Firefox it works great. (On Linux)
P
Phillip

I would use "wkhtmltopdf". It just work great. It uses webkit engine (used in Chrome, Safari, etc.), and it is very easy to use:

wkhtmltopdf stackoverflow.com/questions/923885/ this_question.pdf

That's it!

Try it


I'm in the wkhtmltopdf camp, too. We've been using it for archiving and its AMAZING.
How to use WKHTMLtoPDF in page that required login info ? I am using Jsreport to convert to PDF but I capture my content with HTML2Canvas ,I have issue with sending the Canvas as parameter to JSreport
C
Community

Here is some help if you do the download through a server (this way you can name/convert/post-process/etc your file):

-Post data using toDataURL

-Set the headers

$filename = "test.jpg"; //or png
header('Content-Description: File Transfer');
if($msie = !strstr($_SERVER["HTTP_USER_AGENT"],"MSIE")==false)      
  header("Content-type: application/force-download");else       
  header("Content-type: application/octet-stream"); 
header("Content-Disposition: attachment; filename=\"$filename\"");   
header("Content-Transfer-Encoding: binary"); 
header("Expires: 0"); header("Cache-Control: must-revalidate"); 
header("Pragma: public");

-create image

$data = $_POST['data'];
$img = imagecreatefromstring(base64_decode(substr($data,strpos($data,',')+1)));

-export image as JPEG

$width = imagesx($img);
$height = imagesy($img);
$output = imagecreatetruecolor($width, $height);
$white = imagecolorallocate($output,  255, 255, 255);
imagefilledrectangle($output, 0, 0, $width, $height, $white);
imagecopy($output, $img, 0, 0, 0, 0, $width, $height);
imagejpeg($output);
exit();

-or as transparent PNG

imagesavealpha($img, true);
imagepng($img);
die($img);

N
Nayuki

This is the other way, without strings although I don't really know if it's faster or not. Instead of toDataURL (as all questions here propose). In my case want to prevent dataUrl/base64 since I need a Array buffer or view. So the other method in HTMLCanvasElement is toBlob. (TypeScript function):

    export function canvasToArrayBuffer(canvas: HTMLCanvasElement, mime: string): Promise<ArrayBuffer> {
  return new Promise((resolve, reject) => canvas.toBlob(async (d) => {
    if (d) {
      const r = new FileReader();
      r.addEventListener('loadend', e => {
        const ab = r.result;
        if (ab) {
          resolve(ab as ArrayBuffer);
        }
        else {
           reject(new Error('Expected FileReader result'));
        }
      }); r.addEventListener('error', e => {
        reject(e)
      });
      r.readAsArrayBuffer(d);
    }
    else {
      reject(new Error('Expected toBlob() to be defined'));
    }
  }, mime));
}

Another advantage of blobs is you can create ObjectUrls to represent data as files, similar to HTMLInputFile's 'files' member. More info:

https://developer.mozilla.org/en/docs/Web/API/HTMLCanvasElement/toBlob


This answer must be marked as the correct one and need to be upvoted. Thanks!
C
Cybermaxs

Another interesting solution is PhantomJS. It's a headless WebKit scriptable with JavaScript or CoffeeScript.

One of the use case is screen capture : you can programmatically capture web contents, including SVG and Canvas and/or Create web site screenshots with thumbnail preview.

The best entry point is the screen capture wiki page.

Here is a good example for polar clock (from RaphaelJS):

>phantomjs rasterize.js http://raphaeljs.com/polar-clock.html clock.png

Do you want to render a page to a PDF ?

> phantomjs rasterize.js 'http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes' jakarta.pdf

+1: PhantomJS is simple, well documented and well thought-out system, perfect for this job. It allows much more than just grabbing a canvas too - for instance, you can modify the page or part of it (through JS) before grabbing so make it look just the way you want it. Perfect!
PhantomJs is now obsolete
PhantomJS is not "obsolete" - it is unmaintained.... It still supports most ES6 stuff, and is still the only modern/decent headless browser that works on x86 and does not need compiling. Therefore it still the only [proper] headless browser that works on various embedded or lightweight distros/systems.
P
Phillip

If you are using jQuery, which quite a lot of people do, then you would implement the accepted answer like so:

var canvas = $("#mycanvas")[0];
var img = canvas.toDataURL("image/png");

$("#elememt-to-write-to").html('<img src="'+img+'"/>');

Do note that the only usage of jQuery here is the selection of the canvas. .toDataURL is native JS.
I have save issue some one can help me see this link :stackoverflow.com/questions/25131763/…
Pure (100%) jQuery solution is the following: $('<img>').attr('src',$('#mycanvas')[0].toDataURL('image/png')).appendTo($('#element-to-write-to').empty()); Exactly one line.
C
Carson

The key point is

canvas.toDataURL(type, quality)

And I want to provide an example for someone like me who wants to save SVG to PNG(also can add some text if you wish), which may be from an Online source or font-awesome icon, etc.

Example

100% javascript and no other 3-rd library.

if you want to run on stackoverflow and move your mouse on the picture may get error

DOMException: The Clipboard API has been blocked because of a permissions policy applied to the current document

You can copy the code on your local machine and run it again, will be fine.


P
Peter

On some versions of Chrome, you can:

Use the draw image function ctx.drawImage(image1, 0, 0, w, h); Right-click on the canvas


f
ferralucho

You can use jspdf to capture a canvas into an image or pdf like this:

var imgData = canvas.toDataURL('image/png');              
var doc = new jsPDF('p', 'mm');
doc.addImage(imgData, 'PNG', 10, 10);
doc.save('sample-file.pdf');

More info: https://github.com/MrRio/jsPDF


B
B''H Bi'ezras -- Boruch Hashem

The simple answer is just to take the blob of it and set the img src to a new object URL of that blob, then add that image to a PDF using some library, like

var ok = document.createElement("canvas") ok.width = 400 ok.height = 140 var ctx = ok.getContext("2d"); for(let k = 0; k < ok.height; k++) ( k % Math.floor( ( Math.random() ) * 10 ) == 0 ) && (y => { for(var i = 0; i < ok.width; i++) { if(i % 25 == 0) { ctx.globalAlpha = Math.random() ctx.fillStyle = ( "rgb(" + Math.random() * 255 + "," + Math.random() * 255 + "," + Math.random() * 255 + ")" ); (wdth => ctx.fillRect( Math.sin( i * Math.PI / 180 ) * Math.random() * ok.width, Math.cos( i * Math.PI / 180, ) * wdth + y, wdth, wdth ) )(15) } } })(k) ok.toBlob(blob => { k.src = URL.createObjectURL(blob) })

Alternatively, if you wanted to work with low-level byte data, you can get the raw bytes of the canvas, then, depending on the file spec, write the raw image data into the necessary bytes of the data. you just need to call ctx.getImageData(0, 0, ctx.canvas.widht, ctx.canvas.height) to get the raw image data, then based on the file specification, write it to that


M
MrAni

if you want to emebed the canvas you can use this snippet

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <canvas id=canvas width=200 height=200></canvas>
    <iframe id='img' width=200 height=200></iframe>
    <script>
        window.onload = function() {
            var canvas = document.getElementById("canvas");
            var context = canvas.getContext("2d");
            context.fillStyle = "green";
            context.fillRect(50, 50, 100, 100);
            document.getElementById('img').src = canvas.toDataURL("image/jpeg");
            console.log(canvas.toDataURL("image/jpeg"));
        }
    </script>
</body>
</html>

J
Junior Tour

upload image from <canvas />:

async function canvasToBlob(canvas) {
  if (canvas.toBlob) {
    return new Promise(function (resolve) {
      canvas.toBlob(resolve)
    })
  } else {
    throw new Error('canvas.toBlob Invalid')
  }
}

await canvasToBlob(yourCanvasEl)