HTMLからPDFへの変換のための純粋なクライアント側と純粋なサーバー側の実装ソリューション

HTMLからPDFへの変換のための純粋なクライアント側と純粋なサーバー側の実装ソリューション

必要

ユーザーがフォームに入力して「保存」をクリックすると、PDF ドキュメントを直接ダウンロードできます。

解決

サーバー世代

アイデア

Google Chromeは2017年にChrome Headless機能を独自開発し、同時にPuppeteerをリリースしました。インターフェースがないブラウザでありながら、サーバー機能を完備していると言えます。

したがって、サーバー上で Puppeteer ブラウザを起動し、ターゲット URL を開き、Chrome ブラウザの組み込み変換機能を使用して HTML を PDF に変換できます。

サーバーがコアコードを生成する

まず、puppeteer をインストールする必要があります。NPM のインストールは失敗する可能性があります。cnpm Taobao ミラーを使用してインストールするのが最適です。

依存関係をインストールするには、 cnpm i puppeteer -Sと入力します。

js ファイルを作成し、Puppeteer ブラウザで URL を開いて PDF を保存します。

// html2pdf.js

'puppeteer' という定数が必要です。
(非同期関数(){
    // サービスを開始します const browser = await puppeteer.launch();
    // タブを開く const page = await browser.newPage();
    // このアドレスに移動します await page.goto('https://koa.bootcss.com/#context');
    // HTML ページを PDF に変換し、パスに保存します
    page.pdf({path:"test.pdf",format:'A4'}) を待ちます
    // ブラウザを閉じる await browser.close();
})();

次に、コンソールにnode html2pdf.jsと入力してサービスを開始します。

もちろん、ビジネス ロジックに応じて module.export を使用してモジュール メソッドをエクスポートすることもできます。

欠点

動的フォームデータを保存できません

ページはサーバーから要求されるため、ユーザー入力が要求アドレスに保存されていない場合、傍受された PDF は入力されていないページの初期状態になります。

つまり、静的なページしか変換できませんが、私たちの要件では大量のユーザー入力が必要なので、合格です。

クライアントがコアコードを生成する

アイデア

  • html2canvasを使用して、変換する必要があるDOMノードを入力し、それをトラバースしてキャンバスに変換します。
  • キャンバスを base64 イメージに変換し、jsPDF を使用して PDF ファイルを作成し、そのイメージを PDF に挿入します。

欠点

ねじれ。

ページのスクリーンショットを撮って、それを PDF に挿入するのと似ているため、ページの解像度と構成が出力画像の品質に影響を与える可能性が高いことがわかります。

同時に、スクリーンショットであるため、ページリンクなどの機能が失われる可能性があります。

テキストの切り捨て

キャンバスが PDF ページのサイズより大きい場合、出力は正しくありません。このとき、キャンバスが A4 サイズを超えているかどうかを判断する必要があります。超えている場合は、キャンバスを分割して別のページに挿入します。

ここで再び問題が発生します。画像がセグメント化されているため、キャンバス内のアイテムの構造を分析できず、画像またはテキストが半分に切り取られる可能性が非常に高くなります。

コアコード

私たちのニーズには画像やリンクは含まれていないため、歪みの問題はほとんど影響しません。同時に、私たちのフォームは同じ長さの複数の繰り返し項目で構成されており、これらの項目は非常に短く、A4 用紙を超えることはありません (これは厳密ではありませんが、必要に応じて、DOM 要素の幅と高さを取得し、DOM 要素の高さに応じて切り抜くことができます)。

そこで、アイテムごとにキャンバスを直接分割し、各アイテムを A4 用紙 1 ページに保存する予定です。

始める前に理解しておく必要があるコアメソッドがいくつかあります。

html2キャンバス

   // DOMは変換されるDOMノードです html2canvas(DOM,{
        背景色:"#ffffff",
        幅:幅、
        高さ:高さ、
        スケール:2,
        汚染を許可する:true、
    }).then((キャンバス)=>{
        // キャンバスは変換が成功した後のキャンバスです})

jsPDF

   // インスタンスを作成します。let pdf = new jsPDF('','pt','a4');
    // 画像を PDF ファイルに追加します // 最初のパラメーターは挿入するファイル形式 (base64)、2 番目はファイル形式です // 3 番目と 4 番目は画像の左上隅の座標、最後の 2 つは挿入後の画像の幅と高さです pdf.addImage(image,'JPEG',10,10,height,width);
    // 新しいページを追加する pdf.addPage()
    // PDFファイルを保存する pdf.save()

キャンバス

  // canvas は切り取る画像です // sx、sy は切り取りを開始する座標です // swidth、sHeight は切り取りの幅と高さです // dx、dy は切り取った画像を canvas に挿入する座標です // sWidth、sHeight は切り取った画像の canvas 内での幅と高さです cxt.drawImage(canvas, sx, sy, sWidth, sHeight, dx, dy, sWidth, sHeight);
/**
 * @description: フォームを PDF ファイルに変換します* @return: pdf
 */
送信(){
    // これは変換したいフォームです。このフォームには同一のフォームが多数あります。let form = this.$refs.form;
    // 要素の幅と高さを取得します。let width = form.getBoundingClientRect().width;
    高さを form.getBoundingClientRect().height に設定します。
    html2canvas(フォーム,{
        背景色:"#ffffff",
        幅:幅、
        高さ:高さ、
        スケール:2,
        汚染を許可する:true、
    }).then((キャンバス)=>{
        pdf = new jsPDF('','pt','a4');
        // 画像を切り取ります let canvasList = this.splitCanvas(canvas,this.forms.length);

        // キャンバスリストを走査し、各ページに画像を追加します canvasList.forEach((item,index)=>{
            // 画像フォーマットをbase64に変換する
            itemImage を item.toDataURL('image/jpeg',1.0); とします。
            // 10px の余白を確保します。A4 用紙の幅は 72 解像度のモニターでは 595px です。
            pdf.addImage(itemImage,'JPEG',10,10,575.28,575.28/item.width*item.height);
            // 最後のページでない場合は、ページ区切り index == this.forms.length-1 ? '' : pdf.addPage();
        })
        // ファイルを保存します。let blob = pdf.output('blob');
        
        pdf.save('test.pdf');
    })
},
/**
 * @description: キャンバスをカット * @param {number} num スライスの数 * @param {canvas} キャンバス 
 * @return {array} キャンバスリスト*/
splitCanvas(キャンバス,数値){
    高さを canvas.height、幅を canvas.width とします。
    let chunkHeight = height/num; // 各スライスの高さ let chunkList = []; // 結果のキャンバスを保存
    for(let i=0; i<height; i+=chunkHeight){
        // 切り抜き四角形の位置を初期化します。let sx = 0,sy = i,sWidth = width,sHeight = chunkHeight,dx = 0, dy = 0;
        // キャンバス ノードを作成します。let canvasItem = document.createElement("canvas");
        // キャンバス サイズを初期化します。canvasItem.height = chunkHeight;
        キャンバスアイテムの幅 = 幅;
        cxt = canvasItem.getContext("2d"); とします。
        // 切り取った画像を新しいキャンバス ノードに配置します。cxt.drawImage(canvas,sx,sy,sWidth,sHeight,dx,dy,sWidth,sHeight);
        chunkList.push(キャンバスアイテム); 
    }
    chunkList を返します。
},

最終結果

フォームを保存した後のページ

PDFへの変換の効果

これで、HTML から PDF への純粋なクライアント側および純粋なサーバー側の実装ソリューションに関するこの記事は終了です。HTML から PDF への関連コンテンツの詳細については、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

<<:  MySQL に外部キー制約を追加する具体的な方法

>>:  Dockerリンクはコンテナの相互接続を実現します

推薦する

このリファレンスとJavaScriptのカスタムプロパティの詳細な説明

目次1. このキーワード2. カスタム属性3. 包括的なケース1:タブの実装付録要約する1. このキ...

Linux に nodejs 環境とパス構成をインストールするための詳細な手順

Linux に Node.js をインストールする方法は 2 つあります。1 つは簡単で、解凍して使...

Vue3+el-tableは行と列の変換を実現します

目次行と列の変換トランスクリプトの構成を分析するvue3 + el-table で作成されたトランス...

Linuxターミナルでファイルを作成する2つの一般的な方法を簡単に理解する

mkdir コマンドを使用して新しいディレクトリを作成できることは誰もが知っていますが、多くの場合、...

WeChatアプレットがログインインターフェースを実装

WeChatアプレットのログインインターフェースは参考までに実装されています。具体的な内容は次のとお...

Expressプロジェクトファイルディレクトリの説明と詳細な機能の説明

app.js: スタートアップファイル、またはエントリファイルpackage.json: プロジェク...

最も完全な 50 の MySQL データベース クエリ演習

このデータベース クエリ ステートメントは、インターネット上の 50 個のデータベース クエリ練習問...

Nginx リバース プロキシ学習例チュートリアル

目次1. リバースプロキシの準備1. LinuxシステムにTomcatをインストールする2. Tom...

JavaScript におけるイベント委譲メカニズムと深いコピーと浅いコピーの簡単な分析

目次1. イベントの委任イベントバブリングイベントキャプチャイベントの泡立ちの昇華考える2. 深いコ...

Linux コマンドラインのワイルドカードとエスケープ文字の実装

ハードディスクのファイル属性のバッチ表示など、特定の種類のファイルに対してバッチ操作を実行する場合、...

この記事ではSQL CASE WHENの使い方を詳しく説明します

目次シンプルな CASEWHEN 関数:これは、CASEWHEN 条件式関数を使用するのと同じです。...

JavaScript スクリプトが実行されるタイミングの詳細な説明

JavaScript スクリプトは HTML 内のどこにでも埋め込むことができますが、いつ呼び出され...

Ubuntu 18.04 Server バージョンのインストールと使用方法 (画像とテキスト)

1 システムのインストール手順OSバージョン:1804イメージのダウンロード: http://cd...

MySQLインスタンスクラッシュ事例の詳細な分析

[問題の説明]私たちの実稼働環境には、複数の MySQL サーバー (MySQL 5.6.21) の...

Node.jsはブレークポイント再開を実装する

目次ソリューション分析スライス履歴書のダウンロード具体的な解決プロセス論理的分析フロントエンドサーバ...