前端生成PDF文件实现方案

一、技术选型

1.html转换成canvas后生成图片导出pdf(本文选用)

  • html转canvas插件:html2canvas是一款将HTML代码转换成Canvas的插件;
  • canvas生成pdf:jsPDF是一个使用Javascript语言生成PDF的开源库

2.HTML代码转出pdf

wkhtmltopdf是一款将HTML代码转换成pdf的插件,表格场景使用居多

二、技术实现(基于html2canvas和jsPDF实现)

1.安装插件

npm i html2canvas -S
npm i jspdf -S

2.注册及实现封装

以下封装的代码可以直接引用到项目文件中使用,需要注意以下几点:

  1. 由于需要固定导出pdf图片大小,所以需要给导出的dom上添加.pdf-screen样式类用于查找导出元素,然后更改导出元素样式;
  2. 导出页面尺寸固定为a4大小;
/**
 * @file 导出pdf文件
 */
import html2canvas from 'html2canvas';
import JsPDF from 'jspdf';

/* eslint-disable */

const PDF = {};

// a4
let a4Width = 595.28; 
let a4Height = 841.89;

let defaultOptions = {
    name: new Date().getTime(),
    scale: window.devicePixelRatio * 2,
    padding: 0,
    width: 595.28 * 2,
    allowTaint: true,
    onclone: function (dom) {
        let screen = dom.querySelector('.pdf-screen');
        screen.style.width = 595.28 * 2 + 'px';
        screen.style.padding = '10px';
    }
}

PDF.install = function (Vue, rootOptions = {}) {
    Vue.prototype.$pdf = function (dom, options = rootOptions) {
        
        options = Object.assign(defaultOptions, options);
        
        html2canvas(dom, options).then(canvas => {
            let position = 0;

            // 生成的画布元素宽高(需要收缩回原比例大小)
            let canvasWidth = canvas.width / options.scale;
            let canvasHeight = canvas.height / options.scale;

            // 页面等比例缩放后宽高
            let pageWidth = a4Width;
            let pageHeight = (a4Width / canvasWidth) * canvasHeight;
            
            //返回图片dataURL,参数:图片格式和清晰度(0-1)
            let jpeg = canvas.toDataURL('image/jpeg', 1.0);

            //方向默认竖直,尺寸ponits,格式a4[595.28,841.89]
            let doc = new JsPDF('', 'pt', 'a4');
            
            //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
            //当内容未超过pdf一页显示的范围,无需分页
            if (canvasHeight < pageHeight) {
                doc.addImage(jpeg, 'JPEG', 0, 0, pageWidth, pageHeight); // 从图片顶部开始打印
            } else {
                while (canvasHeight > 0) {
                    doc.addImage(jpeg, 'JPEG', 0, position, pageWidth, pageHeight);
                    canvasHeight -= pageHeight;
                    position -= a4Height;

                    //避免添加空白页
                    if (canvasHeight > 0) {
                        doc.addPage();
                    }
                }
            }
            doc.save(options.name + '.pdf');
        });
    };
};

export default PDF;

3.使用方式

// 在 main.js 中导入插件
import pdf from "./plugins/pdf";

// 注册插件
Vue.use(PDF);

// 在需要导出pdf的函数中调用
// dom是需要导出的最外层元素
this.$pdf(dom, options);

// 使用案例(该配置可参考HTML2CANVAS相关配置)
this.$pdf(this.$refs.screen, {
    name: 'filename',   // 导出文件名
    scale: 2,   // 导出文件清晰度,值越大清晰度越高,文件体积越大(默认值为设备dpr*2)
    ignoreElements: (element) => {  // 忽略渲染元素(通过查询dom元素,不局限于类名查询)
        if (element.className.indexOf('className') !== -1) {
            return true;
        }
    }
});

三、遇到问题及解决方案

1. pdf内容截断问题 (待解决)

问题描述:由于实现原理是将html转换成canvas后生成图片的形式导出pdf文件,会导致canvas生成的长图在分页保存的过程中出现内容被截断的情况;

2. PDF导出文件分辨率问题(已解决)

问题描述:canvas生成的图片分辨率过低;

解决方案:可对dom元素按比例进行缩放后,再生成图片并导出到pdf文件中;

四、待优化点

1.导出时附带水印效果

五、参考资料

  • wkhtmltopdf官方文档
  • htmp2canvas官方文档
  • jsPDF官方文档

你可能感兴趣的:(前端技术实现方案,javascript)