html转pdf

1、需要工具

1.1 html2canvas()

1.2 jspdf()

2、开始步骤

2.1 将 html 转化为 canvas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* @name 将页面转化为canvas * @param {*} html 页面如
* @param {boolean} isOne 是否只要单页
* @param {boolean} isLandscape 是否是横向
* @returns
*/
async function htmlcanvasBase64(html, isOne = false, isLandscape) {
let contentWidth = html.clientWidth // 获得该容器的宽(但这个值可能在缩小和放大页面存在问题)
let contentHeight = html.clientHeight // 获得该容器的高
let canvas = document.createElement('canvas')
let scale = 2 // 解决清晰度问题,先放大 2倍
canvas.width = contentWidth * scale // 将画布宽&&高放大两倍
canvas.height = contentHeight * scale

canvas.getContext('2d').fillStyle = '#ffffff'
canvas.getContext('2d').scale(scale, scale)
// 需要设置canvas的偏移量(0:X轴,0: Y轴)
canvas.getContext('2d').translate(0, 0)

let opts = {
scale: scale,
canvas: canvas,
width: contentWidth,
height: contentHeight,
useCORS: true,
}

return html2canvas(html, opts, isLandscape).then((canvas) => {
let pageData = canvas.toDataURL('image/jpeg', 1.0) // 清晰度 0 - 1
// pageDate 就是我们想项目要的base64 图片
})
}

2.2 将 canvas 的 base64 转化为 pdf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* @name 将canvas的base64转化为pdf
* @param {string} pageData
* @param {boolean} isOne 是否只要单页
* @param {boolean} isLandscape 是否是横向
* @returns
*/
async function canvasOfPdf(pageData, isOne = false, isLandscape) {
let pdf

if (isOne) {
// 单页
// jspdf.js 插件对单页面的最大宽高限制 为 14400
let limit = 14400
if (contentHeight > limit) {
let contentScale = limit / contentHeight
contentHeight = limit
contentWidth = contentScale * contentWidth
}
let orientation = 'p'
if (contentWidth > contentHeight) {
orientation = 'l'
}
// orientation Possible values are "portrait" or "landscape" (or shortcuts "p" or "l")
pdf = new jsPDF(orientation, 'pt', [contentWidth, contentHeight]) // 下载尺寸 a4 纸 比例
// pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置
pdf.addImage(pageData, 'JPEG', 0, 20, contentWidth, contentHeight)
} else {
//a4纸的尺寸[595.28,841.89],html 页面生成的 canvas 在pdf中图片的宽高
//841.89(横向) 两边相距距离 例如相距10 值减去10*2
//595.28(纵向) 两边相距距离 例如相距10 值减去10*2
let imgWidth = isLandscape ? 811.89 : 565.28
let imgHeight = (imgWidth / contentWidth) * contentHeight

//一页 pdf 显示 html 页面生成的 canvas高度
let pageHeight
if (isLandscape)
pageHeight = (contentWidth / ((595.28 / 841.89) * imgWidth)) * imgWidth
if (!isLandscape)
pageHeight = (contentWidth / imgWidth) * (imgWidth / (595.28 / 841.89))

//未生成 pdf 的 html页面高度
let leftHeight = contentHeight * (595.28 / 841.89)
//页面偏移
let position = 0
// pdf = new jsPDF('', 'pt', 'a4') // 下载尺寸 a4 纸 比例
pdf = new jsPDF(isLandscape ? 'l' : 'p', 'pt', 'a4') // 下载尺寸 a4 纸 比例
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
//当内容未超过pdf一页显示的范围,无需分页
// //canvas // 距离左边 // 距离顶部 // 内容宽 // 内容高
// pdf.addImage(pageData, 'JPEG', (841.89 - imgWidth) / 2, 10, imgWidth, imgHeight)

if (leftHeight < pageHeight) {
if (isLandscape)
pdf.addImage(pageData, 'JPEG', (841.89 - imgWidth) / 2, 10, imgWidth, imgHeight)
if (!isLandscape)
pdf.addImage(pageData, 'JPEG', (595.28 - imgWidth) / 2, 10, imgWidth, imgHeight)
} else {
while (leftHeight > 0) {
if (isLandscape)
pdf.addImage(pageData, 'JPEG', (841.89 - imgWidth) / 2, position, imgWidth, imgHeight)
if (!isLandscape)
pdf.addImage(pageData, 'JPEG', (595.28 - imgWidth) / 2, position, imgWidth, imgHeight)
leftHeight -= pageHeight
position -= 841.89
//避免添加空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
}
return pdf
// pdf.save() // 前端下载
// pdf.output('datauristring') // pdf的base64编码
// pdf.deletePage(页码); // 删除指定页
}

3、存在异常问题

3.1 多行文本导出 pdf 时会出现异常问题

存在问题: 多行文本导出时只会导出一行文本,多行内容会丢失

解决办法:将 textarea 文本框修改为 div,并配置word-wrap = ‘break-word’;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 配置多行文本打印异常问题,以下方法是使用jquery
Array.prototype.slice.call($('textarea')).forEach((el) => {
var div = document.createElement('div');
div.innerHTML = el.value;
div.style.width = el.style.width || '100%';
div.style.height = el.style.height || '94px';
div.style.display = 'inline-block';
div.style.wordWrap = 'break-word';
div.style.padding = '4px 11px';
div.style.border = '1px solid #d9d9d9';
div.style.borderRadius = '4px';
el.parentNode.insertBefore(div, el); // 挂载到指定节点
el.style.display = 'none'; // 隐藏多行文本框
});