html-> pdf -> 下载
方式一: 图片 -> pdf
html 转 canvas, 使用html2canvas插件 canvas 转pdf, 使用jspdf插件,pdf 不可以文本选择和复制文本的, pdf 是由图片 生成的pdf
预览pdf, word功能,可以使用 blob -> 本地url -> 浏览器打开
1.html2canvas 使用克隆节点,达到更改html样式。
2.useCORS: true 允许加载在线图片
3.scale: 3 canvas 抗锯齿,放大更清晰
- PDF.output('blob') 生成blob, blob转本地url, 可以达到浏览器打开生成的pdf
- PDF.save(fileName)就是 将生成的pdf下载
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
/**
* 导出 PDF
*/
export const handleHtmlToPDF = (eleId, fileName) => {
const A4_WIDTH = 592.28;
const A4_HEIGHT = 841.89;
/*
// 克隆需要导出的节点dom,并添加到body
const printDom = document.getElementById(eleId).cloneNode(true);
printDom.setAttribute(
'style',
'font-size: 26px;font-weight: 700;'
);
document.body.appendChild(printDom)
// 生成完毕删除克隆的节点
document.body.removeChild(printDom)
*/
const printDom = document.getElementById(eleId)
// 获取目标元素的高度(去除滚动条时高度)
const domScrollHeight = printDom.scrollHeight;
const domScrollWidth = printDom.scrollWidth;
// 根据 A4 的宽高计算 dom 页面一页应该对应的高度
const pageHeight = (printDom.offsetWidth / A4_WIDTH) * A4_HEIGHT;
// // 将所有不允许被截断的元素进行处理
// const wholeNodes = document.querySelectorAll('.whole-node');
// // 添加空白块的总高度
// let allEmptyNodeHeight = 0;
// for (let i = 0; i < wholeNodes.length; i++) {
// // 判断当前的不可分页元素是否在两页显示
// const topPageNum = Math.ceil(wholeNodes[i].offsetTop / pageHeight);
// const bottomPageNum = Math.ceil(
// (wholeNodes[i].offsetTop + wholeNodes[i].offsetHeight) / pageHeight,
// );
// // 说明该 dom 会被截断
// if (topPageNum !== bottomPageNum) {
// // 插入空白块使被截断元素下移
// const divParent = wholeNodes[i].parentNode;
// const newBlock = document.createElement('div');
// newBlock.className = 'empty-node';
// newBlock.style.background = '#fff';
// // 计算插入空白块的高度,可以适当留出空间使得内容不会太靠边,根据自己需求而定
// const _H = topPageNum * pageHeight - wholeNodes[i].offsetTop;
// newBlock.style.height = _H + 50 + 'px';
// divParent.insertBefore(newBlock, wholeNodes[i]);
// // 更新插入空白块的总高度
// allEmptyNodeHeight = allEmptyNodeHeight + _H + 50;
// }
// }
// // 设置打印区域的高度 (目标元素的高度 + 添加的空白块的高度)
// printDom.setAttribute(
// 'style',
// `height: ${domScrollHeight + allEmptyNodeHeight}px; width: ${domScrollWidth}px;`,
// );
// 以上完成 dom 层面的分页,可以转为图片进一步处理了
return html2canvas(printDom, {
height: printDom.offsetHeight,
width: printDom.offsetWidth,
allowTaint: false,
useCORS: true, //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求
scale: 3,
}).then((canvas) => {
// // dom 已经转换为 canvas 对象,可以将插入的空白块删除了
// const emptyNodes: any = document.querySelectorAll('.empty-node');
// for (let i = 0; i < emptyNodes.length; i++) {
// emptyNodes[i].style.height = 0;
// emptyNodes[i].parentNode.removeChild(emptyNodes[i]);
// }
const contentWidth = canvas.width;
const contentHeight = canvas.height;
// 生成 pdf 的页面高度;
const pageHeight = (contentWidth / A4_WIDTH) * A4_HEIGHT;
// html 页面实际高度
let htmlHeight = contentHeight;
// 页面偏移量
let position = 0;
// html 页面生成的 canvas 在 pdf 中图片的宽高
const imgWidth = A4_WIDTH;
const imgHeight = (A4_WIDTH / contentWidth) * contentHeight;
// 将图片转为 base64 格式
const pageData = canvas.toDataURL('image/jpeg', 1.0);
// 计算分页的 pdf
const PDF = new jsPDF('', 'pt', 'a4');
// html 页面的实际高度小于生成 pdf 的页面高度时,即内容未超过 pdf 一页显示的范围,无需分页
if (htmlHeight <= pageHeight) {
PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
} else {
while (htmlHeight > 0) {
PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
htmlHeight -= pageHeight;
position -= A4_HEIGHT;
if (htmlHeight > 0) {
PDF.addPage();
}
}
}
PDF.save(fileName);
// PDF.output('datauristring') 生成 base64 pdf
//const link = window.URL.createObjectURL(PDF.output('blob'));
// const myWindow = window.open(link)
}).catch((error) => {
console.log(error)
})
}
// base64 转 blob
function toBlob(base64Data) {
let byteString = base64Data
if (base64Data.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(base64Data.split(',')[1]); // base64 解码
} else {
byteString = unescape(base64Data.split(',')[1]);
}
// 获取文件类型
const mimeString = base64Data.split(';')[0].split(":")[1]; // mime类型
// ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区
// let arrayBuffer = new ArrayBuffer(byteString.length) // 创建缓冲数组
// let uintArr = new Uint8Array(arrayBuffer) // 创建视图
const uintArr = new Uint8Array(byteString.length); // 创建视图
for (let i = 0; i < byteString.length; i += 1) {
uintArr[i] = byteString.charCodeAt(i);
}
// 生成blob
const blob = new Blob([uintArr], {
type: mimeString
})
// 使用 Blob 创建一个指向类型化数组的URL, URL.createObjectURL是new Blob文件的方法,可以生成一个普通的url,可以直接使用,比如用在img.src上
return blob;
}
方式二: 直接html -> pdf,可以复制和选择
jspdf 和 jspdf-autotable
- 导入字体,防止乱码。import '@/assets/simhei-normal.js',生成方法百度。pdf.setFont("simhei")
- jsPDF 不同字体,可以每一次都设置字体,颜色,位置。
- text 用来加载文本, addImage 加载图片, table 使用jspdf-autotable加载
- jspdf-autotable 属性设置时使用 html, 不要写多余的属性,否则会改变页面布局
- didDrawPage 生命周期回调,其他方法参看官网
import jsPDF from 'jspdf'
import 'jspdf-autotable'
import '@/assets/simhei-normal.js'
var pdf = new jsPDF("", "pt", 'a4');
pdf.setFont("simhei"); // 使用字体
pdf.setFontSize(12)
const {width, height} = pdf.internal.pageSize
let startY = 40
pdf.text('xxx', width / 2, startY, {
align: 'center',
})
startY += 20
pdf.setFontSize(10)
pdf.text('申请单号:xxx', 40, startY , {
align: 'left',
})
pdf.text('提交时间:xxx', width - 40, startY, {
align: 'right',
})
// autoTable调用 ,font:中修改为自定义字体名称,注意:fontStyle需要与addFont中的类型对应。
pdf.autoTable({
html: '#meetTable',
styles: {
fillColor: [255, 255, 255],
font: 'simhei',
fontStyle: 'normal',
textColor: [0, 0, 0],
halign: 'center',
valign: 'middle',
}, // 表格样式
theme: 'grid', // 主题
didDrawPage: (HookData) => {
startY = HookData.cursor.y + 30
pdf.text('申请记录', width / 2, startY, {
align: 'center',
})
},
startY: startY + 15, // 距离上边距离
})
pdf.autoTable({
html: '#meetTableHistory',
styles: {
fillColor: [255, 255, 255],
font: 'simhei',
fontStyle: 'normal',
textColor: [0, 0, 0],
halign: 'center',
valign: 'middle',
}, // 表格样式
theme: 'grid', // 主题
didDrawPage: (HookData) => {
startY = HookData.cursor.y + 20
pdf.text('经办人确认签字', 45, startY, {
align: 'left',
})
},
startY: startY + 10, // 距离上边距离
})
pdf.save("test.pdf")
打印
使用插件print-js,可以打印图片。也可以打印html, pdf, json
scanStyles: false 使用自己的css, 不使用自带的css样式
targetStyles:['*'] 打印内容使用所有HTML样式,没有设置这个属性/值,设置分页打印没有效果
代码 使用原生的代码 table , button.不能使用组件库封装代码。
将打印的内容,另存为pdf, 需要用户去选择,体验有点麻烦。pdf 可以文本选择和复制文本的
import print from 'print-js'
print({
printable: 'attend-meet',
type: 'html',
documentTitle: 'xxx',
scanStyles: false,
css: ['/static/print-style.css'],
targetStyles: ['*'], // 打印内容使用所有HTML样式,没有设置这个属性/值,设置分页打印没有效果
})
css样式 放在public/static 文件夹下
/* 默认A4并且无页眉页脚打印 */
@page {
size:portrait A4;
margin: 8mm;
}
/* page-break-after:always; 强制分页*/