import jsPDF from "jspdf";
import html2canvas from "html2canvas";
/**
* 生成pdf(处理多页pdf截断问题)
* @param {Object} param
* @param {HTMLElement} param.element - 需要转换的dom根节点
* @param {number} [param.contentWidth=550] - 一页pdf的内容宽度,0-595
* @param {number} [param.contentHeight=800] - 一页pdf的内容高度,0-842
* @param {string} [param.outputType='save'] - 生成pdf的数据类型,默认是save下载下来,添加了'file'类型,其他支持的类型见http://raw.githack.com/MrRio/jsPDF/master/docs/jsPDF.html#output
* @param {number} [param.scale=window.devicePixelRatio * 2] - 清晰度控制,canvas放大倍数,默认像素比*2
* @param {string} [param.direction='p'] - 纸张方向,l横向,p竖向,默认A4纸张
* @param {string} [param.fileName='document.pdf'] - pdf文件名,当outputType='file'时候,需要加上.pdf后缀
* @param {number} param.baseX - pdf页内容距页面左边的高度,默认居中显示,为(A4宽度 - contentWidth) / 2)
* @param {number} param.baseY - pdf页内容距页面上边的高度,默认 15px
* @param {HTMLElement} param.header - 页眉dom元素
* @param {HTMLElement} param.footer - 页脚dom元素
* @param {string} [param.isPageMessage=false] - 是否显示当前生成页数状态
* @param {string} [param.isTransformBaseY=false] - 是否将baseY按照比例缩小(一般固定A4页边距时候可以用上)
* @returns {Promise} 根据outputType返回不同的数据类型,是一个对象,包含pdf结果及需要计算的元素位置信息
*/
export class PdfLoader {
constructor(element, param = {}) {
if (!(element instanceof HTMLElement)) {
throw new TypeError("element节点请传入dom节点");
}
this.element = element;
this.contentWidth = param.contentWidth || 550;
this.outputType = param.outputType || "save";
this.fileName = param.fileName || "导出的pdf文件";
this.scale = param.scale;
this.baseX = param.baseX;
this.baseY = param.baseY == null ? 15 : param.baseY;
this.isTransformBaseY = param.isTransformBaseY || false;
this.header = param.header;
this.footer = param.footer;
this.isPageMessage = param.isPageMessage;
this.direction = param.direction || "p"; // 默认竖向,l横向
this.A4_WIDTH = 595; // a4纸的尺寸[595,842],单位像素
this.A4_HEIGHT = 842;
if (this.direction === "l") {
// 如果是横向,交换a4宽高参数
[this.A4_HEIGHT, this.A4_WIDTH] = [this.A4_WIDTH, this.A4_HEIGHT];
}
// 页眉页脚高度
this.pdfFooterHeight = 0;
this.pdfHeaderHeight = 0;
this.pdf = null;
this.rate = 1; // 缩放比率
this.pages = []; // 当前分页数据
}
/**
* 将元素转化为canvas元素
* @param {HTMLElement} element - 当前要转换的元素
* @param {width} width - 内容宽度
* @returns
*/
async createAndDisplayCanvas() {
let imgData = await this.toCanvas(this.element, this.contentWidth);
let canvasEle = document.createElement("canvas");
canvasEle.width = imgData.width;
canvasEle.height = imgData.height;
canvasEle.style.position = "fixed";
canvasEle.style.top = "0";
canvasEle.style.right = "0";
this.canvasEle = canvasEle;
document.body.appendChild(canvasEle);
const ctx = canvasEle.getContext("2d");
const img = await this.loadImage(imgData.data);
ctx.drawImage(img, 0, 0, imgData.width, imgData.height);
this.scan(ctx, imgData);
}
// url 图片路径
/**
* 加载图片资源
*
* @param url 图片资源链接
* @returns 返回Promise对象,当图片加载成功时resolve,否则reject
*/
loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.setAttribute("crossOrigin", "anonymous");
img.src = url;
img.onload = () => {
// 当图像加载完成后进行resolve
resolve(img);
};
img.onerror = () => {
reject(new Error("图像加载失败"));
};
});
}
/**
* 扫描图像并确定每一页的起始高度
*
* @param ctx 绘制上下文
* @param imgData 图像数据
* @throws 当ctx或imgData为null/undefined时抛出错误
*/
scan(ctx, imgData) {
if (!ctx || !imgData) {
throw new Error("Invalid arguments: ctx or imgData is null/undefined");
}
let originalPageHeight = parseInt(this.originalPageHeight, 10);
let shouldContinueScanning = true;
while (shouldContinueScanning) {
// console.log(this.pages); // 调试代码,生产环境中应去除
let imageData = ctx.getImageData(0, originalPageHeight, imgData.width, 1);
const uniqueArr = Array.from(new Set(imageData.data));
if (uniqueArr.length === 1) {
this.pages.push(originalPageHeight);
originalPageHeight += parseInt(this.originalPageHeight, 10);
if (originalPageHeight > imgData.height) {
shouldContinueScanning = false;
if (this.canvasEle) {
this.canvasEle.remove();
this.canvasEle = null;
}
}
} else {
if (originalPageHeight == this.pages.at(-1)) {
// 防止无限递减
shouldContinueScanning = false;
} else {
originalPageHeight = Math.max(0, originalPageHeight - 1); // 防止originalPageHeight变为负数
}
}
}
}
async toCanvas(element, width) {
// canvas元素
let canvas = await html2canvas(element, {
allowTaint: true, // 允许渲染跨域图片
scale: this.scale || window.devicePixelRatio * 2, // 增加清晰度
useCORS: true, // 允许跨域
});
// 获取canvas转化后的宽度
const canvasWidth = canvas.width;
// 获取canvas转化后的高度
const canvasHeight = canvas.height;
// 高度转化为PDF的高度
const height = (width / canvasWidth) * canvasHeight;
// 转化成图片Data
const canvasData = canvas.toDataURL("image/jpeg", 1.0);
canvas = null;
return { width, height, data: canvasData };
}
/**
* 生成pdf方法,外面调用这个方法
* @returns {Promise} 返回一个promise
*/
getPdf() {
// 滚动置顶,防止顶部空白
window.pageYOffset = 0;
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
return new Promise(async (resolve, reject) => {
// jsPDF实例
const pdf = new jsPDF({
unit: "pt", // mm,pt,in,cm
format: "a4",
orientation: this.direction,
});
this.pdf = pdf;
let pdfFooterHeight = 0;
let pdfHeaderHeight = 0;
// 距离PDF左边的距离,/ 2 表示居中 ,,预留空间给左边, 右边,也就是左右页边距
let baseX = (this.A4_WIDTH - this.contentWidth) / 2;
// 距离PDF 页眉和页脚的间距, 留白留空
let baseY = this.baseY;
// 元素在网页页面的宽度
const elementWidth = this.element.scrollWidth;
// PDF内容宽度 和 在HTML中宽度 的比, 用于将 元素在网页的高度 转化为 PDF内容内的高度, 将 元素距离网页顶部的高度 转化为 距离Canvas顶部的高度
const rate = this.contentWidth / elementWidth;
this.rate = rate;
if (this.isTransformBaseY) {
this.baseY = baseY = baseY * rate;
}
// 页脚元素 经过转换后在PDF页面的高度
if (this.footer) {
pdfFooterHeight = (await this.toCanvas(this.footer, this.A4_WIDTH))
.height;
this.pdfFooterHeight = pdfFooterHeight;
}
// 页眉元素 经过转换后在PDF的高度
if (this.header) {
pdfHeaderHeight = (await this.toCanvas(this.header, this.A4_WIDTH))
.heig
没有合适的资源?快使用搜索试试~ 我知道了~
网页导出 pdf 内容被截断的终极解决方案
共2个文件
js:2个
需积分: 0 20 下载量 121 浏览量
2024-04-11
09:51:53
上传
评论 1
收藏 5KB ZIP 举报
温馨提示
html2canvas 与 jspdf 相结合生成 pdf 内容被截断的终极解决方案,设置背景色为白色,然后转成图片后,获取截断处图片像素点,从截断处往上一行行扫描像素点颜色,碰到这一行颜色都是全白的,代表是从这里开始截断,将这个高度开始将往下的内容都放到下一页,设置背景色为白色,然后转成图片后,获取截断处图片像素点,从截断处往上一行行扫描像素点颜色,碰到这一行颜色都是全白的,代表是从这里开始截断,将这个高度开始将往下的内容都放到下一页 支持自定义页眉页脚 页码数
资源推荐
资源详情
资源评论
收起资源包目录
pdf.zip (2个子文件)
outputPDF.js 14KB
index.js 264B
共 2 条
- 1
资源评论
高级bug制造机
- 粉丝: 318
- 资源: 2
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功