>  기사  >  웹 프론트엔드  >  HTML을 PDF로 변환하는 방법은 무엇입니까? 방법 소개

HTML을 PDF로 변환하는 방법은 무엇입니까? 방법 소개

青灯夜游
青灯夜游앞으로
2020-12-17 17:42:592669검색

HTML을 PDF로 변환하는 방법은 무엇입니까? 방법 소개

이 기사에서는 Node.js, Puppeteer, 헤드리스 Chrome 및 Docker를 사용하여 복잡한 스타일의 React 페이지에서 PDF 문서를 생성하는 방법을 보여 드리겠습니다.

관련 추천: "nodejs Tutorial"

배경: 몇 달 전 한 고객이 사용자가 React 페이지 콘텐츠를 PDF 형식으로 얻을 수 있는 기능을 개발해 달라고 요청했습니다. 이 페이지는 기본적으로 많은 SVG가 포함된 환자 사례에 대한 보고서 및 데이터 시각화입니다. 레이아웃을 조작하고 HTML 요소의 일부 재배열을 수행하기 위한 특별한 요청도 있습니다. 따라서 PDF에는 원본 React 페이지와 다른 스타일과 추가 콘텐츠가 있어야 합니다.

이 작업은 간단한 CSS 규칙으로 해결하는 것보다 훨씬 복잡하기 때문에 먼저 가능한 구현 방법을 탐색했습니다. 우리는 3가지 주요 솔루션을 찾았습니다. 이 블로그 게시물은 가능성과 최종 구현 과정을 안내합니다.

디렉토리:

  • 클라이언트 측에서 생성됩니까, 아니면 서버 측에서 생성됩니까?
  • 옵션 1: DOM에서 스크린샷 만들기
  • 옵션 2: PDF 라이브러리만 사용
  • 최종 옵션 3: Node.js, Puppeteer 및 Headless Chrome

    • 스타일 제어
    • 파일을 클라이언트에 보내고 저장
  • Docker에서 Puppeteer 사용
  • 옵션 3 +1: CSS 인쇄 규칙
  • 요약

클라이언트 측에서 생성됩니까, 아니면 서버 측에서 생성됩니까?

PDF 파일은 클라이언트 측과 서버 측 모두에서 생성될 수 있습니다. 그러나 사용자의 브라우저가 제공할 수 있는 모든 리소스를 사용하고 싶지 않기 때문에 백엔드에서 이를 처리하도록 하는 것이 더 합리적일 수 있습니다.

그래도 두 가지 방법 모두에 대한 해결 방법을 보여드리겠습니다.

옵션 1: DOM에서 스크린샷 만들기

언뜻 보기에 이 솔루션은 가장 간단한 것처럼 보이고 실제로도 그렇습니다. 그러나 자체적인 한계가 있습니다. PDF에서 텍스트를 선택하거나 텍스트에서 검색을 수행하는 등 특별한 요구 사항이 없는 경우 간단하고 사용하기 쉬운 방법입니다.

방법은 간단하고 간단합니다. 페이지에서 스크린샷을 만들어 PDF 파일에 넣으세요. 매우 간단합니다. 이를 달성하기 위해 두 가지 패키지를 사용할 수 있습니다.

  • Html2canvas, DOM을 기반으로 스크린샷을 생성
  • jsPdf, PDF를 생성하는 라이브러리

코딩 시작:

npm install html2canvas jspdf 코드> <code>npm install html2canvas jspdf

import html2canvas from &#39;html2canvas&#39;
import jsPdf from &#39;jspdf&#39;
 
function printPDF () {
    const domElement = document.getElementById(&#39;your-id&#39;)
    html2canvas(domElement, { onclone: (document) => {
      document.getElementById(&#39;print-button&#39;).style.visibility = &#39;hidden&#39;
}})
    .then((canvas) => {
        const img = canvas.toDataURL(&#39;image/png&#39;)
        const pdf = new jsPdf()
        pdf.addImage(imgData, &#39;JPEG&#39;, 0, 0, width, height)
        pdf.save(&#39;your-filename.pdf&#39;)
})

就这样!

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

方案2:只使用 PDF 库

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

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

doc = new PDFDocument
doc.pipe fs.createWriteStream(&#39;output.pdf&#39;)
doc.font(&#39;fonts/PalatinoBold.ttf&#39;)
   .fontSize(25)
   .text(&#39;Some text with an embedded font!&#39;, 100, 100)
 
doc.image(&#39;path/to/image.png&#39;, {
   fit: [250, 300],
   align: &#39;center&#39;,
   valign: &#39;center&#39;
});
 
doc.addPage()
   .fontSize(25)
   .text(&#39;Here is some vector graphics...&#39;, 100, 100)
 
doc.end()

这段代码段来自 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

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

그렇습니다!

html2canvasonclone 메소드를 참고하세요. 스크린샷을 찍기 전에 DOM을 조작해야 할 때(예: 인쇄 버튼 숨기기) 매우 편리합니다. 나는 이 패키지를 사용하는 많은 프로젝트를 보았습니다. 하지만 안타깝게도 백엔드에서 PDF 생성을 수행해야 하기 때문에 이는 우리가 원하는 것이 아닙니다.

옵션 2: 그냥 PDF 라이브러리를 사용하세요 🎜🎜🎜NPM에는 jsPDF(위에서 언급한 대로) 또는 PDFKit🎜. 문제는 이러한 라이브러리를 사용하려면 페이지를 재구성해야 한다는 것입니다. PDF 템플릿과 React 페이지 모두에 모든 후속 변경 사항을 적용해야 하므로 유지 관리성이 확실히 손상됩니다. 🎜🎜아래 코드를 참고해주세요. PDF 문서를 직접 수동으로 작성해야 합니다. DOM을 살펴보고 각 요소를 찾아 PDF 형식으로 변환해야 하는데 이는 지루한 작업입니다. 더 쉬운 방법을 찾아야 합니다. 🎜
await page.type(&#39;#email&#39;, process.env.PDF_USER)
await page.type(&#39;#password&#39;, process.env.PDF_PASSWORD)
await page.click(&#39;#submit&#39;)
🎜이 코드 조각은 PDFKit 문서에서 가져온 것입니다. 그러나 기존(계속 변경되는) HTML 페이지를 변환하는 대신 PDF 파일을 직접 생성하는 것이 목표라면 여전히 유용할 수 있습니다. 🎜

🎜최종 솔루션 3: Node.js 기반 Puppeteer 및 헤드리스 Chrome🎜🎜🎜 인형사🎜? 문서 내용은 다음과 같습니다. 🎜
Puppeteer는 DevTools 프로토콜에서 Chrome 또는 Chromium을 제어하기 위한 고급 API를 제공하는 노드 라이브러리입니다. Puppeteer는 기본적으로 헤드리스 모드에서 Chrome 또는 Chromium을 실행하지만 전체(비헤드리스) 모드에서 실행되도록 구성할 수도 있습니다.
🎜기본적으로 Node.js에서 실행되는 브라우저입니다. 문서를 읽으면 가장 먼저 언급되는 것은 Puppeteer를 사용하여 페이지의 🎜스크린샷과 PDF를 생성🎜할 수 있다는 것입니다. 훌륭한! 이것이 바로 우리가 원하는 것입니다. 🎜🎜먼저 npmi i puppeteer를 사용하여 Puppeteer를 설치하고 기능을 구현하세요. 🎜
await page.addStyleTag({ content: &#39;.nav { display: none} .navbar { border: 0px} #print-button {display: none}&#39; })
🎜이것은 URL로 이동하여 사이트의 PDF 파일을 생성하는 간단한 기능입니다. 🎜🎜먼저 브라우저를 시작한 다음(PDF 생성은 헤드리스 모드에서만 지원됨) 새 페이지를 열고 뷰포트를 설정한 다음 제공된 URL로 이동합니다. 🎜

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

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

注意:page.pdf 方法接收 options 对象,你可以使用 'path' 选项将文件保存到磁盘。如果未提供路径,则 PDF 将不会被保存到磁盘,而是会得到缓冲区。(稍后我将讨论如何处理它。)

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

await page.type(&#39;#email&#39;, process.env.PDF_USER)
await page.type(&#39;#password&#39;, process.env.PDF_PASSWORD)
await page.click(&#39;#submit&#39;)

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

样式控制

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

await page.addStyleTag({ content: &#39;.nav { display: none} .navbar { border: 0px} #print-button {display: none}&#39; })

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

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

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

printPDF.then(pdf => {
    res.set({ &#39;Content-Type&#39;: &#39;application/pdf&#39;, &#39;Content-Length&#39;: pdf.length })
    res.send(pdf)

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

function getPDF() {
 return axios.get(`${API_URL}/your-pdf-endpoint`, {
   responseType: &#39;arraybuffer&#39;,
   headers: {
     &#39;Accept&#39;: &#39;application/pdf&#39;
   }
 })

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

savePDF = () => {
    this.openModal(‘Loading…’) // open modal
   return getPDF() // API call
     .then((response) => {
       const blob = new Blob([response.data], {type: &#39;application/pdf&#39;})
       const link = document.createElement(&#39;a&#39;)
       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>

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

在 Docker 中使用 Puppeteer

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

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

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

const browser = await puppeteer.launch({
  headless: true,
  args: [&#39;--disable-dev-shm-usage&#39;]
});

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

方案 3 + 1:CSS 打印规则

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

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

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

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

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

让我们来看一个例子吧。

@media print {
    .print-button {
        display: none;
    }
    
    .content div {
        break-after: always;
    }
}

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

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

总结

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

  • DOM에서 스크린샷 생성: 페이지에서 스냅샷을 생성해야 할 때(예: 썸네일 생성) 유용할 수 있지만, 대량의 데이터를 처리해야 할 경우에는 무리가 될 수 있습니다.
  • PDF 라이브러리만: 처음부터 프로그래밍 방식으로 PDF 파일을 생성하려는 경우 이것이 완벽한 솔루션입니다. 그렇지 않으면 HTML과 PDF 템플릿을 모두 유지해야 하는데 이는 절대 금물입니다.
  • Puppeteer: Docker에서 작업하는 것은 상대적으로 어렵지만 구현에 가장 좋은 결과를 제공했고 코딩하기가 가장 쉬웠습니다.
  • CSS 인쇄 규칙: 사용자가 페이지 내용을 파일로 인쇄하는 방법을 충분히 교육받았고 페이지가 상대적으로 단순하다면 이것이 아마도 가장 쉬운 솔루션일 것입니다. 우리의 경우에서 볼 수 있듯이 그렇지 않습니다.

즐거운 인쇄 되세요!

영어 원본 주소: https://blog.risingstack.com/pdf-from-html-node-js-puppeteer/

더 많은 프로그래밍 관련 지식을 보려면 다음을 방문하세요: 프로그래밍 입문! !

위 내용은 HTML을 PDF로 변환하는 방법은 무엇입니까? 방법 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제