记录jsPdf相关使用
相关文档:
一:我的需求
浏览器接口:window.print()
,可以直接调用打印,并存储为PDF
文档
用工具库:html2canvas
+jsPDF
实现以上(类似的)PDF
二:我的实现
2.1:基础参数
首先定义一个标准数据,以Windows
下的A4
打印为基础:
// 基础参数 const printParams = { a4: { dpi: 96, // windows 下默认 dpi width: 210, // A4 标准宽度 mm,含边距 height: 297, // A4 标准高度 mm, 含边距 marginX: 10, // 默认打印左右边距,约为 10mm marginY: 10, // 默认打印上下边距,约为 10mm }, // 以下是设置值/初始值 dpi: 288, // 设置的打印密度,数值越大越清晰,PDF文件越大 format: 'a4', // 打印类型,这里只有 A4 orientation: 'portrait', // portrait | landscape // 以下为计算出的值,方便调用 ratio: 1, // dpi 比例 width: 0, // PDF 文档宽度,不含边距 widthFull: 0, // PDF 文档完整宽度 height: 0, // PDF 文档高度,不含边距 heightFull: 0, // PDF 文档完整高度 marginX: 0, // PDF 左右边距 marginY: 0, // PDF 上下边距 }
数据初始化:
// 根据设置的 dpi,format,orientation 数据初始化 const { dpi: setDpi, orientation, format } = printParams; const { dpi, width, height, marginX, marginY } = printParams[format]; printParams.ratio = setDpi/dpi; let setWidth; switch (orientation) { case 'landscape': printParams.width = height - 2 * marginY; // 不含边距的内容宽度 printParams.height = width - 2 * marginX; // 不含边距的内容高度 printParams.widthFull = height; // 整页PDF宽度 printParams.heightFull = width; // 整页PDF高度 printParams.marginX = marginY; // 左右单边边距 printParams.marginY = marginX; // 上下单边边距 setWidth = mm2px(height, dpi); // 页面宽度像素值 break; case 'portrait': default: printParams.width = width - 2 * marginX; printParams.height = height - 2 * marginY; printParams.widthFull = width; printParams.heightFull = height; printParams.marginX = marginX; printParams.marginY = marginY; setWidth = mm2px(width, dpi); } // 设置body宽度,给 window.print 一个“最适合”的宽度 window.document.body.style.width = `${setWidth}px`;
这里有用到一个mm
/px
的转换,把以前写过的方法拿过来:
/** * 工具类:mm转px * */ function mm2px(mm, dpi, radio) { if (mm == null) return 0; dpi = dpi || printParams.dpi; const ret = parseFloat(`${(mm * dpi * 10) / 254}`); if (radio == null || radio <= 0) radio = 4; radio = 10 ** radio; return Math.floor(ret * radio) / radio; } /** * 工具类:px转mm * */ function px2mm(px, dpi, radio) { if (px == null) return 0; dpi = dpi || printParams.dpi; const ret = parseFloat(`${(px * 254) / (dpi * 10)}`); if (radio == null || radio <= 0) radio = 4; radio = 10 ** radio; return Math.floor(ret * radio) / radio; }
2.2:生成图片
// 需要用到的数据 const $pdfDom = document.getElementById('pdf'); const { ratio, orientation, format, width, height, widthFull, marginX, marginY } = printParams; html2canvas($pdfDom, { scale: window.devicePixelRatio * Math.ceil(ratio), // 缩放 allowTaint: true, useCORS: true, }).then((canvas) => { const canvasWidth = this.px2mm(canvas.width); const canvasHeight = this.px2mm(canvas.height); // ··· 以下是 pdf 生成 // ··· });
此处用到html2canvas
实现绘制页面生成图片,主要影响清晰度的是参数scale
:
scale | window.devicePixelRatio | The scale to use for rendering. Defaults to the browsers device pixel ratio. |
默认值为window.devicePixelRatio
,主要用于防止页面缩放导致的生成图片不清晰问题
为了实现更好地缩放,最好乘以一个整数值,以保证像素值的对应,就把它当作windows
下的高分屏缩放理解就行
这里用px2mm
转换成mm
主要方便用于后面的PDF
编辑
2.3:生成PDF
// 得到图片数据 const pageData = canvas.toDataURL('image/jpeg', 1.0); // 创建PDF文档对象 const PDF = new JsPDF({ orientation, format }); // 标准宽度下,图片的相对高度 const imageHeight = (width / canvasWidth) * canvasHeight; // 考虑可能有多页的情况,用 leftHeight 循环判断 let leftHeight = imageHeight; // 每次绘制图片的位置垂直偏移量 let position = marginY; while (leftHeight > 0) { // 1:图形绘制到PDF PDF.addImage(pageData, 'JPEG', marginX, position, width, imageHeight); leftHeight -= height; position -= height; // 2:页头页脚添加,用 rect 遮罩 PDF.setFillColor(255, 255, 255); // 填充白色,每次填充前都要定义 PDF.rect(0, 0, widthFull, marginY, 'F'); PDF.rect(0, height + marginY, widthFull, marginY, 'F'); // 添加空白页 if (leftHeight > 0) { PDF.addPage(); } } PDF.save(`${title}.pdf`);
这里有几个点,需要留意:
- 默认PDF为单页,需要判断“图片”相对高度,超出标准高度时添加新页面
- 每次添加新页面,
PDF
绘制即“当前页”,相对位置都是相对“当前页”而言 - 绘制遵循页面标准的层级,后绘制内容会覆盖已绘制内容
- 垂直偏移
position
是图片相对“当前页”的偏移,向上偏移一个内容页的距离后,当前页绘制的就是下一页该显示的图片内容 - 页眉页脚的模拟绘制,每次都要设置填充颜色(默认为黑色),因为每次都是“不同页”,参考第
2
点
三:实现效果
目前只需要模拟A4
下的打印,在对比使用window.print()
生成的PDF
和手动生成的PDF
之后,还是比较满意的,以后再有自定义编辑页眉页脚,到时候再说
要说有什么不满意的地方,那肯定是哪里不满意再改哪里,哪里不购加哪里
不过呢,有个东西肯定只会越改(加)越糟的,那就是文件大小:
Holy shit,大了整整20倍·····
这和生成图片时设置的缩放比(scale
)有关,目前我测试时改值为4.5
(1.5*288/96
),既使变成1/5
也还有400KB,大约4倍的差距,看来文件大小的劣势还是挺明显的
不过呢,现在谁还在乎这么点大小呢···谁还在乎呢···谁···