vue将html生成pdf并分页

jspdf + html2canvas

此方案有很多的css兼容问题,比如虚线边框、svg、页数多了内容显示不全、部分浏览器兼容问题,光是解决这些问题就耗费了我不少岁月和精力

后面了解到新的技术方案:

jspdf + html-to-image

javascript 复制代码
npm install --save html-to-image
npm install --save jspdf 

原理都是一样,先将html转成图片,再分页生成pdf

区别在于html-to-image可以生成多种格式,并且没有发现html2canvas上的css兼容问题

我这里用的是toCanvas

新建公共js文件

javascript 复制代码
// 导出页面为PDF格式
import JSPDF from "jspdf";
import { toCanvas  } from 'html-to-image'

/***
 * elementName: 需要输出PDF的DOM的id
 */
export const  ExportSavePdf = (elementName,pageTotal) =>{
    var element = document.getElementById(elementName)
    return new Promise((resolve) => {
        toCanvas(element,{ useCORS: true ,allowTaint:true}).then(function(canvas) {
            var pdf = new JSPDF("p", "mm", "a4") // A4纸,纵向
            var ctx = canvas.getContext("2d")
            ctx.scale(2, 2);
            var a4w = 210;
            var a4h = 297 // A4大小,210mm x 297mm,四边各保留20mm的边距
            var imgHeight = Math.floor(a4h * canvas.width / a4w) // 按A4显示比例换算一页图像的像素高度
            var renderedHeight = 0
            let pageNum = 0;
            while (renderedHeight < canvas.height ) {
                pageNum ++ 
                var page = document.createElement("canvas")
                page.width = canvas.width
                page.height = Math.min(imgHeight, canvas.height - renderedHeight) // 可能内容不足一页
    
                // 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
                page.getContext("2d").putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0)
                pdf.addImage(page.toDataURL("image/jpeg", 1.0), "JPEG", 0, 0, a4w, Math.min(a4h, a4w * page.height / page.width)) // 添加图像到页面,保留10mm边距
    
                renderedHeight += imgHeight
                if (renderedHeight < canvas.height && pageNum < pageTotal) { pdf.addPage() } // 如果后面还有内容,添加一个空页
                // delete page;
            }
            //这里可根据自己需求返回不同类型的数据
            resolve(pdf.output('blob'))
        }).catch(function (error) {
            console.error(error)
            resolve(false)
        })
    })
}

在vue页面引入后调用

javascript 复制代码
//pageTotal表示当前pdf的总页数,这个可以在预览的时候计算出来
ExportSavePdf('pdfBox', _this.pageTotal).then((res) => {
                    if (res === false) {
                        this.$message.error('保存失败!');
                        return;
                    }
                    //获取的blob格式数据
                    let pdfBlob = res;
                    //后面是将blob数据上传到oss,这里的可以根据自己需求来
                    getOss({})
                        
                });

当然也有部分兼容问题,下面是我项目中遇到的问题以及我的解决方案:

javascript 复制代码
// 解决兼容问题,在保存之前调用,注意使用$nextTick
        compatibilityProblem() {
            // 去掉所有标签中的包含"v:"的属性
            const elements = Array.from(document.querySelector('#pdfBox').getElementsByTagName('*'));
            for (var i = 0; i < elements.length; i++) {
                var attributes = elements[i].attributes;
                // 遍历当前标签的所有属性
                for (var j = attributes.length - 1; j >= 0; j--) {
                    var attributeName = attributes[j].name;

                    // 如果属性名称中包含 "v:",则移除该属性
                    if (attributeName.includes('v:')) {
                        elements[i].removeAttribute(attributeName);
                    }
                }
            }
            // 去掉拼音a的宋体样式
            const songSpan = Array.from(document.querySelectorAll('#pdfBox span[style*="font-family:SimSun"]'));
            const aList = ['a', 'ā', 'á', 'ǎ', 'à'];
            const aSpan = songSpan.filter((item) => aList.indexOf(item.innerText) !== -1);
            for (let a of aSpan) {
                a.style.fontFamily = 'inherit';
            }
            // 解决图片跨域问题
            let imgs = Array.from(document.querySelectorAll('#pdfBox img'));
            for (let item of imgs) {
                item.onload = function () {
                    const protocol = window.location.protocol.replace(':', '');
                    if (item.getAttribute('src').split('://').length) {
                        const imgProtocol = item.getAttribute('src').split('://')[0];
                        const src = item.getAttribute('src');
                        if (imgProtocol !== protocol) {
                            item.setAttribute('src', src.replace(imgProtocol, protocol));
                        }
                    }
                    item.setAttribute('crossorigin', 'anonymous');
                };
            }
        },
相关推荐
仰望.5 小时前
vue 表格组件 vxe-table 树表格懒加载支持拖拽排序的详细用法
前端·javascript·vue.js·vxe-table
康小旺9 小时前
AI 写 Vue 总写成同一个味道:给它装 Skills,装的不是知识是边界
vue.js·typescript
惠惠软件11 小时前
PDF加盖骑缝章标注工具,PDF 文档上批量添加骑缝章或盖章的小工具
数据库·pdf·办公文件
徐小夕11 小时前
JitKnow AI知识库 4.7 发布|多文档推理+拖拽 Agent + 技能库,打造更懂业务的 AI 知识中台
前端·vue.js·github
一枕槐安48711 小时前
多模态RAG实战:PDF图文混合文档检索问答本地跑通
langchain·pdf·milvus·ai应用开发·多模态rag
sycmancia14 小时前
Qml——Text元素
java·前端·html
u01110267514 小时前
Canvas 导出 PNG 时 quality 参数为什么不起作用
前端·javascript·vue.js
正弦波_Wave14 小时前
【Orcad原理图打印Pdf】
笔记·pdf
开开心心就好15 小时前
电脑小说阅读器,支持朗读和自定义皮肤
服务器·开发语言·javascript·算法·pdf·c#·音视频
计算机编程-吉哥16 小时前
计算机毕业设计 基于 SpringBoot + Vue 的剧本杀系统 Java毕业设计【任务书、开题报告、中期报告、论文、源码】
java·vue.js·spring boot·毕业设计·课程设计·计算机毕业设计选题