html2canvas、pdf-lib、file-saver将html页面导出成pdf

2024-08-30 23:20

本文主要是介绍html2canvas、pdf-lib、file-saver将html页面导出成pdf,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

html2canvas、pdf-lib、file-saver将html页面导出成pdf

项目背景

需要根据用户的账号信息,生成一个pdf报告发给客户,要求报告包含echart饼图、走势图等。

方案

使用html2canvas,将页面转成图片,再通过pdf-lib将图片转成pdf文件,最后通过file-saver保存到客户端。

需要注意:由于截长图放到pdf里面,会导致图片被截断,就是可能是某一行的文案或者某一个图被中间截断,所以方案是出一版ui设计稿,把页面、边框、背景都设计好,并且每一页的模块也是需要固定的

这里要求设计出的设计稿是595 * 842的,刚好的标准的A4格式,也就是pdf默认的大小格式,截图生成pdf采取的是分页截图,通过循环每次生成一页图片,放到pdf文件,最终导出文件
在这里插入图片描述

在这里插入图片描述

使用html2canvas,将页面转成图片

const canvas = await html2canvas(element, {scale: 3, // 提高分辨率height: element.scrollHeight,width: element.scrollWidth,useCORS: true,
})
  1. scale: 用于提高Canvas的分辨率。这个值通常大于1,以便在Canvas上渲染出更清晰的图像
  2. height和width: 分别设置为element.scrollHeight和element.scrollWidth,这确保了Canvas的大小与元素的可滚动区域的大小相匹配,即使元素的实际显示区域(即视口)较小
  3. useCORS: 设置为true,允许html2canvas处理跨域图像。这意味着如果元素中包含了来自不同源的图像,并且这些图像服务器支持CORS,那么这些图像也可以被正确地渲染到Canvas上

通过pdf-lib将图片转成pdf文件并保存

const canvas = await html2canvas(element, {scale: canvasConfig.scale, // 提高分辨率height: element.scrollHeight,width: element.scrollWidth,useCORS: true,
})
const imgData = canvas.toDataURL('image/png')
const pngImage = await pdfDoc.embedPng(imgData)
let page = pdfDoc.addPage([595, 842])
// 把整个页面塞到pdf
page.drawImage(pngImage, {x: 0,y: 0,width: canvas.width,height: canvas.height,
})
const pdfBytes = await pdfDoc.save()
const blob = new Blob([pdfBytes], { type: 'application/pdf' })
saveAs(blob, 'example.pdf')
  1. 通过pdfDoc.embedPng()将html2canvas生成的图片转成png图片
  2. pdfDoc.addPage新增一页pdf,宽高定义为标准的A4格式
  3. 将图片塞到pdf里面,需要注意:y值得从左下角开始计算得,并不是左上角
  4. 最后将pdf转成blob,通过saveAs保存为pdf文件

完整代码

const canvasConfig = {scale: 3,pageWidth: 595,pageHeight: 842,
}const processCreatePdf = async () => {try {const pdfDoc = await PDFDocument.create([canvasConfig.pageWidth, canvasConfig.pageHeight])for (let pageIndex = 1; pageIndex <= 10; pageIndex++) {const element = document.getElementById(`page-${pageIndex}`)if (!element) { continue }const canvas = await html2canvas(element, {scale: canvasConfig.scale, // 提高分辨率height: element.scrollHeight,width: element.scrollWidth,useCORS: true,})const imgData = canvas.toDataURL('image/png')const pngImage = await pdfDoc.embedPng(imgData)let page = pdfDoc.addPage([canvas.width, canvas.height])// 把整个页面塞到pdfpage.drawImage(pngImage, {x: 0,y: 0,width: canvas.width,height: canvas.height,})}const pdfBytes = await pdfDoc.save()const blob = new Blob([pdfBytes], { type: 'application/pdf' })saveAs(blob, 'example.pdf')} catch (e) {console.error(e)}
}

生成得pdf效果如下:
在这里插入图片描述
在这里插入图片描述

整体得下效果还行,但是生成得echart饼图比较模糊
在这里插入图片描述

试了很多方法都无法解决该问题,最终只能通过调用echart官方api,生成图片,再把图片覆盖到页面中得饼图位置

echart图片模糊解决方案

先贴上代码

// 饼图子组件中、生成组件方法
useImperativeHandle(ref, () => ({getCanvasImg: (pageHeight, scale) => {let { x, y, width, height } = getChartPosition(pageHeight, 'chart-id', scale)x =  x * scaley = y * scalewidth = width * scaleheight = height * scaleconst url = getCanvasImg(echartRef, width, height)return { x, y, width, height, url }},
}))// 获取echart在pdf的位置
const getChartPosition = (pageHeight, chartId) => {const chartElement = document.getElementById(chartId) || {offsetLeft: 0,offsetTop: 0,clientHeight: 0,clientWidth: 0,}return {x: chartElement.offsetLeft,y: pageHeight - chartElement.offsetTop - chartElement.clientHeight,width: chartElement.clientWidth,height: chartElement.clientHeight,}
}// 调用官方api生成图片
const getCanvasImg = (ref, width, height) => {const chart = ref.current?.getEchartsInstance()return chart?.getDataURL({type: 'png',pixelRatio: 3,backgroundColor: '#FEFBF9',width: width,height,})
}
  1. 通过getChartPosition获取echart在pdf页面中的定位,包括x、y、width、height
  2. 通过echart官方api生成图片,这里width、height规定为echart元素的大小,避免图片变形
  3. 可以看到x、y、width、height都成以了scale,这个是放大的倍数,和上述html2canvas生成截图的参数一致

在生成页面中,调用方法getCanvasImg动态插入echart

const processCreatePdf = async () => {try {const pdfDoc = await PDFDocument.create([canvasConfig.pageWidth, canvasConfig.pageHeight])for (let pageIndex = 1; pageIndex <= 10; pageIndex++) {const element = document.getElementById(`page-${pageIndex}`)if (!element) { continue }const canvas = await html2canvas(element, {scale: canvasConfig.scale, // 提高分辨率height: element.scrollHeight,width: element.scrollWidth,useCORS: true,})const imgData = canvas.toDataURL('image/png')const pngImage = await pdfDoc.embedPng(imgData)let page = pdfDoc.addPage([canvas.width, canvas.height])// 把整个页面塞到pdfpage.drawImage(pngImage, {x: 0,y: 0,width: canvas.width,height: canvas.height,})await processInsertChartImg(pdfDoc, page, pageIndex)}const pdfBytes = await pdfDoc.save()const blob = new Blob([pdfBytes], { type: 'application/pdf' })saveAs(blob, 'example.pdf')} catch (e) {// eslint-disable-next-line no-consoleconsole.error(e)}
}/** 由于直接生成的canvas的echart不清晰,手动插入echart */
const processInsertChartImg = async (pdfDoc, page, pageIndex) => {const targetRef = pageRefMap[`${pageIndex}`]if (!targetRef) { return }for (let i = 1; i <= 5; i++) {const funcName = `getCanvasImg${i > 1 ? i : ''}`const func = targetRef.current[funcName]if (!func) { continue }const { x, y, url, width, height } = func(canvasConfig.pageHeight, canvasConfig.scale, canvasConfig.pageWidth)if (!url) { continue }const chartImg = await pdfDoc.embedPng(url)page.drawImage(chartImg, { x, y, width, height })}
}
  1. 多了processInsertChartImg方法用于动态插入echart
  2. 这里考虑了多张图的场景,不需要的可以简写

效果:
在这里插入图片描述

这篇关于html2canvas、pdf-lib、file-saver将html页面导出成pdf的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122221

相关文章

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

oracle 11g导入\导出(expdp impdp)之导入过程

《oracle11g导入导出(expdpimpdp)之导入过程》导出需使用SEC.DMP格式,无分号;建立expdir目录(E:/exp)并确保存在;导入在cmd下执行,需sys用户权限;若需修... 目录准备文件导入(impdp)1、建立directory2、导入语句 3、更改密码总结上一个环节,我们讲了

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法

Vue3绑定props默认值问题

《Vue3绑定props默认值问题》使用Vue3的defineProps配合TypeScript的interface定义props类型,并通过withDefaults设置默认值,使组件能安全访问传入的... 目录前言步骤步骤1:使用 defineProps 定义 Props步骤2:设置默认值总结前言使用T

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Python自动化处理PDF文档的操作完整指南

《Python自动化处理PDF文档的操作完整指南》在办公自动化中,PDF文档处理是一项常见需求,本文将介绍如何使用Python实现PDF文档的自动化处理,感兴趣的小伙伴可以跟随小编一起学习一下... 目录使用pymupdf读写PDF文件基本概念安装pymupdf提取文本内容提取图像添加水印使用pdfplum

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装

Java高效实现Word转PDF的完整指南

《Java高效实现Word转PDF的完整指南》这篇文章主要为大家详细介绍了如何用Spire.DocforJava库实现Word到PDF文档的快速转换,并解析其转换选项的灵活配置技巧,希望对大家有所帮助... 目录方法一:三步实现核心功能方法二:高级选项配置性能优化建议方法补充ASPose 实现方案Libre