はじめに
こんにちは!今週のテックブログは、AI SaaS事業本部で「Digital Billder請求書」の開発を担当している林が執筆します。
AI Saas 事業本部で開発・提供しているDigital Billderは、建設業界における「見積・発注・請求」といった一連の業務をクラウドで一元管理できるWebアプリケーションです。
建設業界では、協力会社とのやり取りが紙やFAX、電話で個別に行われることが多く、書類の作成・管理やコミュニケーションに多大な時間がかかるという課題がありました。Digital Billderは、こうしたアナログな業務プロセスをデジタル化し、協力会社とのやり取りも含めたバックオフィス業務全体の効率化を実現します。
Digital Billderでは、請求書PDFや添付資料PDFをアップロードできる機能を搭載しています。また、ユーザーがPDF上に文字や曲線を書き込み、そのままデータベースに保存できる PDFアノテーション機能 も提供しており、より柔軟で実用的な文書管理を可能にします。PDFアノテーションは、多くのビジネスアプリケーションにおいて重要な役割を果たす機能です。

PDFアノテーションライブラリには有料のものが多く、無料ライブラリの多くは機能が不十分でした。そこで私たちは、PDFファイルに注釈を追加し、永続化できる機能を自社で開発しました。
本記事では、PDFアノテーションの実装の詳細について、解説します。
1. 座標の保存とイベント処理の仕組み
この章では、PDFアノテーション機能を実現するために採用した「座標の保存」と「イベント処理」の仕組みについて説明します。PDFアノテーション機能を実装する上で考慮した三つの要素について順を追って整理し、仕組みの全体像を解説します。
イベント発火の仕組み
初めに、ブラウザ上で「イベントがどのように発火するのか」について説明します。マウスカーソルが特定の要素(例:div 要素)の境界内を移動している間、pointermove / mousemoveイベントが連続的に発生します。pointermove / mousemoveイベントは、通常ブラウザのリフレッシュレート(約60Hz)と同期しており、秒間およそ60回の頻度で呼び出されます。pointermove / mousemove イベントが発生すると、これらのイベントに登録した関数が呼び出されます。
座標の取得と保存
次に、このイベントを使って「座標をどのように取得・保存するのか」について説明します。イベントが発生するたびに、ハンドラ関数内で「現在のカーソル座標」が配列に追加されます。この仕組みにより、カーソルが描いた軌跡が座標の集合として保存されます。連続する座標を時系列で記録することで、なめらかな曲線のパスを再現でます。
座標の描画
次に、取得して保存した「座標をどのように描画するか」について説明します。保存した座標は、そのままでは数値の配列に過ぎません。そこで、これらを SVG パス に変換し、対象となる要素(例:PDFビューア上)にオーバーレイすることで、ユーザーが描いた線が画面上に表示されます。
このように、ブラウザ上のマウス移動によって イベントが発生すると、カーソル位置が記録され、時系列で軌跡データとして蓄積されます。蓄えた座標はSVGパスに変換され、要素上に重ねて描画されることで、ユーザーの動きが線として再現されます。
2. データ管理
この章では、データ管理について説明します。PDFアノテーション機能では、効率的に管理でき、用途に応じて柔軟に対応可能なデータ管理を実現しています。
データ構造の設計方針
PDFアノテーション機能を実装する際、PDFとアノテーションのデータを一体化して扱うと、編集や削除のたびにファイル全体を保存し直す必要があり、データサイズの肥大化や再生成の負担が発生します。また、履歴管理や複数ユーザーでの同時編集といった拡張性の確保も難しくなります。そこで、PDF自体を静的なデータとして保持しつつ、アノテーションを独立したデータ構造として切り離して管理することが重要になります。このデータ分離により、アノテーションを柔軟に保存・更新できるだけでなく、履歴追跡、同時編集、パフォーマンス最適化といった柔軟な機能拡張が可能になります。
データモデル
アノテーションデータは、複数のテーブルに分けて設計されています。ひとつのPDFファイルに対する注釈全体を表すのが InvoicePdfAnnotation テーブルです。このテーブルを中心に、個別の注釈を表すデータが関連付けられます。テキストの入力に関する情報は TextAnnotation テーブルで管理されており、ここにはテキストボックスの位置やサイズ、フォントの大きさ、色などの情報が保存されます。一方で、フリーハンドによる描画データは FreehandAnnotation テーブルで扱われ、座標や色、曲線の情報が格納されます。特に曲線のデータは配列として表現され、JSON形式で保存されるため、柔軟かつ拡張性のある管理が可能になります。
データモデル関係図

このように、PDFファイルとアノテーションデータを分離する設計によって、元のPDFを改変することなく注釈を管理でき、履歴の追跡や複数ユーザーでの同時編集が容易になります。また、ファイル全体を再アップロードせずに済むため、処理の効率化も期待できます。データモデルとしては、PDFに付随する注釈全体を表す InvoicePdfAnnotation を基盤に、テキスト注釈を保持する TextAnnotation、手書き注釈をJSON形式で保存する FreehandAnnotation で構成されており、拡張性と柔軟性を重視した設計になっています。
3. 座標管理
この章では、座標管理について説明します。PDFアノテーション機能を構築するうえで、欠かせない要素が「座標管理」です。ユーザーが描いた注釈を正しく保存しても、表示環境や印刷時に位置がずれてしまうと実用性が大きく損なわれます。どの環境でも一貫した位置に注釈を再現するために必要な正規化座標システムと単位変換の仕組みについて解説します。
正規化座標システムの必要性
PDFアノテーション機能においては、画面上の表示と印刷結果の位置を正確に一致させることが求められます。しかし、WebとPDFでは扱う座標の基準が異なります。Web画面ではピクセル単位が用いられますが、これは解像度やズーム倍率といった表示環境に左右される相対的な値です。一方、PDFはポイント単位で管理されており、1ポイントは常に 1/72 インチとして定義されています。そのため印刷時にも実際の物理的なサイズと一致します。
この違いを吸収するために導入されるのが「正規化座標」です。座標をページ全体の幅や高さで割り、0から1の範囲で比率として保存することで、環境に依存しない位置の表現が可能になります。こうすることで、画面サイズやズーム倍率が変わっても「ページのどこに注釈があるのか」を一意に表せます。
表示と印刷を一致させる仕組み
PDFアノテーション機能における大きな課題は、Web上での描画とPDFとして出力したときの注釈位置を完全に一致させることです。その解決の鍵となるのが、前項の座標の正規化と座標の単位変換です。正規化によって環境依存の影響を排除し、さらにピクセル単位の座標をPDFのポイント単位に変換する仕組みを組み込むことで、どの環境で表示しても、また印刷しても、注釈の位置がずれることなく正しく再現されます。

このように、座標の正規化や管理の仕組みによって、ユーザーはどの環境でも正確に注釈を再現できるようになります。
まとめ
PDFアノテーション機能の実装においては、
- 座標の保存とイベント処理の仕組み:
divのイベント発火で座標を収集し、SVGとしてPDF上に描画 - データ分離: PDF本体とアノテーションを分離し、履歴管理や複数ユーザー対応を可能に
- 座標管理: 正規化座標と単位変換によって、表示と印刷での位置を完全に一致させる
という3点が特に重要でした。
この仕組みによって、ユーザーは「どの環境でも同じ見た目でPDFに注釈を残せる」という体験を得られます。さらに、履歴管理・同時編集・パフォーマンス最適化など、拡張が可能な設計になっています。
このように、PDFアノテーションはシンプルに見えて、多くの工夫が求められる開発となっています。
We’re Hiring!
私たちは、一緒に新しい機能を考え、実装し、そして継続的に改善していくエンジニア仲間を募集しています。Digital Billderの開発では、フロントエンドではReactやTypeScriptを、バックエンドではGraphQL、NestJS、Prisma、PostgreSQLといった技術を活用しています。また、PDFやファイル処理といった開発にも携わる機会があり、幅広い領域に挑戦できる面白い環境です。
最新の技術に触れながらプロダクトを成長させたい方や、チームと一緒にものづくりを楽しみたい方にとって、きっと魅力的な職場になると思います。少しでも興味を持っていただけたら、ぜひカジュアル面談をお申し込みください!