使用JavaScript在div中从HTML生成pdf


232

我有以下html代码:

<!DOCTYPE html>
<html>
    <body>
        <p>don't print this to pdf</p>
        <div id="pdf">
            <p><font size="3" color="red">print this to pdf</font></p>
        </div>
    </body>
</html>

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

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


1
正如上面提到的我希望使用“文本”功能。我想给它HTML。您的链接仅处理纯文本,而不处理html
John Crawford

2
jsPDF 确实具有fromHTML功能;请参见以下网址
mg1075 2014年

Answers:


228

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

  1. 转到https://github.com/MrRio/jsPDF并下载最新版本。
  2. 在您的项目中包括以下脚本:
    • jspdf.js
    • jspdf.plugin.from_html.js
    • jspdf.plugin.split_text_to_size.js
    • jspdf.plugin.standard_fonts_metrics.js

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

<!DOCTYPE html>
<html>
  <body>
    <p id="ignorePDF">don't print this to pdf</p>
    <div>
      <p><font size="3" color="red">print this to pdf</font></p>
    </div>
  </body>
</html>

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

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中也有说明。它指出:

因为匹配是针对节点树中的每个元素完成的,所以我的愿望是使其尽可能快。在那种情况下,这意味着“仅元素ID匹配”。元素ID仍以jQuery样式“ #id”完成,但这并不意味着支持所有jQuery选择器。

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

var elementHandler = {
  '#ignoreElement': function (element, renderer) {
    return true;
  },
  '#anotherIdToBeIgnored': function (element, renderer) {
    return true;
  }
};

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

我们支持特殊的元素处理程序。使用jQuery样式的ID选择器为ID或节点名注册它们。(“ #iAmID”,“ div”,“ span”等。)目前不支持任何其他类型的选择器(类的复合)。

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

<body>
  <ul>
    <!-- This is printed as the element contains a textnode -->        
    <li>Print me!</li>
  </ul>
  <div>
    <!-- This is not printed because jsPDF doesn't deal with the value attribute -->
    <input type="textarea" value="Please print me, too!">
  </div>
</body>

3
我猜元素处理程序可以是一个类吗?从语义上讲,这将更符合HTML5标准。ID不仅在CSS中具有过多的特殊性,而且还必须是唯一的。
势在必行的2015年

不,据我所知,如它们的示例所述,类目前不是有效的选择器:“我们支持特殊的元素处理程序。请使用jQuery样式的ID选择器为ID或节点名称注册它们。(“#iAmID”, “”(“ div”,“ span”等)。目前不支持任何其他类型的选择器(类的复合)。”。不过,如果标签名称与您的用例相符并且没有在页面上隐藏其他重要元素,则可以使用标签名称。
snrlx

他们在其页面parall.ax/products/jspdf上声明通过垫片支持IE6 +。我自己还没有尝试过。
snrlx

2
@snrlx我得到空白的pdf并出现此错误:renderer.pdf.sHashCode不是函数
Lisa Solomon

5
“如果要忽略某些元素,则必须用ID标记它们”-一个很棒的库,被倒置的需求毁坏了。OP想要打印一个<div>,可能是数百个之一-因此他必须标记所有不需要的 DOM元素?
Mawg说恢复莫妮卡的时间

53

这是简单的解决方案。这对我有用。您可以使用javascript打印概念并将其简单另存为pdf。

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <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><title>DIV Contents</title>');
            printWindow.document.write('</head><body >');
            printWindow.document.write(divContents);
            printWindow.document.write('</body></html>');
            printWindow.document.close();
            printWindow.print();
        });
    </script>
</head>
<body>
    <form id="form1">
    <div id="dvContainer">
        This content needs to be printed.
    </div>
    <input type="button" value="Print Div Contents" id="btnPrint" />
    </form>
</body>
</html>

27
“并且简单地将其另存为pdf”-我错过了这一部分。你怎么做呢?
Mawg说要恢复Monica's

9
这对我有用,为解决CSS样式问题,我创建了另一个名为printPDF.css的css文件,并使用链接标记添加了该文件,就像上面的示例中一样:printWindow.document.write('<html> <head> <title> DIV内容</ title>');printWindow.document.write('<link rel =“ stylesheet” href =“ ../ css / printPDF.css” />');; printWindow.document.write('</ head> <body>');
Prime_Coder

2
一些评论:1)您不需要特定的样式表即可进行打印。在您当前的样式表中,执行以下操作:@media print {.pageBreak {page-break-before:始终;} .labelPdf {font-weight:bold; font-size:20px; } .noPrintPdf {display:none; 并根据需要使用这些类。2).live(“ click”,...)对我不起作用,因此我改用.on(“ click”,...)。
Davidson Lima

1
@DavidsonLima此代码创建一个新窗口,该窗口将看不到旧窗口的CSS。这就是为什么它“忽略” css,而不忽略,只是在新窗口中没有加载它。只需将其加载到头部,它将被渲染。
卢卡斯·里西斯

1
万一有人试图使用Angular做到这一点,请将CSS文件放在资产文件夹中。
rgantla

16

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

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
}

1
我很好奇,这对OP以外的人有帮助吗?通过阅读代码,我似乎了解到它仅适用于具有ID的元素。无论如何,我可能不知道如何进行这项工作,这可能比这还要复杂。
迈克尔

14
根据我的观察,非常讽刺的是,fromHTML仅在作为参数发送的元素不包含任何HTML时才起作用:仅支持纯文本。Kinda杀死了整个imo的目的。
Michael

为我完美地工作。您不需要传入的元素具有ID。这就是复制者找到他要传递的节点的方式。此外,此解决方案也可以在没有“ printDoc.autoPrint()”的情况下使用。如果要在代码中保留此特定行,则需要包括autoPrint-Plugin。
snrlx

13

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

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

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


2
只是它的一个简单补充,如果您想创建iframe的可下载pdf,请使用开发者控制台: document.querySelector("#myIframe").contentWindow.print()
ioCron

11

如前所述,您应该使用jsPDFhtml2canvas。我还在jsPDF问题中找到了一个函数,该函数可自动将pdf拆分为多个页面(

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');
    }
  });
}

3
它不会转换图像
Probosckie

1
感谢您的回答,您能否提供有关如何将其转换为A4页面格式的任何提示?
johannesMatevosyan

3
这实际上并不是一个好的矢量pdf,它使用画布制作了很多位图,并将它们堆叠为图像。结果有很多缺点-大,质量低,无法从PDF复制和粘贴等...
Roman Zenka,

6

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

优点

1.无需外部库。

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

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

4.核心html / js功能

---只需添加以下代码

CSS

@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函数单击

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

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

范例-

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

浏览器的打印功能存在问题。用户通常为“打印”视图选择默认选项(页边距,页面大小等)。因此,在没有培训用户的情况下很难生成具有所需样式的所需PDF,这更加困难,而且几乎是不可能的……
Rahmat Ali

4

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

$(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);

        });
});

这项工作,但它将内容转换为图像
Samad

另外,如何设置分页符,以便如果内容/图像不适合当前页面,则将其打印在新页面上?
谢克尔·谢特

4
  • 无依赖,纯JS
  • 要添加CSS或图像-请勿使用相对URL,请使用完整URL http://...domain.../path.css左右。它创建单独的HTML文档,没有主要内容的上下文。
  • 您也可以将图像嵌入为base64

这为我服务了多年:

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>${title}</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;
}

1
问题是pdf不会包含任何CSS效果。
Shadab Faiz

@ShadabFaiz它将但可能与主窗口不同。您仍然可以在此html中添加自定义CSS。
卢卡斯·里西斯

是的 同意您可以在窗口中添加CSS,但是如何在pdf文件中打印结果?
Mirko Cianfarani

1
但是不渲染图像。
1

1
我喜欢这个!在这里和那里进行一些调整,看起来不错。一件事不要去除多余的间距,<body >因为它需要这个:P
Phil Roggenbuck

2

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

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

...
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" }
            ]
        }
    }
}
...

“导出为PDF”功能导致PDF文档为空。
理查德·纳雷辛斯基

您端可能是错误的配置...如果需要,请发布带有shieldui标签的问题
Vladimir Georgiev

2

使用pdfMake.js此Gist

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

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

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(`<b>Sample</b>`);
pdfMake.createPdf({content:docDef}).download('sample.pdf');

备注:

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

如果我正确阅读了代码,这不支持CSS边框样式(例如在表格上),对吗?
ninjagecko

1

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

$(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中都已损坏。

我还包括以下内容:

<script src="js/jspdf.js"></script>
    <script src="js/jspdf.plugin.from_html.js"></script>
    <script src="js/jspdf.plugin.addhtml.js"></script>
    <script src="//mrrio.github.io/jsPDF/dist/jspdf.debug.js"></script>
    <script src="http://html2canvas.hertzen.com/build/html2canvas.js"></script>
    <script type="text/javascript" src="./libs/FileSaver.js/FileSaver.js"></script>
    <script type="text/javascript" src="./libs/Blob.js/Blob.js"></script>
    <script type="text/javascript" src="./libs/deflate.js"></script>
    <script type="text/javascript" src="./libs/adler32cs.js/adler32cs.js"></script>

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

2
您能否举个例子,说明如何传递html
Erum,2015年

但是您的代码是<script src =“ html2canvas.hertzen.com/build/html2canvas.js" > < /… >>,这意味着它需要互联网吗?
Erum 2015年

1
@Erum,您可以下载文件。
Eduardo Cuomo

0

要将div捕获为PDF,可以使用https://grabz.it解决方案。它具有一个简单,灵活的JavaScript API,可让您捕获单个HTML元素(例如div或span)的内容

为了实现它,您需要首先获取一个应用程序密钥和机密,然后下载(免费)SDK。

现在是一个例子。

假设您有HTML:

<div id="features">
    <h4>Acme Camera</h4>
    <label>Price</label>$399<br />
    <label>Rating</label>4.5 out of 5
</div>
<p>Cras ut velit sed purus porttitor aliquam. Nulla tristique magna ac libero tempor, ac vestibulum felisvulput ate. Nam ut velit eget
risus porttitor tristique at ac diam. Sed nisi risus, rutrum a metus suscipit, euismod tristique nulla. Etiam venenatis rutrum risus at
blandit. In hac habitasse platea dictumst. Suspendisse potenti. Phasellus eget vehicula felis.</p>

要捕获功能ID下的内容,您需要:

//add the sdk
<script type="text/javascript" src="grabzit.min.js"></script>
<script type="text/javascript">
//login with your key and secret. 
GrabzIt("KEY", "SECRET").ConvertURL("http://www.example.com/my-page.html",
{"target": "#features", "format": "pdf"}).Create();
</script>

请注意target: #feature#feature是CSS选择器,就像前面的示例一样。现在,在加载页面时,现在将在与脚本标签相同的位置创建图像屏幕快照,其中将包含功能div的所有内容,仅包含其他内容。

您可以对div屏幕截图机制执行的其他配置和自定义操作,请在此处查看

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.