vue将html生成pdf并分页

news2024/12/23 18:32:41

jspdf + html2canvas

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

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

jspdf + html-to-image

npm install --save html-to-image
npm install --save jspdf 

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

区别在于html-to-image可以生成多种格式,并且没有发现html2canvas上的css兼容问题
可根据自己需求来调用对应的api
我这里用的是toCanvas
新建公共js文件

// 导出页面为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页面引入后调用

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

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

// 解决兼容问题,在保存之前调用,注意使用$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');
                };
            }
        },

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1581598.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux查看系统配置信息的命令【lscpu】【free】【df】【uname】【lsblk】

目录 1.查看CPU信息【lscpu】 2.查看内存信息【free】 3.查看文件系统信息【df】 4.查看系统信息【uname】 知识扩展&#xff1a;Red Hat Enterprise Linux 和 Debian GNU/Linux 两者的发展介绍 知识扩展&#xff1a;Centos 和 ubuntu的区别 知识扩展&#xff1a;更多 …

Jenkins使用-绑定域控与用户授权

一、Jenkins安装完成后&#xff0c;企业中使用&#xff0c;首先需要绑定域控以方便管理。 操作方法&#xff1a; 1、备份配置文件&#xff0c;防止域控绑定错误或授权策略选择不对&#xff0c;造成没办法登录&#xff0c;或登录后没有权限操作。 [roottest jenkins]# mkdir ba…

最短编辑距离(线性dp)-java

最短编辑问题也是一种非常经典的二维线性dp问题。 文章目录 前言 一、最短编辑距离问题 二、算法思路 1.dp[i][j]的情况 2.边界问题初始化 3.状态转移方程 三、代码如下 1.代码如下 2.读入数据 3.代码运行结果 总结 前言 最短编辑问题也是一种非常经典的二维线性dp问题。 提示&…

NzN的数据结构--插入排序

排序排序我要Disney&#xff0c;今天我们先来看看经典排序算法里的插入排序&#xff0c;先三连后看才是好习惯&#xff01;&#xff01;&#xff01; 目录 一、排序的概念及应用 1. 排序的概念 2. 排序的应用 3. 常见的排序算法 二、插入排序 1. 基本思想 2. 直接插入排…

算法打卡day40|动态规划篇08| Leetcode 139.单词拆分|多重背包理论|背包问题总结篇

目录 算法题 Leetcode 139.单词拆分 个人思路 解法 动态规划 回溯法 多重背包理论基础 背包问题总结篇 解题思路 背包递推公式 遍历顺序 01背包 完全背包 算法题 Leetcode 139.单词拆分 题目链接:139.单词拆分 大佬视频讲解&#xff1a;单词拆分视频讲解 个人思…

使用pytorch构建控制生成GAN(Controllable GAN)网络模型

本文为此系列的第四篇Controllable GAN&#xff0c;上一篇为Conditional GAN。文中使用训练好的模型和优化噪声向量来操纵生成图像的特定属性&#xff0c;若有不懂的无监督知识点可以看本系列第一篇。 原理 本文主要讲什么是控制生成&#xff0c;以及如何做到控制生成。 什么是…

华为S5735S核心交换配置实例

以下脚本实现创建vlan2,3&#xff0c;IP划分&#xff0c;DHCP启用&#xff0c;接口划分&#xff0c;ssh,telnet,http,远程登录启用 默认用户创建admin/admin123提示首次登录需要更改用户密码 sysname test-Hxvlan 2 description to test1…

JavaScript(1)神秘的编程技巧

大家都感兴趣的箭头函数 箭头函数在许多场景中都可以发挥作用&#xff0c;尤其适用于简化函数声明和提高代码的可读性。以下是箭头函数可以使用的一些常见方面&#xff1a; &#xff08;1&#xff09;回调函数&#xff1a; 箭头函数特别适合作为回调函数&#xff0c;例如在事…

html基础(2)(链接、图像、表格、列表、id、块)

1、链接 <a href"https://www.example.com" target"_blank" title"Example Link">Click here</a> 在上示例中&#xff0c;定义了一个链接&#xff0c;在网页中显示为Click here&#xff0c;鼠标悬停指示为Example Link&#xff0c…

电脑开机提示“no bootable device”,无法进入系统

当您的Windows 10电脑开机时提示“no bootable device”,这意味着计算机无法找到一个可以启动操作系统的设备。这个问题通常与硬件连接、BIOS设置、硬盘问题、引导扇区故障或系统文件损坏等有关。以下是一系列详细的解决步骤: 检查硬件连接:关闭电脑,拔掉电源线,打开机箱检…

Android OOM问题定位、内存优化

一、常用工具&#xff1a; 1、LeakCanary val refWatcher: RefWatcher? TestApp.getRefWatcher(activity) refWatcher?.watch(activity);//检测是否有泄露&#xff0c;即触发GC回收&#xff0c;看activity是否被回收&#xff0c;没有被回收就是泄露了。 二、常见的几种内…

PanTools v1.0.20 多网盘批量管理工具 批量管理、分享、转存、重命名、复制...

一款针对多个热门网盘的文件管理、批量分享、批量转存、批量复制、批量重命名、批量链接检测、跨账号移动文件、多账号文件搜索等&#xff0c;支持不同网盘的不同账号的资源文件操作。适用于网站站长、资源爱好者等&#xff0c;对于管理名下具有多个网盘多个账号具有实用的效果…

计算系数(acwing,数论)

题目描述&#xff1a; 给定一个多项式 (axby)^k&#xff0c;请求出多项式展开后 x^n*y^m 项的系数。 输入格式&#xff1a; 共一行&#xff0c;包含 5 个整数&#xff0c;分别为 a&#xff0c;b&#xff0c;k&#xff0c;n&#xff0c;m&#xff0c;每两个整数之间用一个空格…

STC89C52学习笔记(七)

STC89C52学习笔记&#xff08;七&#xff09; 综述&#xff1a;本文介绍了串口以及讲述了串口相关寄存器如何配置并给予相关代码。 一、修改代码注意事项 在修改代码时不要一次性加入一堆代码&#xff0c;不利于定位错误。可以先注释一些代码&#xff0c;待解决完毕问题后再…

基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务多租户系统架构

简介 基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务多租户系统架构。并引入组件化的思想实现高内聚低耦合并且高度可配置化&#xff0c;适合学习和企业中使用。 真正实现了基于RBAC、jwt和oauth2的无状态统一权限认证的解决方案&#x…

基于ubuntu22.04系统安装nvidia A100驱动与NVLink启用

1、官方仓库 针对驱动包下载认准nvidia官网 dpkg -i nvidia-driver-local-repo-ubuntu2204-550.54.15_1.0-1_amd64.deb apt update apt search nvidia-driver-5502、安装 根据步骤1apt search nvidia-driver-550查出版本&#xff1a;此驱动包封在nvidia-driver-local-repo-ub…

关于vue使用第三方faceBook登录

在这里引入这位博主的 https://blog.csdn.net/qq_40942490/article/details/118333318https://blog.csdn.net/qq_40942490/article/details/118333318在他的基础上我增加了退出登录操作,一定要清除所有的cookie不然就会导致退出登录后无法再次使用faceBook登录 FB.getLoginSt…

【leetcode面试经典150题】28.盛最多水的容器(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

2024Peak码支付系统网站源码

系统简介 Peak码支付-是专为个人站长打造的聚合免签系统&#xff0c;拥有卓越的性能和丰富的功能。用全新轻量化的界面UI&#xff0c;让您可以更加方便快捷地解决知识付费和运营赞助的难题。同时&#xff0c;它基于高性能SpeedPHPLayuiPearAdmin架构&#xff0c;提供实时监控和…

【Springboot开发】后端代码基础框架

前言&#xff1a;主要介绍最基础的springboot开发架构 目录 1. overall2. 配置文件2.1 YAML2.2 properties2.3 配置文件加载顺序2.4 多配置文件 3. 代码包3.1 infrastructure3.1.1 persistence 3.2 application3.2.1 dto3.2.2 converter3.2.3 service 3.3 api3.3.1 vo3.3.2 req…