Skip to content

利用jsPDF实现纯前端html转PDF #19

@jimdeng92

Description

@jimdeng92

jsPDF 是前端一个非常优秀的库,它可以自定义 PDF 内容,包括在 PDF 中添加文字,图片,图形,表格等。

它还能将页面中的 DOM 直接转为 PDF 中的内容。这里我想要分享一下包括怎样初始化库并且使用,以及在使用 jsPDF 中遇到的问题做个整理,供需要的童鞋参考。首先说明一下,我的使用环境是 @vue/cli,其他环境也适用,基本类似。

安装和使用 jsPDF

npm install jspdf --save
# or
yarn add jspdf

安装完成后,在需要的组件中使用:

<script>
import { jsPDF } from 'jspdf'

export default {
  // ...
  methods: {
    downloadPDF() {
      // 更多参数参考文档:http://raw.githack.com/MrRio/jsPDF/master/docs/jsPDF.html
      var doc = new jsPDF({
        orientation: 'l', // pdf页方向: p 竖向 l 横向
        unit: 'px', // 单位
        format: [1600, 900.01], // 默认 a4,可自定义尺寸,竖向多0.01是防止多一个空白页
        encryption: { // 加密
          ownerPassword: 'owner', // 管理员密码(所有权限)
          userPassword: 'user' // 用户密码
          // userPermissions: ['print', 'modify', 'copy', 'annot-forms'] // 用户权限列表
        }
      })

      // html 转 pdf
      doc.html(this.$refs.pdfContent, {
        callback: (doc) => {
          doc.save()
        }
      })
    }
  }
}
</script>

有些博主会在 DOM 超出一屏时使用 addPage(),但我使用时 jsPDF 实际上已经自动分页了,我们只需要在 $refs.pdfContent 中定义多个 1600 * 900 的子元素就能完美适配,实现起来非常简单。

一些坑

1、PDF 生成后乱码

原因:jsPDF 默认不支持中文。

解决办法肯定有,那就是自己引用编译后的字体包。步骤:

下载免费商用字体-阿里巴巴普惠体

下载 jsPDF 最新版本源码,打开项目下的 fontconverter/fontconverter.html 文件,上传字体的 ttf 版本。(注意只能用ttf版本的字体)

fontconverter.html截图

点击 Create 后会生成如下格式的js文件:

import { jsPDF } from "jspdf"

var font = 'AAEA...'

var callAddFont = function () {
this.addFileToVFS('Alibaba-PuHuiTi-Regular-normal.ttf', font);
this.addFont('Alibaba-PuHuiTi-Regular-normal.ttf', 'Alibaba-PuHuiTi-Regular', 'normal');
};
jsPDF.API.events.push(['addFonts', callAddFont])

可是这文件足足有12M,我们需要把它与项目分离出来,修改一下,只保留变量,这样我就可以把文件放 CDN 或者 /static 目录下了。

var AlibabaPuHuiTiRegular = 'AAEA...'

在 index.html 中引入 js,此时变量就挂载在了全局。我们来使用字体:

jsPDF.API.events.push(['addFonts', function () {
  const {AlibabaPuHuiTiRegular} = window
  this.addFileToVFS('AlibabaPuHuiTiRegular.ttf', AlibabaPuHuiTiRegular)
  this.addFont('AlibabaPuHuiTiRegular.ttf', 'AlibabaPuHuiTiRegular', 'normal')
}])

var doc = new jsPDF({
  // ...
})

doc.setFont('AlibabaPuHuiTiRegular', 'normal')

doc.html(this.$refs.pdfPageContent, {
  callback: (doc) => {
    doc.save()
  }
})

设置完成后还需要在css中使用:定义 $refs.pdfPageContentfont-family: 'AlibabaPuHuiTiRegular'。这样在下载 pdf 乱码的问题就解决了。

PS:

  • 设置 font-family 并不能改变 web 页面中的字体,只是供 jsPDF 在生成时使用。
  • 如果包含粗体/斜体,需要生成新的字体并引入,通过修改 jsPDF 的 'normal' 字段并没有什么用,也无法通过 css 的 font-weight 修改。

二、背景图片无法显示

解决办法:不使用背景图片,使用 <img /> 定位解决。

三、canvas 背景是黑色

解决办法:在画 canvas 时首先画一个跟背景一样的底色。

四、伪元素定位问题

在元素设置了行高后,伪元素定位不正确。

解决办法:不使用行高,用 padding。

特别的话

还有一些其他的适配问题没有解决的,比如:部分字会重叠、有些表现在 win 和在 Mac 上不一致、opacity 不生效、transform 后盒子内的文字消失等等。

特别需要说明的是,我利用了 opacity 不生效的 BUG(也不能算 BUG,也许是限制吧),在用户点击下载时弹出一个透明的遮罩,渲染完成后再真正去下载。这里的好处主要还是方便迭代,只需要注释一个 css 属性就能看到真实 DOM,并且不影响生成 PDF。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions