首页 > 其他分享 >使用 html2Canvas 与 JsPDF 生成海报和pdf文件下载等业务,解决pdf分页样式问题

使用 html2Canvas 与 JsPDF 生成海报和pdf文件下载等业务,解决pdf分页样式问题

时间:2023-02-16 21:23:35浏览次数:42  
标签:JsPDF canvas base64 生成 html2Canvas let var pdf

需求场景

  1. 吧页面元素内容转成图片,生成一个海报 保存或者上传服务器
  2. 当前元素内容生成一个pdf文件 完成下载到客户端,并且上传到服务器

1.开发前的准备

// 元素转图片
npm install --save html2canvas
// 图片转pdf文件
npm install jspdf

废话不多说直接上全部核心代码 记得看注释

2.getFile.js

import JsPDF from 'jspdf'
import html2Canvas from "html2canvas"

// 解决了 pdf分页上一页与下一页内容错开样式不好看问题 
// ele:需要截图的元素节点对象 document.querySelector('#pdfDom')
// pdfFileName:下载的文件名称
const PdfLoader = (ele, pdfFileName) => {
    ele.style.fontFamily = '宋体';
    ele.style.padding = '0 20px';
    // 预留一定的时间给dom页面渲染完成 (如果你能保证dom已经渲染完成了包括图片 才去进行下面转化操作那就可以不用这个延迟器)
    setTimeout(() => {
        html2Canvas(ele, {
            // dpi: 300, // 清晰度
            scale: 1, // 将Canvas放大倍数 可以获得更具清晰的图片内容
            // !!!注意如果你生成的元素内容非常多是一个非常长列表 建议scale不要写太高或者删除这个属性 ,因为html2Canvas会吧内容转成
            // base64 会有一定的内容上限 最终返回没有base64编码(目前我尝试过生成55页的PDF,估计上限在70-100页)
            useCORS: true, //是否允许跨域
            allowTaint: false,
            height: ele.offsetHeight,
            width: ele.offsetWidth,
            windowWidth: document.body.scrollWidth,
            windowHeight: document.body.scrollHeight,
        }).then(canvas => {


            //未生成pdf的html页面高度
            var leftHeight = canvas.height;
            var a4Width = 595.28
            var a4Height = 841.89
            //一页pdf显示html页面生成的canvas高度;
            var a4HeightRef = Math.floor(canvas.width / a4Width * a4Height);
            //pdf页面偏移
            var position = 0;
            var pageData = canvas.toDataURL('image/jpeg', 1.0); // 生成的base64 如果你只是要图片 到这里就可以拿到base64图片编码(可以查一下base64转二进制 使用new FormData对象传给后端到服务器)
            
            
            var pdf = new JsPDF('x', 'pt', 'a4'); //生成A4内容大小的pdf每页 更多参数配置可以看看下面的网站
            // https://blog.csdn.net/weixin_42333548/article/details/107630706
            var index = 1,
                canvas1 = document.createElement('canvas'),
                height;
            pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen');
            // 处理 pdf 上一页 与 下一页内容之间交叉不好看的断点样式
            // 并且把内容转成二进制 生成pdf文件
            function createImpl(canvas) {
                if (leftHeight > 0) {
                    index++;
                    var checkCount = 0;
                    if (leftHeight > a4HeightRef) {
                        var i = position + a4HeightRef;
                        for (i = position + a4HeightRef; i >= position; i--) {
                            var isWrite = true
                            for (var j = 0; j < canvas.width; j++) {
                                var c = canvas.getContext('2d').getImageData(j, i, 1, 1).data
                                if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) {
                                    isWrite = false
                                    break
                                }
                            }
                            if (isWrite) {
                                checkCount++
                                if (checkCount >= 10) {
                                    break
                                }
                            } else {
                                checkCount = 0
                            }
                        }
                        height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef);
                        if (height <= 0) {
                            height = a4HeightRef;
                        }
                    } else {
                        height = leftHeight;
                    }

                    canvas1.width = canvas.width;
                    canvas1.height = height;
                    var ctx = canvas1.getContext('2d');
                    ctx.drawImage(canvas, 0, position, canvas.width, height, 0, 0, canvas
                        .width, height);


                    if (position != 0) {
                        pdf.addPage();
                    }
                    pdf.addImage(canvas1.toDataURL('image/jpeg', 1.0), 'JPEG', 0, 0, a4Width,
                        a4Width /
                        canvas1.width * height);
                    leftHeight -= height;
                    position += height;
                    if (leftHeight > 0) {
                        //给pdf文件 添加全屏水印
                        // const base64 = '';  // 吧你要添加的水印内容搞成一张小图片 然后手动去转成base64编码 放在这里就可以了
                        // for (let i = 0; i < 6; i++) {
                        //     for (let j = 0; j < 5; j++) {
                        //         const left = (j * 120) + 20;
                        //         pdf.addImage(base64, 'JPEG', left, i * 150, 20, 30);
                        //     };
                        // };
                        pdf.addImage(pageData, 'JPEG', 0, i * 150, 20, 30);
                        setTimeout(createImpl, 500, canvas);
                    } else {
                        pdfSave()
                    }
                }
            }

            //当内容未超过pdf一页显示的范围,无需分页
            if (leftHeight < a4HeightRef) {
                pdf.addImage(pageData, 'JPEG', 0, 0, a4Width, a4Width / canvas.width * leftHeight);

                pdfSave()
            } else {
                try {
                    pdf.deletePage(0);
                    setTimeout(createImpl, 500, canvas);
                } catch (err) {
                    console.log(err);
                }
            }

            function pdfSave() {
                // pdf文件生成完毕 自动下载到客户本地
                pdf.save(pdfFileName + '.pdf')
                setTimeout(() => {
                    // 吧pdf文件上传到服务器
                    let base64 = pdf.output("datauristring");
                    let file = convertBase64ToFile(base64, "报告");
                    let formData = new FormData();
                    formData.append("file", file);
                    // 直接post 接口吧formData对象传给后端接口去让后端去获取就可以了
                }, 1000)
            };
        })
    }, 500)


}

const convertBase64ToFile = (urlData, filename) => {
    var arr = urlData.split('base64,');
    var type = arr[0].match(/:(.*?);/)[1];
    var fileExt = type.split('/')[1];
    var bstr = atob(arr[1]);
    var n = bstr.length;
    var u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename + "." + fileExt, {
        type: type
    });
};


export default PdfLoader;

 

3.页面使用

import PdfLoader from './pdf.js';

PdfLoader(document.getElementById('pdfDom'),'测试文件')

注意:如果你要生成的元素内容有图片 一定要把图片转成base64编码 在页面上去展示不然 在生成pdf或者canvas的时候会跨域,

  如果你们的图片地址在腾讯云或者阿里上这种第三方 然后在使用地址转base64遇到了跨域问题 去找后端解决 让他们去配置白名单关闭跨域,(你可以尝试使用百度图库网络地址转base64不会遇到跨域问题)。

// 图片地址转base64
// url:https:xxxxx
const getBase64Image = function(url) {
    function newImg(url) {
        return new Promise((resolve, reject) => {
            let image = new Image();
            image.crossOrigin = 'Anonymous';
            image.src = url;
            image.onload = function() {
                resolve(image);
            };
        });
    };
    return newImg(url).then((img) => {
        let canvas = document.createElement("canvas");
        canvas.width = img.width;
        canvas.height = img.height;
        let ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0, img.width, img.height);
        let ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
        let dataURL = canvas.toDataURL("image/" + ext);
        return dataURL;
    });

}

 

需求建议:如果你是通过条件查询后端返回数据 来动态生成这个元素 又不想让用户看到你渲染元素的过程,可以像我一样做个一个全局的遮罩弹窗并且展示下载进度 可以有一个更好的交互体验 执行完成后全部统一关闭。

 

 

最终给大家看看生成的pdf文件打开的结果。

 

标签:JsPDF,canvas,base64,生成,html2Canvas,let,var,pdf
From: https://www.cnblogs.com/Allen-project/p/17128341.html

相关文章

  • Pandoc 将 markdown 文件转化为 pdf 命令
    之前查阅其它博客发现命令会报错,经尝试,现在使用Pandoc将markdown文件转化为pdf文件的命令如下:pandoc-s--toc--pdf-engine=xelatex-VCJKmainfont="SimSun"-V......
  • vue js 预览word, Excel, TXT,PDF 图片等。
    npm安装插件npminstallxlsx--savenpminstalldocx-preview--save  npminstallaxios template部分//这里封装成一个组件<template><divclass=""......
  • 微信小程序:打开pdf
    wxml<blockwx:for="{{list}}"wx:key="id"wx:for-item="item"wx:for-index="index"><viewclass="list_item"bi......
  • C# JPG转PDF
    需先引用itextsharp.dll//方法一///<summary>///JPG转PDF///</summary>///<paramname="jpgfile">图片路径</param>......
  • openoffice 文件转化为pdf
    /**转换组件属性设置*/functionOpenOfficeMakePropertyValue($name,$value,$osm){$oStruct=$osm->Bridge_GetStruct("com.sun.star.beans.PropertyValu......
  • 机器学习 入门 书籍 电子书 pdf
    机器学习入门书籍推荐(机器学习、机器学习实战、深度学习、神经网络与深度学习等),关注公众号:后厂村搬砖工。发送:算法工程师即可 ......
  • 前端导出pdf字体表格被截断问题解决
    最近有个导出pdf的需求,写好之后分页出现字体,表格被截断的问题,影响美观,需要解决。  经过多方查找,发现一个比较好的思路,设置背景色为白色,然后转成图片后,获取截断处图片......
  • 将Markdown文件转换成PDF文件
    1.下载并安装1.1vscode  首先需要说明的是vscode分为System和User两种版本。对于完成该任务来说,并没有太大区别。这里提供一个windows64位的User版本的下载地址:https:......
  • reportlab 生成pdf
    使用fromreportlab.lib.stylesimportgetSampleStyleSheet产生基础格式。如何修改需要的格式:styles=getSampleStyleSheet()style.normal=copy.deepcopy(styles['N......
  • 移动端重排版PDF阅读器比较
    PDF是一种跨操作系统平台的电子文件格式,它能在各种不同的平台上以相同的版式显示。很多扫描书籍或者电子书籍都会采用PDF格式存储。但是移动端由于屏幕的限制,以原版展示PD......