前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Javascript 将 HTML 页面生成 PDF 并下载

Javascript 将 HTML 页面生成 PDF 并下载

作者头像
IT派
发布于 2018-08-10 06:22:40
发布于 2018-08-10 06:22:40
3.3K00
代码可运行
举报
文章被收录于专栏:IT派IT派
运行总次数:0
代码可运行

最近碰到个需求,需要把当前页面生成 pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :)

项目源码地址:https://github.com/linwalker/render-html-to-pdf

html2canvas
简介

我们可以直接在浏览器端使用html2canvas,对整个或局部页面进行“截图”。但这并不是真的截图,而是通过遍历页面DOM结构,收集所有元素信息及相应样式,渲染出canvas image。

由于html2canvas只能将它能处理的生成canvas image,因此渲染出来的结果并不是100%与原来一致。但它不需要服务器参与,整个图片都由客户端浏览器生成,使用很方便。

使用

使用的API也很简洁,下面代码可以将某个元素渲染成canvas:

  1. html2canvas(element, {
  2. onrendered: function(canvas) {
  3. // canvas is the final renderedelement
  4. }
  5. });

通过onrendered方法,可以将生成的canvas进行回调,比如插入到页面中:

  1. html2canvas(element, {
  2. onrendered: function(canvas) {
  3. document.body.appendChild(canvas);
  4. }
  5. });

做个小例子(demo1)代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                                  one          ...                                    it is a title                      Stone Giant        Coming ...         以一团石头...              write by linwalker @2017    

这个例子将页面body中的元素渲染成canvas,并插入到body中。

nvas,并插入到body中。

jsPDF

jsPDF库可以用于浏览器端生成PDF。

文字生成PDF

使用方法如下:

  1. // 默认a4大小,竖直方向,mm单位的PDF
  2. var doc = new jsPDF();
  3. // 添加文本‘Download PDF’
  4. doc.text('Download PDF!', 10, 10);
  5. doc.save('a4.pdf');
图片生成PDF

使用方法如下:

  1. // 三个参数,第一个方向,第二个单位,第三个尺寸格式
  2. var doc = new jsPDF('landscape','pt',[205, 115])
  3. // 将图片转化为dataUrl
  4. var imageData = ‘data:image/png;base64,iVBORw0KGgo...’;
  5. doc.addImage(imageData, 'PNG', 0, 0, 205, 115);
  6. doc.save('a4.pdf');
文字与图片生成PDF
  1. // 三个参数,第一个方向,第二个尺寸,第三个尺寸格式
  2. var doc = new jsPDF('landscape','pt',[205, 155])
  3. // 将图片转化为dataUrl
  4. var imageData = ‘data:image/png;base64,iVBORw0KGgo...’;
  5. //设置字体大小
  6. doc.setFontSize(20);
  7. //10,20这两参数控制文字距离左边,与上边的距离
  8. doc.text('Stone', 10, 20);
  9. // 0, 40, 控制文字距离左边,与上边的距离
  10. doc.addImage(imageData, 'PNG', 0, 40, 205, 115);
  11. doc.save('a4.pdf')

生成pdf需要把转化的元素添加到jsPDF实例中,也有添加html的功能,但某些元素无法生成在pdf中,因此可以使用html2canvas + jsPDF的方式将页面转成pdf。通过html2canvas将遍历页面元素,并渲染生成canvas,然后将canvas图片格式添加到jsPDF实例,生成pdf。

html2canvas + jsPDF

单页

将demo1的例子修改下:

  1. <script type="text/javascript" src="./js/jsPdf.debug.js">script>
  2. <script type="text/javascript">
  3. var downPdf = document.getElementById("renderPdf");
  4. downPdf.onclick = function() {
  5. html2canvas(document.body, {
  6. onrendered:function(canvas) {
  7. //返回图片dataURL,参数:图片格式和清晰度(0-1)
  8. var pageData = canvas.toDataURL('image/jpeg', 1.0);
  9. //方向默认竖直,尺寸ponits,格式a4[595.28,841.89]
  10. var pdf = new jsPDF('', 'pt', 'a4');
  11. //addImage后两个参数控制添加图片的尺寸,此处将页面高度按照a4纸宽高比列进行压缩
  12. pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 592.28/canvas.width * canvas.height );
  13. pdf.save('stone.pdf');
  14. }
  15. })
  16. }
  17. script>

如果页面内容根据a4比例转化后高度超过a4纸高度呢,生成的pdf会怎么样?会分页吗?

你可以试试,验证一下自己的想法。

jsPDF提供了一个很有用的API, addPage(),我们可以通过 pdf.addPage(),来添加一页pdf,然后通过 pdf.addImage(...),将图片赋予这页pdf来显示。

那么我们如何确定哪里分页?

这个问题好回答,我们可以设置一个 pageHeight,超过这个高度的内容放入下一页pdf。

来捋一下思路,将html页面内容生成canvas图片,通过 addImage将第一页图片添加到pdf中,超过一页内容,通过 addPage()添加pdf页数,然后再通过 addImage将下一页图片添加到pdf中。

嗯~,很好!巴特,难道没有发现问题吗?

这个方法实现的前提是 — — 我们能根据 pageHeight先将整页内容生成的canvas图片分割成对应的小图片,然后一个萝卜一个坑,一页一页 addImage进去。

What? 想一想我们的canvas是肿么来的,不用拉上去,直接看下面:

  1. html2canvas(document.body, {
  2. onrendered:function(canvas) {
  3. //it is here we handle the canvas
  4. }
  5. })

这里的 body就是要生成canvas的元素对象,一个元素生成一个canvas;那么我们需要一页一页的canvas,也就是说。。。

你觉得可能吗? 我觉得不太现实,按这思路要获取页面上不同位置的DOM元素,然后通过 htnl2canvas(element,option)来处理,先不说能不能刚好在每个 pageHeight的位置刚好找到一个DOM元素,就算找到了,这样做累不累。

累的话 :)可以看看下面这种方法。

多页

我提供的思路是我们只生成一个canvas,对就一个,转化元素就是你要转成pdf内容的母元素,在这篇demo里就是 body了;其他不变,也是超过一页内容就 addPage,然后 addImage,只不过这里添加的是同一个canvas。

当然这样做只会出现多页重复的pdf,那到底怎么实现正确分页显示。其实主要利用了jsPDF的两点:

  • 超过jsPDF实例格式尺寸的内容不显示( varpdf=newjsPDF('','pt','a4');demo中就是a4纸的尺寸)
  • addImage有两个参数可以控制图片在pdf中的位置

虽然每一页pdf上显示的图片是相同的,但我们通过调整图片的位置,产生了分页的错觉。以第二页为例,将竖直方向上的偏移设置为 -841.89即一张a4纸的高度,又因为超过a4纸高度范围的图片不显示,所以第二页显示了图片竖直方向上[841.89,1682.78]范围内的内容,这就得到了分页的效果,以此类推。

还是看代码吧:

  1. html2canvas(document.body, {
  2. onrendered:function(canvas) {
  3. var contentWidth = canvas.width;
  4. var contentHeight = canvas.height;
  5. //一页pdf显示html页面生成的canvas高度;
  6. var pageHeight = contentWidth / 592.28 * 841.89;
  7. //未生成pdf的html页面高度
  8. var leftHeight = contentHeight;
  9. //页面偏移
  10. var position = 0;
  11. //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
  12. var imgWidth = 595.28;
  13. var imgHeight = 592.28/contentWidth * contentHeight;
  14. var pageData = canvas.toDataURL('image/jpeg', 1.0);
  15. var pdf = new jsPDF('', 'pt', 'a4');
  16. //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
  17. //当内容未超过pdf一页显示的范围,无需分页
  18. if (leftHeight < pageHeight) {
  19. pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight );
  20. } else {
  21. while(leftHeight > 0) {
  22. pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
  23. leftHeight -= pageHeight;
  24. position -= 841.89;
  25. //避免添加空白页
  26. if(leftHeight > 0) {
  27. pdf.addPage();
  28. }
  29. }
  30. }
  31. pdf.save('content.pdf');
  32. }
  33. })
两边留边距

修改imgWidth,并且在addImage时x方向参数设置你要的边距,具体代码如下:

  1. var imgWidth = 555.28;
  2. var imgHeight = 555.28/contentWidth * contentHeight;
  3. ...
  4. pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight );
  5. ...
  6. pdf.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight);
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT派 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
玩转Spring Cloud Alibaba
在开源领域,RPC框架非常多,可以说是一片红海,各种类型的框架,比如Spring Cloud、Dubbo和蚂蚁金服的Sofa RPC等等。 当然这里要说的就是Spring Cloud Alibaba,为什么要说它呢? 首先,Spring Cloud Alibaba并不是一款纯碎的RPC框架,它是一款微服务治理框架,也就是说无论是你想自研微服务框架还是直接使用开源的微服务框架,那么使用Spring Cloud Alibaba都可以做到开箱即用,并且也可以做到向Spring Boot和Spring Clou
博文视点Broadview
2023/04/04
3390
玩转Spring Cloud Alibaba
面渣逆袭:微服务三十三问,两万字图文详解!速收藏!
微服务(Microservices)是一种软件架构风格,将一个大型应用程序划分为一组小型、自治且松耦合的服务。每个微服务负责执行特定的业务功能,并通过轻量级通信机制(如HTTP)相互协作。每个微服务可以独立开发、部署和扩展,使得应用程序更加灵活、可伸缩和可维护。
三分恶
2023/09/16
1.6K0
面渣逆袭:微服务三十三问,两万字图文详解!速收藏!
Spring Cloud Alibaba + Dubbo 搭建一个微服务架构
在阅读这篇文章前,推荐一篇“好”文章:MediaCrawler 提取评论生词云:小红书实例-麦琳评论区
王二蛋
2024/11/27
2300
Spring Cloud Alibaba面试题
从配置中心角度来看,性能方面Nacos的读写性能最高,Apollo次之,Spring Cloud Config依赖Git场景不适合开放的大规模自动化运维API。
小熊学Java
2023/07/16
9000
Spring Cloud Alibaba面试题
聊聊最新微服务架构技术栈选型
目前微服务早已火遍大江南北,对于开发来说,我们时刻关注着技术的迭代更新,而项目采用什么技术栈选型落地是开发、产品都需要关注的事情,该篇文章主要分享一些目前普遍公司都在用的技术栈,快来分享一下你当前所在用的技术吧。
码猿技术专栏
2023/05/01
9930
聊聊最新微服务架构技术栈选型
SpringCloud Alibaba微服务解决方案
我们都知道,SpringCloud是微服务的一站式解决方案,是众多组件的集合,而因为SpringCloud中几乎所有的组件使用的都是Netflix公司的产品,其中大部分已经进入了停止更新或者维护阶段。我们需要一些别的组件来代替它们,基于此,SpringCloud Alibaba诞生了。 本篇文章我们通过几个具体的业务场景,将SpringCloud Aibaba技术栈融入其中,来感受一下它的便利与强大。
wangweijun
2022/01/10
5550
SpringCloud Alibaba微服务解决方案
SpringCloud Alibaba之 Seata 分布式事务
事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销。简单地说,事务提供一种“要么什么都不做,要么做全套”机制。
终码一生
2022/04/14
3390
SpringCloud Alibaba之 Seata 分布式事务
构建弹性可扩展的微服务架构:基于Spring Cloud Alibaba 的实践
随着互联网业务的不断发展,传统的单体应用逐渐无法满足日益复杂的业务需求和用户量的增长。微服务架构应运而生,它将应用拆分成一系列小型、自治的服务,使得应用的开发、测试、部署和扩展更加灵活高效。Spring Cloud Alibaba 是 Spring Cloud 与 Alibaba 开源的一系列微服务组件的集合,为构建弹性可扩展的微服务架构提供了强有力的支持。
海拥
2023/08/09
4180
技术硬实力,聊聊写Spring Cloud Alibaba实战派这本书的初衷
在写这本书之前,我先后在两家杭州的“独角兽”公司担任技术负责人,并推进公司核心业务的“中台化”改造。在落地业务中台和技术中台的过程中,督促并指导开发人员统一使用Spring Cloud Alibaba作为中台服务最底层的基础框架。为了快速推进业务服务Spring Cloud Alibaba化的进度,我冲在业务的第一线,收集和整理开发人员在使用Spring Cloud Alibaba过程中反馈的技术问题,并提供有效的技术解决方案,直至项目落地。
35岁程序员那些事
2022/09/23
4020
超全!我整理一波最常用的开源项目
发这篇文章的起因是看到知乎有个类似的问题,然后感觉高赞的回答不是很让人满意,获得这么高的点赞也是让我很迷。
cxuan
2020/09/29
1.8K0
超全!我整理一波最常用的开源项目
聊聊Spring Cloud Alibaba的架构思想
Spring Cloud Alibaba致力于提供微服务开发的一站式解决方案,它是Spring Cloud组件被植入Alibaba元素之后的产物。利用Spring Cloud Alibaba,可以快速搭建微服务架构并完成技术升级。中小企业如果需要快速落地业务中台和技术中台,并向数字化业务转型,那Spring Cloud Alibaba绝对是一个“神器”。
35岁程序员那些事
2022/09/23
6060
聊聊Spring Cloud Alibaba的架构思想
xxl-job分布式定时任务
参考:https://blog.csdn.net/huangjinjin520/article/details/106880276/
java后端指南
2021/11/11
4790
xxl-job分布式定时任务
Spring Cloud Alibaba系列学习文章二
使用Spring Boot可以大大的简化Spring应用的开发工作,在Spring Boot中无论官方组件还是框架都会提供各种start来方便开发者来依赖和集成。由于采用了依赖约定大于配置的思想,开发者可以做很少的配置工作就可以完成框架集成的工作,往往开发者只需要很少的代码量就可以实现以前大量配置文件才能做到的功能。
小马哥学JAVA
2022/11/17
5500
JAVA最流行的技术选型分类整理
Eureka(Netflix),Consul,Nacos,Etcd,Zookeeper
Java技术江湖
2019/09/24
2.7K0
JAVA最流行的技术选型分类整理
Spring Cloud Alibaba学习指南
自2018年Netflix公司宣布对核心组件Hystrix、Ribbon、zuul、Eureka等进入维护状态后,Spring Cloud 也随即宣布Spring Cloud Netflix项目进入维护模式。
王二蛋
2024/04/17
2770
Spring Cloud Alibaba学习指南
不用找了,这本书帮你完全搞定Spring Cloud Alibaba,你还犹豫什么?
本书聚焦于Spring Cloud Alibaba微服务架构实战,全面分析了基于Spring Cloud Alibaba的微服务架构全栈技术原理。
35岁程序员那些事
2022/09/23
9360
优秀的技术选型(摘选)
1. 优秀的技术选型(摘选) 1.1. 缓存 redis因为是单线程,不适合高耗时操作,对数据量比较大的缓存还是memcached比较合适 1.2. 分库分表 sharding-jdbc,驱动层,不需要额外机器 mycat,代理层,如果有运维团队,又舍得机器可以用这个 1.3. 数据同步 mysql在分库分表时,要做的一个重要操作,数据迁移 对mysql来说,canal是国内用的最多的方案,其次databus canal、maxwell等支持将要同步的数据写入mq,后续处理方便 ETL(抽取,清洗,转换),
老梁
2019/09/11
6730
Spring Cloud Alibaba技术栈(下)
启动 Seata Server 进入 bin 目录中, 在 window 下启动 seata-server.bat, 在 linux 下启动 seata-server.bat
时间静止不是简史
2020/10/10
9520
Spring Cloud Alibaba技术栈(下)
lagou 爪哇 3-5 spring cloud (下) 笔记
为了⽀撑⽇益增⻓的庞⼤业务量,我们会使⽤微服务架构设计我们的系统,使得 我们的系统不仅能够通过集群部署抵挡流量的冲击,⼜能根据业务进⾏灵活的扩展。那么,在微服务架构下,⼀次请求少则经过三四次服务调⽤完成,多则跨越⼏⼗ 个甚⾄是上百个服务节点。那么问题接踵⽽来:
acc8226
2022/05/17
6400
lagou 爪哇 3-5 spring cloud (下) 笔记
三分钟搞定 XXL-JOB 分布式任务调度平台
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/05/16
1.5K0
三分钟搞定 XXL-JOB 分布式任务调度平台
推荐阅读
相关推荐
玩转Spring Cloud Alibaba
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档