C#でPDFを作成・編集・出力する方法|ライブラリ選定から実装サンプルまで解説

はじめに

C#でPDFを作成・編集・出力する処理は、業務システムやWebアプリケーションでよく必要になります。たとえば、請求書、見積書、納品書、帳票、検査レポート、契約書、証明書などをPDFとして生成し、ブラウザでダウンロードさせたり、メールに添付したり、既存PDFに追記したりするケースです。

一方で、C#には「PDFを自由に作成・編集するための標準API」は用意されていないため、実務ではPDFライブラリの選定が重要になります。PDFをゼロから作るのか、HTMLからPDF化するのか、既存PDFを編集するのか、日本語フォントを扱うのか、商用利用するのかによって、適したライブラリは変わります。

この記事では、「csharp pdf」で検索している方に向けて、C#でPDFを作成・編集・出力するための基礎知識、ライブラリの選び方、主要ライブラリの比較、実装サンプル、エラー対策、ベストプラクティスまでをまとめて解説します。

1. C#でPDFを扱う前に押さえる基礎知識

1-1. C#でPDF作成・編集・出力が必要になる主なケース

C#でPDF処理が必要になる代表的な場面は、業務データを「改ざんされにくい形式」「印刷しやすい形式」「環境に依存せず閲覧できる形式」で配布したい場合です。

よくあるケースは次のとおりです。

  • 請求書PDFを自動生成する

  • 見積書や納品書をPDFで出力する

  • 売上レポートや分析レポートをPDF化する

  • 既存PDFに日付、担当者名、受付番号などを追記する

  • 複数のPDFを結合して1ファイルにまとめる

  • PDFに透かしやスタンプを追加する

  • Web画面やHTMLテンプレートをPDFとして保存する

  • パスワード付きPDFを生成する

  • PDFをASP.NETからブラウザへ返す

特に業務システムでは、データベースに保存された情報をもとにPDFを動的生成するパターンが多くあります。C#はWindowsアプリ、ASP.NET Core、バッチ処理、クラウド環境など幅広い実行環境で使われるため、PDF生成処理との相性も高い言語です。

1-2. PDF作成・PDF編集・PDF出力の違い

C#でPDFを扱うときは、「作成」「編集」「出力」を分けて考えると実装方針を決めやすくなります。

PDF作成とは、白紙のPDFを新規に生成し、テキスト、表、画像、線、ページ番号などを配置する処理です。請求書や帳票をゼロから作る場合が該当します。

PDF編集とは、既存のPDFを読み込み、文字を追記したり、透かしを入れたり、ページを結合・分割・削除したりする処理です。テンプレートPDFに情報を差し込む場合や、複数資料をまとめる場合に使います。

PDF出力とは、作成または編集したPDFをファイルとして保存したり、HTTPレスポンスとしてブラウザに返したり、メールに添付したりする処理です。

たとえば、請求書を作る場合は「PDF作成」、Webアプリでダウンロードさせる場合は「PDF出力」、既存の申請書PDFに受付番号を入れる場合は「PDF編集」が中心になります。

1-3. .NET Framework/.NET 6以降/ASP.NETでの対応範囲

C#でPDFを扱う場合、実行環境によって利用できるライブラリや注意点が異なります。

.NET Frameworkでは、長年利用されてきたPDFライブラリが多く、Windowsサーバーや社内業務システムで使いやすい傾向があります。ただし、新規開発では.NET 6以降、.NET 8、.NET 10などのLTS系や新しい.NETを選ぶケースが増えています。

.NET 6以降では、クロスプラットフォーム対応が重要です。Windowsだけでなく、Linuxコンテナ、Docker、Azure App Service、AWS、GCPなどでPDF生成を実行する場合、フォント、ネイティブ依存関係、HTMLレンダリングエンジンの有無を確認する必要があります。

ASP.NETやASP.NET Coreでは、PDFをファイルとして保存するだけでなく、FileResultなどを使ってブラウザに直接返す実装がよく使われます。この場合、メモリ上でPDFを生成し、byte[]Streamとしてレスポンスに渡す設計にすると扱いやすくなります。

1-4. 標準機能だけでPDFを扱えるのか

C#や.NETの標準機能だけで、本格的なPDF作成・編集を行うのは現実的ではありません。テキストファイルや画像ファイルとは異なり、PDFはページ構造、フォント、座標、圧縮、埋め込み画像、メタ情報、暗号化、フォーム、注釈など多くの仕様を持つ複雑なフォーマットです。

単純に「PDFファイルを保存する」だけならファイル入出力APIで可能ですが、「文字や表を配置してPDFを作る」「既存PDFを編集する」「HTMLをPDFに変換する」といった処理にはPDFライブラリが必要です。

そのため、C#でPDFを扱う場合は、PDFsharp、iText、QuestPDF、IronPDF、Syncfusion PDF、Spire.PDFなどのライブラリを用途に応じて選定するのが一般的です。

2. C#向けPDFライブラリの選び方

2-1. 無料・有料ライブラリの違い

C#向けPDFライブラリには、無料で使えるものと有料ライセンスが必要なものがあります。

無料ライブラリは、初期費用を抑えられる点がメリットです。簡単なPDF作成、社内ツール、小規模システム、検証用途で使いやすい一方、HTML to PDF、高度な編集、電子署名、商用サポートなどは弱い場合があります。

有料ライブラリは、HTMLからPDFへの高精度変換、既存PDFの編集、結合、分割、暗号化、電子署名、帳票出力、サポート体制などが充実している傾向があります。商用システムや保守性が重視されるプロジェクトでは、有料ライブラリのほうが結果的に開発コストを抑えられることもあります。

重要なのは、「無料だから選ぶ」「有名だから選ぶ」のではなく、作りたいPDFの要件に合っているかを確認することです。

2-2. 商用利用時に確認すべきライセンス

PDFライブラリを商用利用する場合は、必ずライセンスを確認しましょう。特に注意したいのは、オープンソースであっても商用利用時に制約があるケースです。

PDFsharpはMIT Licenseで公開されているため、比較的利用しやすい選択肢です。PDFsharp公式ドキュメントでも、PDFsharpプロジェクトはMIT Licenseで公開されていると説明されています。docs.pdfsharp.net

iTextは強力なPDFライブラリですが、AGPLと商用ライセンスのデュアルライセンスモデルを採用しています。AGPLの条件に合わない商用アプリケーションへ組み込む場合は、商用ライセンスの検討が必要です。iTextpdf+1

QuestPDFはライセンス体系を確認して利用する必要があります。公式サイトでは、CommunityライセンスはMIT条件で商用利用も可能と説明されていますが、利用条件や組織規模によって有料ライセンスが必要になる場合があります。questpdf.com

IronPDFは商用向けの有料ライブラリとして提供されており、公式サイトでは30日間の無料トライアルや商用ライセンスについて案内されています。ironpdf.com+1

ライセンスはバージョンや利用形態によって変わる可能性があるため、導入前に必ず公式サイトの最新情報を確認してください。

2-3. 日本語フォント対応の有無

C#でPDFを作成するときに非常に重要なのが、日本語フォント対応です。英数字だけのPDFでは問題がなくても、日本語を出力した瞬間に文字化けしたり、四角い豆腐文字になったりすることがあります。

日本語PDFを作る場合は、次の点を確認しましょう。

  • 日本語フォントを明示的に指定できるか

  • フォントをPDFに埋め込めるか

  • Linux環境やDocker環境でも同じフォントを使えるか

  • 太字、斜体、等幅フォントに対応できるか

  • ライセンス上、フォントをサーバーに配置してよいか

Windows開発環境では表示できても、Linuxサーバーにデプロイしたら文字化けするケースはよくあります。サーバー側に日本語フォントが存在しない場合は、Noto Sans CJK、Noto Serif CJKなどの利用を検討し、フォントファイルをアプリケーションに含めるか、サーバーへインストールする設計が必要です。

2-4. HTMLからPDFを生成できるか

HTMLからPDFを生成したい場合は、HTML to PDFに対応したライブラリを選びましょう。

HTML to PDFは、請求書、帳票、レポートなどをHTMLとCSSでデザインし、それをPDFとして出力できる点が便利です。Webエンジニアが多いチームでは、C#の座標指定でPDFを組むよりも、HTMLテンプレートを使うほうが保守しやすい場合があります。

IronPDFはChromeベースのレンダリングを使ったHTML to PDFを主な特徴としており、公式サイトでもHTML、CSS、JavaScriptを利用したPDF生成が案内されています。ironpdf.com+1

Syncfusion PDFもHTML to PDF、Excel to PDF、Word to PDFなどの変換機能を含む.NET向けPDFライブラリとして提供されています。Syncfusion

一方、HTML変換に対応していないライブラリでは、テキストや表をコードで直接配置する必要があります。どちらが適しているかは、レイアウトの複雑さと保守体制で判断しましょう。

2-5. 既存PDFの編集・結合・分割に対応しているか

PDFライブラリによって、新規作成は得意でも既存PDFの編集は苦手という場合があります。

既存PDFを扱う場合は、次の機能を確認してください。

  • 既存PDFの読み込み

  • ページ追加

  • テキスト追記

  • 画像追記

  • 透かし追加

  • スタンプ追加

  • ページ結合

  • ページ分割

  • ページ削除

  • メタ情報の変更

  • 暗号化・パスワード設定

  • フォーム入力

  • 電子署名

PDFsharpはPDF作成やページ操作に利用されることが多いライブラリですが、高度なPDF編集機能を求める場合は、iText、Syncfusion PDF、Spire.PDF、IronPDFなども比較対象になります。iText公式サイトでは、PDFの作成、操作、編集に対応するJavaおよび.NET向けSDKとして説明されています。iTextpdf

2-6. ASP.NETやクラウド環境で使いやすいか

ASP.NET Coreやクラウド環境でPDFを生成する場合は、ローカル開発環境だけでなく、本番環境で安定して動くかが重要です。

確認すべきポイントは次のとおりです。

  • Linuxコンテナで動作するか

  • Azure App ServiceやAWS Lambdaなどで使えるか

  • ネイティブ依存関係があるか

  • 一時ファイルの保存先を制御できるか

  • メモリ使用量が大きすぎないか

  • 日本語フォントを配置できるか

  • 同時実行時にファイルロックが起きないか

  • 商用サポートがあるか

特にHTML to PDF系ライブラリは、内部でブラウザエンジンやネイティブコンポーネントを利用する場合があります。DockerやLinuxサーバーで使う場合は、必要なパッケージ、フォント、実行権限を事前に確認しておきましょう。

3. C#で使える主要PDFライブラリ比較

3-1. PDFsharpの特徴と向いている用途

PDFsharpは、C#でPDFを作成・加工するための代表的なライブラリのひとつです。MIT Licenseで公開されているため、ライセンス面で採用しやすい点が魅力です。docs.pdfsharp.net

PDFsharpが向いている用途は、比較的シンプルなPDF作成、既存PDFのページ結合、ページ追加、軽微な描画処理などです。座標を指定してテキストや線を描画する方式のため、細かい位置調整をコードで制御できます。

一方で、複雑な帳票レイアウトやHTMLからPDFへの変換には、別のライブラリのほうが向いている場合があります。また、日本語フォントを扱う場合はフォント設定を明示的に行う必要があります。

PDFsharpは次のようなケースに向いています。

  • 無料でC# PDF処理を始めたい

  • シンプルなPDFを作りたい

  • PDFのページ結合を行いたい

  • 座標指定で細かく描画したい

  • ライセンスをシンプルにしたい

3-2. iText/iTextSharpの特徴と注意点

iTextは、PDF作成・編集・操作の機能が非常に豊富なライブラリです。公式サイトでも、Javaおよび.NET向けのPDF SDKとして、PDFドキュメントの作成、操作、編集に対応すると説明されています。iTextpdf

iTextは、既存PDFの編集、フォーム、電子署名、PDF/A、暗号化など、エンタープライズ用途で必要になりやすい機能を備えています。ただし、ライセンスには注意が必要です。iTextはAGPLと商用ライセンスのデュアルライセンスモデルを採用しているため、ソースコードを公開しない商用製品に組み込む場合などは、商用ライセンスの確認が必要です。iTextpdf+1

なお、古い情報では「iTextSharp」という名前がよく使われますが、現在C#でiTextを使う場合は、対象バージョンやパッケージ体系を公式情報で確認することが重要です。

iTextが向いているケースは次のとおりです。

  • 高度なPDF編集が必要

  • 電子署名やPDF/Aなどの要件がある

  • 既存PDFの操作が多い

  • エンタープライズ用途で使いたい

  • 商用ライセンスを前提に導入できる

3-3. QuestPDFの特徴と向いている用途

QuestPDFは、C#コードで宣言的にPDFレイアウトを記述できるモダンなライブラリです。公式GitHubでは、保守しやすい型安全なC#コードでレポート、請求書、エクスポート用PDFを設計できるライブラリとして説明されています。GitHub

HTMLをPDFに変換する方式ではなく、C#の fluent API を使ってレイアウトを組み立てます。表、列、行、余白、ページヘッダー、ページフッターなどをコードで整理しやすいため、帳票やレポート生成に向いています。

QuestPDFが向いているケースは次のとおりです。

  • 請求書や帳票をC#コードで作りたい

  • レイアウトを型安全に管理したい

  • HTML to PDFではなくコードでPDFを組みたい

  • レポートや明細表を作りたい

  • 保守しやすいPDF生成処理を作りたい

ただし、QuestPDFはライセンス条件を確認して使う必要があります。公式ライセンスページでは、CommunityライセンスやProfessionalライセンスなどが案内されています。questpdf.com

3-4. IronPDFの特徴と向いている用途

IronPDFは、HTMLからPDFを生成する用途に強い商用ライブラリです。公式サイトでは、ChromeベースのレンダリングによるHTML to PDF、PDFの作成・編集、AzureやAWSなどへのデプロイ対応が案内されています。ironpdf.com+1

HTML、CSS、JavaScriptを使ってPDFの見た目を作れるため、Web画面に近いレイアウトや、既存のRazorテンプレートを活用したPDF出力に向いています。請求書、レポート、契約書、証明書などをWebテンプレートとして管理したい場合に便利です。

IronPDFが向いているケースは次のとおりです。

  • HTMLからPDFを作りたい

  • CSSを使って帳票デザインを管理したい

  • RazorテンプレートをPDF化したい

  • 商用サポートを重視したい

  • クラウド環境でPDF生成したい

一方で、商用ライセンスが必要になるため、コストと機能のバランスを確認して導入しましょう。

3-5. Syncfusion PDFやSpire.PDFなどの選択肢

Syncfusion PDFは、.NET向けの高機能PDFライブラリです。公式サイトでは、PDFの作成、編集、保護、変換、データ抽出、電子署名、リダクション、フォーム処理、OCRなど幅広い機能が紹介されています。Syncfusion

SyncfusionはPDF以外のUIコンポーネントやドキュメント処理機能も充実しているため、すでにSyncfusion製品を使っているプロジェクトでは採用しやすい選択肢です。

Spire.PDF for .NETは、C#やVB.NETでPDFの作成、読み取り、編集、操作を行うためのライブラリです。公式GitHubやNuGetでは、Adobe Acrobatなどの外部ソフトなしでPDFを作成・処理できる.NET向けAPIとして説明されています。GitHub+1

これらのライブラリは、PDF作成だけでなく、変換、編集、セキュリティ、フォーム処理など幅広い機能を求める場合に候補になります。導入時は、価格、ライセンス、対応.NETバージョン、サーバー環境、サポート体制を比較しましょう。

3-6. 用途別おすすめライブラリ早見表

用途候補ライブラリ選定ポイント
シンプルなPDF作成PDFsharp無料で始めやすく、座標指定の描画に向く
請求書・帳票作成QuestPDFC#コードで保守しやすいレイアウトを作れる
HTMLからPDF生成IronPDF、Syncfusion PDFHTML、CSS、Razorテンプレートを活用しやすい
既存PDFの高度な編集iText、Syncfusion PDF、Spire.PDF編集、フォーム、暗号化、電子署名などを確認
PDF結合・分割PDFsharp、iText、Syncfusion PDF、Spire.PDFページ操作のしやすさを確認
商用サポート重視IronPDF、Syncfusion PDF、iText、Spire.PDFサポート、ライセンス、保守性を確認
無料・OSS重視PDFsharp、条件を満たすQuestPDFライセンス条件を必ず確認

4. C#でPDFを新規作成する方法

4-1. NuGetでPDFライブラリをインストールする

C#でPDFライブラリを使う場合、多くはNuGetからパッケージをインストールします。

Visual Studioを使う場合は、プロジェクトを右クリックし、「NuGet パッケージの管理」から対象ライブラリを検索してインストールします。

.NET CLIを使う場合は、次のようにインストールできます。

Bash
dotnet add package PDFsharp

QuestPDFを使う場合は次のように追加します。

Bash
dotnet add package QuestPDF

IronPDFを使う場合は次のように追加します。

Bash
dotnet add package IronPdf

実際には、ライブラリごとにパッケージ名や対象フレームワークが異なるため、公式ドキュメントやNuGetページで最新のパッケージ名を確認してください。

4-2. テキストをPDFに出力する基本サンプル

まずはPDFsharpを使って、C#で簡単なPDFを作成する例を見てみましょう。

C#
using PdfSharp.Drawing;
using PdfSharp.Pdf;

var document = new PdfDocument();
document.Info.Title = "C# PDF Sample";

var page = document.AddPage();
var graphics = XGraphics.FromPdfPage(page);

var font = new XFont("Arial", 20, XFontStyleEx.Regular);

graphics.DrawString(
"Hello PDF from C#",
font,
XBrushes.Black,
new XRect(0, 100, page.Width, page.Height),
XStringFormats.TopCenter
);

document.Save("sample.pdf");

このコードでは、新しいPDFドキュメントを作成し、1ページ追加して、中央上部にテキストを描画しています。

PDFsharpのような座標指定型のライブラリでは、「どのページの、どの位置に、どのフォントで描画するか」をコードで指定します。細かい制御ができる一方で、複雑な帳票では座標管理が大変になりやすい点に注意が必要です。

4-3. 表や明細データをPDFに出力する方法

請求書や帳票では、明細行を表形式で出力することが多くあります。座標指定で表を作る場合は、線、列幅、行高、文字位置を計算しながら描画します。

簡単な考え方は次のとおりです。

C#
var startX = 50;
var startY = 150;
var rowHeight = 25;

var headers = new[] { "品名", "数量", "単価", "金額" };
var widths = new[] { 200, 80, 100, 100 };

for (int i = 0; i < headers.Length; i++)
{
graphics.DrawRectangle(
XPens.Black,
startX + widths.Take(i).Sum(),
startY,
widths[i],
rowHeight
);

graphics.DrawString(
headers[i],
new XFont("Arial", 10, XFontStyleEx.Bold),
XBrushes.Black,
new XRect(startX + widths.Take(i).Sum(), startY, widths[i], rowHeight),
XStringFormats.Center
);
}

実務では、明細行がページをまたぐ場合の改ページ処理が必要です。たとえば、現在のY座標がページ下部に近づいたら新しいページを追加し、ヘッダー行を再描画する処理を入れます。

表や帳票が中心のプロジェクトでは、QuestPDFのように表レイアウトを扱いやすいライブラリを選ぶと、コードの見通しが良くなります。

4-4. 画像やロゴをPDFに挿入する方法

会社ロゴや印影、商品画像などをPDFに挿入する場合は、画像ファイルを読み込み、指定位置に描画します。

PDFsharpの例は次のとおりです。

C#
using PdfSharp.Drawing;

var image = XImage.FromFile("logo.png");

graphics.DrawImage(
image,
x: 50,
y: 40,
width: 120,
height: 40
);

画像を扱うときは、次の点に注意してください。

  • 画像ファイルのパスが正しいか

  • 実行環境に画像ファイルが配置されているか

  • Webアプリでは相対パスではなく絶対パスを使う

  • 解像度が低すぎないか

  • 画像サイズが大きすぎてPDF容量が肥大化しないか

  • Linux環境でファイル名の大文字・小文字が一致しているか

ASP.NET Coreでは、IWebHostEnvironment.WebRootPathContentRootPathを使って画像の実パスを組み立てると安全です。

4-5. 日本語を文字化けさせずに出力する方法

日本語をPDFに出力するには、日本語に対応したフォントを指定する必要があります。たとえば、Windows環境なら「Yu Gothic」や「Meiryo」、クロスプラットフォーム環境なら「Noto Sans CJK JP」などを検討します。

重要なのは、開発PCに存在するフォントが本番サーバーにも存在するとは限らないことです。Windowsでは問題なく表示されても、Linuxコンテナにデプロイすると日本語が表示されないことがあります。

対策は次のとおりです。

  • 日本語フォントを明示的に指定する

  • サーバーに日本語フォントをインストールする

  • Dockerイメージにフォントを含める

  • ライブラリのフォント埋め込み設定を確認する

  • PDF生成時に英数字だけでなく日本語のテストデータを使う

特に請求書や契約書など、社外に提出するPDFでは文字化けは重大な問題になります。日本語、半角英数字、全角記号、丸数字、機種依存文字などを含めたテストを行いましょう。

5. HTMLからPDFを生成する方法

5-1. HTML to PDFが向いているケース

HTML to PDFは、HTMLとCSSで作ったレイアウトをPDFに変換する方法です。C#で座標を計算しながらPDFを描画するよりも、Web制作に近い感覚で帳票を作れる点がメリットです。

HTML to PDFが向いているケースは次のとおりです。

  • 請求書や帳票の見た目をHTMLで管理したい

  • Razorテンプレートを使いたい

  • CSSで余白、罫線、フォント、色を調整したい

  • Web画面とPDFのデザインを近づけたい

  • デザイナーやフロントエンド担当者がレイアウトを修正する

  • 複雑な表や装飾を扱いたい

一方で、HTML to PDFはレンダリングエンジンによって結果が変わることがあります。ブラウザでは正しく見えるのにPDF化すると改ページが崩れる、背景色が出ない、フォントが変わるといった問題が起きることもあります。

5-2. Razor/HTMLテンプレートからPDFを作る流れ

ASP.NET CoreでHTMLからPDFを作る場合、一般的な流れは次のようになります。

  1. 請求書や帳票のデータを取得する

  2. RazorビューやHTMLテンプレートにデータを差し込む

  3. HTML文字列を生成する

  4. HTML to PDFライブラリでPDFに変換する

  5. PDFをファイル保存またはブラウザへ返す

IronPDFを使う場合の簡単な例は次のとおりです。

C#
using IronPdf;

var html = """
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: 'Noto Sans CJK JP', sans-serif; }
h1 { font-size: 24px; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #333; padding: 8px; }
</style>
</head>
<body>
<h1>請求書</h1>
<p>株式会社サンプル 御中</p>
<table>
<tr><th>品名</th><th>数量</th><th>金額</th></tr>
<tr><td>開発費</td><td>1</td><td>100,000</td></tr>
</table>
</body>
</html>
""";

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);

pdf.SaveAs("invoice.pdf");

HTML to PDFでは、C#側で細かい座標を計算するよりも、HTMLテンプレートの保守性が重要になります。テンプレート、CSS、画像、フォントをどこに配置するかを設計しておきましょう。

5-3. CSSを反映してレイアウトを整える方法

HTMLからPDFを生成するときは、通常のWebページとは異なり、印刷向けCSSを意識する必要があります。

特に重要なのは、ページサイズ、余白、改ページ、表の折り返しです。

CSS
@page {
size: A4;
margin: 20mm;
}

body {
font-family: 'Noto Sans CJK JP', sans-serif;
font-size: 12px;
}

table {
width: 100%;
border-collapse: collapse;
}

tr {
page-break-inside: avoid;
}

.section {
page-break-inside: avoid;
}

.page-break {
page-break-before: always;
}

帳票では、1ページに収まると思っていた明細がデータ件数によって2ページ目に流れることがあります。そのため、固定データだけでなく、明細0件、1件、20件、100件などのパターンで表示確認することが大切です。

また、CSS GridやFlexbox、position fixedなどの対応状況はライブラリやレンダリングエンジンによって異なる場合があります。複雑なレイアウトでは、早い段階でPDF化テストを行いましょう。

5-4. 請求書・帳票・レポート作成の実装例

HTMLテンプレートを使った請求書PDFの例を考えます。

C#側では、請求書データをモデルとして用意します。

C#
public class Invoice
{
public string InvoiceNo { get; set; } = "";
public string CustomerName { get; set; } = "";
public DateTime IssueDate { get; set; }
public List<InvoiceItem> Items { get; set; } = new();
}

public class InvoiceItem
{
public string Name { get; set; } = "";
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal Amount => Quantity * UnitPrice;
}

HTMLテンプレートでは、明細行を繰り返し出力します。

HTML
<h1>請求書</h1>
<p>請求番号:{{InvoiceNo}}</p>
<p>発行日:{{IssueDate}}</p>
<p>{{CustomerName}} 御中</p>

<table>
<thead>
<tr>
<th>品名</th>
<th>数量</th>
<th>単価</th>
<th>金額</th>
</tr>
</thead>
<tbody>
{{Items}}
</tbody>
</table>

実務では、Razorビューを文字列としてレンダリングし、HTML to PDFライブラリに渡す構成がよく使われます。テンプレートとデータ取得処理を分離しておくと、レイアウト修正とビジネスロジック修正を切り分けやすくなります。

5-5. HTML変換時に起きやすいレイアウト崩れの対策

HTML to PDFでよくあるトラブルは、ブラウザ表示とPDF出力の差です。

主な原因と対策は次のとおりです。

問題原因対策
改ページ位置が不自然表やブロックがページをまたぐpage-break-inside: avoidを使う
フォントが変わるサーバーにフォントがないフォントを配置・埋め込みする
画像が表示されない相対パスが解決できない絶対パスまたはBase64を使う
CSSが効かない対応していないCSSを使っている印刷向けCSSに寄せる
背景色が出ない印刷設定やCSS指定の問題ライブラリ設定とCSSを確認する
余白がずれる@pageやPDF設定が不一致ページサイズとマージンを統一する

HTML to PDFでは、PDF化専用のHTMLテンプレートを作るのが基本です。通常のWeb画面をそのままPDF化しようとすると、ナビゲーション、ボタン、レスポンシブCSS、JavaScriptの影響で崩れやすくなります。

6. C#で既存PDFを編集する方法

6-1. 既存PDFを読み込む基本処理

既存PDFを編集する場合は、ライブラリでPDFを読み込み、編集用のドキュメントとして開きます。

PDFsharpで既存PDFを読み込む例は次のとおりです。

C#
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

var document = PdfReader.Open("template.pdf", PdfDocumentOpenMode.Modify);

// ここでページ追加、テキスト追記、メタ情報変更などを行う

document.Save("output.pdf");

既存PDFを編集するときは、読み込みモードに注意してください。閲覧用、インポート用、編集用など、ライブラリによってモードが分かれていることがあります。

また、パスワード付きPDF、暗号化PDF、署名済みPDFは、通常のPDFと同じように編集できない場合があります。

6-2. PDFにテキストを追記する方法

既存PDFにテキストを追記するには、対象ページを取得し、そのページ上に文字を描画します。

C#
using PdfSharp.Drawing;
using PdfSharp.Pdf.IO;

var document = PdfReader.Open("template.pdf", PdfDocumentOpenMode.Modify);
var page = document.Pages[0];

using var graphics = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append);

var font = new XFont("Arial", 12, XFontStyleEx.Regular);

graphics.DrawString(
"受付番号:A-0001",
font,
XBrushes.Black,
new XPoint(50, 80)
);

document.Save("edited.pdf");

この例では、1ページ目に受付番号を追記しています。テンプレートPDFに文字を差し込む場合は、あらかじめ差し込み位置の座標を決めておくと実装しやすくなります。

注意点として、PDFはWordのように既存テキストを自然に再編集できる形式ではありません。既存の文章の途中に文字を挿入して自動で折り返すような処理は難しいため、「指定位置に追記する」「上からスタンプする」という考え方が基本です。

6-3. 透かしやスタンプを追加する方法

PDFに透かしを入れる場合は、各ページに半透明の文字や画像を描画します。

C#
foreach (var page in document.Pages)
{
using var graphics = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append);

var font = new XFont("Arial", 48, XFontStyleEx.Bold);

graphics.TranslateTransform(page.Width / 2, page.Height / 2);
graphics.RotateTransform(-45);

graphics.DrawString(
"CONFIDENTIAL",
font,
new XSolidBrush(XColor.FromArgb(80, 128, 128, 128)),
new XPoint(0, 0),
XStringFormats.Center
);
}

透かしは、社外秘、見本、承認済、無効、再発行などのステータス表示によく使われます。

実務では、透かしを本文の前面に出すか背面に出すかも重要です。前面に出すと視認性は高くなりますが、本文が読みづらくなることがあります。背面に出す場合は、既存PDFの構造によって期待どおり表示されないこともあるため、出力確認が必要です。

6-4. PDFページを結合・分割・削除する方法

複数のPDFを1つにまとめる場合は、各PDFを読み込み、ページを順番に追加します。

C#
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

var output = new PdfDocument();

var files = new[] { "part1.pdf", "part2.pdf", "part3.pdf" };

foreach (var file in files)
{
var input = PdfReader.Open(file, PdfDocumentOpenMode.Import);

for (int i = 0; i < input.PageCount; i++)
{
output.AddPage(input.Pages[i]);
}
}

output.Save("merged.pdf");

PDFを分割する場合は、必要なページだけを新しいPDFに追加して保存します。

C#
var input = PdfReader.Open("source.pdf", PdfDocumentOpenMode.Import);

for (int i = 0; i < input.PageCount; i++)
{
var output = new PdfDocument();
output.AddPage(input.Pages[i]);
output.Save($"page-{i + 1}.pdf");
}

ページ削除は、編集モードでPDFを開き、対象ページを削除します。

C#
var document = PdfReader.Open("source.pdf", PdfDocumentOpenMode.Modify);
document.Pages.RemoveAt(0);
document.Save("deleted.pdf");

PDFの結合・分割処理では、ファイルサイズが大きい場合やページ数が多い場合にメモリ使用量が増えます。大量処理では、バッチ単位を分ける、不要なファイルをすぐ解放する、ストリームを適切に閉じるといった工夫が必要です。

6-5. PDFのメタ情報を変更する方法

PDFには、タイトル、作成者、件名、キーワードなどのメタ情報を設定できます。

C#
var document = PdfReader.Open("source.pdf", PdfDocumentOpenMode.Modify);

document.Info.Title = "請求書";
document.Info.Author = "Sample Company";
document.Info.Subject = "2026年6月分 請求書";
document.Info.Keywords = "invoice, pdf, csharp";

document.Save("metadata.pdf");

メタ情報は、ファイル管理、検索、文書分類に役立ちます。ただし、PDFビューアによって表示方法が異なるため、重要な情報はPDF本文にも記載しておくのが安全です。

また、個人情報や社外秘情報をメタ情報に入れる場合は注意が必要です。本文には表示されない情報でも、PDFプロパティから確認できる場合があります。

7. C#でPDFを出力・保存・ダウンロードする方法

7-1. ローカルフォルダにPDFを保存する

コンソールアプリやバッチ処理では、生成したPDFをローカルフォルダや共有フォルダに保存するケースがあります。

C#
var outputPath = Path.Combine(
AppContext.BaseDirectory,
"output",
"invoice.pdf"
);

Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);

document.Save(outputPath);

保存時は、次の点に注意してください。

  • 保存先フォルダが存在するか

  • アプリケーションに書き込み権限があるか

  • 同名ファイルを上書きしてよいか

  • ファイル名に使用できない文字が含まれていないか

  • 複数処理が同時に同じファイルへ書き込まないか

Webアプリでは、アプリケーションの実行フォルダに直接PDFを保存するより、専用の一時フォルダ、クラウドストレージ、Blob Storageなどを使うほうが安全な場合があります。

7-2. ASP.NETでPDFをブラウザに返す

ASP.NET CoreでPDFをブラウザに返す場合は、Fileメソッドを使います。

C#
[HttpGet]
public IActionResult DownloadInvoice()
{
byte[] pdfBytes = GeneratePdf();

return File(
pdfBytes,
"application/pdf",
"invoice.pdf"
);
}

application/pdfを指定することで、ブラウザはPDFとして扱います。第3引数にファイル名を指定すると、ダウンロード時のファイル名として利用されます。

ブラウザ上でPDFを表示させたい場合は、ファイル名を指定せずに返すか、Content-Dispositionをinlineに設定します。ダウンロードさせたい場合は、attachmentとして返します。

7-3. PDFをダウンロードさせる実装例

ASP.NET Coreで動的に請求書PDFを生成し、ダウンロードさせる例です。

C#
[HttpGet("invoices/{id}/pdf")]
public IActionResult DownloadInvoicePdf(int id)
{
var invoice = _invoiceService.GetInvoice(id);

if (invoice == null)
{
return NotFound();
}

byte[] pdfBytes = _pdfService.CreateInvoicePdf(invoice);

var fileName = $"invoice-{invoice.InvoiceNo}.pdf";

return File(pdfBytes, "application/pdf", fileName);
}

PDF生成処理は、Controllerに直接書くよりも、PdfServiceのようなクラスに分離するのがおすすめです。そうすることで、PDF生成処理を単体テストしやすくなり、将来的にライブラリを変更する場合も影響範囲を抑えられます。

7-4. PDFをメール添付する場合の注意点

C#で生成したPDFをメール添付する場合は、byte[]MemoryStreamとしてPDFを扱うと便利です。

C#
using var stream = new MemoryStream(pdfBytes);

var attachment = new Attachment(
stream,
"invoice.pdf",
"application/pdf"
);

メール添付時は、次の点に注意しましょう。

  • PDFファイルサイズが大きすぎないか

  • 添付ファイル名に機種依存文字を使っていないか

  • 個人情報を含むPDFを誤送信しないか

  • パスワード付きPDFが必要か

  • メール本文と添付PDFの宛先が一致しているか

  • 一時ファイルを作る場合は削除しているか

請求書や給与明細など個人情報を含むPDFでは、メール送信前のチェック、送信ログ、再送防止、アクセス制御も重要です。

7-5. PDFを印刷する方法とライブラリ選定の注意点

C#からPDFを印刷する場合、PDF生成とは別の課題があります。PDFファイルを作る機能と、プリンターへ正しく印刷する機能は別物です。

印刷方法には次のような選択肢があります。

  • PDFビューアを起動して印刷する

  • Windowsの印刷APIを使う

  • ライブラリの印刷機能を使う

  • サーバー側ではなくクライアント側で印刷する

サーバーサイドのASP.NETアプリから直接プリンターへ印刷する設計は、権限、プリンタードライバー、同時実行、ネットワーク、クラウド環境との相性に注意が必要です。

Webアプリでは、サーバーはPDFを生成してブラウザへ返し、印刷はユーザーのブラウザやPDFビューアに任せる構成が一般的です。業務端末に直接印刷したい場合は、クライアントアプリや専用印刷サービスの導入を検討しましょう。

8. PDFにセキュリティ機能を追加する方法

8-1. パスワード付きPDFを作成する

PDFには、開くときにパスワードを要求する設定や、編集・印刷などの操作を制限する設定を追加できます。

パスワード付きPDFを作成する場合は、ライブラリが暗号化機能に対応しているかを確認してください。無料ライブラリでは機能が限定されることがあり、商用ライブラリのほうが設定しやすい場合があります。

実装時の注意点は次のとおりです。

  • パスワードを平文でログに出さない

  • 推測されやすいパスワードを使わない

  • パスワードをメール本文に同時送信しない

  • パスワードの受け渡し方法を別経路にする

  • パスワード再発行の運用を決める

特に個人情報や機密情報を含むPDFでは、PDFの暗号化だけに頼らず、送信先確認、ダウンロード認証、アクセス期限なども含めて設計しましょう。

8-2. コピー・印刷制限を設定する

PDFでは、コピー禁止、印刷禁止、編集禁止などの権限制御を設定できる場合があります。

ただし、これらの制限はPDFビューアの対応に依存する部分があります。完全な情報漏えい防止策ではなく、あくまで抑止策として考えるべきです。

実務では、次のような使い方が考えられます。

  • 閲覧は許可するが印刷は制限する

  • コピーを制限する

  • 編集を制限する

  • 高解像度印刷を制限する

  • 透かしと組み合わせる

重要な文書では、PDF権限設定だけでなく、ユーザー名や出力日時を透かしとして入れると、漏えい時の追跡性を高められます。

8-3. 電子署名・証明書対応が必要なケース

電子契約、正式な申請書、証明書、監査資料などでは、PDFに電子署名が必要になる場合があります。

電子署名が必要なケースでは、次の点を確認しましょう。

  • PDF電子署名に対応したライブラリか

  • 証明書ファイルを扱えるか

  • タイムスタンプに対応できるか

  • 署名後にPDFを改変しない設計になっているか

  • 長期署名やPDF/Aが必要か

  • 法務・監査要件を満たせるか

電子署名は単なるPDF生成よりも要件が複雑です。ライブラリの機能だけでなく、証明書管理、秘密鍵の保護、署名フロー、運用ルールを含めて検討する必要があります。

8-4. 個人情報を含むPDF出力時の注意点

PDFには、氏名、住所、電話番号、メールアドレス、請求金額、口座情報、マイナンバーに近い情報など、個人情報や機密情報が含まれることがあります。

個人情報を含むPDFを扱う場合は、次の点に注意してください。

  • 出力対象者を間違えない

  • 他人の情報が混在しない

  • 一時ファイルを放置しない

  • ログにPDF内容やパスワードを出さない

  • ダウンロードURLを推測可能にしない

  • 認可チェックを必ず行う

  • メール誤送信を防ぐ

  • 不要になったPDFを削除する

  • バックアップや監査ログの扱いを決める

PDF生成処理では、見た目の正しさだけでなく、セキュリティと運用の安全性も重要です。

9. C#でPDF処理を実装するときのよくあるエラーと対策

9-1. 日本語が文字化けする

日本語文字化けは、C# PDF開発で非常によくある問題です。

原因は主に次のとおりです。

  • 日本語非対応フォントを使っている

  • サーバーに指定フォントが存在しない

  • フォントがPDFに埋め込まれていない

  • 文字コードやHTMLのmeta charset指定が不適切

  • ライブラリ側のフォント設定が不足している

対策としては、日本語対応フォントを明示的に指定し、本番環境にも同じフォントを配置します。HTML to PDFの場合は、<meta charset="utf-8">を入れ、CSSで日本語フォントを指定しましょう。

HTML
<meta charset="utf-8">
<style>
body {
font-family: 'Noto Sans CJK JP', sans-serif;
}
</style>

PDF生成のテストでは、「株式会社」「髙橋」「①」「㈱」「〜」など、文字化けしやすい文字も含めて確認すると安心です。

9-2. フォントが埋め込まれない

PDFを別のPCで開いたときに表示が変わる場合、フォントが埋め込まれていない可能性があります。

フォントが埋め込まれないと、閲覧環境に存在する代替フォントで表示され、レイアウトが崩れたり、文字が表示されなかったりします。

対策は次のとおりです。

  • ライブラリのフォント埋め込み設定を確認する

  • 埋め込み可能なフォントを使う

  • フォントライセンスを確認する

  • DockerやLinux環境にフォントを配置する

  • PDF出力後に別環境で表示確認する

フォントにはライセンスがあります。商用システムにフォントファイルを含める場合は、そのフォントが再配布やサーバー利用に対応しているか確認してください。

9-3. 画像が表示されない

PDFに画像が表示されない場合、原因の多くはファイルパスです。

よくある原因は次のとおりです。

  • 相対パスが実行時のカレントディレクトリと合っていない

  • WebアプリでURLと物理パスを混同している

  • Linux環境でファイル名の大文字・小文字が違う

  • 画像ファイルがデプロイされていない

  • アプリケーションに読み取り権限がない

  • 対応していない画像形式を使っている

ASP.NET Coreでは、次のように実パスを組み立てると管理しやすくなります。

C#
var logoPath = Path.Combine(
_environment.WebRootPath,
"images",
"logo.png"
);

HTML to PDFの場合は、画像をBase64化してHTMLに埋め込む方法もあります。ただし、画像が多いとHTMLサイズが大きくなるため注意してください。

9-4. HTMLからPDF化するとCSSが崩れる

HTMLからPDF化したときにCSSが崩れる場合、Web表示用のCSSをそのまま使っていることが原因になりやすいです。

対策は次のとおりです。

  • PDF専用のCSSを用意する

  • @pageでページサイズと余白を指定する

  • 複雑なレスポンシブCSSを避ける

  • 改ページ制御を明示する

  • 外部CSSの読み込みパスを確認する

  • JavaScript依存のレイアウトを避ける

  • フォントを明示的に指定する

PDFはA4など固定サイズの紙面で出力されるため、ブラウザの可変幅レイアウトとは考え方が異なります。最初から印刷用レイアウトとして設計することが大切です。

9-5. サーバー環境でPDF生成に失敗する

開発PCでは動くのに、本番サーバーでPDF生成に失敗することがあります。

主な原因は次のとおりです。

  • サーバーにフォントがない

  • 必要なネイティブライブラリがない

  • 一時フォルダに書き込み権限がない

  • 実行ユーザーの権限が不足している

  • 画像やCSSのパスが違う

  • Dockerイメージに必要パッケージが入っていない

  • メモリ不足やタイムアウトが発生している

対策として、開発環境と本番環境をできるだけ近づけることが重要です。Dockerを使う場合は、フォントや依存パッケージをDockerfileで明示し、CI環境でもPDF生成テストを行うと安全です。

9-6. ファイルロックや権限エラーが発生する

PDF保存時にファイルロックや権限エラーが発生する場合、Streamやドキュメントオブジェクトが適切に破棄されていない可能性があります。

対策は次のとおりです。

  • usingでStreamを確実に破棄する

  • 同じファイル名に同時書き込みしない

  • 一時ファイル名にGUIDを使う

  • 保存先フォルダの権限を確認する

  • PDF生成後にファイルを開きっぱなしにしない

  • Webアプリでは可能な限りメモリ上でPDFを返す

一時ファイルを使う場合は、処理完了後に削除する仕組みも必要です。例外発生時にも削除されるよう、try-finallyを使うと安全です。

10. 用途別の実装パターン

10-1. 請求書PDFを作成する

請求書PDFでは、会社情報、請求先、請求番号、発行日、明細、合計金額、振込先、備考などを出力します。

実装パターンは大きく2つあります。

1つ目は、QuestPDFやPDFsharpなどを使ってC#コードでレイアウトする方法です。データとレイアウトを型安全に管理しやすく、帳票生成処理をコード中心で保守できます。

2つ目は、HTMLテンプレートを作成し、IronPDFやSyncfusion PDFなどでPDF化する方法です。CSSで見た目を調整できるため、デザイン変更が多い請求書に向いています。

QuestPDFを使った簡単なイメージは次のとおりです。

C#
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;

QuestPDF.Settings.License = LicenseType.Community;

Document.Create(container =>
{
container.Page(page =>
{
page.Margin(30);

page.Header()
.Text("請求書")
.FontSize(24)
.Bold();

page.Content()
.Column(column =>
{
column.Item().Text("株式会社サンプル 御中");
column.Item().Text("請求番号:INV-001");
column.Item().Text("合計金額:110,000円");

column.Item().Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.ConstantColumn(80);
columns.ConstantColumn(100);
});

table.Header(header =>
{
header.Cell().Text("品名");
header.Cell().Text("数量");
header.Cell().Text("金額");
});

table.Cell().Text("システム開発費");
table.Cell().Text("1");
table.Cell().Text("100,000円");
});
});
});
})
.GeneratePdf("invoice.pdf");

実務では、消費税、源泉徴収、端数処理、インボイス制度に関わる登録番号、振込期限なども要件として確認しましょう。

10-2. 帳票・レポートPDFを作成する

帳票やレポートPDFでは、表、グラフ、集計値、ページ番号、ヘッダー、フッターが重要になります。

実装時のポイントは次のとおりです。

  • データ取得処理とPDF生成処理を分離する

  • ページヘッダーとフッターを共通化する

  • 明細行が多い場合の改ページを考慮する

  • 集計行や小計行の表示位置を制御する

  • 出力条件をPDF内に記載する

  • 生成日時や出力者を入れる

レポートPDFはデータ量が多くなりやすいため、メモリ使用量にも注意が必要です。大量データを一度にPDF化する場合は、ページ単位、部署単位、期間単位などで分割生成する設計も検討しましょう。

10-3. 既存PDFに追記する

既存PDFに追記するパターンは、申請書、証明書、契約書、受付票などでよく使われます。

実装の流れは次のとおりです。

  1. テンプレートPDFを用意する

  2. C#でPDFを読み込む

  3. 差し込み位置の座標を決める

  4. 氏名、日付、番号などを描画する

  5. 新しいPDFとして保存する

この方法では、テンプレートPDFのデザインをWordやデザインツールで作成し、C#では値の差し込みだけを担当できます。

ただし、テンプレートPDFのレイアウトが変わると座標も変わるため、差し込み位置を設定ファイル化しておくと保守しやすくなります。

10-4. 複数PDFを結合する

複数PDFの結合は、添付資料をまとめる、帳票を一括出力する、部署別レポートを1つにする、といった場面で使われます。

基本的な流れは次のとおりです。

  1. 結合対象ファイルの一覧を取得する

  2. 出力用PDFを作成する

  3. 各PDFを読み込む

  4. ページを順番に追加する

  5. 1つのPDFとして保存する

注意点は、結合順序です。ファイル名順、作成日時順、画面で選択した順番など、業務上正しい順序を明確にしましょう。

また、パスワード付きPDFや破損PDFが混じっていると結合に失敗することがあります。結合前にファイル存在チェック、拡張子チェック、読み込みチェックを行うと安定します。

10-5. WebアプリでPDFを動的生成する

ASP.NET CoreでPDFを動的生成する場合は、ControllerからPDF生成サービスを呼び出し、byte[]で返す構成が扱いやすいです。

C#
public interface IPdfService
{
byte[] CreateInvoicePdf(int invoiceId);
}

Controllerでは、PDF生成ロジックを直接持たず、サービスに委譲します。

C#
[HttpGet("invoice/{id}/download")]
public IActionResult Download(int id)
{
var pdf = _pdfService.CreateInvoicePdf(id);

return File(pdf, "application/pdf", $"invoice-{id}.pdf");
}

この構成にすると、PDF生成処理を単体テストしやすくなります。また、将来的にPDFsharpからQuestPDFへ変更する場合や、HTML to PDF方式へ切り替える場合も、Controllerへの影響を抑えられます。

Webアプリでは、認可チェックも重要です。URLに請求書IDを指定する場合、ログインユーザーがそのPDFを閲覧してよいか必ず確認してください。

11. C# PDF開発で押さえておきたいベストプラクティス

11-1. レイアウトを保守しやすくする設計

PDF生成処理は、最初は簡単でも、項目追加やレイアウト変更によって複雑になりがちです。

保守しやすくするには、次のような設計を意識しましょう。

  • PDF生成専用クラスを作る

  • データ取得とPDF描画を分離する

  • ヘッダー、フッター、明細表を部品化する

  • 余白、フォントサイズ、色を定数化する

  • テンプレート方式を検討する

  • 座標をハードコードしすぎない

  • サンプルPDFをテストデータとして管理する

請求書や帳票では、法改正、会社ロゴ変更、項目追加、備考欄追加などの変更が発生します。変更に強い構成にしておくことが重要です。

11-2. 大量PDF生成時のパフォーマンス対策

大量のPDFを生成する場合は、パフォーマンスとメモリ使用量に注意が必要です。

対策は次のとおりです。

  • 不要な画像を圧縮する

  • 大きすぎるフォントや画像を使わない

  • 生成したPDFをすぐに破棄する

  • 一度に処理する件数を制限する

  • バッチ処理では分割実行する

  • 同時実行数を制御する

  • 一時ファイルの削除を徹底する

  • 例外発生時にリトライ可能な設計にする

Webリクエスト内で重いPDFを大量生成すると、タイムアウトやメモリ不足につながります。大量出力はバックグラウンドジョブ化し、完了後にダウンロードリンクを提供する設計も検討しましょう。

11-3. 例外処理とログ出力の設計

PDF生成では、フォント、画像、ファイルパス、権限、データ不整合など、さまざまな理由で例外が発生します。

ログに残すべき情報は次のとおりです。

  • PDF種別

  • 対象データID

  • ユーザーID

  • 生成日時

  • 使用テンプレート

  • 出力先

  • 例外メッセージ

  • スタックトレース

一方で、PDF本文に含まれる個人情報やパスワードをログに出してはいけません。ログは障害調査に必要な情報に絞り、機密情報を含めないように設計しましょう。

11-4. テストしやすいPDF生成処理にする方法

PDF生成処理は見た目の確認が必要なため、テストが難しい領域です。しかし、設計を工夫すれば自動テストしやすくなります。

テストのポイントは次のとおりです。

  • PDF生成サービスを独立させる

  • 入力データを固定する

  • PDFファイルが生成されることを確認する

  • ファイルサイズが0でないことを確認する

  • 例外が発生しないことを確認する

  • テキスト抽出できるライブラリで内容を検証する

  • 重要な帳票は目視確認用のゴールデンファイルを用意する

見た目の完全一致を自動テストするのは難しいですが、生成可否、ページ数、主要テキスト、ファイルサイズなどを確認するだけでも品質は上がります。

11-5. 将来的なライブラリ変更を見据えた実装方針

PDFライブラリは、ライセンス変更、サポート終了、.NETバージョン変更、クラウド環境への移行などによって、将来的に変更が必要になる可能性があります。

そのため、アプリケーション全体が特定ライブラリに強く依存しすぎないようにしましょう。

おすすめは、PDF生成処理をインターフェースで抽象化することです。

C#
public interface IInvoicePdfGenerator
{
byte[] Generate(Invoice invoice);
}

実装クラスでは、PDFsharp、QuestPDF、IronPDFなど具体的なライブラリを使います。

C#
public class QuestPdfInvoiceGenerator : IInvoicePdfGenerator
{
public byte[] Generate(Invoice invoice)
{
// QuestPDFを使ってPDFを生成する
return Array.Empty<byte>();
}
}

Controllerや業務ロジックは、具体的なPDFライブラリではなく、IInvoicePdfGeneratorに依存させます。これにより、ライブラリ変更時の影響範囲を小さくできます。

まとめ

C#でPDFを作成・編集・出力するには、標準機能だけで実装しようとせず、用途に合ったPDFライブラリを選ぶことが重要です。

シンプルなPDF作成や結合ならPDFsharp、C#コードで保守しやすい帳票を作るならQuestPDF、HTMLやRazorテンプレートからPDFを生成するならIronPDFやSyncfusion PDF、高度なPDF編集や電子署名が必要ならiText、Syncfusion PDF、Spire.PDFなどが候補になります。

ライブラリ選定では、機能だけでなく、商用ライセンス、日本語フォント対応、HTML to PDF対応、既存PDF編集、クラウド環境での動作、サポート体制を確認しましょう。

また、C# PDF開発では、日本語文字化け、フォント埋め込み、画像パス、CSS崩れ、サーバー権限、ファイルロックといったトラブルが発生しやすいため、早い段階で本番環境に近い条件で検証することが大切です。

PDF生成処理は業務システムの重要な出力機能です。データ取得、レイアウト、出力、セキュリティ、テストを分離して設計し、保守しやすいC# PDF実装を目指しましょう。