关于jspdf:使用Java的div从HTML生成pdf

Generate pdf from HTML in div using Javascript

我有以下html代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
    <body>
        <p>
don't print this to pdf
</p>
       
            <p>
<font size="3" color="red">print this to pdf</font>
</p>
       
    </body>
</html>

我要做的就是将div中ID为" pdf"的任何内容打印为pdf。 这必须使用JavaScript来完成。 然后应使用文件名" foobar.pdf"自动下载" pdf"文档

我一直在使用jspdf来执行此操作,但是它具有的唯一功能是仅接受字符串值的"文本"。 我想将HTML提交给jspdf,而不是文本。


jsPDF可以使用插件。为了使其能够打印HTML,您必须包括某些插件,因此必须执行以下操作:

  • 转到https://github.com/MrRio/jsPDF并下载最新版本。
  • 在您的项目中包括以下脚本:

    • jspdf.js
    • jspdf.plugin.from_html.js
    • jspdf.plugin.split_text_to_size.js
    • jspdf.plugin.standard_fonts_metrics.js
  • 如果要忽略某些元素,则必须用ID标记它们,然后可以在jsPDF的特殊元素处理程序中忽略该ID。因此,您的HTML应该如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!DOCTYPE html>
    <html>
      <body>
        <p id="ignorePDF">don't print this to pdf
    </p>
       
          <p>
    <font size="3" color="red">print this to pdf</font>
    </p>
       
      </body>
    </html>

    然后,使用以下JavaScript代码在弹出窗口中打开创建的PDF:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var doc = new jsPDF();          
    var elementHandler = {
      '#ignorePDF': function (element, renderer) {
        return true;
      }
    };
    var source = window.document.getElementsByTagName("body")[0];
    doc.fromHTML(
        source,
        15,
        15,
        {
          'width': 180,'elementHandlers': elementHandler
        });

    doc.output("dataurlnewwindow");

    对我而言,这创建了一个漂亮整洁的PDF,其中仅包含"将其打印为pdf"行。

    请注意,特殊元素处理程序仅处理当前版本中的ID,这在GitHub Issue中也有说明。它指出:

    Because the matching is done against every element in the node tree, my desire was to make it as fast as possible. In that case, it meant"Only element IDs are matched" The element IDs are still done in jQuery style"#id", but it does not mean that all jQuery selectors are supported.

    因此,用类选择器(如" .ignorePDF")替换" #ignorePDF"对我不起作用。相反,您将必须为每个元素添加相同的处理程序,您要忽略该元素,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    var elementHandler = {
      '#ignoreElement': function (element, renderer) {
        return true;
      },
      '#anotherIdToBeIgnored': function (element, renderer) {
        return true;
      }
    };

    从示例中还可以看出,可以选择诸如" a"或" li"之类的标签。不过,对于大多数用例来说,这可能是无限制的:

    We support special element handlers. Register them with jQuery-style
    ID selector for either ID or node name. ("#iAmID","div","span" etc.)
    There is no support for any other type of selectors (class, of
    compound) at this time.

    要添加的非常重要的一件事是您丢失了所有样式信息(CSS)。幸运的是,jsPDF能够很好地格式化h1,h2,h3等,足以满足我的目的。另外,它将仅打印文本节点内的文本,这意味着它将不打印textareas等的值。例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <body>
     
    <ul>

        <!-- This is printed as the element contains a textnode -->        
       
    <li>
    Print me!
    </li>

     
    </ul>

     
        <!-- This is not printed because jsPDF doesn't deal with the value attribute -->
        <input type="textarea" value="Please print me, too!">
     
    </body>


    这是简单的解决方案。这对我有用。您可以使用javascript打印概念并将其简单另存为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
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
       
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
        <script type="text/javascript">
            $("#btnPrint").live("click", function () {
                var divContents = $("#dvContainer").html();
                var printWindow = window.open('', '', 'height=400,width=800');
                printWindow.document.write('<html><head>DIV Contents');
                printWindow.document.write('</head><body >');
                printWindow.document.write(divContents);
                printWindow.document.write('</body></html>');
                printWindow.document.close();
                printWindow.print();
            });
       
    </head>
    <body>
        <form id="form1">
       
            This content needs to be printed.
       
        <input type="button" value="Print Div Contents" id="btnPrint" />
        </form>
    </body>
    </html>


    您可以使用autoPrint()并将输出设置为" dataurlnewwindow",如下所示:

    1
    2
    3
    4
    5
    6
    function printPDF() {
        var printDoc = new jsPDF();
        printDoc.fromHTML($('#pdf').get(0), 10, 10, {'width': 180});
        printDoc.autoPrint();
        printDoc.output("dataurlnewwindow"); // this opens a new popup,  after this the PDF opens the print window view but there are browser inconsistencies with how this is handled
    }


    如前所述,您应该使用jsPDF和html2canvas。我还在jsPDF问题中发现了一个函数,该函数可自动将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
    44
    45
    46
    47
    48
    49
    50
    51
    52
    function makePDF() {

        var quotes = document.getElementById('container-fluid');

        html2canvas(quotes, {
            onrendered: function(canvas) {

            //! MAKE YOUR PDF
            var pdf = new jsPDF('p', 'pt', 'letter');

            for (var i = 0; i <= quotes.clientHeight/980; i++) {
                //! This is all just html2canvas stuff
                var srcImg  = canvas;
                var sX      = 0;
                var sY      = 980*i; // start 980 pixels down for every new page
                var sWidth  = 900;
                var sHeight = 980;
                var dX      = 0;
                var dY      = 0;
                var dWidth  = 900;
                var dHeight = 980;

                window.onePageCanvas = document.createElement("canvas");
                onePageCanvas.setAttribute('width', 900);
                onePageCanvas.setAttribute('height', 980);
                var ctx = onePageCanvas.getContext('2d');
                // details on this usage of this function:
                // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Slicing
                ctx.drawImage(srcImg,sX,sY,sWidth,sHeight,dX,dY,dWidth,dHeight);

                // document.body.appendChild(canvas);
                var canvasDataURL = onePageCanvas.toDataURL("image/png", 1.0);

                var width         = onePageCanvas.width;
                var height        = onePageCanvas.clientHeight;

                //! If we're on anything other than the first page,
                // add another page
                if (i > 0) {
                    pdf.addPage(612, 791); //8.5" x 11" in pts (in*72)
                }
                //! now we declare that we're working on that page
                pdf.setPage(i+1);
                //! now we add content to that page!
                pdf.addImage(canvasDataURL, 'PNG', 20, 40, (width*.62), (height*.62));

            }
            //! after the for loop is finished running, we save the pdf.
            pdf.save('test.pdf');
        }
      });
    }


    如果您需要特定页面的可下载pdf文件,只需添加这样的按钮

    1
    <h4 onclick="window.print();"> Print </h4>

    使用window.print()打印所有页面而不仅仅是div


    一种方法是使用window.print()函数。不需要任何库

    优点

    1.无需外部库。

    2.我们也只能打印选定的身体部位。

    3.没有css冲突和js问题。

    4.核心html / js功能

    ---只需添加以下代码

    CSS到

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @media print {
            body * {
                visibility: hidden; // part to hide at the time of print
                -webkit-print-color-adjust: exact !important; // not necessary use        
                   if colors not visible
            }

            #printBtn {
                visibility: hidden !important; // To hide
            }

            #page-wrapper * {
                visibility: visible; // Print only required part
                text-align: left;
                -webkit-print-color-adjust: exact !important;
            }
        }

    JS代码-在btn上调用bewlow函数单击

    1
    2
    3
    $scope.printWindow = function () {
      window.print()
    }

    注意:在每个CSS对象中使用!important

    范例-

    1
    2
    3
    .legend  {
      background: #9DD2E2 !important;
    }


    我使用jspdf和html2canvas进行CSS渲染,并导出特定div的内容,因为这是我的代码

    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
    $(document).ready(function () {
        let btn=$('#c-oreder-preview');
        btn.text('download');
        btn.on('click',()=> {

            $('#c-invoice').modal('show');
            setTimeout(function () {
                html2canvas(document.querySelector("#c-print")).then(canvas => {
                    //$("#previewBeforeDownload").html(canvas);
                    var imgData = canvas.toDataURL("image/jpeg",1);
                    var pdf = new jsPDF("p","mm","a4");
                    var pageWidth = pdf.internal.pageSize.getWidth();
                    var pageHeight = pdf.internal.pageSize.getHeight();
                    var imageWidth = canvas.width;
                    var imageHeight = canvas.height;

                    var ratio = imageWidth/imageHeight >= pageWidth/pageHeight ? pageWidth/imageWidth : pageHeight/imageHeight;
                    //pdf = new jsPDF(this.state.orientation, undefined, format);
                    pdf.addImage(imgData, 'JPEG', 0, 0, imageWidth * ratio, imageHeight * ratio);
                    pdf.save("invoice.pdf");
                    //$("#previewBeforeDownload").hide();
                    $('#c-invoice').modal('hide');
                });
            },500);

            });
    });


    如果要导出表,可以查看Shield UI Grid小部件提供的此导出示例。

    通过扩展如下配置来完成:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    ...
    exportOptions: {
        proxy:"/filesaver/save",
        pdf: {
            fileName:"shieldui-export",
            author:"John Smith",
            dataSource: {
                data: gridData
            },
            readDataSource: true,
            header: {
                cells: [
                    { field:"id", title:"ID", width: 50 },
                    { field:"name", title:"Person Name", width: 100 },
                    { field:"company", title:"Company Name", width: 100 },
                    { field:"email", title:"Email Address" }
                ]
            }
        }
    }
    ...


    使用pdfMake.js和此要点。

    (我在这里找到了Gist以及指向html-to-pdfmake软件包的链接,但最终我暂时不使用它。)

    npm install pdfmake之后,将要点保存在htmlToPdf.js中,我这样使用它:

    1
    2
    3
    4
    5
    6
    7
    8
    const pdfMakeX = require('pdfmake/build/pdfmake.js');
    const pdfFontsX = require('pdfmake-unicode/dist/pdfmake-unicode.js');
    pdfMakeX.vfs = pdfFontsX.pdfMake.vfs;
    import * as pdfMake from 'pdfmake/build/pdfmake';
    import htmlToPdf from './htmlToPdf.js';

    var docDef = htmlToPdf(`Sample`);
    pdfMake.createPdf({content:docDef}).download('sample.pdf');

    备注:

    • 我的用例是从markdown文档(带有markdown-it)创建相关的html,然后生成pdf,然后上传其二进制内容(我可以通过pdfMakegetBuffer()函数获得),全部来自浏览器。对于这种类型的html来说,生成的pdf比我尝试过的其他解决方案更好。
    • 我对从jsPDF.fromHTML()中得到的结果感到不满意,因为该解决方案容易被HTML中的特殊字符混淆,这些字符显然被解释为一种标记,并且完全弄乱了生成的PDF。
    • 对我来说,使用基于画布的解决方案(例如已弃用的jsPDF.from_html()函数,不要与接受的答案混淆),因为我希望生成的PDF中的文本可粘贴,而基于画布的解决方案会生成位图基于PDF。
    • 将md到pdf之类的pdf转换器直接降价仅是服务器端的,对我而言不起作用。
    • 使用浏览器的打印功能对我不起作用,因为我不想显示生成的PDF,而是上载其二进制内容。


    • 无依赖,纯JS

    这为我服务了多年:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    export default function printDiv({divId, title}) {
      let mywindow = window.open('', 'PRINT', 'height=650,width=900,top=100,left=150');

      mywindow.document.write(`<html><head>${title}`);
      mywindow.document.write('</head><body >');
      mywindow.document.write(document.getElementById(divId).innerHTML);
      mywindow.document.write('</body></html>');

      mywindow.document.close(); // necessary for IE >= 10
      mywindow.focus(); // necessary for IE >= 10*/

      mywindow.print();
      mywindow.close();

      return true;
    }


    我能够获得jsPDF来从div打印动态创建的表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $(document).ready(function() {

            $("#pdfDiv").click(function() {

        var pdf = new jsPDF('p','pt','letter');
        var specialElementHandlers = {
        '#rentalListCan': function (element, renderer) {
            return true;
            }
        };

        pdf.addHTML($('#rentalListCan').first(), function() {
            pdf.save("caravan.pdf");
        });
        });
    });

    可以与Chrome和Firefox完美兼容...格式在IE中都已损坏。

    我还包括以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <script src="js/jspdf.js">
        <script src="js/jspdf.plugin.from_html.js">
        <script src="js/jspdf.plugin.addhtml.js">
        <script src="//mrrio.github.io/jsPDF/dist/jspdf.debug.js">
        <script src="http://html2canvas.hertzen.com/build/html2canvas.js">
        <script type="text/javascript" src="./libs/FileSaver.js/FileSaver.js">
        <script type="text/javascript" src="./libs/Blob.js/Blob.js">
        <script type="text/javascript" src="./libs/deflate.js">
        <script type="text/javascript" src="./libs/adler32cs.js/adler32cs.js">

        <script type="text/javascript" src="js/jspdf.plugin.addimage.js">
        <script type="text/javascript" src="js/jspdf.plugin.sillysvgrenderer.js">
        <script type="text/javascript" src="js/jspdf.plugin.split_text_to_size.js">
        <script type="text/javascript" src="js/jspdf.plugin.standard_fonts_metrics.js">