把网页下载为图片或打印成PDF

最近遇到了需要把网页下载为图片和打印为PDF的需求,特此整理。

一、借助浏览器

一般浏览器都自带PDF打印功能:

至于保存为图片,有很多浏览器插件(比如screenshot、花瓣)等,都可以将网页保存为图片。

缺点:

  1. 网页中的图标、背景等可能无法打印成PDF,因此效果不好;
  2. 很多时候我们想打印网页的某一部分(而不是整个网页),因此可定制性较差。

因此我们需要结合js来实现以上需求。

二、用js

1、js + 浏览器打印功能

(1)打印整个页面:

window.print()

(2)打印页面中的指定元素:
一般来说,我们会用CSS将不需要打印的元素隐藏掉,这样body中实际上就只剩我们希望打印的页面部分:

@media print {
    body * {
        visibility: hidden;
    }
    #printArea, #printArea * {
        visibility: visible;
    }
    #printArea {
        position: fixed;
        left: 0;
        top: 0;
        right:0;
        bottom:0;
    }
}

再调用window.print()即可。

但是测试后发现,这种方法还是存在问题:如果要打印的元素较长(大于一屏),大于一屏的部分不会被打印。

我们再换一种思路:

//打印区域的内容
var printContents = document.getElementById('printArea').innerHTML
//页面的所有内容
var originalContents = document.body.innerHTML
        
document.body.innerHTML = printContents
window.print()
document.body.innerHTML = originalContents
//直接恢复页面可能会造成页面“假死”,可通过重新加载的方式恢复
window.location.reload()

打印的时候将要打印的部分设置为当前页面的全部内容,打印完之后又将页面恢复。

分别打印一个页面上的多个区域:

//第一个打印区域的内容
var printContents1 = document.getElementById('printArea1').innerHTML
//第二个打印区域的的内容
var printContents2 = document.getElementById('printArea2').innerHTML
//页面的所有内容
var originalContents = document.body.innerHTML

//打印第一个区域
document.body.innerHTML = printContents1
window.print()
//打印第二个区域
document.body.innerHTML = printContents2
window.print()
//恢复页面
document.body.innerHTML = originalContents
//直接恢复页面可能会造成页面“假死”,可通过重新加载的方式恢复
window.location.reload()

这种方法可以使得每次打印时要打印的内容都在页面开头,并且对于超过一屏的打印也正确。

存在的问题:
打印完之后需手动将页面恢复至原来的html结构,恢复之后发现页面“假死”,无法再进行其他交互,因此我调用了window.location.reload()将页面刷新。但是这样做实际上是没有保存页面目前的一些数据和交互信息,因此用户体验也不好。

如果不希望牺牲用户体验,可以将要打印的内容写入新的浏览器窗口并打开,打印完之后关闭该新窗口:

var printContents = document.getElementById('printArea').innerHTML
//打开打印窗口
var newWin = window.open()
newWin.document.write(printContents)
newWin.print()
//关闭打印窗口
newWin.close()

我们会发现,使用这种方法的话,打印内容的css样式实际上是失效的,因为在打印窗口页面并没有引入相关的css,所以我们需要手动引入:

var printContents = document.getElementById('printArea').innerHTML
var newWin = window.open()
//引入css
var printCommonStyle=`
    <style>
        *{
            box-sizing: border-box;
        }
        .text-center{
            text-align: center;
        }
        
        a,a:link,a:visited,a:hover,a:active{
            color: #000;
            text-decoration: none;
        }
        
        /*设置打印时的table样式*/
        table{
            border-spacing: 0px;
            border-bottom: 1px solid;
        }
        th,td {
            padding: 4px 4px;
            word-break: break-all;
            border-top: 1px solid;
            border-left: 1px solid;
        }
        tr>th:last-child,
        tr>td:last-child {
            border-right: 1px solid;
        }
    </style>
`
newWin.document.write( printCommonStyle+printContents )
newWin.print()
newWin.close()

这样也能实现一些场景下的打印要求(比如表格、文字等)。

2、js插件实现打印功能

下载为图片:html2canvas + a标签download属性
打印为PDF:html2canvas + jsPDF

首先我们看看这几个js的使用方法:

1)html2canvas

步骤:引入html2canvas.js,在Promise.then中获取转换好的canvas:

//通过html2canvas把html渲染成canvas,然后获取图片数据
html2canvas(document.getElementById('showPart')).then(function(canvas) {            
     //将canvas转成PDF或者下载图片
    //.........................................
});
2)下载图片

其实要用到a标签的download特性:

//通过html2canvas把html渲染成canvas,然后获取图片数据
html2canvas(document.getElementById('showPart')).then(function(canvas) {
    //下载图片
    var aElement=document.createElement('a');
    aElement.setAttribute('href',imageData);    //重点看这里
    aElement.setAttribute('download','test.png');    //重点看这里
    document.body.insertAdjacentElement('beforeend',aElement)
    aElement.click();
});
3)jsPDF

(1)文字打印成PDF:

var doc=new jsPDF()
doc.text('hello world!',10,10)
 doc.save('a4.pdf')

(2)图片打印成PDF:

我们看看官网的例子:

注意:jsPDF需要的是data URL的图片格式。

借助于官网的例子,我们进行一下模拟,代码:

var imageData='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH3gwKEAonDW4kCwAAAodJREFUaN7t2LtrFUEYxuFHTWGCBrzFS6WilcGgnSAoGLX2UgiChSGF2NtYiEII+C8E0oid0VqxUBDsFAUrRS0UUYkBlaNgvBQzMcclObvubLIp9q12l/lm3t/M7M73LY0aNWpUp5ZV2dnmC08LtXt3dffSA8iYX4F16I33nzGJn1VDJANkjPdgEMcxgLXx+Sc8wU3cRasqkCSAjPmtuIyTEWQutXADl/C6CoiuJPxZbcMYDuW068EZbMFwO0RZLS8b2Db7PcLM55lv1yCuxNjCL3+lABkzJ0rEnYixSUoF6NJ5z3dST4xN2sapAGuEr01ZDcQ+agNYm2hgjdlPbS0AVRyESX2kAkxiKiF+KvZRG8AUHifEP06cgGSAaSE9aJWIbcXY6ToBCLnNRIm4iRibpKpyoe0Yx4GCofdxFi9Jy4WqWAHRyBCu6bydWrHN0Iz5VC1UOn0Me82eEVN4hFuWUjo9DwSzBc3KeP9dpqCpwnxlADkw/6jKcrIygA3D90rFfRw7WA9AB8PLhG3TjdVYFZ9/xRd8E7bT76qA/gtgHuN92ClklruwAxuj+fZ34Cve4wWeCTXyc3xIASkEMIfxddiHo9gvlJS9/zEhv4U/Fa/wALfxUCYvKgKSO2DGfJ9QhJzGHmGrVKFvQl50XSj6/65KHkRHgIz5I7gozHhVB2BWv4QVGcGdIhDzArSZXy4c+yPCCiyGPsTJGo9Q80IUmcmjGF1E8+JYozic1zAPoBvnsH4Rzc9oPc7Lec/yADahvwbzM+qPHkoDdKnu710Z5Y6/UF+TRVMRgDohc8fO2x7TeBuvf9Vg/q2cmjkP4A1OFWi3UJqOHkoD/BDylUaNGjVqNKf+AN+rkUhAKMRGAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTExLTEyVDExOjAyOjI0KzA4OjAwmpHX/gAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNC0xMi0xMFQxNjoxMDozOSswODowMOM3CTMAAABNdEVYdHNvZnR3YXJlAEltYWdlTWFnaWNrIDcuMC4xLTYgUTE2IHg4Nl82NCAyMDE2LTA5LTE3IGh0dHA6Ly93d3cuaW1hZ2VtYWdpY2sub3Jn3dmlTgAAABh0RVh0VGh1bWI6OkRvY3VtZW50OjpQYWdlcwAxp/+7LwAAABh0RVh0VGh1bWI6OkltYWdlOjpIZWlnaHQAMTI4Q3xBgAAAABd0RVh0VGh1bWI6OkltYWdlOjpXaWR0aAAxMjjQjRHdAAAAGXRFWHRUaHVtYjo6TWltZXR5cGUAaW1hZ2UvcG5nP7JWTgAAABd0RVh0VGh1bWI6Ok1UaW1lADE0MTgxOTkwMzk6CQwlAAAAEnRFWHRUaHVtYjo6U2l6ZQAxLjA3S0Lad01iAAAAX3RFWHRUaHVtYjo6VVJJAGZpbGU6Ly8vaG9tZS93d3dyb290L3NpdGUvd3d3LmVhc3lpY29uLm5ldC9jZG4taW1nLmVhc3lpY29uLmNuL3NyYy8xMTgyMS8xMTgyMTEyLnBuZ9ePzJoAAAAASUVORK5CYII='
var doc = new jsPDF()
doc.addImage(imageData,'png',0,0,100,100)
doc.save('resume.pdf')
4)得到data url

其实这个功能只是调用一个toDataURL的API而已,所以不需要用什么插件,直接调用就可以了:

//默认的转换类型:"image/png"
var imageData=canvas.toDataURL()

知道上述几个API和插件的基本用法后,我们来将它们组合起来:先使用html2canvas把网页转成canvas,再使用toDataURL()方法把canvas转成data url,最后用jsPDF把data url转成PDF,简直完美!

完整代码如下:

html2canvas(document.getElementById('showPart')).then(function(canvas) {
    //通过html2canvas把html渲染成canvas
    document.body.appendChild(canvas);
            
    //canvas转成dataurl
    var imageData=canvas.toDataURL()
            
    //dataurl转成PDF          
    var doc = new jsPDF('','pt','a4')
    doc.addImage(imageData,'png', 0, 0, 595, 595/canvas.width * canvas.height)  //使图片占满整张A4纸
    doc.save('test.pdf')
});

------补充-------

测试发现,以上代码得到的结果存在两个严重问题:

  1. 当图片比较长时,超出A4纸长度的那部分图片会被截掉
  2. 最终得到的PDF不清晰,很模糊

我翻看了很多资料,试了很多方法,分页解决了,但是得到的PDF还是很模糊。目前还未解决,先在这里占个坑吧,等到解决了再来整理。

总之,目前还没找到完美的方案来解决打印的问题,如果您有什么解决方案,可以多多交流哦~~

参考:
1、Print <div id=“printarea”></div> only?
2、Javascript 将html转成pdf,下载,支持多页哦
3、html导出pdf文件
4、问:html5 canvas绘制图片模糊的问题

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 7,115评论 4 39
  • 今天一个人去散步,突然发现自己很孤单,以前一直以来都有很多很多的朋友在身边,当他人约我一起吃饭也好,散步也好,怎样...
    高藝菲Sophia阅读 102评论 2 1
  • 总想把你珍藏 只因你纯真模样 总想留住时光 深情依然 月夜的故事 吐放玫瑰花香 萦绕成茧 你在那里 牵动思念的线 ...
    律墨阅读 129评论 9 5
  • 姓名:邵逸轩 公司:宁波禾隆新材料股份有限公司 六项精进第340期努力一组学员 【日精进打卡第13天】 【知~学习...
    邵逸轩阅读 81评论 0 0