当前位置: 首页 > news >正文

前端生成pdf之html2canvas+jsPDF,以及解决图片不显示bug

前端如何生成pdf

开发背景: 需要给页面中相应的内容生成pdf,查找文档后发现要用到两个插件。html2canvas 以及 jsPDF

  • html2canvas 给dom结构转化为canvas,然后生成各种类型图片
  • jsPDF 把canvas 生成的图片url 转化为pdf
// 插入图片
pdf.addImage(image, format, x, y, width, height, alias, compression, rotation)
// 保存为pdf
pdf.save("example.pdf");

参数

  1. image:表示要插入的图片资源,可以是图片文件的路径或者base64编码字符串。
  2. format:表示要插入的图片格式,包括:‘JPEG’, ‘PNG’, ‘GIF’, ‘BMP’, ‘TIFF’, ‘RAW’, ‘JPEG2000’。
  3. x:图片在PDF中的x轴坐标,单位为pt(点)。
  4. y:图片在PDF中的y轴坐标,单位为pt(点)。
  5. width:图片在PDF中的宽度,单位为pt(点)。
  6. height:图片在PDF中的高度,单位为pt(点)。
  7. alias(可选):指定图片资源的别名。
  8. compression(可选):指定图片的压缩质量,取值为0-1之间的浮点数。
  9. rotation(可选):指定图片的旋转角度,取值范围为0-360之间的整数。

下面是dom转化为pdf 的基本代码

import html2canvas from 'html2canvas';
import jsPdf from 'jspdf';function printPDF () {const domElement = document.getElementById('your-id');html2canvas(domElement, {onclone: (document) => {document.getElementById('print-button').style.visibility = 'hidden';}}).then((canvas) => {const img = canvas.toDataURL('image/png');const pdf = new jsPdf();pdf.addImage(imgData, 'JPEG', 0, 0, width, height);pdf.save('your-filename.pdf');
})

需要特别注意一下 html2canvas onclone 方法。它是当DOM被克隆后进行渲染时的回调函数,可用于修改将要渲染的内容,而不会影响原始DOM。当你需要在生成快照前操作 DOM(例如隐藏打印按钮)时,会非常有用。

难点突破

1. 使用html2canvas转为图片遇到跨域问题

跨域图片不会生成:由于跨域的问题(比如图片在第三方存放),导致图片不会被绘制,需要多方配置;
1. html2canvas的配置项中配置 allowTaint:trueuseCORS:true(二者不可共同使用)
2. img标签增加 crossOrigin='anonymous'
3. 图片服务器配置Access-Control-Allow-Origin 或使用代理
其中第三步是最重要的,不设置则前两步设置了也无效。
如果后端不能改图片服务器,咱们前端也能设置图片代理,例如nginx server配置

	location /img {proxy_pass 服务器真正地址;}

2. html2canvas转化的图片,ios设备长按可以保存,Android设备长按无法保存

  1. 原因分析:转换后使用canvas.toDataURL拿到的是base64的图片,此图片可以直接展示在网页上,但是在Android设备上长按此图片无法保存,ios设备可以长按保存;原因是Android无法长按保存base64的图片,
  2. 解决办法:可以考虑将图片先上传到服务器拿到url再展示

3. safir长按保存不了图片

使用-webkit-touch-callout:default不规范的属性,iOS 2.0及更高版本的Safari浏览器可用

4. 生成的图片中若包含二维码,微信长按图片偶现无法识别

不使用路由切换,使用window.location.href直接跳转刷新页面

5. 生成的PDF超出一页后分页问题

  1. 需要计算图片的宽高,一页展示多少

看详细代码

printPDF(id, name){const domElement = document.getElementById(id);html2canvas(domElement, {allowTaint: false,useCORS: true,   // 用于解决图片跨域onclone: (document) => {document.getElementById('print-button').style.visibility = 'hidden';}}).then((canvas) => {const fileName = name + (new Date()).toLocaleString() + '.pdf'var pdf = new jsPdf('p', 'mm', 'a4'); // A4纸,纵向var ctx = canvas.getContext('2d');var a4w = 190; var a4h = 277; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277var imgHeight = Math.floor(a4h * canvas.width / a4w); // 按A4显示比例换算一页图像的像素高度var renderedHeight = 0;while (renderedHeight < canvas.height) {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', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)); // 添加图像到页面,保留10mm边距renderedHeight += imgHeight;if (renderedHeight < canvas.height) {pdf.addPage();// 如果后面还有内容,添加一个空页}// delete page;}// 保存文件pdf.save(fileName);})}

相关文章:

前端生成pdf之html2canvas+jsPDF,以及解决图片不显示bug

前端如何生成pdf 开发背景&#xff1a; 需要给页面中相应的内容生成pdf&#xff0c;查找文档后发现要用到两个插件。html2canvas 以及 jsPDF html2canvas 给dom结构转化为canvas&#xff0c;然后生成各种类型图片jsPDF 把canvas 生成的图片url 转化为pdf // 插入图片 pdf.addI…...

PHP常用符号和函数

// 单行注解 /* */ 多行注解 引号的使用 ’ ’ 单引号,没有任何意义,不经任何处理直接拿过来; ” “双引号,php动态处理然后输出,一般用于变量. 变量形态: 一种是True 即 真的; 另一种是False 即假的 常见变量形态: string 字串(数字\汉字\等等) integer 整数(1、2、…...

C#,入门教程(12)——数组及数组使用的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(11)——枚举&#xff08;Enum&#xff09;的基础知识和高级应用https://blog.csdn.net/beijinghorn/article/details/123917587 数组是一种数据集合&#xff0c;是一组完全相同的、按顺序存放的数据。 需要记住数组的几个特征&…...

C语言中的副作用、序列点以及复杂表达式的求值顺序

C语言中复杂表达式的求值顺序 num (5 2) * (9 * 6);如果我问你&#xff1a;上面的c程序在执行的时候到底是先算&#xff08;5 2&#xff09;还是&#xff08;9 * 6&#xff09;&#xff1f;你会怎么回答&#xff1f; 无非就两种&#xff1a;先算&#xff08;5 2&#xff…...

C++学习笔记——队列模拟

目录 一、模拟队列 二、模拟队列的知识点 三、队列 3.1入队操作 3.2出队操作 3.3访问队首元素 3.4访问队尾元素 3.5判断队列是否为空 3.6获取队列的大小 四、实现队列的基本功能 一、模拟队列 当涉及到数据存储和处理时&#xff0c;队列是一种常见的数据结构&#x…...

jvm垃圾回收相关的算法

什么是垃圾 JVM主要通过以下几种方式来判断对象是否需要回收&#xff1a; 引用计数法&#xff1a;JVM通过引用计数器来判断对象的引用数量&#xff0c;当引用数量为0时&#xff0c;表示对象可以被回收。 可达性分析算法&#xff1a;JVM通过根对象&#xff08;如栈中的引用、静…...

每日coding

2085、统计出现过一次的公共字符串 给你两个字符串数组 words1 和 words2 &#xff0c;请你返回在两个字符串数组中 都恰好出现一次 的字符串的数目。 示例 1&#xff1a; 输入&#xff1a;words1 ["leetcode","is","amazing","as"…...

软件工程_复习

软件工程 软件危机(1968 60年代) 产生软件危机的原因: 一方面与软件本身的特点有关,另一方面也和软件开发和维护的方法不正确有关。 与软件本身特点有关: 1.软件不同于硬件,软件是计算机系统中的逻辑部件,缺乏“可见性”,管理和控制软件开发过程相当困难 2.软件在运行过…...

07GoF之三种工厂模式

GoF&#xff08;Gang of Four&#xff09;:四人组,《Design Patterns: Elements of Reusable Object-Oriented Software》&#xff08;《设计模式》)的作者,设计了23种设计模式.但时代发展,还有其它的设计模式&#xff0c;比如&#xff1a;JavaEE的设计模式&#xff08;DAO模式…...

JAVA面试部分——后端-线程前篇

3.1 线程和进程 在计算机科学中&#xff0c;进程和线程是操作系统管理资源的两种不同方式。 进程&#xff08;Process&#xff09;&#xff1a;是程序在计算机上的一次执行活动。每个进程都有自己的内存空间&#xff0c;包括代码、数据和系统资源。一个进程可以包含多个线程。…...

【小沐学C++】C++ 实现鼠标键盘钩子HOOK

文章目录 1、简介2、相关函数2.1 SetWindowsHookEx2.2 UnhookWindowsHookEx2.3 CallNextHookEx 3、相关结构体3.1 KBDLLHOOKSTRUCT3.2 MSLLHOOKSTRUCT 4、挂钩过程5、代码测试5.1 代码1 结语 1、简介 https://learn.microsoft.com/zh-cn/windows/win32/winmsg/about-hooks 挂…...

【pycharm】常见问题与解决

记录一些Pycharm中经常遇到的问题 1. “Open file or Project” always in loading state (hang) a) 老版本 (2021年左右) since build 211.6693.14, it is possible to use a native file chooser on Windows. The feature is experimental; to enable it, add the followi…...

flask web学习之表单(一)

文章目录 一、使用Flask-WTF处理表单1.1 安装Flask-WTF库1.2 定义WTForms表单类常用的WTForm字段实例化字段类常用参数常用的WTForm验证器 1.3 输出HTML代码使用render_kw属性在调用字段时传入 1.4 在模板中渲染表单 在web程序中&#xff0c;表单是用户交互最常见的方式之一。用…...

@ControllerAdvice 使用场景

ControllerAdvice 是Spring 框架中的注解&#xff0c;多用在Spring MVC应用程序中。 使用场景1&#xff1a;处理异常 # 示例1 import org.apache.ibatis.javassist.NotFoundException; import org.springframework.http.HttpStatus; import org.springframework.http.Respons…...

二极管选型怎么选?常用参数要熟练~

同学们大家好&#xff0c;今天我们继续学习杨欣的《电子设计从零开始》&#xff0c;这本书从基本原理出发&#xff0c;知识点遍及无线电通讯、仪器设计、三极管电路、集成电路、传感器、数字电路基础、单片机及应用实例&#xff0c;可以说是全面系统地介绍了电子设计所需的知识…...

【小白专用】C#关于角色权限系统

&#xff08;C#&#xff09;用户、角色、权限 https://www.cnblogs.com/huangwen/articles/638050.html 权限管理系统——数据库的设计&#xff08;一&#xff09; https://www.cnblogs.com/cmsdn/p/3371576.html 权限管理系统——菜单模块的实现&#xff08;二&#xff09; …...

代码随想录算法训练营

一刷打卡记录&#xff1a; 日期打卡2023/10/25 day01二分查找有点转不过弯&#xff0c;快慢指针能理解&#xff0c;自己写也可能写不出来&#xff0c;但是能记住了&#xff0c;能看懂&#xff0c;还有其他解法待补充看完&#xff0c;花了挺长时间的2023/10/26 day02还好&#…...

统计学-R语言-3

文章目录 前言给直方图增加正态曲线的不恰当之处直方图与条形图的区别核密度图时间序列图洛伦茨曲线计算绘制洛伦茨曲线所需的各百分比数值绘制洛伦茨曲线 练习 前言 本篇文章是介绍对数据的部分图形可视化的图型展现。 给直方图增加正态曲线的不恰当之处 需要注意的是&#…...

spring动态控制定时任务

在spring框架中&#xff0c;对于简单的定时任务&#xff0c;可以使用 Scheduled 注解实现&#xff0c;在实际项目中&#xff0c;经常需要动态的控制定时任务&#xff0c;比如通过接口增加、启动、停止、删除定时任务&#xff0c;动态的改变定时任务的执行时间等。 我们可以通过…...

3. Mybatis 中SQL 执行原理

2. Mybatis 中SQL 执行原理 这里有两种方式&#xff0c;一种为常用的 Spring 依赖注入 Mapper 的方式。另一种为直接使用 SqlSessionTemplate 执行 Sql 的方式。 Spring 依赖注入 Mapper 的方式 Mapper 接口注入 SpringIOC 容器 Spring 容器在扫描 BeanDefinition 阶段会扫…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...