记录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

scalewindow.devicePixelRatioThe 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`);

这里有几个点,需要留意:

  1. 默认PDF为单页,需要判断“图片”相对高度,超出标准高度时添加新页面
  2. 每次添加新页面,PDF绘制即“当前页”,相对位置都是相对“当前页”而言
  3. 绘制遵循页面标准的层级,后绘制内容会覆盖已绘制内容
  4. 垂直偏移position是图片相对“当前页”的偏移,向上偏移一个内容页的距离后,当前页绘制的就是下一页该显示的图片内容
  5. 页眉页脚的模拟绘制,每次都要设置填充颜色(默认为黑色),因为每次都是“不同页”,参考第2

三:实现效果

目前只需要模拟A4下的打印,在对比使用window.print()生成的PDF和手动生成的PDF之后,还是比较满意的,以后再有自定义编辑页眉页脚,到时候再说

pdf对比
看起来分页和边距都差不多
PDF对比
清晰度也还满意

要说有什么不满意的地方,那肯定是哪里不满意再改哪里,哪里不购加哪里
不过呢,有个东西肯定只会越改(加)越糟的,那就是文件大小:

PDF文件大小对比
2081 VS 124

Holy shit,大了整整20倍·····
这和生成图片时设置的缩放比(scale)有关,目前我测试时改值为4.51.5*288/96),既使变成1/5也还有400KB,大约4倍的差距,看来文件大小的劣势还是挺明显的

不过呢,现在谁还在乎这么点大小呢···谁还在乎呢···谁···