C#でHTMLをPDFに - ライブラリオプションの現実
C#でHTMLをPDFに変換するには、タグのサブセットを解析してCSS 2.1に近似するライブラリではなく、実際にHTMLをレンダリングするライブラリが必要です。Stack OverflowのスレッドやRedditのディスカッションで推奨されているライブラリのほとんどは、最新のCSSをレンダリングできないか、商用利用には不適格なライセンス制限があるか、セキュリティ脆弱性のパッチが適用されていないまま放棄されているかのいずれかです。
この記事では、開発者が"HTML to PDF C#"と検索したときに実際に遭遇するライブラリを比較し、それぞれがレンダリングできるものとできないものを文書化し、方法論によるパフォーマンスベンチマークを含め、各アプローチの実際の運用コストを示します。
クイックスタート:C#でHTMLをPDFに変換する。
using IronPdf;
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>");
pdf.SaveAs("output.pdf");using IronPdf;
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>");
pdf.SaveAs("output.pdf");Imports IronPdf
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>")
pdf.SaveAs("output.pdf")NuGet経由でインストールします:Install-Package IronPDF. Windows、Linux、macOS、およびDockerに、外部依存なしにデプロイできます。
なぜ HTML から PDF への変換は難しいのでしょうか
HTMLをPDFに正しくレンダリングするには、ウェブブラウザが使用するのと同じ5つのコンポーネント、すなわちHTMLパーサー、CSSエンジン(Flexbox、Grid、cascade、specificity、メディアクエリを含む)、JavaScriptランタイム、レイアウトエンジン、そしてこれらすべてをサブピクセル精度でPDFに合成するレンダリングパイプラインを実装する必要があります。
従来のPDFライブラリは、最初の2つを部分的に実装し、JavaScriptを完全にスキップしています。 このため、簡単なHTMLは扱えますが、モダンブラウザが正しくレンダリングするものはすべて壊れてしまいます。 ブラウザの出力に合わせる唯一の方法は、ブラウザエンジンを使用することです。
実際に HTML を PDF に変換するライブラリはどれですか?
wkhtmltopdfラッパー - DLL読み込みエラーエコシステム
開発者がこれらの記事にたどり着く最も一般的な検索クエリは、以下のようなバリエーションです:
System.DllNotFoundException:DLL 'libwkhtmltox' をロードできません。プラットフォーム固有のバリエーションは以下のとおりです:
共有ライブラリ 'wkhtmltox'またはその依存関係の1つをロードできません。
(Linux — libwkhtmltox.so not found)
指定されたモジュールが見つかりません。 (0x8007007E)
(Windows — wkhtmltox.dll path not configured)
dyld:ライブラリがロードされていません: libwkhtmltox.dylib
(macOS — not supported on ARM64/Apple Silicon)これらのエラーは、DinkToPdf、NReco.PdfGenerator、WkHtmlToXSharp、および同じ放棄されたバイナリの周りの他のC#ラッパーから来ています。 wkhtmltopdf GitHub 組織は、2024 年 7 月にアーカイブされました。 基礎となる QtWebKit エンジンは、2015 年に Qt によって廃止されました。プロジェクトのステータスページでは、非推奨と明示されています。
DLLの読み込みの問題以外にも、レンダリングエンジンはSafari 2011の機能程度で止まっています。 Flexboxなし、Gridなし、限られたCSS3、信頼できないJavaScript。 また、パッチが適用されていない重大な脆弱性があります:CVE-2022-35583(CVSS 9.8)は、細工されたHTMLを通してAWSの認証情報を流出させるSSRF攻撃を可能にします。
wkhtmltopdfの時間は過ぎました。 DLLの読み込みエラーは、より深い問題の徴候です:あなたは、先の見えない見捨てられたソフトウェアに依存しています。
iText 7 (pdfHTML アドオン) - 限定 CSS、AGPL ライセンス
iTextのpdfHTMLモジュールは、ブラウザエンジンではなく、カスタムパーサーを使用してHTMLをPDFに変換します。基本的なHTML/CSSは扱えますが、FlexboxやGrid、JavaScriptはレンダリングしません。
pdfHTMLは、サポートされていないCSSに遭遇しても例外をスローしません。 レンダリングできるものはレンダリングし、それ以外は無視します。display: flex コンテナで、gap: 20px と justify-content: space-between を指定すると、間隔を空けずに縦に積み重なった要素としてレンダリングされます。 開発者は、統合中ではなく、統合後にこれを発見します。
ライセンス: AGPL - ネットワークからアクセス可能なアプリケーション全体をオープンソースにするか、商用ライセンスを購入する必要があります。 価格は公開されていません; 第三者機関のデータでは、年間15,000~210,000ドルとなっています。
メモリ使用量はどのように比較されますか?
iTextのpdfHTMLは、文書全体をメモリにロードして処理します。 一般的なビジネス文書であれば何とかなりますが、画像を埋め込んだ大規模なHTMLレポートは、ストリーミングアプローチと比較して、メモリに大きな負担をかける可能性があります。
なぜPdfSharpはHTMLをサポートしないのですか?
PdfSharpは、その人気(NuGetダウンロード数3,490万)と頻繁な推奨により、"HTML to PDF"の検索結果に表示されます。 しかし、PdfSharpにはHTMLパーサーがありません。 座標ベースの描画APIを提供します:DrawString()、DrawRectangle()、DrawImage()で、X/Y 位置を明示的に指定できます。
一般的に提案されている回避策、HtmlRenderer.PdfSharpは、HTML 4.01とCSS Level 2のみをサポートしています。 HTMLに2010年以降に導入されたCSS機能(Flexbox(2012年)、Grid(2017年)、カスタムプロパティ(2017年)、border-radius(2011年))が使用されている場合、レンダリングされません。
HTMLのサポートを期待してPdfSharpを選択した開発者は、座標ベースのコードですべての要素を手動で配置するか、HTMLレンダリングのために2つ目のライブラリを追加することになります。
何がPuppeteer Sharpをリソース集約型にしているのですか?
Puppeteer Sharp は、.NETバインディングを介してヘッドレスChromeを制御します。 Chromeであるため、レンダリング精度はChromeに合わせます。 外部ブラウザのプロセスを管理します。
チュートリアルの5行の例ではなく、同時PDF生成に必要なブラウザプーリングのコードです:
using PuppeteerSharp;
using System.Collections.Concurrent;
public class PdfBrowserPool : IAsyncDisposable
{
private readonly ConcurrentBag<IBrowser> _available = new();
private readonly SemaphoreSlim _semaphore;
private readonly int _maxBrowsers;
public PdfBrowserPool(int maxBrowsers = 4)
{
_maxBrowsers = maxBrowsers;
_semaphore = new SemaphoreSlim(maxBrowsers, maxBrowsers);
}
public async Task InitializeAsync()
{
await new BrowserFetcher().DownloadAsync(); // ~280MB download
for (int i = 0; i < _maxBrowsers; i++)
{
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[] { "--no-sandbox", "--disable-setuid-sandbox",
"--disable-dev-shm-usage" }
});
_available.Add(browser);
}
}
public async Task<byte[]> ConvertHtmlToPdf(string html)
{
await _semaphore.WaitAsync();
IBrowser browser = null;
try
{
if (!_available.TryTake(out browser))
throw new InvalidOperationException("No browser available");
await using var page = await browser.NewPageAsync();
await page.SetContentAsync(html, new NavigationOptions
{
WaitUntil = new[] { WaitUntilNavigation.Networkidle0 }
});
var result = await page.PdfAsync(new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true
});
return result;
}
catch (Exception ex) when (ex is NavigationException or TargetClosedException)
{
// Browser crashed — replace it
browser?.Dispose();
browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[] { "--no-sandbox", "--disable-setuid-sandbox" }
});
throw; // Re-throw after recovery
}
finally
{
if (browser != null) _available.Add(browser);
_semaphore.Release();
}
}
public async ValueTask DisposeAsync()
{
foreach (var browser in _available)
{
await browser.CloseAsync();
browser.Dispose();
}
}
}using PuppeteerSharp;
using System.Collections.Concurrent;
public class PdfBrowserPool : IAsyncDisposable
{
private readonly ConcurrentBag<IBrowser> _available = new();
private readonly SemaphoreSlim _semaphore;
private readonly int _maxBrowsers;
public PdfBrowserPool(int maxBrowsers = 4)
{
_maxBrowsers = maxBrowsers;
_semaphore = new SemaphoreSlim(maxBrowsers, maxBrowsers);
}
public async Task InitializeAsync()
{
await new BrowserFetcher().DownloadAsync(); // ~280MB download
for (int i = 0; i < _maxBrowsers; i++)
{
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[] { "--no-sandbox", "--disable-setuid-sandbox",
"--disable-dev-shm-usage" }
});
_available.Add(browser);
}
}
public async Task<byte[]> ConvertHtmlToPdf(string html)
{
await _semaphore.WaitAsync();
IBrowser browser = null;
try
{
if (!_available.TryTake(out browser))
throw new InvalidOperationException("No browser available");
await using var page = await browser.NewPageAsync();
await page.SetContentAsync(html, new NavigationOptions
{
WaitUntil = new[] { WaitUntilNavigation.Networkidle0 }
});
var result = await page.PdfAsync(new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true
});
return result;
}
catch (Exception ex) when (ex is NavigationException or TargetClosedException)
{
// Browser crashed — replace it
browser?.Dispose();
browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[] { "--no-sandbox", "--disable-setuid-sandbox" }
});
throw; // Re-throw after recovery
}
finally
{
if (browser != null) _available.Add(browser);
_semaphore.Release();
}
}
public async ValueTask DisposeAsync()
{
foreach (var browser in _available)
{
await browser.CloseAsync();
browser.Dispose();
}
}
}Imports PuppeteerSharp
Imports System.Collections.Concurrent
Imports System.Threading
Public Class PdfBrowserPool
Implements IAsyncDisposable
Private ReadOnly _available As New ConcurrentBag(Of IBrowser)()
Private ReadOnly _semaphore As SemaphoreSlim
Private ReadOnly _maxBrowsers As Integer
Public Sub New(Optional maxBrowsers As Integer = 4)
_maxBrowsers = maxBrowsers
_semaphore = New SemaphoreSlim(maxBrowsers, maxBrowsers)
End Sub
Public Async Function InitializeAsync() As Task
Await (New BrowserFetcher()).DownloadAsync() ' ~280MB download
For i As Integer = 0 To _maxBrowsers - 1
Dim browser = Await Puppeteer.LaunchAsync(New LaunchOptions With {
.Headless = True,
.Args = New String() {"--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"}
})
_available.Add(browser)
Next
End Function
Public Async Function ConvertHtmlToPdf(html As String) As Task(Of Byte())
Await _semaphore.WaitAsync()
Dim browser As IBrowser = Nothing
Try
If Not _available.TryTake(browser) Then
Throw New InvalidOperationException("No browser available")
End If
Await Using page = Await browser.NewPageAsync()
Await page.SetContentAsync(html, New NavigationOptions With {
.WaitUntil = New WaitUntilNavigation() {WaitUntilNavigation.Networkidle0}
})
Dim result = Await page.PdfAsync(New PdfOptions With {
.Format = PaperFormat.A4,
.PrintBackground = True
})
Return result
End Using
Catch ex As Exception When TypeOf ex Is NavigationException OrElse TypeOf ex Is TargetClosedException
' Browser crashed — replace it
browser?.Dispose()
browser = Await Puppeteer.LaunchAsync(New LaunchOptions With {
.Headless = True,
.Args = New String() {"--no-sandbox", "--disable-setuid-sandbox"}
})
Throw ' Re-throw after recovery
Finally
If browser IsNot Nothing Then _available.Add(browser)
_semaphore.Release()
End Try
End Function
Public Async Function DisposeAsync() As ValueTask Implements IAsyncDisposable.DisposeAsync
For Each browser In _available
Await browser.CloseAsync()
browser.Dispose()
Next
End Function
End Classこれは、1つのPDFを生成する前に、インフラストラクチャコードの〜60行です。 また、メモリリーク監視(Chromiumプロセスは時間とともにメモリを蓄積します)、ヘルスチェック、20以上のChromium依存関係を含むDockerfileも必要です。 Dockerイメージのサイズは300-400MB増加します。
IronPDFのアプローチと比較してください:
using IronPdf;
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
// Browser pooling, process management, crash recovery — handled internallyusing IronPdf;
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
// Browser pooling, process management, crash recovery — handled internallyImports IronPdf
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(html)
' Browser pooling, process management, crash recovery — handled internallyPuppeteer Sharpは、チームが運用のオーバーヘッドを吸収できるのであれば、実行可能です。 ブラウザのインフラよりもアプリケーションに集中したいチームのために、IronPdfは同じレンダリングを内部で処理します。
なぜQuestPDFはHTMLを変換できないのですか?
QuestPDFは、RedditやStack Overflowのほぼすべての"HTML to PDF C#"ディスカッションに登場します。 このため、開発者はQuestPDFを購入したり、HTML変換を期待して組み込んだのに、HTMLがまったくレンダリングされないという一貫したパターンが発生します。
QuestPDF は、プログラムによる文書作成のための流暢な C# API です。 HTMLからPDFへの変換と格闘するのはもうやめよう"というのが、このツールの位置づけです。 これは意図的な設計上の選択です。2022年から2024年までのGitHubの議論では、実装を開始した後に開発者がこのことを発見しています。 メンテナンス担当者は、HTMLのサポートが予定されていないことを一貫して確認しています。
既存のワークフローでHTMLテンプレート(請求書用のRazorビュー、レポート用のダッシュボードHTML、アーカイブ用のWebコンテンツ)を使用している場合、QuestPDFではすべてのテンプレートをC#流暢なAPIコードで書き直す必要があります。 構造化データを使ってゼロからドキュメントレイアウトを構築する新しいプロジェクトにとって、QuestPDFのAPIはよく設計されており、生産的です。
コミュニティライセンスは、年間総収入100万ドル未満の企業を対象としています。 その上で、商用ライセンスが必要です。
Aspose.PDFについて
Aspose.PDFは、商用ライセンス(~999ドル/開発者)で幅広いPDF機能を提供します。 HTMLの変換には、ブラウザではなくカスタムエンジンを使用します。iTextと同様、基本的なHTMLは処理できますが、最新のCSS機能を正確にレンダリングすることはできません。
最大の関心事はプラットフォームの安定性です:AsposeはSystem.Drawing.Commonに依存しており、Linuxではlibgdiplusが必要です。 マイクロソフトは、.NET 6+で、Windows以外のプラットフォーム向けの翻訳を非推奨としました。 開発者は、Windows では発生しない Linux デプロイメント特有のメモリリークを報告しています。 Windowsのみの環境では、Asposeが可能です。 クロスプラットフォームやコンテナ化されたデプロイメントでは、依存関係の連鎖が継続的なリスクを生み出します。
IronPDFはどのようにHTMLからPDFへの変換を行いますか?
IronPdfはChromiumを直接NuGetパッケージに組み込みます。 CSS Flexbox、Grid、カスタムプロパティ、@font-face、メディアクエリ、JavaScriptはすべて、Chromeと同じように実行されます。 同じレンダリングエンジンを使用しているため、出力はブラウザと一致します。
using IronPdf;
var renderer = new ChromePdfRenderer();
string html = @"
<!DOCTYPE html>
<html>
<head>
<style>
:root { --primary: #2563eb; }
body { font-family: 'Segoe UI', sans-serif; padding: 40px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; }
.card {
background: linear-gradient(135deg, #f8fafc, #e2e8f0);
border-radius: 12px; padding: 24px; text-align: center;
}
.card h3 { color: #6b7280; font-size: 0.8rem; text-transform: uppercase; margin: 0; }
.card .value { font-size: 2rem; font-weight: 700; color: var(--primary); }
table { width: 100%; border-collapse: collapse; margin-top: 30px; }
th { background: var(--primary); color: white; padding: 12px; text-align: left; }
td { padding: 10px; border-bottom: 1px solid #e5e7eb; }
</style>
</head>
<body>
<div class='grid'>
<div class='card'><h3>Revenue</h3><div class='value'>$1.2M</div></div>
<div class='card'><h3>Users</h3><div class='value'>45,230</div></div>
<div class='card'><h3>Uptime</h3><div class='value'>99.97%</div></div>
</div>
<table>
<tr><th>Product</th><th>Revenue</th><th>Growth</th></tr>
<tr><td>Enterprise</td><td>$680K</td><td>+12%</td></tr>
<tr><td>Professional</td><td>$356K</td><td>+8%</td></tr>
</table>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("report.pdf");using IronPdf;
var renderer = new ChromePdfRenderer();
string html = @"
<!DOCTYPE html>
<html>
<head>
<style>
:root { --primary: #2563eb; }
body { font-family: 'Segoe UI', sans-serif; padding: 40px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; }
.card {
background: linear-gradient(135deg, #f8fafc, #e2e8f0);
border-radius: 12px; padding: 24px; text-align: center;
}
.card h3 { color: #6b7280; font-size: 0.8rem; text-transform: uppercase; margin: 0; }
.card .value { font-size: 2rem; font-weight: 700; color: var(--primary); }
table { width: 100%; border-collapse: collapse; margin-top: 30px; }
th { background: var(--primary); color: white; padding: 12px; text-align: left; }
td { padding: 10px; border-bottom: 1px solid #e5e7eb; }
</style>
</head>
<body>
<div class='grid'>
<div class='card'><h3>Revenue</h3><div class='value'>$1.2M</div></div>
<div class='card'><h3>Users</h3><div class='value'>45,230</div></div>
<div class='card'><h3>Uptime</h3><div class='value'>99.97%</div></div>
</div>
<table>
<tr><th>Product</th><th>Revenue</th><th>Growth</th></tr>
<tr><td>Enterprise</td><td>$680K</td><td>+12%</td></tr>
<tr><td>Professional</td><td>$356K</td><td>+8%</td></tr>
</table>
</body>
</html>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("report.pdf");Imports IronPdf
Dim renderer As New ChromePdfRenderer()
Dim html As String = "
<!DOCTYPE html>
<html>
<head>
<style>
:root { --primary: #2563eb; }
body { font-family: 'Segoe UI', sans-serif; padding: 40px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; }
.card {
background: linear-gradient(135deg, #f8fafc, #e2e8f0);
border-radius: 12px; padding: 24px; text-align: center;
}
.card h3 { color: #6b7280; font-size: 0.8rem; text-transform: uppercase; margin: 0; }
.card .value { font-size: 2rem; font-weight: 700; color: var(--primary); }
table { width: 100%; border-collapse: collapse; margin-top: 30px; }
th { background: var(--primary); color: white; padding: 12px; text-align: left; }
td { padding: 10px; border-bottom: 1px solid #e5e7eb; }
</style>
</head>
<body>
<div class='grid'>
<div class='card'><h3>Revenue</h3><div class='value'>$1.2M</div></div>
<div class='card'><h3>Users</h3><div class='value'>45,230</div></div>
<div class='card'><h3>Uptime</h3><div class='value'>99.97%</div></div>
</div>
<table>
<tr><th>Product</th><th>Revenue</th><th>Growth</th></tr>
<tr><td>Enterprise</td><td>$680K</td><td>+12%</td></tr>
<tr><td>Professional</td><td>$356K</td><td>+8%</td></tr>
</table>
</body>
</html>"
Dim pdf = renderer.RenderHtmlAsPdf(html)
pdf.SaveAs("report.pdf")これは、auto-fit/minmax、カスタムプロパティ、linear-gradient、border-radius、:rootセレクタを持つCSS Gridを使用しています。 これらの機能は、iTextのpdfHTMLでは失敗し、wkhtmltopdfでは壊れ、PdfSharpやQuestPDFでは存在しません。
他のライブラリからどのように移行できますか?
iTextSharpやwkhtmltopdfから移行するチームにとって、IronPDFはURLを直接受け取ることができます:
using IronPdf;
var renderer = new ChromePdfRenderer();
// Convert from URL — useful when migrating from wkhtmltopdf URL-based workflows
var pdf = renderer.RenderUrlAsPdf("https://localhost:5001/reports/quarterly");
pdf.SaveAs("report.pdf");
// Convert from local HTML file
var pdfFromFile = renderer.RenderHtmlFileAsPdf("templates/invoice.html");
pdfFromFile.SaveAs("invoice.pdf");using IronPdf;
var renderer = new ChromePdfRenderer();
// Convert from URL — useful when migrating from wkhtmltopdf URL-based workflows
var pdf = renderer.RenderUrlAsPdf("https://localhost:5001/reports/quarterly");
pdf.SaveAs("report.pdf");
// Convert from local HTML file
var pdfFromFile = renderer.RenderHtmlFileAsPdf("templates/invoice.html");
pdfFromFile.SaveAs("invoice.pdf");Imports IronPdf
Dim renderer As New ChromePdfRenderer()
' Convert from URL — useful when migrating from wkhtmltopdf URL-based workflows
Dim pdf = renderer.RenderUrlAsPdf("https://localhost:5001/reports/quarterly")
pdf.SaveAs("report.pdf")
' Convert from local HTML file
Dim pdfFromFile = renderer.RenderHtmlFileAsPdf("templates/invoice.html")
pdfFromFile.SaveAs("invoice.pdf")展開
IronPdfはWindows (x64)、Linux (x64、ARM64)、macOS (x64、Apple Silicon)、Dockerコンテナ上で動作します。 Docker構成は標準的な.NETイメージです:
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "MyApp.dll"]Chromiumのインストール、ネイティブ・ライブラリの依存、サンドボックスの設定は不要です。
ライセンス: 749ドルからの永続ライセンス。価格はironPdf.comで公開されています。 AGPL、文書ごとの料金、収益のしきい値はありません。
パフォーマンスベンチマーク
Ubuntu 22.04と.NET 8を実行するStandard_D4s_v3 Azure VM(4 vCPU、16GB RAM)でテスト。テストドキュメント:CSSグリッドレイアウト、埋め込み画像、JavaScript生成チャートを含む200要素のHTML請求書テンプレート。 各測定は、5回のウォームアップ期間の後、平均50回以上繰り返された。
| シナリオ | IronPDF | シャープ | iText pdfHTML | wkhtmltopdf |
|---|---|---|---|---|
| シンプルなHTML(JSなし) | ~150ms | ~500ms | ~200ms | ~200ms |
| 複雑なCSS(Flexbox/Grid) | ~250ms | ~600ms | ブロークン出力 | ブロークン出力 |
| JavaScriptレンダリングコンテンツ | ~350ms | ~800ms | 失敗例(JSエンジンなし) | 失敗/部分的 |
| 操作ごとのメモリ | ~80MB | ~150MB | ~60MB | ~50MB |
| コールドスタート(第一世代) | 2-5s | 3-8s | <1s | <1s |
iTextとwkhtmltopdfは、ブラウザエンジンを初期化しないため、コールドスタートが速い。複雑なCSSやJavaScriptのコンテンツでは、IronPdfとPuppeteer Sharpのみが使用可能な結果を生成します。
注意:これらは、指定されたハードウェア上での典型的な観察を表しています。 HTMLの複雑さ、ドキュメントの長さ、サーバーのリソースによってパフォーマンスが変わります。 決定する前に、実際の作業負荷でテストしてください。
機能比較
| フィーチャー | IronPDF | iText 7 | シャープ | wkhtmltopdf | PdfSharp | QuestPDF | アスパス |
|---|---|---|---|---|---|---|---|
| HTMLからPDFへ | はい(Chromium) | 制限あり(CSS 2.1) | はい(Chrome) | 非推奨 | なし | なし | 制限的 |
| CSS フレックスボックス/グリッド | はい | なし | はい | なし | なし | なし | なし |
| JavaScriptの実行 | はい | なし | はい | 制限的 | なし | なし | なし |
| クロスプラットフォーム(libgdiplusなし) | はい | はい | はい | 該当なし | 部分的 | はい | なし |
| 公開価格 | $749+ | なし(年俸$15K-$210K) | 無料(MIT) | 無料 | 無料(MIT) | 無料 <$1M | $999+ |
| アクティブメンテナンス | はい | はい | はい | 中止 | はい | はい | はい |
どのライブラリを選べばよいですか?
モダンなCSSによるHTMLテンプレート→IronPdfは、外部のプロセス管理なしで組み込みChromiumを提供します。 もしあなたのチームがブラウザインフラを管理できるのであれば、Puppeteer Sharpは有効な選択肢です。
データからプログラム的な文書生成、HTMLなし→QuestPDFはエレガントで流暢なAPIを提供します。 HTML変換を期待しないでください。
簡単なPDF操作(結合、分割、透かし)→PdfSharpは無料で、HTML以外のタスクに対応できます。
新しいプロジェクトでは避けてください:wkhtmltopdf (放棄、CVEs)、商用ライセンスのないiText (AGPLトラップ)、Linux上のAspose (メモリリーク)。
重要なのは、ワークフローがHTMLテンプレートを使用しているかどうかです。 もしそうであれば、Chromiumベースのソリューションだけが、最新のCSSで正しい出力を生成します。 そうでない場合は、APIの好みとライセンスの制約によって選択することになります。