Slide 1

Slide 1 text

なぜブラウザで 帳票を生成したいのか terurou 2025-11-16 / JSConf JP どのようにブラウザで 帳票を生成するのか

Slide 2

Slide 2 text

デンキヤギ株式会社 • 名古屋の零細ソフトウェア開発会社 • GitHubに初めて就業規則を公開した会社らしい 1

Slide 3

Slide 3 text

2

Slide 4

Slide 4 text

terurouと帳票システム • [2005] セミナー受講システム : 修了証印刷 • PHP4.3の時代にFPDFをフォークして、縦書きサポート+高速化 • [2011] 物流 倉庫管理システム • wkhtmltopdf (CSS組版) ※今ならheadless Chrome • [2014] 販売管理システム • kintone +請求書SaaS(Misoca) • [2016] 金融系システム • JasperReports など 3

Slide 5

Slide 5 text

4 ざっくり、このセッションは、 なんでyagisan-reports作ったの??? という内容になります。

Slide 6

Slide 6 text

予備知識と前提 5

Slide 7

Slide 7 text

帳票とは? 実は明確は定義はないが…、 請求書が一番イメージしやすい • ビジネスにおける指示や記録を残す定型文書 • 紙かPDFで、書籍や資料とは異なるもの • 宛名ラベルや業務日報なども帳票と扱うことは多い 6

Slide 8

Slide 8 text

帳票が登場するビジネス領域の一例 • 経理 • 請求書、稟議書、決算、… • 物流 • 配送指示書、宛名ラベル、… • 製造業(FA) • 生産指示書、検査報告書、… • 監督省庁への届出・申告 • 医療、介護、建築、土木、 不動産、金融、… • 公共・行政 • 教育 • その他一般業務 など 7

Slide 9

Slide 9 text

このセッションでの前提(カバー範囲) • Webシステムに帳票機能を組み込む話 • PDF帳票のみに限定して、紙・印刷は含めない • 「Webシステムで帳票出力」と「印刷主体のシステム」では 求められる要素が全く異なる • 例: 物流倉庫のオートメーションなど 8

Slide 10

Slide 10 text

なぜブラウザで帳票を 生成したいのか? 9

Slide 11

Slide 11 text

10 楽したいし、 コストは安い方がいい

Slide 12

Slide 12 text

11 順に説明していきます

Slide 13

Slide 13 text

WebシステムでPDFを出力する方法 従来的な方法では、以下の2択 • サーバーサイドでPDF生成 • 商用製品やOSSなど、サーバーサイドでPDF生成 • ブラウザのPDF印刷 (Microsoft Print to PDFなど) • Amazonを筆頭にECサイトでよく見る • HTML/CSSでレイアウト 12

Slide 14

Slide 14 text

帳票サーバーの負荷対策で泥沼化しがち • 設計難易度が上がって、対応できる人が減る • 水平分散にするか、ジョブキューにするか、バッチにするか… • 技術検証、サイジング、テストなども必要 • 帳票出力が非同期になるので、UIにどう組み込む? • 出力依頼を出しておいて、完了したらメールで通知? 13

Slide 15

Slide 15 text

サーバーを増やせば、インフラコストもかかる • 単純にサーバー台数の分だけコストがかかる • 商用帳票製品では、サーバー台数分のライセンスが必要に • FaaSを使ったときにライセンス数ってどう数えるんだっけ… • 監視環境やリカバリーも必要 • 一度構築したら数年稼働させるとかザラにある 14

Slide 16

Slide 16 text

15 サーバーサイドでPDF生成することは 人件費もランニングコストがかかるし、 設計難易度も決して低くはない

Slide 17

Slide 17 text

ブラウザのPDF印刷 低コストで有用だが、使えるケースが限定的 • 実行環境に依存して、描画結果が変わってしまう • 全ての環境で同じように描画するには、膨大なテストが必要 • 素朴なレイアウトに留めないと大変なことに • ECの場合、適格請求書の必要事項が記載されていれば、 表示が崩れても経費処理は可能 • ユーザー要件 だが、ダサいのはダサい… 16

Slide 18

Slide 18 text

17 ブラウザのPDF印刷は使えるシーンが限定的だが、 帳票用のサーバーが要らないことがすごく良い

Slide 19

Slide 19 text

18 JavaScriptで動く帳票エンジンがあれば PDF印刷と同じメリットが!

Slide 20

Slide 20 text

19 というノリで作ってるのが

Slide 21

Slide 21 text

ちなみにブラウザでのPDF生成に失敗したら? • エラーが出たときだけ、サーバーでPDFを作ればよい • 大半はブラウザで処理できるので、サーバーは最小限に 20 PDF生成 PDF生成 ブラウザ バックエンド 帳票生成 リクエスト エラー時のみ フォールバック

Slide 22

Slide 22 text

どのようにJavaScriptで 帳票を生成するのか 21

Slide 23

Slide 23 text

ブラウザ上で動作するPDFライブラリ • JavaScript/TypeScript実装のOSS • pdfme ※この中ではかなり高機能 • react-pdf • pdf-lib • PDFKit など • C/C++やRustなどのWASMで使えるライブラリ • 有償のプロプラエタリ製品もいくつかある 22 数年前と比べると、 選択肢が増えてきた

Slide 24

Slide 24 text

yagisan-reportsでのPDF生成 • pdf-libをベースに実装 • pdfmeの他、他社の商用帳票製品でも使われてるらしい • ただし、将来的には自前実装に切り替えたい • pdf-libとは • OSS、pure TypeScript • PDFの生成だけでなく、編集(追記、ページ削除など)も可能 23

Slide 25

Slide 25 text

日本語帳票エンジンに求められる要件 • 日本語サポート • 日本語フォント • 異体字(IVS) • 縦書きテキスト • 高レベルなレイアウトシステム • 動的明細(動的テーブル) • 自動改ページ 24

Slide 26

Slide 26 text

JavaScript製ライブラリの日本語サポート状況 • 日本語フォントには概ね対応しているが… • 異体字、縦書き対応がNG • 対応しているOSSはおそらくない • 人名・社名などで異体字は結構使われている • 特に社名は正しく描画しないと「失礼にあたる」的な話も… 25

Slide 27

Slide 27 text

異体字に対応するには? • fontkitが異体字(IVS)を実装していない • 大半のJS製PDFライブラリがフォント操作に使っている • https://github.com/foliojs/fontkit • これをフォークしたら対応できるのでは? • やった → https://github.com/DenkiYagi/fontkit • yagisan-reports用にかなりカスタマイズしてるので、 オリジナル版とAPI互換がありません… 26

Slide 28

Slide 28 text

縦書きに対応するには • これもpdf-libが実装してない • フォークして対応するしかない… • やった • https://github.com/DenkiYagi/pdf-lib • これもだいぶカスタマイズしてるので、オリジナルと互換なし… 27

Slide 29

Slide 29 text

高レベルなレイアウトシステム 動的明細と自動改ページがないと、請求書でさえ厳しいが 対応しているライブラリはほぼ無い 28

Slide 30

Slide 30 text

pdf-libでの帳票レイアウト • pdf-libでは低レベルAPIしか提供されていない • addPage(), drawText(), drawLine() など • 「この座標に」「文字を書く」「線を引く」みたいな感じ • 低レベルAPIだけでは、請求書を作るだけでも大変 • 単純に実装・テストの工数がかかる • 誰でもできるわけではないので、仕事が分散できない • 例えば1年後に微修正がきたとしても、自分でも覚えてない… 29

Slide 31

Slide 31 text

動的明細を自前で実装する シンプルな実装であれば、そこまで難しくはない • 行と列の数から、罫線やテキスト描画位置を計算する • 技術的な難しさよりも、ひたすら計算が面倒なだけ 30

Slide 32

Slide 32 text

動的明細を高機能にすると面倒に • 明細の文字数が多い場合、当該行だけ複数行表示にする or 縮小して1行に収まるようにする • 文字列+フォント設定から必要な描画サイズを取得して計算 31

Slide 33

Slide 33 text

改ページを自前で実装する 基本概念は難しくない • コンテンツがページに収まらなかったら改ページ • 「要素の途中で改ページが必要になった場合」を ひたすら対応していけば実装できる • ケースの洗い出しとテストが地獄 32

Slide 34

Slide 34 text

改ページの例: 簡単なパターン 複数行テキストの途中で改ページが必要になった場合、 テキストが見切れない位置に調整する 33 あいうえこ かきくけこ さしすせそ たちつてと ページ下端 あいうえこ かきくけこ さしすせそ たちつてと 1ページ目 2ページ目

Slide 35

Slide 35 text

改ページの例: ややこしいパターン グリッドの途中で改ページ 34 あいうえお 12345 かきくけこ さしすせそ たちつてと なにぬねの 67890 ページ下端 改ページしたい位置は ケースバイケース…

Slide 36

Slide 36 text

その他に改ページ関連で出てくること • 動的明細を改ページしたら、ヘッダーを全ページに付ける • でも小計は最後のページにだけしか表示したくない • 強制的に任意位置で改ページしたい • レイアウトを変えたい(例えば、表紙+本体) • どうしても「ちょうどいい改ページ位置」が決められない パターンはどのように振舞うべきか • ページサイズに収まらないようなサイズの画像の挿入など 35

Slide 37

Slide 37 text

改ページ制御は地獄 • 人間が気軽に手を出してよい領域ではない • 商用帳票製品でも実装してないものは多数ある • 安い製品はだいたい逃げている 36

Slide 38

Slide 38 text

37 基礎部分はこんな感じだが、 これだけは足りていない

Slide 39

Slide 39 text

帳票テンプレート(レイアウト記述言語) • 人間が扱いやすい帳票テンプレートが必要不可欠 • 低レベルAPIをラップするレイアウト記述言語が必要 • 商用帳票製品は専用GUIデザイナーを用意しがち • GUIはあれば、もちろん便利だが… • 普段からHTMLやJSXを書いているWebの人間の感覚では、 GUIしかないのは面倒に感じる 38

Slide 40

Slide 40 text

yagisan-reportsでのレイアウト記述言語 • XMLベースの帳票レイアウト記述言語を自前実装 • HTMLやJSXに慣れてるWebの人間には親しみやすい • 最初はJSONで設計していたが、思ってた以上に読みづらい • XAMLやAndroidのLayout XMLに似た構文 • コンポーネントのネストにも対応 39

Slide 41

Slide 41 text

蛇足: あとから生成AIが来た • XMLでレイアウトを記述する仕様にしたおかげで、 生成AI(Coding Agent)対応で非常に有利になった • デンキヤギブースでAI生成デモを展示中 • 内部的にはMCP Serverを作ってるだけ • ただし、MCP Serverには工夫が必要 40

Slide 42

Slide 42 text

パフォーマンスチューニング 数ページ程度なら性能問題はまず出ないが、 数百ページ単位になると話が別 • JITが効くコード • ループの最適化 • メモリー管理、データ配置の最適化 • GCの抑制 など地味な積み重ね 41

Slide 43

Slide 43 text

品質保証: 継続的テスト • ユニットテスト、機能テストは当然やる • ビジュアルリグレッションテスト • 生成したPDFを画像化して、旧バージョンとの比較 • パフォーマンスリグレッションテスト • Valgrind、Cachegrindを使ってブレが出ないように計測 42 リグレッションテストは開発してると 度々ひっかかります…

Slide 44

Slide 44 text

その他、日本語帳票で言われがちなこと • Excelで作られた「罫線とセル結合のおばけ」 • このレイアウトで、動的に行が増える箇所がある • 厚生労働省管轄(医療・介護・社会保険など)の帳票でありがち • 1セルに1文字ずつ入れる • 選択肢は 〇 で囲む • 印影をちょっと文字に重ねたい 43

Slide 45

Slide 45 text

「罫線とセル結合のおばけ」の例 44

Slide 46

Slide 46 text

「罫線とセル結合のおばけ」の例 45

Slide 47

Slide 47 text

まとめ 46

Slide 48

Slide 48 text

47 帳票エンジンは買った方がいいよ!

Slide 49

Slide 49 text

帳票エンジンは買った方が良い理由 • OSSなどで安く上げようとしても、簡単にはいかない • サーバーサイド生成では、負荷分散とか監視とか大変 • 低レベルなライブラリから作るのと工数が爆発する • 長期で保守する必要があるのに、属人性が生まれがち • そもそもみんな帳票に思い入れはないよね 48

Slide 50

Slide 50 text

どうしてもOSSだけでやりたい • pdfmeは試す価値はあり • ただし、ゴリゴリの日本語帳票に向き合うには機能不足 • ダメだったら諦めて商用製品を買うべき • pdfmeもお金を出せば機能追加に応じてくれるらしい 49