Migaro. 技術Tips

                       

ミガロ. 製品の技術情報
IBMiの活用に役立つ情報を掲載!


JavaScriptでPDF出力を実装する方法

JavaScriptだけでpdfファイルを出力できるような、オープンソースで公開されているライブラリがいくつかあります。今回のTipsでは、ブラウザ側(HTML+JavaScript)でオープンソースのPDF-LIBを使用してPDFファイルを出力する方法をご紹介します。

PDF-LIBとは

PDF-LIBはPDFファイルをJavaScripから出力できるオープンソースのライブラリです。
サーバーサイドのnodejsだけでなく、ブラウザ側(HTML+JavaScript)を使用してPDFを出力することができます。
PDFファイルを作成することもできますし、テンプレートになるPDFファイルを読込み、追記することも可能です。
また、文字列書込みだけでなく、画像を挿入したり、図形を描画することもできる便利なライブラリです。

■PDF-LIB
https://pdf-lib.js.org/

リソースの準備

日本語化対応

PDFに日本語を出力する場合には、日本語フォントを読込む必要があります。
まず、使用したいフォントデータを入手してサーバーに配置してください。

フォントファイルの入手とサーバーへの配置

フォントファイルの入手

PDF-LIBで日本語を扱うためには、フォントファイルを入手する必要があります。
フォントファイルはWebフォントのotf形式やttf形式等を利用できます。

しかしながら、PDFファイルの「埋め込みサブセット」(※1.)を使用する場合には、フォントにより使用可、不可があります。

いくつかのフォントファイルを検証した結果、Tipsでは、「埋め込みサブセット」が使用できた、IPAexゴシックフォントを使用しています。

■文字情報技術促進協議会 IPAexフォント
https://moji.or.jp/ipafont/ipaex00401/

※1.「埋め込みサブセット」とは?

PDFファイルの埋め込みサブセットは、PDFファイル内で使用しているフォントデータのみをPDFファイルに含める方式です。フォントデータ全体をPDFファイルに埋め込むのと比較して、ファイルサイズを大幅に減らすことができます。

フォントファイルの配置

入手したフォントファイルをWebサーバー上に配置します。
Tipsでは、「DocumentRoot」にfontディレクトリを作成してフォントファイルを配置しました。

テンプレートのPDFファイル作成

帳票を作成する場合には、PDFのテンプレートを作成しておいて、JavaScriptでデータを書き込む方が簡単に作成できると思います。
Tipsでは、テンプレートとなるPDFファイルを読込み、文字列を追記する例をご紹介します。

テンプレートのPDFファイルを作成

テンプレートとなるPDFファイルをExcelアプリで作成しています。

テンプレートのPDFファイルを配置

テンプレート用に作成したPDFファイルをWebサーバー上に配置します。
Tipsでは、「DocumentRoot」にpdfディレクトリを作成してPDFファイルを配置しました。

PDF-LibでのPDFファイル出力

PDF-LIBの読込み

PDF-LIBのライブラリはCDN上に公開されていますので、HTML内に外部スクリプトファイル読み込みの定義を追加することで使用できます。
pdf-lib.min.jsがPDF-LIBのライブラリ本体です。
もう一つは、fontkit.umd.jsでPDF-LIBと組み合わせて使用するfontkitになります。

Node.jsで動作するオープンソースのフォント解析ライブラリとして「fontkit」があります。
fontkit.umd.jsは「fontkit」をブラウザで使用できるようにしたライブラリです。
この2つのJavaScriptライブラリを読込みます。
<body>タグの終了タグ前等に記述ください。

例) body 終了タグ前に外部スクリプトファイル読込みを追加

  <script src="https://unpkg.com/pdf-lib@1.4.0/dist/pdf-lib.min.js"></script>
  <script src="https://unpkg.com/@pdf-lib/fontkit/dist/fontkit.umd.js"></script> 

JavaScript でテンプレートのPDFを開き書込み

PDFファイルを読込、テキストを追記する例

例で出力している文字は数値のため、カスタムフォントを使用する必要がありませんが、日本語を出力することを前提に、Tipsではフォントファイルを読込んでいます。

PDF-LIBの使用方法は、公式サイトにサンプルの記載があるため実装に困ることはないと思います。Tipsでは、テンプレートのPDFを開きテキストを追記することを例に説明します。

ボタンの追加

ボタンの追加

まず、PDFを出力するボタンをHTMLに追加します。
クリックした際に、JavaScriptのクリックイベントを呼び出しPDFを作成するためです。

ボタンには、カスタムデータ属性 「data-clickpdf」として追加しました。
属性には=”true”と設定しています。
通常は属性値は不要ですが、属性値を設定しない場合、SmartPad4iのエンジンがカスタムデータ属性を無視してしまうため明示的に設定しています。

カスタム属性「data-clickpdf」に設定されたボタンがクリックされた際に、pdfを作成する処理を実行します。
実行されるプログラムは以下です。

$('[data-clickpdf]').on('click',
  function(){
    // PDFLibのオブジェクト取得
    const {  PDFDocument, rgb } = PDFLib;
    // フォントファイルのパス
    const FONT_URL = '/font/ipaexg.ttf';
    // PDFファイルのパス
    const PDF_URL = '/pdf/Tipsbase.pdf';
    // 非同期関数
    async function createPdf() {
      // テンプレートPDFの取得
      const existingPdfBytes = await fetch(PDF_URL).then(res => res.arrayBuffer());
      // PDFの読み込み
      const pdfDoc = await PDFDocument.load(existingPdfBytes);
      // フォントキットの登録
      pdfDoc.registerFontkit(fontkit);
      // フォントデータの読み込み
      const fontData = await pdfDoc.embedFont(await (await fetch(FONT_URL)).arrayBuffer(),{subset:true});
      // テンプレートのPDFファイル取得
      const pages = pdfDoc.getPages();
      // 1ページ目
      const page = pages[0];
      // PDFの縦横幅取得
      const { width, height } = page.getSize();      
      //文字色
      const font_color_base = rgb(0.1, 0.1, 0.1);

      //伝票番号
      page.drawText($('#IJUBG').val() , {
        x: width - 90,
        y: height / 2 + 320,
        size: 11,
        font: fontData,
        color: font_color_base
      });
      
      // window.openで表示 
      window.open(URL.createObjectURL(new Blob([await pdfDoc.save()],{type: 'application/pdf'})));
    }
    // 非同期関数createPdfの呼び出し
    createPdf();
});

プログラム補足

非同期関数について

async function createPdf() の asyncは非同期関数の定義に使用します。
fontデータの解析や、PDFファイルの作成には時間が掛かるため、非同期として処理をします。
また、createPdfの処理内では、awaitを使用して同期的(※2.)にフォントデータや、PDFデータを読み込んでいます。
※2. ここでの同期的の意味は、処理が完了するまで待つことです。

awaitの記述はasyncの内部でしか使用できないため、createPdfはasyncとして定義します。

PDFの読込み

      // テンプレートPDFの取得
      const existingPdfBytes = await fetch(PDF_URL).then(res => res.arrayBuffer());
      // PDFの読み込み
      const pdfDoc = await PDFDocument.load(existingPdfBytes);

PDFの読込みは、PDF-LIBのPDFDocumentを使用して行います。
PDFDocumentのloadメソッドでbase64エンコードした文字列データからPDFを読込んだり、Uint8Array / ArrayBuffer のバイナリデータからPDFを読み込むことができます。

■PDF-LIB PDFDocument
https://pdf-lib.js.org/docs/api/classes/pdfdocument

■PDF-LIB PDFDocument Static load
https://pdf-lib.js.org/docs/api/classes/pdfdocument#static-load

fetchメソッドでPDFのURLをオープン後、レスポンスをarrayBufferメソッドでバイナリデータにしてexistingPdfBytes変数に格納しています。
次に、PDFDocumentのloadメソッドで読み込んだバイナリを渡してpdfDoc変数にPDFを読込みます。

fontの読込み (fontkit)

      // フォントキットの登録
      pdfDoc.registerFontkit(fontkit);
      // フォントデータの読み込み
      const fontData = await pdfDoc.embedFont(await (await fetch(FONT_URL)).arrayBuffer(),{subset:true});

pdfDoc.registerFontkit(fontkit)は、fontkitの読込みです。
fontkitを読み込むことで、カスタムフォントを使用できるようになり、日本語も出力が可能となります。
fontkit.umd.jsを読み込むと、fontkit グローバル変数が定義されるため、registerFontkitメソッドでfontkitグローバル変数を読み込んでいます。

フォントデータの読み込みはPDFDocumentのembedFontメソッドを使用します。
PDFの読み込みと同様で、fetchメソッドを使用してarrayBufferメソッドでバイナリとして取得しています。
embedFontメソッドの第二引数には、オプションとしてEmbedFontOptionsオブジェクトを設定できます。
{subset:true}を設定することで、PDFファイルに使用する文字データのフォントだけを埋め込むサブセット化ができます。

■PDF-LIB PDFDocument embedFont
https://pdf-lib.js.org/docs/api/classes/pdfdocument#embedfont

PDFファイルの情報取得

      // テンプレートのPDFファイル取得
      const pages = pdfDoc.getPages();
      // 1ページ目
      const page = pages[0];
      // PDFの縦横幅取得
      const { width, height } = page.getSize();   

pdfDocにはテンプレートPDFを読み込んだ情報が格納されています。
getPagesメソッドでPDFページをPDFPageオブジェクトを配列として受け取ることができます。
また取得したPDFPageオブジェクトのgetSizeメソッドでPDFページのサイズ(横幅,縦幅)を取得できます。

■PDF-LIB PDFDocument getPages
https://pdf-lib.js.org/docs/api/classes/pdfdocument#getpages

■PDF-LIB PDFPage
https://pdf-lib.js.org/docs/api/classes/pdfpage

文字の描画

例では、フォントの色を定義後、PDFファイルの右上に伝票番号を書込みました。
文字の書き込みには、drawTextメソッドを使用します。
x座標で横軸、y座標で縦軸の位置を決定、フォントサイズやフォントデータ、カラーを指定しています。

  //伝票番号
  page.drawText("出力する文字列" , {
    x: width - 90,     //  x座標
    y: height / 2 + 320,  //   y座標
    size: 11,              //   フォントサイズ
    font: fontData,        //   フォントデータ
    color: font_color_base //  フォントカラー
  });
PDFファイルの座標系

PDFの座標は一般的なA4サイズのファイルの場合、左下が x=0, y=0になり、右上がx=595,y=842です。
また、PDFではポイントの単位で座標を指定します。

PDFPageのgetSizeメソッドで横幅 (width),縦幅(height)を取得していますので、一を指定して出力しています。
例では、静的な数値を指定していますが、通常は変数等に格納して計算で位置を指定することをお勧めします。

■PDF-LIB PDFPage drawText
https://pdf-lib.js.org/docs/api/classes/pdfpage#drawtext

PDFファイル出力例

JavaScriptで作込むと、HTML画面の要素から情報を取得して、PDFファイルを出力することが可能です。

動画では、既存のアプリケーションのHTML内にiframeを定義して同一画面内でプレビュー表示をするように実装してみました。
動画のように、PDF-LIBを使用するとJavaScriptで簡単にPDFの帳票を作成することができます。

おわりに

JavaScriptのみでPDFファイルが出力できるため、アプリケーションに帳票等を追加したい場合に是非ご活用ください。