0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React】html2canvas と jsPDF を使って作成するPDFにヘッダーとフッターを追加する

Posted at

はじめに

この記事では、html2canvas と jsPDF を使って作成するPDFにヘッダーとフッターを追加する方法を記載します。

WebページをPDF化する際、ページ全体にヘッダーやフッター を追加すると、ドキュメントとしての見栄えや分かりやすさが向上します。
今回は html2canvas でHTMLコンテンツを画像化し、jsPDF でPDFに変換する際、各ページにタイトル(ヘッダー)とページ番号(フッター)を付与する方法を記載します。

開発環境

開発環境は以下の通りです。

  • html2canvas 1.4.1
  • jspdf 2.5.2
  • Windows11
  • React 18.3.1
  • TypeScript 5.7.3
  • Node.js 22.13.1
  • npm 11.0.0

実装

以下のサンプルコードでは、長いコンテンツを複数ページに分割しつつ、各ページにヘッダー・フッターを追加しています。

PdfExportWithPageSplitting.tsx
 import { FC, useRef } from "react";
import html2canvas from "html2canvas";
import jsPDF from "jspdf";

const PdfExportWithHeaderFooter: FC = () => {
  /** PDF化する対象の要素を参照するための useRef */
  const contentRef = useRef<HTMLDivElement>(null);

  /** PDFダウンロード処理 */
  const handleDownloadPdf = async () => {
    if (!contentRef.current) return;

    try {
      // 1️. 指定した要素をキャプチャしてCanvasに変換
      const canvas = await html2canvas(contentRef.current);
      // 2️. Canvasを画像として取得(Base64のPNGデータ)
      const imageData = canvas.toDataURL("image/png");

      // 3️. jsPDF インスタンスを作成(A4縦向き)
      const pdf = new jsPDF("p", "mm", "a4");
      // 4️. PDFの幅と高さを取得
      const pdfWidth = pdf.internal.pageSize.getWidth();
      const pdfHeight = pdf.internal.pageSize.getHeight();

      // 5️. ヘッダーとフッターの高さ(mm)を設定
      const headerHeight = 15;
      const footerHeight = 15;
      // 6️. 画像の幅と高さを取得
      const imageWidth = canvas.width;
      const imageHeight = canvas.height;
      // 7️. 画像のスケール比を計算(PDFの幅に合わせる)
      const ratio = pdfWidth / imageWidth;
      // 8️. 画像全体をPDFに合わせた場合の高さを計算
      const scaledHeight = imageHeight * ratio;
      // 9️. 1ページあたりの描画可能な高さを計算(ヘッダーとフッター分を除く)
      const availableHeight = pdfHeight - headerHeight - footerHeight;

      /** 現在の描画開始位置(スケール後の画像上でのY座標) */
      let position = 0;
      /** 現在のページ番号 */
      let pageNumber = 1;

      // 10️. 画像全体が描画されるまでループ
      // 条件:position(描画開始位置)がscaledHeight(スケール後の画像全体の高さ)より小さい間
      while (position < scaledHeight) {
        // 11. 画像を追加
        // - x: 0(左端から配置)
        // - y: headerHeight - position とすることで、ヘッダーの下から画像の適切な部分を描画
        // ※ position が増えると画像が上にシフトし、次ページ以降は前ページの続きが描画される
        pdf.addImage({
          imageData: imageData,
          format: "PNG",
          x: 0,
          y: headerHeight - position,
          width: pdfWidth,
          height: scaledHeight,
        });

        // 12. ヘッダーを追加(ページ上部に中央配置)
        pdf.setFontSize(8);
        pdf.text("PDF Header", pdfWidth / 2, headerHeight / 2 + 3, {
          align: "center",
        });

        // 13. フッターを追加(ページ下部に中央配置、ページ番号を表示)
        pdf.setFontSize(8);
        pdf.text(
          `Page ${pageNumber}`,
          pdfWidth / 2,
          pdfHeight - footerHeight / 2,
          { align: "center" }
        );

        // 14️. 次のページへ移行するために、position を1ページ分の利用可能な高さ分進める
        position += availableHeight;
        pageNumber++; // ページ番号を更新

        // 15️. まだ描画していない画像部分が残っている場合は新しいページを追加
        if (position < scaledHeight) {
          pdf.addPage();
        }
      }

      // 16️. PDFを保存
      pdf.save("document.pdf");
    } catch (error) {
      console.error("PDF生成中にエラーが発生しました:", error);
    }
  };

  return (
    <div>
      {/* PDFに変換する対象のエリア */}
      <div
        ref={contentRef}
        style={{
          padding: 24,
          color: "blue",
          backgroundColor: "white",
          width: 500,
          margin: "0 auto",
        }}
      >
        <h2>aaaPDF化するコンテンツ</h2>
        <p>
          この部分がPDFとして出力されます。試しに長い文章を追加してみましょう。
        </p>
        {[...Array(100)].map((_, i) => (
          <p key={i}>
            さらに長いコンテンツを追加して、ページ分割が発生することを確認します。
          </p>
        ))}
      </div>

      {/* PDFダウンロードボタン */}
      <button
        onClick={handleDownloadPdf}
        style={{ color: "blue", backgroundColor: "white" }}
      >
        PDFをダウンロード
      </button>
    </div>
  );
};

export default PdfExportWithHeaderFooter;

動作確認

動作確認をします。「PDFをダウンロード」ボタンをクリックすると、ヘッダーとフッターが各ページに表示されるPDFファイルとして出力されます。
ただ、あまり見栄えがよい感じにはなっていないです。

Screen-Recording-2025-02-15-182916.gif

関連記事

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?