最近要搞前端html转pdf的功能。折腾了两天,略有所收,踩了一些坑,所以做些记录,为后来的兄弟做些提示,也算是回馈社区。
    
    经过一番调(sou)研(suo)发现html导出pdf一般有这几种方式,各有各有优缺,下面简单介绍。
   
    这篇文章说了啥
   
- 
前端实现(着重) 
- 
后端实现(凑数) 
    正文
   
    通过打印预览实现
   
    介绍
   
    通过打印预览来实现导出pdf并不是什么稀奇事,一般浏览器(Chrome)在页面手动
    
     Ctrl + P
    
    都能将当前页进行打印预览。在打印预览的时候我们更改打印方式,选择
    
     将页面保存为PDF
    
    即可实现页面保存为PDF的功能。
    
    比如此时我进行
    
     Ctrl + P
    
    就可以看到这个功能。
   
     
   
程序中实现这个则要靠下面这个方法来实现:
window.print(); // 在控制台执行print()也能看到上面打印预览的效果当然能导出PDF只是主要需求,我们还有一些其他的需求
- 
     只想将页面的一
 
 部分导出为PDF
 
- 
     我们想导出的PDF是
 
 A4纸大小
 
- 
     我们想导出的PDF是
 
 竖着的
 
- 
     我们还想调整导出PDF的
 
 样式
 
- …
    这些需求通过在对css中
    
     媒体查询
    
    的定义就可以实现
   
@media print {
    @page {
        size: A4 portrait; // A4大小 纵向
    }
    
    .other-ele {
        // 打印时将不需要的元素隐藏
        display: none;
    }
    
    .pdf-title {
        // 只在打印时候显示的元素
        display: block;
    }
    
    .panel-sm {
        // 打印时候改变某些元素的样式
        margin: 0;
        border: 1px solid #bce8f1;
    }
}
    更多的设置可以参考:
    
     CSS 打印
    
   
    最佳实践
   
    需要提醒的是:
    
     如果要改变原有样式,最好是在元素上
     
      新加一个
     
     class或者id来写,而不是在原有class上写。
    
    比如有这样一个元素
   
<h1 class="title">我是PDF标题</h1>打印时候要把这个字体大小设置成18px的话,我们不能这么写
@media print{
   .title { // not work
       font-size: 18px;
   }
}这样写试不起作用的。想要生效得在元素上新加一个class类写
<h1 class="title title-print">我是PDF标题</h1>@media print{
   .title-print {
       font-size: 18px;
   }
}经过实践,这样写才可以生效。
    库或者插件
   
    有人可能觉得这样写略有麻烦,别担心,总有人会让麻烦的事情变得简单,这个人如果不是你,那就一定是他。
    
    基于
    
     window.print()
    
    有人封装了一些插件:
   
- 
     
 PrintArea
 
 可以简单的实现部分区域打印,他的原理是通过把要打印的部分放入一个新的iframe然后触发这个iframe的print。这个插件不太稳定,会出现空白,请酌情使用。
- 
     
 jQuery.print
 
 比上面的稍微好点,支持了一些css方面的东西,具体看这个
 
 jQuery.print中文配置参数
 
    评价
   
    这种方法前端实现,灵活简单,而且在
    
     页面还原上是很好
    
    的,生成pdf的
    
     过程不需要自己操心
    
    ,
    
     页面样式还可控
    
    ,可以说是非常不错的。但是因为浏览器对
    
     print
    
    方法的
    
     支持不一
    
    (
    
     具体支持情况戳这里
    
    ),所以目前也就只能在Chrome上用用。另外,这个方法还需要用户点一下保存按钮,
    
     用户体验上也不太好
    
    。
   
    通过jsPdf实现
   
    介绍
   
    
     jsPdf
    
    是一个可以把html转成pdf的插件,有人多人在用。但是吧,老外做的很多东西没考虑过英文之外的语言(这个可以理解,我要做个啥肯定也是做成中文的,我才不考虑啥日语英语阿拉伯语呢),这个东西也不例外的不支持中文,那咋办呢,很多兄弟想了办法:
   
    
     曲线救国 | html2canvas + jsPdf
    
   
    既然你不支持中文,劳资也懒得跟你废话,劳资我把页面转成图片,怕不怕,图片再导出PDF照样中!这种方式很常见、很省事,问题也很多图片拉伸、模糊,最重要这样导出的PDF是没有灵魂的,因为他里面的内容都是图片,不能复制。因为没有灵魂,所以我没有采用这种方法,如果你喜欢这种可以参照这篇文章
    
     Javascript 将html转成pdf,下载,支持多页哦(html2canvas 和 jsPDF)
    
    ,写的很详细。
   
    
     硬生生支持 | jsPDF-CustomFonts-support
    
   
    既然你不支持其他语言是吧,那我写个插件出来搞到你支持为止。干这活儿的是一个来自韩国的哥们儿,他写一个可以支持其他语言的插件
    
     jsPDF-CustomFonts-support
    
    。原理大概就是利用把你提供的字体文件转成base64格式,然后做成一个js文件,拿这个js文件当做字库。恩,我喜欢这种强上的做法。而且这样导出的pdf内容是可以复制的,简直惊喜。于是,我采用了这样的方式。
    
    我当时是顺着这哥们儿的道往前走的
    
     jsPDF生成pdf文件和中文编码
    
    ,这个过程是非常曲折与动人的,具体不表,只讲里面遇到的问题。
   
    最佳实践
   
挂几个里面遇到的比较坑的错误
- 
     
 jsPdf
 
 官网的api文档打不开
    虽然文档页面打不开,但是在他的github仓库里是有
    
     docs
    
    这个目录的,而且目录下也有文档,那我们就把这个仓库下载来,在本地打开
    
     docs/index.html
    
    来查看文档,效果是一样的。
   
     
   
- 
     
 jsPDF-AutoTable
 
 
 demo
 
 的表格做的很好看,但是他没有提供代码,那我怎么看到他是怎么实现的?
     
   
    demo的实现都在这个
    
     
      examples.js
     
    
    中,没有混淆,没有压缩,可以依葫芦画瓢仿一个demo的表格。
   
- 如何生成自己的字体文件
    
     jsPDF-CustomFonts-support
    
    默认提供了一个字体文件,但是里面有很多汉字不能正常显示,所以你需要自己生成一个字体文件。怎么生生成呢?你需要这样:
   
git clone https://github.com/sphilee/jsPDF-CustomFonts-support
cd jsPDF-CustomFonts-support
npm install
mv fontFilePath/fontName.ttf ./jsPDF-CustomFonts-support/fonts/ # 把你准备的`.ttf`格式字体,放入`jsPDF-CustomFonts-support/fonts/`目录下
node makeFonts.js
    然后
    
     jsPDF-CustomFonts-support/dist/default_vfs.js
    
    就是你要的字体文件。
   
- Uncaught TypeError: jsPDFAPI.addFileToVFS is not a function
    这个错误是
    
     jsPDF-CustomFonts-support
    
    中报出的,是因为在
    
     1.4.0
    
    以下版本的时候
    
     jsPDF
    
    还不支持
    
     addFileToVFS
    
    这个方法,所以最好的方法是使用最新的jspdf版本
    
    用下面这个版本的jspdf替换掉报错的。
   
<script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>- Cannot read property ‘widths’ of undefined
    这个错误是在
    
     jsPDF-AutoTable
    
    中报出的,是因为同时引入
    
     jspdf.customfonts.min.js
    
    和
    
     jspdf.customfonts.debug.js
    
    这两个文件导致的,只引用其中一个就好了。
   
- 
     
 jsPDF-AutoTable
 
 thead中的中文显示乱码
这个问题我找不到原因,但是我找到了一个方法: 隐藏掉thead,通过在tbody中将第一排tr设置样式来模拟thead。实现如下:
doc.autoTable(columns, data, {showHeader: 'never'}); // 不显示thead评价
    
     html2canvas + jsPdf
    
    的方法直接图转pdf,简单,但是质量差点。
    
    
     jsPDF-CustomFonts-support
    
    的方法虽然质量上占优势,但那个字体文件动辄好几兆,甚至十几兆,这对于前端来说是一个不小的开销,对性能影响太。
    
    此外,这两种方式导出PDF都是点一下导出就会下载文件的,不需要用户再次确认下载,这点用户体验还是比较好的。
   
    后端导出pdf
   
    
     iText
    
    、
    
     wkhtmltopdf
    
    、
    
     prince
    
    这三个都是后端生成pdf的工具。这三个都没有node api。故不多说。想看具体的比较可以参考这篇文章
    
     html页面导出为pdf(jsPDF、iText、wkhtmltopdf)
    
    。
   
    参考
   
    
     https://segmentfault.com/a/11…
    
    
    
     https://segmentfault.com/a/11…
    
    
    
     https://segmentfault.com/a/11…
    
    
    
     https://blog.csdn.net/JodenHe…
    
    
    
     https://blog.csdn.net/huyuyan…
    
   
 
