Is there a way to render html to image like PNG? I know that it is possible with canvas but I would like to render standard html element like div for example.
问题:
回答1:
All the answers here use third party libraries while rendering HTML to an image can be relatively simple in pure Javascript. There is was even an article about it on the canvas section on MDN.
The trick is this:
- create an SVG with a foreignObject node containing your XHTML
- set the src of an image to the data url of that SVG
drawImage
onto the canvas- set canvas data to target image.src
const {body} = document
const canvas = document.createElement(\'canvas\')
const ctx = canvas.getContext(\'2d\')
canvas.width = canvas.height = 100
const tempImg = document.createElement(\'img\')
tempImg.addEventListener(\'load\', onTempImageLoad)
tempImg.src = \'data:image/svg+xml,\' + encodeURIComponent(\'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\"><foreignObject width=\"100%\" height=\"100%\"><div xmlns=\"http://www.w3.org/1999/xhtml\"><style>em{color:red;}</style><em>I</em> lick <span>cheese</span></div></foreignObject></svg>\')
const targetImg = document.createElement(\'img\')
body.appendChild(targetImg)
function onTempImageLoad(e){
ctx.drawImage(e.target, 0, 0)
targetImg.src = canvas.toDataURL()
}
Some things to note
- The HTML inside the SVG has to be XHTML
- For security reasons the SVG as data url of an image acts as an isolated CSS scope for the HTML since no external sources can be loaded. So a Google font for instance has to be inlined using a tool like this one.
- Even when the HTML inside the SVG exceeds the size of the image it wil draw onto the canvas correctly. But the actual height cannot be measured from that image. A fixed height solution will work just fine but dynamic height will require a bit more work. The best is to render the SVG data into an iframe (for isolated CSS scope) and use the resulting size for the canvas.
回答2:
Yes. HTML2Canvas exists to render HTML onto <canvas>
(which you can then use as an image).
NOTE: There is a known issue, that this will not work with SVG
回答3:
May I recommend dom-to-image library, that was written solely to address this problem (I\'m the maintainer).
Here is how you use it (some more here):
var node = document.getElementById(\'my-node\');
domtoimage.toPng(node)
.then (function (dataUrl) {
var img = new Image();
img.src = dataUrl;
document.appendChild(img);
})
.catch(function (error) {
console.error(\'oops, something went wrong!\', error);
});
回答4:
There is a lot of options and they all have their pro and cons.
Option 1: Use one of the many available libraries
- dom-to-image
- wkhtmltoimage (included in the wkhtmltopdf tool)
- IMGKit (for ruby and based on wkhtmltoimage)
- imgkit (for python and based on wkhtmltoimage)
- python-webkit2png
- ...
Pros
- Conversion is quite fast most of the time
Cons
- Bad rendering
- Does not execute javascript
- No support for recent web features (FlexBox, Advanced Selectors, Webfonts, Box Sizing, Media Queries, ...)
- Sometimes not so easy to install
- Complicated to scale
Option 2: Use PhantomJs and maybe a wrapper library
- PhantomJs
- node-webshot (javascript wrapper library for PhantomJs)
- ...
Pros
- Execute Javascript
- Quite fast
Cons
- Bad rendering
- No support for recent web features (FlexBox, Advanced Selectors, Webfonts, Box Sizing, Media Queries, ...)
- Complicated to scale
- Not so easy to make it work if there is images to be loaded ...
Option 3: Use Chrome Headless and maybe a wrapper library
- Chrome Headless
- chrome-devtools-protocol
- Puppeteer (javascript wrapper library for Chrome headless)
- ...
Pros
- Execute Javascript
- Near perfect rendering
Cons
- Not so easy to have exactly the wanted result regarding:
- page load timing
- viewport dimensions
- Complicated to scale
- Quite slow and even slower if the html contains external links
Option 4: Use an API
- ApiFlash (based on chrome)
- EvoPDF (has an option for html)
- Grabzit
- HTML/CSS to Image API
- ...
Pros
- Execute Javascript
- Near perfect rendering
- Fast when caching options are correctly used
- Scale is handled by the APIs
- Precise timing, viewport, ...
- Most of the time they offer a free plan
Cons
- Not free if you plan to use them a lot
Disclaimer: I\'m the founder of ApiFlash. I did my best to provide an honest and useful answer.
回答5:
You could use PhantomJS, which is a headless webkit (the rendering engine in safari and (up until recently) chrome) driver. You can learn how to do screen capture of pages here. Hope that helps!
回答6:
You can use an HTML to PDF tool like wkhtmltopdf. And then you can use a PDF to image tool like imagemagick. Admittedly this is server side and a very convoluted process...
回答7:
The only library that I got to work for Chrome, Firefox and MS Edge was rasterizeHTML. It outputs better quality that HTML2Canvas and is still supported unlike HTML2Canvas.
Getting Element and Downloading as PNG
var node= document.getElementById(\"elementId\");
var canvas = document.createElement(\"canvas\");
canvas.height = node.offsetHeight;
canvas.width = node.offsetWidth;
var name = \"test.png\"
rasterizeHTML.drawHTML(node.outerHTML, canvas)
.then(function (renderResult) {
if (navigator.msSaveBlob) {
window.navigator.msSaveBlob(canvas.msToBlob(), name);
} else {
const a = document.createElement(\"a\");
document.body.appendChild(a);
a.style = \"display: none\";
a.href = canvas.toDataURL();
a.download = name;
a.click();
document.body.removeChild(a);
}
});
回答8:
I don\'t expect this to be the best answer, but it seemed interesting enough to post.
Write an app that opens up your favorite browser to the desired HTML document, sizes the window properly, and takes a screen shot. Then, remove the borders of the image.
回答9:
You can\'t do this 100% accurately with JavaScript alone.
There\'s a Qt Webkit tool out there, and a python version. If you want to do it yourself, I\'ve had success with Cocoa:
[self startTraverse:pagesArray performBlock:^(int collectionIndex, int pageIndex) {
NSString *locale = [self selectedLocale];
NSRect offscreenRect = NSMakeRect(0.0, 0.0, webView.frame.size.width, webView.frame.size.height);
NSBitmapImageRep* offscreenRep = nil;
offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
pixelsWide:offscreenRect.size.width
pixelsHigh:offscreenRect.size.height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat:0
bytesPerRow:(4 * offscreenRect.size.width)
bitsPerPixel:32];
[NSGraphicsContext saveGraphicsState];
NSGraphicsContext *bitmapContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep];
[NSGraphicsContext setCurrentContext:bitmapContext];
[webView displayRectIgnoringOpacity:offscreenRect inContext:bitmapContext];
[NSGraphicsContext restoreGraphicsState];
// Create a small + large thumbs
NSImage *smallThumbImage = [[NSImage alloc] initWithSize:thumbSizeSmall];
NSImage *largeThumbImage = [[NSImage alloc] initWithSize:thumbSizeLarge];
[smallThumbImage lockFocus];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[offscreenRep drawInRect:CGRectMake(0, 0, thumbSizeSmall.width, thumbSizeSmall.height)];
NSBitmapImageRep *smallThumbOutput = [[NSBitmapImageRep alloc] initWithFocusedViewRect:CGRectMake(0, 0, thumbSizeSmall.width, thumbSizeSmall.height)];
[smallThumbImage unlockFocus];
[largeThumbImage lockFocus];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[offscreenRep drawInRect:CGRectMake(0, 0, thumbSizeLarge.width, thumbSizeLarge.height)];
NSBitmapImageRep *largeThumbOutput = [[NSBitmapImageRep alloc] initWithFocusedViewRect:CGRectMake(0, 0, thumbSizeLarge.width, thumbSizeLarge.height)];
[largeThumbImage unlockFocus];
// Write out small
NSString *writePathSmall = [issueProvider.imageDestinationPath stringByAppendingPathComponent:[NSString stringWithFormat:@\"/%@-collection-%03d-page-%03d_small.png\", locale, collectionIndex, pageIndex]];
NSData *dataSmall = [smallThumbOutput representationUsingType:NSPNGFileType properties: nil];
[dataSmall writeToFile:writePathSmall atomically: NO];
// Write out lage
NSString *writePathLarge = [issueProvider.imageDestinationPath stringByAppendingPathComponent:[NSString stringWithFormat:@\"/%@-collection-%03d-page-%03d_large.png\", locale, collectionIndex, pageIndex]];
NSData *dataLarge = [largeThumbOutput representationUsingType:NSPNGFileType properties: nil];
[dataLarge writeToFile:writePathLarge atomically: NO];
}];
Hope this helps!
回答10:
Install phantomjs
$ npm install phantomjs
Create a file github.js with following code
var page = require(\'webpage\').create();
//viewportSize being the actual size of the headless browser
page.viewportSize = { width: 1024, height: 768 };
page.open(\'http://github.com/\', function() {
page.render(\'github.png\');
phantom.exit();
});
Pass the file as argument to phantomjs
$ phantomjs github.js
回答11:
You certainly can. GrabzIt\'s JavaScript API allows you to capture a div from a webpage like this:
<script type=\"text/javascript\" src=\"grabzit.min.js\"></script>
<script type=\"text/javascript\">
GrabzIt(\"Your Application Key\").ConvertURL(\"http://www.example.com/my-page.html\",
{\"target\": \"#features\", \"bheight\": -1, \"height\": -1, \"width\": -1}).Create();
</script>
Where #features is the ID of the div to capture. If you wanted to convert HTML to a image. You could use this technique:
GrabzIt(\"Your Application Key\").ConvertHTML(
\"<html><body><h1>Hello World!</h1></body></html>\").Create();
Disclaimer I built this API!
回答12:
Use html2canvas just include plugin and call method to convert HTML to Canvas then download as image PNG
html2canvas(document.getElementById(\"image-wrap\")).then(function(canvas) {
var link = document.createElement(\"a\");
document.body.appendChild(link);
link.download = \"manpower_efficiency.jpg\";
link.href = canvas.toDataURL();
link.target = \'_blank\';
link.click();
});
Source: http://www.freakyjolly.com/convert-html-document-into-image-jpg-png-from-canvas/
回答13:
Drawing DOM objects into a canvas shows a quick way to accomplish that.
From a performance point of view this method should be light. Because the svg renderer and the dom renderer can apparently pass buffers back & forth as needed. Which makes sense because it\'s all core code.
I don\'t have any benchmarks of this, but MDN documents are rather well maintained and not only for gecko. So I\'m guessing you can at least know that bad performance is an issue which will be addressed at some point.
回答14:
Use this code, it will surely work:
<script type=\"text/javascript\">
$(document).ready(function () {
setTimeout(function(){
downloadImage();
},1000)
});
function downloadImage(){
html2canvas(document.querySelector(\"#dvContainer\")).then(canvas => {
a = document.createElement(\'a\');
document.body.appendChild(a);
a.download = \"test.png\";
a.href = canvas.toDataURL();
a.click();
});
}
</script>
Just do not forget to include Html2CanvasJS file in your program. https://html2canvas.hertzen.com/dist/html2canvas.js
回答15:
You can add reference HtmlRenderer to your project and do the following,
string htmlCode =\"<p>This is a sample html.</p>\";
Image image = HtmlRender.RenderToImage(htmlCode ,new Size(500,300));