将HTML渲染为图像


166

有没有一种方法可以将html渲染为类似PNG的图像?我知道画布是可能的,但我想呈现例如div之类的标准html元素。


例如,创建某种用户帮助。可悲的是,这是不可能的(出于安全原因?)。您必须要求用户按PrintScreen才能执行某项操作。
MaxArt 2012年


我想使用HTML / CSS设计徽标。
Martin Delille 2012年

4
@ ErnestFriedman-Hill不是重复的:您提到的问题特定于java。
Martin Delille 2012年

这是将HTML转换为IMAGE png或jpeg格式codepedia.info/2016/02/…
Satinder singh

Answers:


41

我知道这是一个很老的问题,已经有了很多答案,但是我仍然花了几个小时来尝试做自己想做的事情:

  • 给定一个html文件,从命令行生成具有透明背景的(png)图像

使用无头的Chrome(此响应的版本为74.0.3729.157),实际上很容易:

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless --screenshot --window-size=256,256 --default-background-color=0 button.html

命令说明:

  • 您可以从命令行运行Chrome(对于Mac,此处显示,但在Windows或Linux上则类似)
  • --headless 运行Chrome而不打开它,并在命令完成后退出
  • --screenshot将捕获屏幕截图(请注意,它将screenshot.png在运行命令的文件夹中生成一个名为的文件)
  • --window-size只允许捕获屏幕的一部分(格式为--window-size=width,height
  • --default-background-color=0是告诉Chrome使用透明背景而非默认白色的魔术
  • 最后,您提供html文件(作为本地或远程的URL ...)

1
非常好!它也可以与SVG一起使用!最后我切换到您的解决方案!;-)
Martin Delille

1
--screenshot='absolute\path\of\screenshot.png'为我工作
palash

指令行有效。如果我想从express js以编程方式运行此程序,该如何运行?
Squapl食谱

仅供参考,即使在手册页中未提及该选项,它也适用于铬。
Robin Lashof-Regas

该svg对我而言不是svg ...它只是具有SVG扩展名的PNG ...因此请当心。
kristopolous

97

是。存在HTML2Canvas可以将HTML呈现到HTML2Canvas<canvas>(然后可以将其用作图像)。

注意:存在一个已知问题,该问题不适用于SVG


看起来很有趣,但是我没有设法使它起作用,所以我选择了John Fisher解决方案。感谢您提供的信息,以后我会看这个项目!
Martin Delille 2012年

@MartinDelille:这里是使用HTML2Canvas创建图像的文章,您也可以在客户端代码pedpedia.info/2016/02/…上
Satinder singh,

3
dom-to-image(请参阅tsayen的答案)在渲染准确图片方面做得更好。
JBE,2016年

3
该解决方案非常慢
hamboy75

3
另一个问题,如果该元素处于隐藏状态或在另一个元素后面,则将不起作用
Yousef

91

我可以推荐dom-to-image库,该库专门用于解决此问题(我是维护者)。
这是您的用法(此处有更多信息):

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);
    });

2
这很棒!有什么办法使输出大小增加一倍吗?
cronoklee's

谢谢!但是,“将大小加倍”到底是什么意思?
tsayen'2

2
我想到的第一件事-尝试将图片渲染为SVG(使用domtoimage.toSvg),然后在画布上自己渲染,并尝试以某种方式使用该画布的分辨率。可以在lib本身中实现诸如某种渲染选项之类的功能,因此您可以以像素为单位传递图像尺寸。如果您需要它,非常感谢您在github上创建一个问题。
tsayen '16

8
这比html2canvas好得多。谢谢。
c.hughes

1
@Subho,它是一个字符串,其中包含带有base64编码数据的URL
tsayen

66

有很多选择,它们各有利弊。

选项1:使用众多可用库之一

优点

  • 大多数情况下转换速度都非常快

缺点

  • 渲染效果不佳
  • 不执行JavaScript
  • 不支持最新的网络功能(FlexBox,高级选择器,Webfonts,Box大小调整,媒体查询等)
  • 有时安装起来不太容易
  • 规模复杂

选项2:使用PhantomJs以及包装器库

优点

  • 执行JavaScript
  • 蛮快

缺点

  • 渲染效果不佳
  • 不支持最新的网络功能(FlexBox,高级选择器,Webfonts,Box大小调整,媒体查询等)
  • 规模复杂
  • 如果要加载图像,使其工作起来并不容易...

选项3:使用Chrome Headless,并可能使用包装器库

优点

  • 执行JavaScript
  • 接近完美的渲染

缺点

  • 要获得关于以下内容的所需结果并不容易:
    • 页面加载时间
    • 视口尺寸
  • 规模复杂
  • 如果html包含外部链接,则相当慢,甚至更慢

选项4:使用API

优点

  • 执行JavaScript
  • 接近完美的渲染
  • 正确使用缓存选项时快速
  • 规模由API处理
  • 精确的时间,视口,...
  • 大多数时候,他们提供免费计划

缺点

  • 如果您打算大量使用它们,它不是免费的

披露:我是ApiFlash的创始人。我尽力提供诚实和有用的答案。


2
感谢您提供许多可能的解决方案的详细答案
bszom

wkhtmltoimage / pdf确实支持javascript渲染。您可以设置JavaScript延迟或让wkhtml检查特定的window.status(可以在知道js内容完成时使用javascript进行设置)
Daniel

PhantomJS支持动态内容,例如Google Maps。它实际上是一个完整的Web浏览器,只是没有连接显示器。但是,您需要稍等片刻,以便Google Map加载具有地理位置的图块(我等待500毫秒,并允许人们更改延迟-如果不够,请重新运行而不增加时间就可以了,因为这些图块已缓存)。
Ladislav Zima

50

这里的所有答案都使用第三方库,而用纯Javascript渲染HTML到图像可能相对简单。还有甚至一个关于它的文章上MDN画布部分。

诀窍是这样的:

  • 使用包含XHTML的foreignObject节点创建SVG
  • 将图像的src设置为该SVG的数据URL
  • drawImage 在画布上
  • 将画布数据设置为目标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()
}

注意事项

  • SVG内部的HTML必须是XHTML
  • 出于安全原因,由于无法加载外部源,因此SVG作为图像的数据URL充当HTML的隔离CSS作用域。因此,例如,必须使用此类工具内联Google字体。
  • 即使SVG中的HTML超出了图像的大小,它也会正确地绘制到画布上。但是无法从该图像测量实际高度。固定高度的解决方案可以很好地工作,但动态高度需要更多的工作。最好的方法是将SVG数据渲染到iframe中(用于隔离的CSS范围),然后将结果尺寸用于画布。

真好!删除外部库依赖确实是个好方法。我更改了接受的答案:-)
Martin Delille '18

那篇文章不再存在。
MSC

2
很好的答案,但要评论一下HTML和Web API的艺术水平,就像往常一样,这些东西似乎被吸引了一些人—从技术上讲,所有功能都在那儿,但暴露在类似于代码路径的数组(无双关)的后面设计良好的API不会给您带来任何清晰度。简而言之:浏览器允许将a Document呈现为栅格(例如Canvas)是最简单的事,但是由于没有人愿意对其进行标准化,因此我们不得不诉诸疯狂,如上所示(本文作者没有冒犯之处)码)。
AMN

1
这个答案stackoverflow.com/questions/12637395/…似乎表明您可以走得很远。Mozilla和Chrome浏览器具有无限的数据长度,甚至当前的IE都允许4GB,最终可归结为约3GB的图像(由于base64)。但这将是一件好事。
Sjeiti

3
-稍后进行一些快速而肮脏的测试-jsfiddle.net/Sjeiti/pcwudrmc/75965 Chrome,Edge和Firefox的宽度高度至少达到10,000像素,(在这种情况下)字符数约为10,000,000,png文件大小8MB。还没有经过严格的测试,但是对于大多数用例来说已经足够了。
Sjeiti,

13

您可以使用PhantomJS,它是一个无头的Webkit(Safari中的渲染引擎和(直到最近)chrome)驱动程序。您可以在此处了解如何进行页面的屏幕截图。希望有帮助!


此技术效果很好。但是,自您发表评论以来已经过去了2年。您遇到比PhantomJS更快运行的任何东西吗?根据我的经验,在Phantom中生成图像通常需要2-5秒。
Brian Webster

现在可以使用无头Chrome。我不知道它是否更快,但是如果您想要准确的屏幕截图,这是一个不错的方法。
Josh Maag

11

您可以使用HTML到PDF的工具,例如wkhtmltopdf。然后,您可以使用PDF图像工具,例如imagemagick。诚然,这是服务器端的过程,非常复杂。


12
或者,您可以只运行wkhtmltopdf的图像兄弟wkhtmltoimage。
罗曼·斯塔科夫

1
该库用于Node.js。简单的前端怎么样?
Vlad18年

9

我必须为Chrome,Firefox和MS Edge使用的唯一库是rasterizeHTML。与HTML2Canvas相比,它输出的质量更好,并且仍受支持。

获取元素并下载为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);
            }
     });

2
但它不适用于IE。 rasterizeHTML限制
博班斯托扬诺夫斯基

@vabanagas是否有为此使用任何单个文件的javascript?
Mahdi Khalili

这项更改对我有很大帮助:rasterizeHTML.drawHTML(node.innerHTML, canvas) 请注意,我在节点上调用innerHTML
日GH


4

我不希望这是最好的答案,但是看起来足够有趣了。

编写一个应用程序,将您喜欢的浏览器打开到所需的HTML文档,适当调整窗口大小并进行屏幕截图。然后,删除图像的边框。


4

使用此代码,它肯定可以工作:

<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>

只是不要忘记在程序中包含Html2CanvasJS文件。 https://html2canvas.hertzen.com/dist/html2canvas.js


2

仅靠JavaScript不能100%准确地做到这一点。

有一个Qt Webkit 工具和一个python版本。如果您想自己做,我在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];
}];

希望这可以帮助!


你有以上的快速版本吗?或者,如果可以的话,您可以重写它吗?
ssh

您介意共享用于加载要呈现的webView的代码吗?对于我的缩略图渲染器来说,这似乎是一种很有前途的方法。谢谢!
戴夫

1

安装phantomjs

$ npm install phantomjs

使用以下代码创建文件github.js

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();
});

将文件作为参数传递给phantomjs

$ phantomjs github.js


-3

您可以将参考 HtmlRenderer 添加到您的项目中,然后执行以下操作:

string htmlCode ="<p>This is a sample html.</p>";
Image image = HtmlRender.RenderToImage(htmlCode ,new Size(500,300));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.