Laravel + React ブラウザだけでPDF をつくる

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、少し前からReactの記事を公開してきていますが、現状で言いますとVue 3よりReactの方が好きになってきている今日この頃です。

そして、そんなReactで今回つくってみたくなった機能は・・・・・・

JavaScript(ブラウザ)だけでPDFをつくる

というものです。

もちろんPHPのようなサーバーサイドでつくるのもいいですが、JavaScriptだけで作成できるとなると、なんなら静的なHTMLだけで実装ができるちゃうわけですね。

そこで❗

今回は「React」で表示されているページをまるごとPDF化してみたいと思います。

ぜひ何かの参考になりましたら嬉しいです。😄✨

「最寄り駅に
ストリートピアノが
設置されました🎹✨」

開発環境: Laravel 9.x、React、Inertia.js、Vite、TailwindCSS

パッケージをインストールする

では、まずは今回の機能に必要になるパッケージをインストールしておきましょう。

以下のコマンドを実行してください。

npm install jspdf --save-dev

ルートをつくる

続いてルートです。
今回は1つだけでOKです。

routes/web.php

Route::get('pdf', fn() => Inertia::render('Pdf/Index'));

jsPDF を日本語化する

PDFの作成部分を書いていく前に、jsPDFを日本語化する準備をしておきましょう。

まずは、以下のページから「ipaexg00401.zip」をダウンロードします。

📝 IPAexフォント Ver.004.01

そして、ダウンロードしたらzipファイルを展開します。

すると、中に「ipaexg.ttf」というフォント用ファイルがあるので、これをjsPDFで使えるように加工します。

では、以下の(本家が用意してくれている)ページへアクセスしてください。

📝 変換ページ

すると、変換フォームが表示されるので以下を参考にして入力&選択し、「Create」ボタンをクリックしてください。

  • fontName: ipaexg(※1)
  • fontStyle: normal
  • Module format: ES modules
  • File: (先ほど展開した ipaexg.ttf をファイルで選択)

※1 好きな名前でOKですが、その場合はその他の部分もすべて変更してください。

すると、自動的に「ipaexg-normal.js」というファイルがダウンロードされますので、このファイルをLaravelの「resources/js/Pages/Pdf/ipaexg-normal.js」へ移動します。

ビューをつくる

では、先ほど設置したファイルも使ってメインのコードを書いていきましょう❗

resources/js/Pages/Pdf/Index.jsx

import {useEffect, useRef} from "react";
import { jsPDF } from "jspdf";
import "./ipaexg-normal"; // 👈 ダウンロードしたファイル

export default function Index() {

    // PDF
    const pdfRef = useRef(new jsPDF());
    const getFileName = () => {

        const timestamp = new Date().getTime();

        return `download_${timestamp}.pdf`;

    };
    const savePdf = () => {

        const target = document.querySelector('#pdf');

        pdfRef.current.html(target, {
            callback(doc) {

                const fileName = getFileName();
                doc.save(fileName); // 👈 ここで PDF をダウンロード

            },
            x: 15,
            y: 15,
            width: 170,
            windowWidth: 650
        });

    };

    // Font
    useEffect(() => {

        pdfRef.current.setFont('ipaexg'); // 👈 フォントを追加

    }, []);

    return (
        <div className="p-5">
            <div id="pdf" className="mb-5" style={{fontFamily: 'ipaexg', lineHeight: '2.5rem'}}>
                <div>ここの内容が PDF 化されます</div>
                <ul>
                    <li className="bg-red-500 p-3">List 1</li>
                    <li className="bg-blue-500 p-3">List 2</li>
                    <li className="bg-yellow-500 p-3">List 3</li>
                </ul>
            </div>
            <button
                type="button"
                className="text-white bg-blue-700 font-medium rounded-full text-sm px-5 py-2.5 text-center mr-2 mb-2 blue-800"
                onClick={() => savePdf()}>
                保存する
            </button>
        </div>
    );

}

この中で重要なのが、ページを読み込んだ時にフォントをセットしているuseEffectの部分です。

useEffect(() => {

    pdfRef.current.setFont('ipaexg');

}, []);

これは、「ipaexg-normal.js」の中でフォントは追加されていますが、適用はされていないので、setFont()を実行する必要があるためです。

また、以下のようにPDF化するタグへのフォント指定も忘れないでください。

<div id="pdf" className="mb-5" style={{fontFamily: 'ipaexg', lineHeight: '2.5rem'}}>

※ なお、Virtual DOMの中でstyleを使うと処理スピードが遅くなるらしいので、本番環境ではCSS内で指定することをおすすめします👍

作業はこれで終了です。
おつかれさまでした❗

テストしてみる

では、実際にテストしてみましょう❗

まずViteを起動し、「http://******/pdf」へブラウザでアクセスします。

上のようなページが表示されるので、「保存する」ボタンをクリックしましょう。

すると、自動的にPDFがダウンロードされます。

ダウンロードしたファイルを開いてみると・・・・・・

はい❗

日本語にもなっていますし、(ちょっとずれている部分はありますが)うまくHTMLの内容がPDF化されています。

成功です。✨😄👍

企業様へのご提案

今回はすべてブラウザ側でPDFを作成しています。

もちろんサーバー側で作成することもできますが、ブラウザ単体でPDF作成するメリットは「負荷の軽減」です。

つまり、サーバー内でPDFを多く作成するとリソース(≒エネルギー)を多く使ってしまうため弱いサーバーですと、最悪ダウンすることも考えられます。

また、AWSなどでは転送料金が比較的高いと言われていますので、できるだけブラウザでできることはブラウザ内で完結させ、省コスト化するというのも戦略のひとつではないでしょうか。

もしそういったご相談がございましたら、いつでもお気軽にお問い合わせからご連絡ください。

お待ちしております❗

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回は「React + jsPDF」でPDFを作成してみました。

よりきれいに出力するためには、CSSを調整する必要があるようでしたが、TailwindCSSも使えるようでしたし、サイトの性質によっては選択肢のひとつになるんじゃないでしょうか。

ぜひみなさんも楽しんでやってみてくださいね。

ではでは〜❗

「名刺に書いてる
メアドにタイポ発見…💦
(教えてくださってありがとうございます)」

このエントリーをはてなブックマークに追加       follow us in feedly