Articles

ノードを使用してHTMLからPDFを生成します。js and Puppeteer

Mátéboérの写真's Picture

Mátéboér

Risingstackのフルスタック開発者

この記事では、nodeを使用して、重くスタイルのReactページからPDFドキュメントを生成js、人形師、ヘッドレスクローム&ドッカー。

背景

背景: 数ヶ月前、RisingStackのクライアントの一人が、ユーザーがPDF形式のReactページを要求できる機能を開発するように依頼しました。 そのページは、基本的には、多くのSvgを含むデータ視覚化を持つ患者のためのレポート/結果です。 さらに、レイアウトを操作し、HTML要素のいくつかの再配置を行うためのいくつかの特別な要求がありました。 そのため、PDFは元のReactページとは異なるスタイルと追加を持つ必要があります。

割り当ては単純なCSSルールで解決できたものよりも少し複雑であったため、最初に可能な実装を検討しました。 基本的には3つの主要な解決策が見つかりました。 このブログ記事では、これらの可能性と最終的な実装について説明します。私たちが始める前に個人的なコメント:それはかなり面倒なので、バックルアップ!

目次:

  • クライアント側またはバックエンド側?
  • オプション1:DOMからスクリーンショットを作成する
  • オプション2:PDFライブラリのみを使用する
  • 最後のオプション3:人形師、ノード付きjs
    • スタイル操作
    • クライアントにファイルを送信して保存します
  • DockerでPuppeteerを使用します
  • オプション3+1:CSS印刷ルール
  • 要約

クライ

クライアント側とサーバー側の両方でPDFファイルを生成することができます。 しかし、ユーザーのブラウザが提供できるすべてのリソースを使いたくないので、バックエンドにそれを処理させる方が理にかなっているでしょう。それでも、私はまだ両方の方法の解決策を示します。

そうであれば、私はまだ両方の方法の解決策を示します。オプション1

オプション1

オプション1: DOMからスクリーンショットを作成する

一見すると、この解決策は最も簡単なように見え、真実であることが判明しましたが、独自の制限があります。 PDF内の選択可能なテキストや検索可能なテキストなど、特別なニーズがない場合は、pdfを生成するのに適した簡単な方法です。ページからスクリーンショットを作成し、それをPDFファイルに入れます。

この方法は単純で簡単です。 かなり簡単です。 私たちは、このアプローチのための二つのパッケージを使用しました:

Html2Canvas、DOMからスクリーンショットを作るために
jsPdf、PDFを生成するためのライブラリ

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

それはそれです!

メソッドを確認してください。html2canvasoncloneメソッドを確認してください。 写真を撮る前にスナップショットを撮ってDOMを操作する必要がある場合(印刷ボタンを非表示にするなど)に便利であることがわかります。 私はこのパッケージの使用例をかなり見ることができます。 残念ながら、バックエンド側でPDFの作成を処理する必要があったため、私たちは一つではありませんでした。オプション2

オプション2

オプション2: この目的のためにNPMには、jsPDF(上記)やPDFKitのようないくつかのライブラリがあります。 これらのライブラリを使用したい場合は、ページ構造を再度再作成する必要があるという問題があります。 PdfテンプレートとReactページの両方に後続のすべての変更を適用する必要があったので、それは間違いなく保守性を傷つけます。

以下のコードを見てみましょう。 PDF文書を自分で作成する必要があります。 これで、DOMをトラバースして、各要素をPDFの要素に変換する方法を理解できますが、それは面倒な仕事です。 もっと簡単な方法があるはずです。

doc = new PDFDocumentdoc.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: , align: 'center', valign: 'center'}); doc.addPage() .fontSize(25) .text('Here is some vector graphics...', 100, 100) doc.end()

このスニペットはPDFKit docsからのものです。 あなたのターゲットはすぐにPDFファイルではなく、既存の(そして刻々と変化する)HTMLページの変換である場合しかし、それは有用である可能性があります。

最後のオプション3:人形師、ノードとヘッドレスクローム。js

人形師とは何ですか? Puppeteerは、開発ツールプロトコルを介してChromeまたはChromiumを制御するための高レベルAPIを提供するノードライブラリです。 Puppeteerはデフォルトでヘッドレスで実行されますが、フル(ヘッドレスではない)ChromeまたはChromiumを実行するように構成できます。

これは基本的にノードから実行できるブラウザです。js”を発表した。 あなたがドキュメントを読んだ場合、それは人形師について言う最初の事は、あなたがページのスクリーンショットやPdfを生成するためにそれを使用 優れた! それが私たちが探していたものです。Puppeteerをnpmi i puppeteerでインストールし、ユースケースを実装しましょう。

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

これは、URLに移動し、サイトのPDFファイルを生成する簡単な関数です。

まず、ブラウザ(ヘッドレスモードでのみサポートされているPDF生成)を起動し、新しいページを開き、ビューポートを設定し、提供されたURLに移動します。

waitUntil: ‘networkidle0’オプションを設定すると、Puppeteerは、少なくとも500ミリ秒のネットワーク接続がないときにナビゲーションが終了したとみなされます。その後、PDFを変数に保存し、ブラウザを閉じてPDFを返します。ノート

: page.pdfoptionsオブジェクトを受け取り、’path’オプションを使用してファイルをディスクに保存することもできます。 Pathが指定されていない場合、PDFはディスクに保存されず、代わりにバッファが取得されます。 後で、私はあなたがそれをどのように扱うことができるか議論します。保護されたページからPDFを生成するために最初にログインする必要がある場合は、まずログインページに移動し、フォーム要素のIDまたは名前を調べ、:

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

常にログイン資格情報を環境変数に格納します。

スタイル操作

人形師もこのスタイル操作のための解決策を持っています。 PDFを生成する前にスタイルタグを挿入すると、Puppeteerは変更されたスタイルのファイルを生成します。p>

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

クライアントにファイルを送信し、それを保存

さて、今、あなたはバックエンドにPDFファイルを生成しました。 今何をすべきか?

上記のように、ファイルをディスクに保存しないと、バッファが得られます。 適切なコンテンツタイプのバッファをフロントエンドに送信するだけです。これで、生成されたPDFを取得するために、サーバーにリクエストを送信するだけです。

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

リクエストを送信すると、バッファのダウンロードが開始されます。 最後のステップは、バッファをPDFファイルに変換することです。p>

savePDF = () => { this.openModal(‘Loading…’) // open modal return getPDF() // API call .then((response) => { const blob = new Blob(, {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>

それはそれでした! 保存ボタンをクリックすると、PDFはブラウザによって保存されます。DockerでPuppeteerを使用する

これは実装の最も難しい部分だと思います-ので、グーグルの数時間を節約しましょう。

公式ドキュメントには、”ヘッドレスChromeをDockerで起動して実行するのは難しいかもしれない”と述べています。 公式ドキュメントにはトラブルシューティングセクションがあり、執筆時点ではDockerでpuppeteerをインストールする上で必要なすべての情報を見つけるこ

アルパインイメージにPuppeteerをインストールする場合は、ページのこの部分まで少し下にスクロールしてください。 それ以外の場合は、最新のPuppeteerバージョンを実行できず、フラグを使用してshmの使用を無効にする必要があるという事実を強調するかもしれません。

const browser = await puppeteer.launch({ headless: true, args: });

それ以外の場合は、Puppeteerサブプロセスが適切に開始される前にメモリが不足する可能性があります。 上記のトラブルシューティングリンクでそれについての詳細情報。

オプション3+1:CSS印刷ルール

開発者の観点からは、単にCSS印刷ルールを使用するのは簡単だと思うかもしれません。 NPMモジュールはなく、純粋なCSSだけです。 しかし、ブラウザ間の互換性に関しては、どのように運賃を払っていますか?CSS印刷ルールを選択するときは、すべてのブラウザで結果をテストして、同じレイアウトを提供することを確認する必要があり、100%ではありません。たとえば、特定の要素の後に改行を挿入することは難解なユースケースとはみなされませんが、Firefoxでそれを機能させるには回避策を使用する必要があ

あなたが印刷可能なページの作成に多くの経験を持つ戦いに硬化したCSSの魔術師でない限り、これは時間がかかることがあります。

印刷ルールは、印刷スタイルシートをシンプルに保つことができれば素晴らしいです。

例を見てみましょう。

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

上記のこのCSSは、印刷ボタンを非表示にし、すべての後に改ページを挿入しますdivcontent.印刷ルールで何ができるかをまとめた素晴らしい記事があり、ブラウザの互換性を含むそれらの難しさは何ですか。すべてを考慮に入れると、それほど複雑ではないページからPDFを作成したい場合、CSS印刷ルールは優れており効果的です。

概要:ノードを持つHTMLからのPDF。jsとPuppeteer

それでは、HTMLページからPDFファイルを生成するためにここで説明したオプションを簡単に見てみましょう。

  • DOMからのスクリーンショット:これは、ページからスナップショットを作成する必要がある場合(サムネイルを作成する場合など)に便利ですが、処理するデータがたくさんある場合には不足します。
  • PDFライブラリのみを使用してください:プログラムでPDFファイルを最初から作成する必要がある場合、これは完璧な解決策です。 それ以外の場合は、間違いなくno-goであるHTMLとPDFテンプレートを維持する必要があります。
  • 人形師: Dockerで動作させるのは比較的困難ですが、私たちのユースケースに最適な結果をもたらし、コードを書くのも最も簡単でした。
  • CSS印刷ルール:ユーザーがファイルに印刷する方法を知るのに十分な教育を受けており、ページが比較的単純であれば、最も無痛な解決策になる可能性があ あなたが私たちの場合に見たように、それはそうではありませんでした。

幸せな印刷!

関連トピック

ノード。初心者のためのjsチュートリアル/@RisingStack