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

相关文章

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

前端如何通过nginx访问本地端口

《前端如何通过nginx访问本地端口》:本文主要介绍前端如何通过nginx访问本地端口的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、nginx安装1、下载(1)下载地址(2)系统选择(3)版本选择2、安装部署(1)解压(2)配置文件修改(3)启动(4)

HTML中meta标签的常见使用案例(示例详解)

《HTML中meta标签的常见使用案例(示例详解)》HTMLmeta标签用于提供文档元数据,涵盖字符编码、SEO优化、社交媒体集成、移动设备适配、浏览器控制及安全隐私设置,优化页面显示与搜索引擎索引... 目录html中meta标签的常见使用案例一、基础功能二、搜索引擎优化(seo)三、社交媒体集成四、移动

HTML input 标签示例详解

《HTMLinput标签示例详解》input标签主要用于接收用户的输入,随type属性值的不同,变换其具体功能,本文通过实例图文并茂的形式给大家介绍HTMLinput标签,感兴趣的朋友一... 目录通用属性输入框单行文本输入框 text密码输入框 password数字输入框 number电子邮件输入编程框

HTML img标签和超链接标签详细介绍

《HTMLimg标签和超链接标签详细介绍》:本文主要介绍了HTML中img标签的使用,包括src属性(指定图片路径)、相对/绝对路径区别、alt替代文本、title提示、宽高控制及边框设置等,详细内容请阅读本文,希望能对你有所帮助... 目录img 标签src 属性alt 属性title 属性width/h

CSS3打造的现代交互式登录界面详细实现过程

《CSS3打造的现代交互式登录界面详细实现过程》本文介绍CSS3和jQuery在登录界面设计中的应用,涵盖动画、选择器、自定义字体及盒模型技术,提升界面美观与交互性,同时优化性能和可访问性,感兴趣的朋... 目录1. css3用户登录界面设计概述1.1 用户界面设计的重要性1.2 CSS3的新特性与优势1.

HTML5 中的<button>标签用法和特征

《HTML5中的<button>标签用法和特征》在HTML5中,button标签用于定义一个可点击的按钮,它是创建交互式网页的重要元素之一,本文将深入解析HTML5中的button标签,详细介绍其属... 目录引言<button> 标签的基本用法<button> 标签的属性typevaluedisabled

HTML5实现的移动端购物车自动结算功能示例代码

《HTML5实现的移动端购物车自动结算功能示例代码》本文介绍HTML5实现移动端购物车自动结算,通过WebStorage、事件监听、DOM操作等技术,确保实时更新与数据同步,优化性能及无障碍性,提升用... 目录1. 移动端购物车自动结算概述2. 数据存储与状态保存机制2.1 浏览器端的数据存储方式2.1.

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,

Python中图片与PDF识别文本(OCR)的全面指南

《Python中图片与PDF识别文本(OCR)的全面指南》在数据爆炸时代,80%的企业数据以非结构化形式存在,其中PDF和图像是最主要的载体,本文将深入探索Python中OCR技术如何将这些数字纸张转... 目录一、OCR技术核心原理二、python图像识别四大工具库1. Pytesseract - 经典O