• 用js生成PDF的方案


    在java里,我们常用Itext来生成pdf,在pdf文件里组合图片,文字,画表格,画线等操作,还会遇到中文支持的问题。

    那好,现在想直接在web前端就生成pdf怎么办,目前有以下几个解决方案

    1:JSPDF.js

    这个库支持不同类型的PDF文件格式,包括:文本,数字,图形,图片,同时你可以自由的编辑标题或者其它类型元素。

    还支持互动的内容制作,例如,你可以输入文字或者数字,然后jsPDF帮助生成最后的PDF内容。

    缺点,不支持中文,不支持svg导入(解决方案:svg+html转换成canvas)

    http://jspdf.com/

    示例代码如下:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> 
    <head> 
    <title>Downloadify</title> 
    <meta http-equiv="content-type" content="text/html;charset=utf-8"> 
    <style type="text/css" media="screen"> 
    body {background: #fff; 500px; margin: 20px auto;} 
    input, textarea, p { font-family: 宋体, 黑体; font-size: 12pt;} 
    input, textarea { border: solid 1px #aaa; padding: 4px; 98%;} 
    </style> 
    <script type="text/javascript" src="js/swfobject.js"></script> 
    <script type="text/javascript" src="js/downloadify.js"></script> 
    <script type="text/javascript" src="js/jspdf.js"></script> 
    <!-- <script type="text/javascript" src="js/downloadify.min.js"></script> --> 
    <script type="text/javascript"> 
    window.load=function(){ 
    Downloadify.create('downloadify',{ 
    filename: function(){ 
    return document.getElementById('filename').value; 
    }, 
    data: function(){ 
    var doc = new jsPDF(); 
    doc.text(20, 20, document.getElementById('data').value); 
    doc.addPage(); 
    doc.text(20, 20, document.getElementById('data').value); 
    return doc.output(); 
    }, 
    onComplete: function(){ alert('成功保存文件!'); }, 
    onCancel: function(){ alert('您已经取消保存文件'); }, 
    onError: function(){ alert('出现错误了'); }, 
    swf: 'js/downloadify.swf', 
    downloadImage: 'js/download.png', 
    100, 
    height: 30, 
    transparent: true, 
    append: false 
    }); 

    </script> 
    </head> 
    <body onload="load();"> 
    <input type="text" name="filename" value="文件名.pdf" id="filename" /><br /> 
    <textarea cols="60" rows="10" name="data" id="data">it seem do not support to Chinese</textarea> 
    <p id="downloadify">You must have Flash 10 installed to download this file.</p> 
    </body> 
    </html>

    2:pdfmake

    pdfmake的基本使用方法

    1.包含以下两个文件

    1
    2
    <script src="build/pdfmake.min.js"></script>
     <script src="build/vfs_fonts.js"></script>

    2.在JS代码中声明一个Document-definition对象,这个是pdfmake自己的术语。简单点说,就是创建一个至少包含content属性的对象。然后就可以调用pdfMake的方法导出PDF,具体见如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <script type="text/javascript">
     //创建Document-definition对象 
     var dd = {
          content: [
           'One paragraph',
           'Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines'
           ]
        };
     //导出PDF
     pdfMake.createPdf(dd).download();
     </script>

    pdfmake支持中文

    三个步骤:

    1.到pdfmake项目网站,把工程都下载下来,然后进入工程目录将字体文件(比如微软雅黑.ttf)放到examples/fonts目录下,然后使用grunt dump_dir生成新的vfs_fonts.js文件;

    从上面描述可知该工程是通过grunt管理的,如果无相关知识,请上网先补习下。

    grunt dump_dir命令会将fonts目录下所有文件都打包,因此无用文件请别放进去。

    微软雅黑.ttf网上一搜一大把,WINDOWS电脑系统盘下存放字体的目录也找得到。

    2.回到自己的例子代码中,JS代码中修改pdfMake的fonts对象,声明当前要用到字体:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    pdfMake.fonts = {
         Roboto: {
           normal: 'Roboto-Regular.ttf',
           bold: 'Roboto-Medium.ttf',
           italics: 'Roboto-Italic.ttf',
           bolditalics: 'Roboto-Italic.ttf'
         },
         微软雅黑: {
           normal: '微软雅黑.ttf',
           bold: '微软雅黑.ttf',
           italics: '微软雅黑.ttf',
           bolditalics: '微软雅黑.ttf',
         }
       };

    3.Document-definition对象中声明默认要使用的字体,具体来说:就是声明一个对象,除了content属性,还要有一个defaultStyle属性,该defaultStyle下面还有再有一个font属性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var dd = {
          content: [
           '中英文测试',
           'Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines'
           ],
           defaultStyle: {
             font: '微软雅黑'
           }
        };

    以下为根据如上步骤做的一个完整例子源码:

    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
      <meta charset="utf-8">
      <title>my first export PDF</title>
      <script src="build/pdfmake.min.js"></script>
      <script src="build/vfs_fonts.js"></script>
      <script>
      function down() {
        var dd = {
          content: [
           '中英文测试',
           'Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines'
           ],
           defaultStyle: {
             font: '微软雅黑'
           }
        };
        pdfMake.fonts = {
          Roboto: {
            normal: 'Roboto-Regular.ttf',
            bold: 'Roboto-Medium.ttf',
            italics: 'Roboto-Italic.ttf',
            bolditalics: 'Roboto-Italic.ttf'
          },
          微软雅黑: {
            normal: '微软雅黑.ttf',
            bold: '微软雅黑.ttf',
            italics: '微软雅黑.ttf',
            bolditalics: '微软雅黑.ttf',
          }
        };
        pdfMake.createPdf(dd).download();
      }
      </script>
      </head>
      <body>
      <button onclick="down()">下载</button>
      </body>
    </html>

    插入图片

    在插入图片方面,jsPDF要求先将图片转换成Data URL才行,而pdfmake允许直接指定路径,看起来是很方便,但这是有条件的,必须是以node.js作为服务器,或者将图片放到vfs_fonts.js中,所以总的来说,用处不大,还是一样得将图片转换成Data URL形式才行。

    为解决此问题,我写了一个ImageDataURL的函数对象,可同时传入多个图片地址。在图片都加载完成后,ImageDataURL.oncomplete将被触发,在回调中通过this.imgdata取出各个图片的Data URL,根据pdfmake的要求组织下,就可正确生成pdf了。

    ImageDataURL的原理是通过H5的canvas标签,将图片绘制在canvas上,然后通过canvas的toDataURL得到图像的Data URL。使用时请注意浏览器兼容性问题。

    以下为将sampleImage.jpg, sampleage.jpg, sampleImage.jpg依次写入PDF的例子,测试时sampleage.jpg不存在,PDF直接忽略。

    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
      <meta charset="utf-8">
      <title>my second export PDF</title>
      <script src="build/pdfmake.min.js"></script>
      <script src="build/vfs_fonts.js"></script>
      <script>
        
      function down() {
        var x = new ImageDataURL(["sampleImage.jpg""samplage.jpg""sampleImage.jpg"]);
        x.oncomplete = function() {
          var imgs = new Array();
          console.log("complete");
          for (key in this.imgdata) {
            if (this.imgdata[key] == this.emptyobj)//不存在的图片直接忽略
              continue;
            imgs.push({image:this.imgdata[key]});//pdfmake的图片格式{image:image dataurl}
          }
          var dd = {
            content: [
             'Title',
             imgs,
             ],
          };
          pdfMake.createPdf(dd).download();
        }
      }
      </script>
      </head>
      <body>
      <button onclick="down()">下载</button>
      <script>
      function ImageDataURL(urls) {//urls必须是字符串或字符串数组
        this.completenum = 0;
        this.totalnum = 0;
        this.imgdata = new Array();
        this.emptyobj = new Object();
        this.oncomplete = function(){};
        this.getDataURL = function(url, index) {
          var c = document.createElement("canvas");
          var cxt = c.getContext("2d");
          var img = new Image();
          var dataurl;
          var p;
          p = this;
          img.src = url;
          img.onload = function() {
            var i;
            var maxwidth = 500;
            var scale = 1.0;
            if (img.width > maxwidth) {
              scale = maxwidth / img.width;
              c.width = maxwidth;
              c.height = Math.floor(img.height * scale);
            else {
              c.width= img.width;
              c.height= img.height;
            }
            cxt.drawImage(img, 0, 0, c.width, c.height);
      
            p.imgdata[index] = c.toDataURL('image/jpeg');
            for (i = 0; i < p.totalnum; ++i) {
              if (p.imgdata[i] == null)
                break;
            }
            if (i == p.totalnum) {
              p.oncomplete();
            }
          };
          img.onerror = function() {
            p.imgdata[index] = p.emptyobj;
            for (i = 0; i < p.totalnum; ++i) {
              if (p.imgdata[i] == null)
                break;
            }
            if (i == p.totalnum) {
              p.oncomplete();
            }
          };
        }
        if (urls instanceof Array) {
          this.totalnum = urls.length; 
          this.imgdata = new Array(this.totalnum);
          for (key in urls) {
            this.getDataURL(urls[key], key);
          }
        else {
          this.imgdata = new Array(1);
          this.totalnum = 1;
          this.getDataURL(urls, 0);
        }
      }
      
      </script>
      </body>
    </html>
     
     
     
     
     

    引入依赖脚本

    原理上需要先把需要保存的节点渲染到一个canvas,然后利用这个canvas制作pdf,所以先引入以下两个依赖代码文件:

    <script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.js"></script>
    <script src="https://cdn.bootcss.com/jspdf/1.3.4/jspdf.debug.js"></script>
    • 1
    • 2

    编写所需代码

    假设所需要打印的dom的父容器的class名是right-aside,就可以写这样一个函数:

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

    以上代码将会以a4纸的尺寸来生成pdf文件,可以分页,代码来自于网络!

    坑点提示

    jsPDF打印成pdf文件时,注意页面要回到打印区域的顶部,我在该demo设置了先回到页面顶部,再打印,这样就不会出现黑块了。

    jsPDF打印成pdf文件时,注意设置打印区域dom的背景色为白色,即#FFFFFF

  • 相关阅读:
    uwsgi配置详解
    一文搞定SonarQube接入C#(.NET)代码质量分析
    推荐一款 Diffy:Twitter 的开源自动化测试工具
    推荐一款简单易用线上引流测试工具:GoReplay
    kanboard安装及使用
    性能相关博客
    性能工具之JMeter5.0核心类StandardJMeterEngine源码分析
    使用sshkey连接github等服务器
    [转]关于浏览器css选择器性能优化
    [转]你不淘汰自己,就会被别人淘汰
  • 原文地址:https://www.cnblogs.com/Alex80/p/9105408.html
Copyright © 2020-2023  润新知