jsPDFで帳票をPDF出力しよう

著者名:mura-(Alphasta)

目次

はじめに

このTipsでは、ブラウザー上でPDFを生成するためのJavaScriptライブラリの jsPDF (External link) を使用してkintoneアプリから取得したデータをPDFとして表示する方法を紹介します。
kintoneではレコード印刷画面から手軽に印刷できますが、レイアウトを柔軟に調整したい場合は、jsPDFの活用がおすすめです。
jsPDFを活用することで、帳票など任意のPDFを自由に生成できます。

動作イメージ

このカスタマイズを適用すると、次のようにレコードをPDFとして表示できます。

デモ環境

デモ環境で実際に動作を確認できます。
https://dev-demo.cybozu.com/k/352/ (External link)

ログイン情報は cybozu developer networkデモ環境 で確認してください。

カスタマイズを適用するアプリ

kintoneアプリストアの「商品見積書パック」から、今回のカスタマイズを適用するための見積アプリを追加します。
フィールドコードなどはそのまま利用します。
ただし、PDFのプレビュー表示のために下記 スペースフィールド を追加してください。

要素ID 備考
gen-pdf-button PDF作成ボタン設置のため
preview PDFプレビューのため

PC用のJavaScriptファイル

  1. フォントの用意
    日本語を表示できるように、日本語フォントを用意する必要があります。
    今回のサンプルは IPAexフォント (External link) のIPAexゴシックを利用しています。
    次のページからダウンロードできます。
    IPAex フォント Ver.004.01 | 一般社団法人 文字情報技術促進協議会 (External link)

  2. フォントをJSファイルに変換
    jsPDFの公式で、フォントをJavaScriptに変換するツールが用意されていますので、任意のフォントを用意し、次のページからフォントを変換してください。
    変換が成功するとJavaScriptファイルがダウンロードされます。
    font converter (External link)

  3. アプリへアップロード
    アプリの設定画面で「JavaScript / CSSによるカスタマイズ」を開き、JavaScriptファイルを指定します。

    1. jsPDFのライブラリ
      今回は執筆時点で最新のバージョンv3.0.1を利用しています。
      https://cdnjs.cloudflare.com/ajax/libs/jspdf/3.0.1/jspdf.umd.min.js
    2. フォントのJavaScriptファイル
      手順2の「フォントをJSファイルに変換」でダウンロードしたファイルです。
      このサンプルでは、ファイル名をipaexg-normal.jsとします。
    3. JavaScriptカスタマイズファイル
      後述のサンプルコードをエディターにコピーして、ファイルの拡張子を「.js」、エンコードを「UTF-8」として保存してください。
      このサンプルでは、ファイル名をindex.jsとします。

サンプルコード

以下のサンプルコードを参考に記述します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
(() => {
  'use strict';

  // kintoneの詳細画面が表示されたときに実行されるイベント
  kintone.events.on('app.record.detail.show', event => {
    const record = event.record;
    generatePDFWithJsPDF(record);
  });

  // 作成画面・編集画面にPDF作成ボタンを追加
  kintone.events.on(['app.record.create.show', 'app.record.edit.show'], event => {
    const spaceElement = kintone.app.record.getSpaceElement('gen-pdf-button');
    const button = document.createElement('button');
    button.innerText = 'PDF作成';
    button.onclick = () => {
      const record = kintone.app.record.get();
      generatePDFWithJsPDF(record.record);
    };
    spaceElement.appendChild(button);
  });

  // PDFのヘッダー部分を生成する関数
  const createHeaderSection = (doc, record) => {
    doc.setFontSize(22);
    doc.text('お見積書', 105, 20, {align: 'center'});

    doc.setFontSize(12);
    doc.text(record.宛名.value, 20, 40);
    doc.text('様', 80, 40);

    doc.text('見積番号', 150, 40);
    doc.text(record.見積番号.value, 170, 40);
    doc.text('見積日', 150, 50);
    doc.text(record.見積日.value, 170, 50);
  };

  // テーブルのヘッダー部分を生成する関数
  const createTableHeader = doc => {
    doc.setFillColor(240, 240, 240);
    doc.rect(20, 60, 170, 10, 'F');
    doc.setDrawColor(0, 0, 0);
    doc.rect(20, 60, 170, 10);

    doc.setFontSize(11);
    ['型番', '商品名', '単価', '数量', '小計'].forEach((text, i) => {
      const x = [35, 75, 105, 135, 165][i];
      doc.text(text, x, 66, {align: 'center'});
    });

    [50, 100, 120, 150].forEach(x => doc.line(x, 60, x, 70));
  };

  const generatePDFWithJsPDF = async record => {
    const {jsPDF: JSPDF} = window.jspdf;
    const doc = new JSPDF();

    // アップロードしたフォントに応じて書き換えてください
    doc.setFont('ipaexg');

    createHeaderSection(doc, record);
    createTableHeader(doc);

    // 明細行の生成
    const details = record.見積明細.value;
    details.forEach((detail, index) => {
      const y = 70 + (index * 10);
      if (index % 2 === 1) {
        doc.setFillColor(250, 250, 250);
        doc.rect(20, y, 170, 10, 'F');
      }
      doc.setDrawColor(0, 0, 0);
      doc.rect(20, y, 170, 10);
      [50, 100, 120, 150].forEach(x => doc.line(x, y, x, y + 10));

      doc.text(detail.value.型番.value || '', 25, y + 6);
      doc.text(detail.value.商品名.value || '', 55, y + 6);
      doc.text(detail.value.単価.value || '', 115, y + 6, {align: 'right'});
      doc.text(detail.value.数量.value || '', 145, y + 6, {align: 'right'});
      doc.text(detail.value.小計.value || '', 185, y + 6, {align: 'right'});
    });

    const tableHeight = 70 + (details.length * 10);

    // 合計と備考の表示
    doc.setFillColor(240, 240, 240);
    doc.rect(120, tableHeight + 15, 70, 10, 'F');
    doc.rect(120, tableHeight + 15, 70, 10);
    doc.text('合計金額', 125, tableHeight + 22);
    doc.text(record.合計金額.value, 185, tableHeight + 22, {align: 'right'});

    doc.text('備考', 20, tableHeight + 40);
    doc.rect(20, tableHeight + 45, 170, 20);
    doc.text(record.備考.value || '', 25, tableHeight + 55);

    // プレビュー表示
    const previewDiv = kintone.app.record.getSpaceElement('preview');
    const iframe = document.createElement('iframe');
    iframe.style.width = '100%';
    iframe.style.height = '500px';
    iframe.src = doc.output('datauristring');
    previewDiv.innerHTML = '';
    previewDiv.appendChild(iframe);
  };
})();

サンプルコードの解説

jsPDFについて

サンプルコードで使用しているjsPDFの主なメソッドについては、以下の公式ドキュメントで詳細を確認できます。

主によく使用するメソッドは次のとおりです。

  • doc.text(text, x, y, options): テキストの描画
  • doc.rect(x, y, width, height, style): 四角形の描画
  • doc.line(x1, y1, x2, y2): 直線の描画
  • doc.setFontSize(size): フォントサイズの設定
  • doc.setTextColor(r, g, b): テキスト色の設定
  • doc.setFillColor(r, g, b): 塗りつぶし色の設定
  • doc.setDrawColor(r, g, b): 線の色の設定

PDFの生成方法について

jsPDFでPDFを生成する方法はいくつかあります。

  1. 座標指定による方法(本サンプルで使用)

    • 要素の位置をX,Y座標で直接指定できる。
    • ファイルサイズが小さく、テキストの品質が高い。
    • レイアウトを調整しづらい。
  2. HTML/Canvasからの変換

    • HTML要素をCanvasに変換してPDF化する。(html2canvasなどと組み合わせ)
    • CSSでレイアウトを調整できるため実装しやすい。
    • 画像として変換されるため、ファイルサイズが大きくなる。

本サンプルではコードをシンプルにするため、座標指定による方法を採用していますが、より複雑な帳票にしたいなど必要に応じてhtml2canvas等を利用したり、他のPDFライブラリも検討してみてください。

座標系について

jsPDFでは、PDFページ上の位置を座標で指定します。

  • 原点(0,0)は左上
  • X座標は右に向かって増加
  • Y座標は下に向かって増加
  • 単位はミリメートル(mm)

たとえば、doc.text('お見積書', 105, 20)は、以下の位置にテキストを配置します。

  • X座標: 105mm(ページの中央付近)
  • Y座標: 20mm(ページ上部)

コードの構成

コードは大きく分けて4つの主要な部分で構成されています。

  1. 関数の定義

    • createHeaderSection: PDFのヘッダー部分(タイトル、宛名、見積番号など)を生成
    • createTableHeader: 明細テーブルのヘッダー行を生成
    • generatePDFWithJsPDF: PDF全体の生成を制御する主要な関数
  2. ヘッダー部分の生成(createHeaderSection)

    1
    2
    3
    4
    5
    
    const createHeaderSection = (doc, record) => {
      doc.setFontSize(22);
      doc.text('お見積書', 105, 20, {align: 'center'});
      // ...
    };
    • フォントサイズを設定し、中央そろえでタイトルを配置
    • 宛名、見積番号、見積日を適切な位置に配置
  3. テーブルヘッダーの生成(createTableHeader)

    1
    2
    3
    4
    5
    
    const createTableHeader = doc => {
      doc.setFillColor(240, 240, 240);
      doc.rect(20, 60, 170, 10, 'F');
      // ...
    };
    • グレーの背景色を設定
    • 列名(型番、商品名など)を配置
    • 縦線で列を区切
  4. 明細行の生成と表示(generatePDFWithJsPDF内)

    1
    2
    3
    4
    5
    6
    7
    
    details.forEach((detail, index) => {
      const y = 70 + (index * 10);
      if (index % 2 === 1) {
        doc.setFillColor(250, 250, 250);
        // ...
      }
    });
    • 偶数行と奇数行で背景色を変えて視認性を向上
    • 各セルの値を適切な位置に配置
    • 右寄せ/左寄せを使い分けて数値を見やすく表示
  5. プレビュー表示の実装

    1
    2
    3
    4
    
    const iframe = document.createElement('iframe');
    iframe.style.width = '100%';
    iframe.style.height = '500px';
    iframe.src = doc.output('datauristring');
    • iframeを使用してPDFをプレビュー表示
    • doc.output('datauristring')を使用してブラウザー上で直接表示

動作確認

カスタマイズを適用したアプリを開き、レコードを作成・編集し、PDF作成ボタンを押すことでPDFが正しく表示されることを確認します。

おわりに

このTipsでは、jsPDFと連携させてPDFを表示する方法を紹介しました。
今回は簡易的な見積書を作成しましたが、これを応用することで、より複雑な帳票を作成可能です。

information

このTipsは、2025年4月版kintoneで動作を確認しています。