主要应用场景是导出 pdf 的时候图片过长的情况下没法在一页内完整导出,此时需要对图片进行分页切割,这里实现的就是一个基于 canvas 的图片分页切割方法
代码实现
ts
/**
* 获取 base64 图片的尺寸。
*
* @param {string} base64 - 图片的Base64编码字符串。
* @returns {Object} 包含图片宽度和高度的对象,格式为{width: number, height: number}。
*/
export function getPngDimensions(base64: string) {
// 从base64字符串中提取一部分数据并解码
const header = atob(base64.slice(22, 70)).slice(16, 24);
// 将解码后的字符串转换为Uint8Array
const uint8 = Uint8Array.from(header, c => c.charCodeAt(0));
// 使用DataView来处理Uint8Array的数据
const dataView = new DataView(uint8.buffer);
return {
// 从DataView中获取图片宽度
width: dataView.getInt32(0),
// 从DataView中获取图片高度
height: dataView.getInt32(4),
};
}
/**
* 根据给定的页面尺寸和边距,对PDF中的图片进行分割。
*
* @param {Object} imageContent - 图片Base64编码字符串。
* @param {Object} pageSize - 页面尺寸对象,格式为{width: number, height: number},单位是毫米。
* @param {number[]} pageMargins - 页面边距数组,包含两个值,表示水平(左右)和垂直(上下)方向的间距,单位是毫米。
* @returns {Promise<Object[]>} 返回分割后的图片对象,包含每一页分割后的图片大小
*/
export function splitPdfImage(
imageContent: string,
pageSize: { width: number; height: number },
pageMargins: number[]
) {
// 获取图片的宽度和高度
const { width, height } = getPngDimensions(imageContent);
// 创建一个div元素,用于模拟页面
const pageDom = document.createElement('div');
// 设置div元素的宽度,考虑页面边距
pageDom.style.width = pageSize.width - pageMargins[0] * 4 +'mm';
// 设置div元素的高度,考虑页面边距
pageDom.style.height = (pageSize.height - pageMargins[1] * 2.5) +'mm';
// 设置div元素的定位为绝对定位
pageDom.style.position = 'absolute';
// 设置div元素的顶部位置为0
pageDom.style.top = '0';
// 将div元素添加到文档主体中
document.body.append(pageDom);
const scale = 2;
const num = 38;
// 计算页面高度,基于div元素的高度和一些缩放因子
const PAGE_HEIGHT = Math.floor(pageDom.clientHeight / num) * num * scale;
// 计算页面宽度,基于div元素的宽度和缩放因子
const PAGE_WIDTH = pageDom.clientWidth * scale;
// 计算图片在当前页面宽度下的打印高度
const printHeight = (height * PAGE_WIDTH) / width;
return new Promise(resolve => {
const content: any[] = [];
// 如果图片的打印高度大于页面高度,则需要分割图片
if (printHeight > PAGE_HEIGHT) {
const img = new Image();
// 当图片加载完成时执行以下操作
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')!;
const printHeight = (img.height * PAGE_WIDTH) / img.width;
canvas.width = PAGE_WIDTH;
// 循环分割图片
for (let pages = 0; printHeight > pages * PAGE_HEIGHT; pages++) {
// 设置当前canvas的高度,避免最后一张图片超出实际高度
canvas.height = Math.min(PAGE_HEIGHT, printHeight - pages * PAGE_HEIGHT);
// 在canvas上绘制图片的相应部分
ctx.drawImage(img, 0, -pages * PAGE_HEIGHT, canvas.width, printHeight);
// 将分割后的图片内容添加到content数组中
content.push({
image: canvas.toDataURL('image/png'),
width: Math.min(width, Math.floor(PAGE_WIDTH / scale)),
height: Math.floor(canvas.height / scale),
});
}
// 清理canvas
ctx!.clearRect(0, 0, canvas.width, canvas.height)
canvas.width = 1
canvas.height = 1
canvas.remove()
img.src = '';
img.remove();
resolve(content);
};
img.src = imageContent;
} else {
// 如果图片高度不超过页面高度,直接返回原图片内容
resolve([{ image: imageContent, width, height }]);
}
}).then((res) => {
// 移除之前创建的模拟页面的div元素
pageDom.remove();
return res;
});
}
使用示例
ts
splitPdfImage(imageContent, pdfPage.pageSize, pdfPage.pageMargins).then(res => {
console.log('---split image---', res)
})