使用Javascript从div中的HTML生成pdf

IT技术 javascript jspdf
2021-02-02 17:11:41

我有以下 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 中的任何内容打印为 pdf,id 为“pdf”。这必须使用 JavaScript 来完成。然后应自动下载“pdf”文档,文件名为“foobar.pdf”

我一直在使用 jspdf 来做到这一点,但它唯一的功能是“文本”,它只接受字符串值。我想将 HTML 提交给 jspdf,而不是文本。

6个回答

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 的特殊元素处理程序中忽略它。因此,您的 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 问题 中说明它指出:

因为匹配是针对节点树中的每个元素进行的,所以我的愿望是尽可能快地进行匹配。在那种情况下,这意味着“仅匹配元素 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 等,这对我的目的来说已经足够了。此外,它只会打印文本节点内的文本,这意味着它不会打印 textarea 等的值。例子:

<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>
这个插件可能很好,但它的文档很差。我在 stackoverflow 中看到的大多数示例甚至都不起作用。
2021-03-11 17:11:41
我猜元素处理程序可以是一个类?这在语义上更符合 HTML5 标准。一个 ID 不仅在 CSS 中具有太多的特殊性,它还必须是唯一的。
2021-03-16 17:11:41
“如果你想忽略某些元素,你必须用一个 ID 来标记它们”——一个很棒的图书馆,被这种倒置的要求毁了。OP 想要打印一个<div>,可能是数百个中的一个 - 所以他必须标记所有不需要的DOM 元素?
2021-03-31 17:11:41
@snrlx 我得到空白 pdf 和这个错误:renderer.pdf.sHashCode is not a function
2021-04-02 17:11:41
如果我没记错 doc.fromHTML不再支持
2021-04-08 17:11:41

这是简单的解决方案。这对我有用。您可以使用 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>
以防万一有人试图用 Angular 实现这一点,请将您的 CSS 文件放在 assets 文件夹中。
2021-03-18 17:11:41
“并简单地将其另存为 pdf”-我错过了那部分。你怎么做呢?
2021-03-25 17:11:41
这对我有用,为了解决 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>');
2021-03-25 17:11:41
@DavidsonLima 此代码创建新窗口,该窗口将看不到旧窗口 css。这就是为什么它“忽略”css,它不是忽略,只是在新窗口中它没有加载。只需在头部加载它,它就会被渲染。
2021-03-30 17:11:41
一些评论: 1) 您不需要用于打印的特定样式表。在您当前的样式表中,执行以下操作:@media print { .pageBreak { page-break-before: always; } .labelPdf { 字体粗细:粗体;字体大小:20px;} .noPrintPdf { 显示:无;并根据需要使用这些类。2) .live("click", ...) 对我不起作用,所以我使用 .on("click", ...) 代替。
2021-04-07 17:11:41

您可以使用 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
}
非常适合我。您要传入的元素不一定要有 ID。这就是复制者找到他想要传入的节点的方式。此外,这个解决方案也可以在没有“printDoc.autoPrint()”的情况下工作。如果您想在代码中保留此特定行,则需要包含 autoPrint-Plugin。
2021-03-25 17:11:41
我很好奇,这对 OP 以外的任何人都有效吗?通过阅读代码,我似乎明白它只适用于具有 ID 的元素。它可能比那更复杂一点,无论如何我不知道如何进行这项工作。
2021-03-28 17:11:41
根据我的观察,非常具有讽刺意味的是,仅当作为参数发送的元素不包含任何 HTML 时, fromHTML 才有效:仅支持纯文本。有点扼杀了整个事情的目的imo。
2021-04-08 17:11:41

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

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

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

只是对它的一个简单补充,如果您想创建 iframe 的可下载 pdf,请使用开发人员控制台: document.querySelector("#myIframe").contentWindow.print()
2021-03-22 17:11:41
它不打印多页 HTML。只有单页pdf
2021-03-29 17:11:41
  • 无依赖,纯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;
}

当然,这将打开打印对话框,用户必须知道她/他可以选择打印到 pdf 选项,以获取 pdf。可以预先选择打印机,如果用户确认可以实际打印该文件。为了避免这种情况并提供没有任何额外内容的PDF,您需要制作PDF文件。可能在服务器端。您可以使用仅带有发票的小型 html 页面,并将其转换为带有无头 chrome 的 PDF 文件。使用 puppeteer 非常容易。无需安装/配置 chrome,只需安装 npm 包 puppeteer(由 chrome 团队管理)并运行它。请记住,这实际上会在没有 GUI 的情况下启动真正的 chrome,因此您需要为此配备一些 RAM 和 CPU。大多数服务器在流量足够低的情况下都可以。这是代码示例,但这必须在 BACKEND 上运行。节点。它也是缓慢的调用,它是资源密集型调用。

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://your-domain.com/path-to-invoice', {
    waitUntil: 'networkidle2',
  });
  await page.pdf({ path: 'invoice-file-path.pdf', format: 'a4' });

  await browser.close();
})();

在此处了解更多信息:https : //pptr.dev/

@ShadabFaiz 它将但它可能与主窗口不同。您仍然可以添加自定义 css 至此 html。
2021-03-11 17:11:41
是的。我同意你可以在窗口中添加 css 但是如何在文件 pdf 中打印结果?
2021-03-15 17:11:41
我喜欢这个!在这里和那里进行了一些调整,看起来不错。一件小事不要删除额外的间距,<body >它需要这个:P
2021-03-16 17:11:41
但是不渲染图像。
2021-03-17 17:11:41
问题是 pdf 中不会有任何 css 效果。
2021-04-08 17:11:41