Heim > Web-Frontend > js-Tutorial > Hauptteil

4 Möglichkeiten, HTML in PDF zu konvertieren

青灯夜游
Freigeben: 2020-11-11 17:53:49
nach vorne
2706 Leute haben es durchsucht

Wie konvertiert man HTML in PDF? Der folgende Artikel zeigt, wie Sie mit Node.js, Puppeteer, Headless Chrome und Docker PDF-Dokumente aus komplex gestalteten React-Seiten generieren. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.

Hintergrund: Vor ein paar Monaten bat uns ein Kunde, eine Funktion zu entwickeln, mit der Benutzer React-Seiteninhalte im PDF-Format erhalten könnten. Bei dieser Seite handelt es sich im Wesentlichen um einen Bericht und eine Datenvisualisierung eines Patientenfalls, in dem viele SVGs enthalten sind. Es gibt auch spezielle Anfragen, das Layout zu manipulieren und einige Neuanordnungen von HTML-Elementen vorzunehmen. Daher sollte das PDF im Vergleich zur ursprünglichen React-Seite einen anderen Stil und zusätzlichen Inhalt aufweisen.

Da diese Aufgabe viel komplexer ist, als sie mit einfachen CSS-Regeln zu lösen, haben wir zunächst mögliche Implementierungsmethoden untersucht. Wir haben 3 Hauptlösungen gefunden. Dieser Blogbeitrag führt Sie durch die Möglichkeiten und die mögliche Umsetzung.

Verzeichnis:

    Wird es auf der Clientseite oder auf der Serverseite generiert?
  • Option 1: Screenshots vom DOM erstellen
  • Option 2: Einfach PDF-Bibliothek verwenden
  • Letzte Option 3: Node.js, Puppeteer und Headless Chrome

      Stilkontrolle
    • Datei an Client senden und speichern
  • Verwenden von Puppeteer in Docker
  • Option 3 +1: CSS-Druckregeln
  • Zusammenfassung
Wird es auf der Clientseite oder auf der Serverseite generiert?

PDF-Dateien können sowohl auf der Client- als auch auf der Serverseite generiert werden. Aber es ist wahrscheinlich sinnvoller, die Arbeit dem Backend zu überlassen, da Sie nicht alle Ressourcen verbrauchen möchten, die der Browser des Benutzers bereitstellen kann.

Trotzdem werde ich die Lösung für beide Methoden zeigen.

Option 1: Screenshot vom DOM erstellen

Auf den ersten Blick scheint diese Lösung die einfachste zu sein, und das ist auch der Fall, sie hat jedoch ihre eigenen Einschränkungen. Dies ist eine benutzerfreundliche Methode, wenn Sie keine besonderen Anforderungen haben, z. B. die Auswahl von Text in einer PDF-Datei oder die Durchführung einer Textsuche.

Die Methode ist einfach und unkompliziert: Erstellen Sie einen Screenshot der Seite und fügen Sie ihn in eine PDF-Datei ein. Sehr unkompliziert. Um dies zu erreichen, können wir zwei Pakete verwenden:

    Html2canvas, das Screenshots basierend auf DOM generiert
  • jsPdf, eine Bibliothek, die PDF generiert
Codierung starten:

npm install html2canvas jspdf

import html2canvas from 'html2canvas'
import jsPdf from 'jspdf'
 
function printPDF () {
    const domElement = document.getElementById('your-id')
    html2canvas(domElement, { onclone: (document) => {
      document.getElementById('print-button').style.visibility = 'hidden'
}})
    .then((canvas) => {
        const img = canvas.toDataURL('image/png')
        const pdf = new jsPdf()
        pdf.addImage(imgData, 'JPEG', 0, 0, width, height)
        pdf.save('your-filename.pdf')
})
Nach dem Login kopieren
npm install html2canvas jspdf
doc = new PDFDocument
doc.pipe fs.createWriteStream('output.pdf')
doc.font('fonts/PalatinoBold.ttf')
   .fontSize(25)
   .text('Some text with an embedded font!', 100, 100)
 
doc.image('path/to/image.png', {
   fit: [250, 300],
   align: 'center',
   valign: 'center'
});
 
doc.addPage()
   .fontSize(25)
   .text('Here is some vector graphics...', 100, 100)
 
doc.end()
Nach dem Login kopieren

就这样!

请注意 html2canvasonclone方法。当你在截图之前需要操纵 DOM(例如隐藏打印按钮)时,它是非常方便的。我看到过很多使用这个包的项目。但不幸的是,这不是我们想要的,因为我们需要在后端完成对 PDF 的创建工作。

方案2:只使用 PDF 库

NPM上有几个库,如 jsPDF(如上所述)或PDFKit。他们的问题是,如果我想使用这些库,我将不得不重新调整页面结构。这肯定会损害可维护性,因为我需要将所有后续更改应用到 PDF 模板和 React 页面中。

请看下面的代码。你需要亲自手动创建 PDF 文档。你需要遍历 DOM 并找出每个元素并将其转换为 PDF 格式,这是一项繁琐的工作。必须找到一个更简单的方法。

const puppeteer = require('puppeteer')
 
async function printPDF() {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto('https://blog.risingstack.com', {waitUntil: 'networkidle0'});
  const pdf = await page.pdf({ format: 'A4' });
 
  await browser.close();
  return pdf
})
Nach dem Login kopieren

这段代码段来自 PDFKit 文档。但是如果你的目标是直接生成一个 PDF 文件,而不是对一个已经存在的(并且不断变化的)HTML 页面进行转换,它还是很有用的。

最终方案3:基于 Node.js 的 Puppeteer 和 Headless Chrome

什么是 Puppeteer?其文档中写道:

Puppeteer 是一个 Node 库,它提供了一个高级 API 来控制 DevTools 协议上的 Chrome 或 Chromium。 Puppeteer 默认以 headless 模式运行 Chrome 或 Chromium,但其也可以被配置为完整的(non-headless)模式运行。

它本质上是一个可以从 Node.js 运行的浏览器。如果你读过它的文档,其中首先提到的就是你可以用 Puppeteer 来生成页面的截图和PDF。优秀!这正是我们想要的。

先用 npmi i puppeteer 安装 Puppeteer,并实现我们的功能。

await page.type('#email', process.env.PDF_USER)
await page.type('#password', process.env.PDF_PASSWORD)
await page.click('#submit')
Nach dem Login kopieren
Nach dem Login kopieren

这是一个简单的功能,可导航到 URL 并生成站点的 PD F文件。

首先,我们启动浏览器(仅在 headless 模式下支持 PDF 生成),然后打开新页面,设置视口,并导航到提供的URL。

设置 waitUntil:'networkidle0' 选项意味着当至少500毫秒没有网络连接时,Puppeteer 会认为导航已完成。 (可以从 API docs 获取更多信息。)

之后,我们将 PDF 保存为变量,关闭浏览器并返回 PDF。

注意:page.pdf 方法接收 optionsEinfach so!

🎜Bitte beachten Sie die onclone-Methode von html2canvas. Dies ist sehr praktisch, wenn Sie das DOM bearbeiten müssen, bevor Sie einen Screenshot erstellen (z. B. die Schaltfläche „Drucken“ ausblenden). Ich habe viele Projekte gesehen, die dieses Paket verwenden. Leider ist dies nicht das, was wir wollen, da wir die PDF-Erstellung im Backend durchführen müssen. 🎜🎜Option 2: Verwenden Sie einfach die PDF-Bibliothek 🎜🎜Es gibt mehrere Bibliotheken auf NPM, wie jsPDF (wie oben erwähnt) oder PDFKit. Das Problem dabei ist, dass ich die Seite neu strukturieren muss, wenn ich diese Bibliotheken verwenden möchte. Dies beeinträchtigt definitiv die Wartbarkeit, da ich alle nachfolgenden Änderungen sowohl auf die PDF-Vorlage als auch auf die React-Seite anwenden müsste. 🎜🎜Bitte beachten Sie den Code unten. Sie müssen das PDF-Dokument manuell selbst erstellen. Sie müssen durch das DOM gehen, jedes Element finden und es in das PDF-Format konvertieren, was eine mühsame Aufgabe ist. Es muss ein einfacherer Weg gefunden werden. 🎜
await page.addStyleTag({ content: '.nav { display: none} .navbar { border: 0px} #print-button {display: none}' })
Nach dem Login kopieren
Nach dem Login kopieren
🎜Dieser Codeausschnitt stammt aus der PDFKit-Dokumentation. Es kann jedoch dennoch nützlich sein, wenn Ihr Ziel darin besteht, direkt eine PDF-Datei zu generieren, anstatt eine vorhandene (und sich ständig ändernde) HTML-Seite zu konvertieren. 🎜🎜Endlösung 3: Puppeteer und Headless Chrome basierend auf Node.js🎜🎜Was ist Puppeteer? Die Dokumentation lautet: 🎜
Puppeteer ist eine Node-Bibliothek, die eine High-Level-API zur Steuerung von Chrome oder Chromium über das DevTools-Protokoll bereitstellt. Puppeteer führt Chrome oder Chromium standardmäßig im Headless-Modus aus, kann aber auch für die Ausführung im vollständigen (nicht Headless)-Modus konfiguriert werden.
🎜Es handelt sich im Wesentlichen um einen Browser, der von Node.js aus ausgeführt wird. Wenn Sie die Dokumentation lesen, wird als Erstes erwähnt, dass Sie Puppeteer verwenden können, um „Screenshots und PDFs“ von Seiten zu erstellen. exzellent! Genau das wollen wir. 🎜🎜Verwenden Sie zunächst npmi i puppeteer, um Puppeteer zu installieren und unsere Funktionen zu implementieren. 🎜
printPDF.then(pdf => {
    res.set({ 'Content-Type': 'application/pdf', 'Content-Length': pdf.length })
    res.send(pdf)
Nach dem Login kopieren
Nach dem Login kopieren
🎜Dies ist eine einfache Funktion, die zu einer URL navigiert und eine PDF-Datei der Website generiert. 🎜🎜Zuerst starten wir den Browser (PDF-Generierung wird nur im Headless-Modus unterstützt), öffnen dann eine neue Seite, legen den Darstellungsbereich fest und navigieren zur angegebenen URL. 🎜🎜 Das Setzen der Option waitUntil:'networkidle0' bedeutet, dass Puppeteer die Navigation als abgeschlossen betrachtet, wenn mindestens 500 Millisekunden lang keine Netzwerkverbindung besteht. (Weitere Informationen finden Sie in den API-Dokumenten.) 🎜🎜🎜Danach speichern wir das PDF als Variable, schließen den Browser und kehren zum PDF zurück. 🎜🎜🎜Hinweis: Die Methode page.pdf empfängt ein options-Objekt und Sie können die Option „Pfad“ verwenden, um die Datei auf der Festplatte zu speichern. Wenn kein Pfad angegeben wird, wird die PDF-Datei nicht auf der Festplatte gespeichert, sondern zwischengespeichert. (Ich werde später besprechen, wie ich damit umgehen soll.) 🎜

如果需要先登录才能从受保护的页面生成 PDF,首先你要导航到登录页面,检查表单元素的 ID 或名称,填写它们,然后提交表单:

await page.type('#email', process.env.PDF_USER)
await page.type('#password', process.env.PDF_PASSWORD)
await page.click('#submit')
Nach dem Login kopieren
Nach dem Login kopieren

要始终将登录凭据保存在环境变量中,不要硬编码!

样式控制

Puppeteer 也有这种样式操作的解决方案。你可以在生成 PDF 之前插入样式标记,Puppeteer 将生成具有已修改样式的文件。

await page.addStyleTag({ content: '.nav { display: none} .navbar { border: 0px} #print-button {display: none}' })
Nach dem Login kopieren
Nach dem Login kopieren

将文件发送到客户端并保存

好的,现在你已经在后端生成了一个 PDF 文件。接下来做什么?

如上所述,如果你不把文件保存到磁盘,将会得到一个缓冲区。你只需要把含有适当内容类型的缓冲区发送到前端即可。

printPDF.then(pdf => {
    res.set({ 'Content-Type': 'application/pdf', 'Content-Length': pdf.length })
    res.send(pdf)
Nach dem Login kopieren
Nach dem Login kopieren

现在,你只需在浏览器向服务器发送请求即可得到生成的 PDF。

function getPDF() {
 return axios.get(`${API_URL}/your-pdf-endpoint`, {
   responseType: 'arraybuffer',
   headers: {
     'Accept': 'application/pdf'
   }
 })
Nach dem Login kopieren

一旦发送了请求,缓冲区的内容就应该开始下载了。最后一步是将缓冲区数据转换为 PDF 文件。

savePDF = () => {
    this.openModal(‘Loading…’) // open modal
   return getPDF() // API call
     .then((response) => {
       const blob = new Blob([response.data], {type: 'application/pdf'})
       const link = document.createElement('a')
       link.href = window.URL.createObjectURL(blob)
       link.download = `your-file-name.pdf`
       link.click()
       this.closeModal() // close modal
     })
   .catch(err => /** error handling **/)
 }
<button onClick={this.savePDF}>Save as PDF</button>
Nach dem Login kopieren

就这样!如果单击“保存”按钮,那么浏览器将会保存 PDF。

在 Docker 中使用 Puppeteer

我认为这是实施中最棘手的部分 —— 所以让我帮你节省几个小时的百度时间。

官方文档指出“在 Docker 中使用 headless Chrome 并使其运行起来可能会非常棘手”。官方文档有疑难解答部分,你可以找到有关用 Docker 安装 puppeteer 的所有必要信息。

如果你在 Alpine 镜像上安装 Puppeteer,请确保在看到页面的这一部分时再向下滚动一点。否则你可能会忽略一个事实:你无法运行最新的 Puppeteer 版本,并且你还需要用一个标记禁用 shm :

const browser = await puppeteer.launch({
  headless: true,
  args: ['--disable-dev-shm-usage']
});
Nach dem Login kopieren

否则,Puppeteer 子进程可能会在正常启动之前耗尽内存。

方案 3 + 1:CSS 打印规则

可能有人认为从开发人员的角度来看,简单地使用 CSS 打印规则很容易。没有 NPM 模块,只有纯 CSS。但是在跨浏览器兼容性方面,它的表现如何呢?

在选择 CSS 打印规则时,你必须在每个浏览器中测试结果,以确保它提供的布局是相同的,并且它不是100%能做到这一点。

例如,在给定元素后面插入一个 break-after 并不是一个多么高深的技术,但是你可能会惊讶的发现要在 Firefox 中使用它需要使用变通方法。

除非你是一位经验丰富的 CSS 大师,在创建可打印页面方面有很多的经验,否则这可能会非常耗时。

如果你可以使打印样式表保持简单,打印规则是很好用的。

让我们来看一个例子吧。

@media print {
    .print-button {
        display: none;
    }
    
    .content p {
        break-after: always;
    }
}
Nach dem Login kopieren

上面的 CSS 隐藏了打印按钮,并在每个 p 之后插入一个分页符,其中包含content 类。有一篇很棒的文章总结了你可以用打印规则做什么,以及它们有什么问题,包括浏览器兼容性。

考虑到所有因素,如果你想从不那么复杂的页面生成 PDF,CSS打印规则非常有效。

总结

让我们快速回顾前面介绍的方案,以便从 HTML 页面生成 PDF 文件:

  • 从 DOM 产生截图:当你需要从页面创建快照时(例如创建缩略图)可能很有用,但是当你需要处理大量数据时就会有些捉襟见肘。
  • 只用 PDF 库:如果你打算从头开始以编程方式创建 PDF 文件,这是一个完美的解决方案。否则,你需要同时维护 HTML 和 PDF 模板,这绝对是一个禁忌。
  • Puppeteer:尽管在 Docker 上工作相对困难,但它为我们的实现提供了最好的结果,而且编写代码也是最简单的。
  • CSS打印规则:如果你的用户受过足够的教育,知道如何把页面内容打印到文件,并且你的页面相对简单,那么它可能是最轻松的解决方案。正如你在我们的案例中所看到的,事实并非如此。

打印快乐!

原文:https://blog.risingstack.com/pdf-from-html-node-js-puppeteer/

更多编程相关知识,请访问:编程教学!!

Das obige ist der detaillierte Inhalt von4 Möglichkeiten, HTML in PDF zu konvertieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:segmentfault.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage