IronPDF vs EvoPdf: 2026년에 더 나은 가치를 제공하는 .NET PDF 라이브러리는?
현대 .NET 애플리케이션을 구축할 때 PDF 기능이 필요하다면, 적절한 라이브러리를 선택하는 것이 개발 속도, 애플리케이션 성능 및 장기 유지보수 비용에 큰 영향을 미칠 수 있습니다. IronPDF와 EvoPdf는 .NET 생태계에서 PDF 생성 및 조작에 대한 두 가지 뚜렷한 접근 방식을 대표하며, 각각의 고유한 강점은 다양한 개발 시나리오에 맞춰져 있습니다.
이 비교는 렌더링 정확도, API 설계, 성능 고려사항, 플랫폼 호환성 및 총 소유 비용을 포함한 중요한 차원에서 두 라이브러리를 검토합니다. 대량 문서 처리 시스템을 구축하든, 문서에 서명하든, 단순히 HTML 보고서를 PDF로 변환하든, 이러한 차이점을 이해하면 기술 요구 사항과 예산 제약에 맞는 정보에 입각한 결정을 내리는 데 도움이 될 것입니다.
IronPDF와 EvoPdf는 한눈에 어떻게 비교됩니까?
자세한 비교에 들어가기 전에, .NET 개발자에게 가장 중요한 주요 카테고리에서 이들 라이브러리가 어떻게 비교되는지 개요를 살펴보겠습니다:
| 카테고리 | 기능/측면 | IronPDF | EvoPdf | 주요 장점 |
|---|---|---|---|---|
| 핵심 아키텍처 | 설계 철학 | 단순성 우선, 직관적인 API | 변환 설정에 대한 세밀한 제어 | 팀 선호도에 따라 다름 |
| API 복잡성 | RenderHtmlAsPdf()와 같은 간단한 메서드 | ConvertHtml()와 같은 메서드를 가진 Converter 클래스 | 둘 다: 단일 호출 변환 | |
| 플랫폼 지원 | 크로스 플랫폼 | 플랫폼별 NuGet 패키지를 통해 Windows, Linux, macOS 지원 | EvoPdf Next: 플랫폼별 NuGet 패키지를 통해 Windows, Linux, macOS 지원; EvoPdf Classic: Windows, 별도로 호스팅된 EVO PDF 서버를 호출하는 크로스 플랫폼 클라이언트 | 둘 다: 크로스 플랫폼 로컬 렌더링 (EvoPdf Next) |
| .NET 버전 | .NET 10, 9, 8, 7, 6, 5, Core 3.1+, Standard 2.0, Framework 4.6.2+ | Next: .NET 10, 9, 8, 7, 6, Standard 2.0, Framework 4.6.2+; Classic: Framework 2.0+ | 둘 다: 최신 프레임워크 지원 | |
| 운영 체제 | Windows, Linux, macOS, Docker, Azure, AWS | Windows, Linux, macOS, Docker, Azure (EvoPdf Next) | 둘 다: Docker 및 클라우드 지원 | |
| HTML to PDF | 렌더링 엔진 | Chromium (표준 패키지의 CEF 109; IronPdf.UpdatedChrome의 CEF 131) | Chromium 142 (EvoPdf Next); EVO의 자체 엔진 (EvoPdf Classic) | 둘 다: Chromium 기반 |
| CSS3/HTML5 지원 | 번들된 Chromium 버전에 따라 다름: Windows에서는 표준 패키지의 109와 IronPdf.UpdatedChrome의 131 (예: Tailwind CSS v4는 Chrome 111 이상 필요) | Next: flexbox, grid, ES2015+, 웹 폰트 | 둘 다: 엔진 버전에 따라 다름 | |
| JavaScript 실행 | 렌더 지연과 함께 JavaScript 지원 | 변환 지연과 함께 JavaScript 지원 | 둘 다: 동적 콘텐츠 | |
| 보안 및 암호화 | 암호화 수준 | RC4-128, AES-128, AES-256 | RC4-40, RC4-128, AES-128, AES-256 | 둘 다: AES-256 사용 가능 |
| 권한 옵션 | 세밀한 권한 제어 | 세밀한 권한 (인쇄, 복사, 편집, 주석, 양식 채우기, 조립) | 둘 다: 세밀한 제어 | |
| 디지털 서명 | 통합된 시각적 서명 | 이미지 및 타임스탬프 지원이 있는 시각적 서명 | 둘 다: 시각적 서명 | |
| 콘텐츠 조작 | 편집 방법 | 진정한 콘텐츠 제거, 한 줄 API | 편집 기능 문서화되지 않음 | IronPDF: 내장된 편집 기능 |
| 워터마킹 | HTML/CSS 기반, 전체 스타일링 | 워터마크 및 스탬프를 위한 HTML 템플릿 | 둘 다: HTML 기반 | |
| 스탬핑 | 통합된 스탬퍼 클래스 | 템플릿을 통한 HTML 스탬프 | IronPDF: 전용 스탬퍼 클래스 | |
| 파일 변환 | DOCX to PDF | 내장된 DocxToPdfRenderer | EVO PDF Toolkit에 포함 (배포 라이선스: $650), HTML to PDF 라이선스에는 포함되지 않음 | IronPDF: 기본 라이선스에 포함 |
| 개발자 경험 | 코드 예제 | 100개 이상의 실행 가능한 샘플 | GitHub의 코드 샘플, 온라인 데모 | IronPDF: 광범위한 리소스 |
| 문서화 | 튜토리얼, 사용 방법, 비디오, API 참조 | 문서화, API 참조, 지원 FAQ | IronPDF: 다양한 학습 경로 | |
| 성능 | 동시성 | 비동기 렌더링 메서드 | 비동기 메서드 (EvoPdf Next); 병렬 변환을 위한 지속적인 렌더러 | 둘 다: 동시 작업 부하 |
| 라이선스 및 가격 | 입문 수준 | Lite: $999 (1 개발자, 1 프로젝트) | 배포: $450 (1 서버에서 1 애플리케이션, 무제한 개발자, 재배포 없음) | EvoPdf: 초기 비용 낮음 |
| 팀 라이선스 | Plus: $1,499 (3 개발자, 3 프로젝트) | 어느 티어도 개발자를 세지 않음; 회사: $1,200 (무제한 애플리케이션 및 서버) | EvoPdf: 팀 크기에 따라 가격이 달라지지 않음 | |
| 재배포 | +$1,999 로열티 프리 (OEM/SaaS); IronPDF를 노출하는 공개 API는 SDK 라이선스가 필요 | 회사 라이선스만; 독립형 변환 서비스나 SDK로는 불가 | EvoPdf: 회사 라이선스에 포함 | |
| 스위트 옵션 | Iron Suite Lite: $2,998 (10 제품; 1 개발자, 1 위치, 1 프로젝트) | EVO PDF Toolkit Company: $1,400 | 다른 범위: Iron Suite는 OCR, 바코드 및 오피스 도구 추가 | |
| 지원 | 지원 포함 | 예: 24/5 라이브 채팅; 프리미엄 지원으로 24/7 지원 (시험 기간 동안 포함) | 예, 첫 해 (표준 또는 우선, 라이선스에 따라) | 둘 다: 지원 포함 |
| 최적의 용도 | 사용 사례 | 현대 웹 앱, 복잡한 PDF, 편집 | HTML, Word 및 Excel을 PDF로 변환, 회사 전체 라이선스 | 상황에 따라 다름 |
참고. EvoPdf는 대규모 팀을 위한 경쟁력 있는 가격을 제공합니다. IronPDF는 기본 라이선스에 내장된 편집 및 DOCX to PDF 변환을 포함합니다.
IronPDF와 EvoPdf 이해하기: 핵심 강점과 철학
.NET PDF 라이브러리 시장에서 IronPDF가 돋보이는 이유는 무엇입니까?
IronPDF는 개발자 생산성을 핵심으로 설계된 .NET PDF 라이브러리입니다. 단순성을 기반으로 하면서도 강력함을 잃지 않고, IronPDF는 .NET 개발자가 직관적인 API를 사용하여 PDF 문서를 생성, 편집 및 조작할 수 있도록 합니다. 이 라이브러리의 두드러진 특징은 JavaScript를 실행하고 웹 폰트를 로드하는 Chromium 기반 렌더링 엔진입니다. 얼마나 많은 최신 CSS를 처리할 수 있는지는 배포하는 Chromium 버전에 따라 다릅니다. 표준 IronPdf 패키지는 오래된 Windows 서버와의 호환성을 위해 Chromium 109를 번들로 제공하며, Windows 전용 IronPdf.UpdatedChrome 패키지는 Chromium 131을 번들로 제공하여 subgrid와 같은 최신 CSS 기능을 올바르게 처리하지만, 32비트 프로세스, Windows Server 2012 또는 SingleProcess 모드를 지원하지 않으므로 두 패키지는 모든 대상에서 상호 교환할 수 없습니다.
이 라이브러리는 HTML 문서를 렌더링하고, 기존 PDF를 조작하며, 암호화, 서명 및 편집으로 문서를 보호하는 애플리케이션을 목표로 합니다. 광범위한 기능 세트에는 기본 PDF 생성뿐만 아니라 디지털 서명, 양식 채우기, IronOCR을 통한 OCR 통합 및 DOCX to PDF와 같은 문서 형식 변환과 같은 고급 기능이 포함됩니다. IronPDF의 크로스 플랫폼 호환성은 단순한 프레임워크 지원을 넘어 Windows, Linux, macOS, Docker 컨테이너 및 Azure 및 AWS와 같은 클라우드 플랫폼에서 실행됩니다.
EvoPdf는 PDF 생성에 어떻게 다르게 접근합니까?
EvoPdf는 HTML to PDF 변환에 중점을 두고 있으며, 사용자 정의 및 제어에 중점을 둡니다. 이 라이브러리는 개발자에게 렌더링 매개변수, 페이지 레이아웃 및 변환 설정을 세밀하게 조정할 수 있는 변환 프로세스에 대한 세밀한 제어를 제공합니다. EvoPdf는 두 가지 에디션으로 제공됩니다: Chromium 기반 렌더링 엔진(Chromium 142, EVO PDF에 따르면)을 사용하는 EvoPdf Next는 Windows, Linux 및 macOS에서 실행되며, EVO의 자체 렌더링 엔진을 사용하는 EvoPdf Classic은 Windows에서 실행됩니다. EVO PDF Toolkit은 Word, Excel, RTF 및 Markdown을 PDF로 변환하고 PDF 처리 구성 요소를 추가합니다.
EvoPdf의 주목할 만한 강점은 라이선스입니다: 어느 라이선스 티어도 개발자를 세지 않으며, 회사 라이선스는 무제한 애플리케이션 및 서버를 커버하고 자체 애플리케이션 내에서 재배포를 포함합니다. 대량 서버 작업 부하의 경우, EvoPdf Next는 변환 간에 렌더링 엔진을 실행 상태로 유지하는 지속적인 렌더러 모드를 문서화합니다. 이 라이브러리는 인증 쿠키가 필요한 페이지를 변환하는 ASP.NET 시나리오도 지원합니다.
라이브러리 간의 크로스 플랫폼 기능은 어떻게 비교됩니까?
IronPDF의 현대적인 크로스 플랫폼 아키텍처
IronPDF는 여러 .NET 버전, 운영 체제 및 클라우드 플랫폼에서 실행됩니다:
- .NET 버전 지원:
- .NET 10, 9, 8, 7, 6, 5, Core 3.1+
- .NET Standard 2.0+
- .NET Framework 4.6.2+
- C#, VB.NET 및 F#에 대한 전체 지원
- 운영 체제 호환성:
- Windows (x64; IronPdf.UpdatedChrome은 32비트 프로세스를 지원하지 않으므로 표준 패키지에서만 x86 지원)
- Linux (Ubuntu, Debian, CentOS; IronPdfEngine 컨테이너를 통한 Alpine)
- macOS (Intel 및 Apple Silicon)
- 사전 구성된 이미지를 사용한 Docker 컨테이너
- 클라우드 플랫폼 통합:
- Azure App Service, Functions 및 VMs
- AWS Lambda 및 EC2
- Kubernetes 배포
IronPDF는 플랫폼별 NuGet 패키지(IronPdf는 Windows, IronPdf.Linux, IronPdf.MacOs 및 IronPdf.MacOs.ARM)로 Chromium 바이너리를 제공합니다. Linux에서는 IronPDF가 기본적으로 누락된 시스템 종속성을 자동으로 설치하려고 시도합니다; 이는 sudo 권한이 필요하며, 그렇지 않으면 수동으로 종속성을 설치합니다. 이는 대부분의 환경에서 배포 단계를 짧게 유지합니다.
EvoPdf의 플랫폼 지원
EvoPdf는 유사한 범위의 .NET 버전을 지원하며, 플랫폼 지원은 에디션에 따라 다릅니다:
- .NET 프레임워크 지원:
- EvoPdf Next: .NET 10, 9, 8, 7, 6, .NET Standard 2.0, .NET Framework 4.6.2에서 4.8.1까지
- EvoPdf Classic: .NET 10에서 6까지, .NET Standard 2.0, .NET Framework 2.0 이상
- 플랫폼 고려사항:
- EvoPdf Next: Windows (x64, ARM64), Linux (x64, ARM64) 및 macOS (ARM64), 플랫폼별 NuGet 패키지 하나씩
- EvoPdf Next는 Azure App Service, Azure Functions 및 Docker에서 실행되며, 렌더링 엔진은 애플리케이션 프로세스 내에 호스팅되며 설치할 브라우저가 없습니다
- EvoPdf Classic: Windows에서만 로컬 렌더링
- EvoPdf Classic 클라이언트: Windows, Linux 및 macOS에서 실행되며 별도로 설치된 EVO PDF 서버로 변환을 전송 (Azure 클라우드 서비스, IIS 애플리케이션 또는 Windows 서비스로 설치)
EvoPdf Next는 Windows, Linux 및 macOS에서 네이티브 로컬 렌더링을 제공합니다. Linux 또는 macOS에서 실행해야 하는 EvoPdf Classic 기반 애플리케이션은 EVO PDF가 Classic에서 Next로의 마이그레이션 가이드를 게시한 EvoPdf Next로 이동하거나, 별도의 EVO PDF 서버에서 Classic 렌더링을 유지하고 클라이언트 라이브러리를 통해 호출할 수 있습니다.
일반 작업에 대한 더 나은 PDF 기능을 제공하는 라이브러리는 무엇입니까?
이 기사에 있는 모든 코드 예제는 완전한 .NET 8 콘솔 프로그램입니다: dotnet new console로 프로젝트를 생성하고, 예제의 첫 번째 주석에 명시된 NuGet 패키지를 추가한 다음 Program.cs를 예제로 교체하십시오. 예제는 자체 using 지시문을 선언하므로, 암시적 사용 여부에 관계없이 빌드됩니다. 이들은 IronPdf 2026.9.2, EvoPdf.HtmlToPdf 14.0.2 및 EvoPdf.PdfSecurity 14.0.2에 대해 컴파일되고 실행되었습니다. IronPDF는 IronPdf.License.LicenseKey 또는 appsettings.json의 IronPdf.LicenseKey 항목을 통해 설정된 라이선스 키가 필요합니다. EvoPdf는 키 없이 실행되며 출력에 평가 워터마크를 추가합니다.
HTML to PDF 변환: 렌더링 품질 및 성능
두 라이브러리의 가장 기본적인 기능은 HTML to PDF 변환이며, 그들의 API는 다른 접근 방식을 취합니다.
IronPDF HTML to PDF 예제
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Engines.Chrome;
using IronPdf.Rendering;
var renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
EnableJavaScript = true,
// Page margins in millimetres
MarginTop = 20,
MarginBottom = 20,
MarginLeft = 15,
MarginRight = 15,
CssMediaType = PdfCssMediaType.Print,
ViewPortWidth = 1920,
FitToPaperMode = FitToPaperModes.Automatic
}
};
// Render only after the page calls window.ironpdf.notifyRender(), or after 10 seconds at most
renderer.RenderingOptions.WaitFor.JavaScript(10000);
// The page loads Chart.js and a Google Font from their CDNs, so the renderer needs network access
var htmlContent = @"
<!DOCTYPE html>
<html>
<head>
<link href='https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap' rel='stylesheet'>
<style>
body { font-family: 'Roboto', sans-serif; }
.chart-container { width: 100%; height: 400px; }
@media print {
.no-print { display: none; }
}
</style>
<script src='https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js'></script>
</head>
<body>
<h1>Dynamic Sales Report</h1>
<div class='chart-container'>
<canvas id='salesChart'></canvas>
</div>
<script>
new Chart(document.getElementById('salesChart'), {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
datasets: [{
label: 'Sales 2025',
data: [65, 78, 90, 81, 96],
borderColor: 'rgb(75, 192, 192)',
tension: 0.4
}]
},
// No animation, so the chart is fully drawn when the constructor returns
options: { animation: false }
});
window.ironpdf.notifyRender();
</script>
</body>
</html>";
using var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("sales-report.pdf");' NuGet: Install-Package IronPdf
Imports IronPdf
Imports IronPdf.Engines.Chrome
Imports IronPdf.Rendering
Dim renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.EnableJavaScript = True,
' Page margins in millimetres
.MarginTop = 20,
.MarginBottom = 20,
.MarginLeft = 15,
.MarginRight = 15,
.CssMediaType = PdfCssMediaType.Print,
.ViewPortWidth = 1920,
.FitToPaperMode = FitToPaperModes.Automatic
}
}
' Render only after the page calls window.ironpdf.notifyRender(), or after 10 seconds at most
renderer.RenderingOptions.WaitFor.JavaScript(10000)
' The page loads Chart.js and a Google Font from their CDNs, so the renderer needs network access
Dim htmlContent = "
<!DOCTYPE html>
<html>
<head>
<link href='https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap' rel='stylesheet'>
<style>
body { font-family: 'Roboto', sans-serif; }
.chart-container { width: 100%; height: 400px; }
@media print {
.no-print { display: none; }
}
</style>
<script src='https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js'></script>
</head>
<body>
<h1>Dynamic Sales Report</h1>
<div class='chart-container'>
<canvas id='salesChart'></canvas>
</div>
<script>
new Chart(document.getElementById('salesChart'), {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
datasets: [{
label: 'Sales 2025',
data: [65, 78, 90, 81, 96],
borderColor: 'rgb(75, 192, 192)',
tension: 0.4
}]
},
options: { animation: false }
});
window.ironpdf.notifyRender();
</script>
</body>
</html>"
Using pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("sales-report.pdf")
End Using' NuGet: Install-Package IronPdf
Imports IronPdf
Imports IronPdf.Engines.Chrome
Imports IronPdf.Rendering
Dim renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.EnableJavaScript = True,
' Page margins in millimetres
.MarginTop = 20,
.MarginBottom = 20,
.MarginLeft = 15,
.MarginRight = 15,
.CssMediaType = PdfCssMediaType.Print,
.ViewPortWidth = 1920,
.FitToPaperMode = FitToPaperModes.Automatic
}
}
' Render only after the page calls window.ironpdf.notifyRender(), or after 10 seconds at most
renderer.RenderingOptions.WaitFor.JavaScript(10000)
' The page loads Chart.js and a Google Font from their CDNs, so the renderer needs network access
Dim htmlContent = "
<!DOCTYPE html>
<html>
<head>
<link href='https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap' rel='stylesheet'>
<style>
body { font-family: 'Roboto', sans-serif; }
.chart-container { width: 100%; height: 400px; }
@media print {
.no-print { display: none; }
}
</style>
<script src='https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js'></script>
</head>
<body>
<h1>Dynamic Sales Report</h1>
<div class='chart-container'>
<canvas id='salesChart'></canvas>
</div>
<script>
new Chart(document.getElementById('salesChart'), {
type: 'line',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
datasets: [{
label: 'Sales 2025',
data: [65, 78, 90, 81, 96],
borderColor: 'rgb(75, 192, 192)',
tension: 0.4
}]
},
options: { animation: false }
});
window.ironpdf.notifyRender();
</script>
</body>
</html>"
Using pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("sales-report.pdf")
End Using이 IronPDF 예제는 다음을 보여줍니다:
- JavaScript 실행: 페이지의 JavaScript(여기서는 Chart.js)가 렌더링 전에 실행됩니다.
- 명시적 렌더 신호:
WaitFor.JavaScript()는 고정된 지연 시간으로 추측하는 대신 페이지가window.ironpdf.notifyRender()를 호출할 때까지 기다립니다. - 웹 폰트: 페이지에서 참조하는 폰트가 로드되고 포함됩니다.
- 인쇄 미디어 쿼리:
CssMediaType.Print는 페이지의@media print규칙을 적용합니다.
EvoPdf HTML to PDF 예제
// NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
using System.IO;
using EvoPdf;
HtmlToPdfConverter converter = new HtmlToPdfConverter();
// Production use needs a license key. Leave this line out to evaluate:
// the output then carries an evaluation watermark. An invalid key makes the conversion throw.
// converter.LicenseKey = "<your EvoPdf license key>";
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
converter.PdfDocumentOptions.PdfPageOrientation = PdfPageOrientation.Portrait;
// EvoPdf margins are in points (1/72 inch): 57 pt is about 20 mm, 43 pt about 15 mm,
// matching the IronPDF example above, whose margins are in millimetres
converter.PdfDocumentOptions.TopMargin = 57;
converter.PdfDocumentOptions.BottomMargin = 57;
converter.PdfDocumentOptions.LeftMargin = 43;
converter.PdfDocumentOptions.RightMargin = 43;
converter.JavaScriptEnabled = true;
converter.ConversionDelay = 2; // seconds
// For pages behind HTTP authentication, also set
// converter.AuthenticationOptions.Username and .Password
// Convert HTML string
string htmlString = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; }
</style>
</head>
<body>
<h1>Invoice</h1>
<table>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Price</th>
</tr>
<tr>
<td>Product A</td>
<td>5</td>
<td>$50.00</td>
</tr>
</table>
</body>
</html>";
// Perform conversion
byte[] pdfBytes = converter.ConvertHtml(htmlString, "");
// Save to file
File.WriteAllBytes("invoice.pdf", pdfBytes);// NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
using System.IO;
using EvoPdf;
HtmlToPdfConverter converter = new HtmlToPdfConverter();
// Production use needs a license key. Leave this line out to evaluate:
// the output then carries an evaluation watermark. An invalid key makes the conversion throw.
// converter.LicenseKey = "<your EvoPdf license key>";
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
converter.PdfDocumentOptions.PdfPageOrientation = PdfPageOrientation.Portrait;
// EvoPdf margins are in points (1/72 inch): 57 pt is about 20 mm, 43 pt about 15 mm,
// matching the IronPDF example above, whose margins are in millimetres
converter.PdfDocumentOptions.TopMargin = 57;
converter.PdfDocumentOptions.BottomMargin = 57;
converter.PdfDocumentOptions.LeftMargin = 43;
converter.PdfDocumentOptions.RightMargin = 43;
converter.JavaScriptEnabled = true;
converter.ConversionDelay = 2; // seconds
// For pages behind HTTP authentication, also set
// converter.AuthenticationOptions.Username and .Password
// Convert HTML string
string htmlString = @"
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; }
</style>
</head>
<body>
<h1>Invoice</h1>
<table>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Price</th>
</tr>
<tr>
<td>Product A</td>
<td>5</td>
<td>$50.00</td>
</tr>
</table>
</body>
</html>";
// Perform conversion
byte[] pdfBytes = converter.ConvertHtml(htmlString, "");
// Save to file
File.WriteAllBytes("invoice.pdf", pdfBytes);' NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
Imports System.IO
Imports EvoPdf
Dim converter As New HtmlToPdfConverter()
' Production use needs a license key. Leave this line out to evaluate:
' the output then carries an evaluation watermark. An invalid key makes the conversion throw.
' converter.LicenseKey = "<your EvoPdf license key>"
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4
converter.PdfDocumentOptions.PdfPageOrientation = PdfPageOrientation.Portrait
' EvoPdf margins are in points (1/72 inch): 57 pt is about 20 mm, 43 pt about 15 mm,
' matching the IronPDF example above, whose margins are in millimetres
converter.PdfDocumentOptions.TopMargin = 57
converter.PdfDocumentOptions.BottomMargin = 57
converter.PdfDocumentOptions.LeftMargin = 43
converter.PdfDocumentOptions.RightMargin = 43
converter.JavaScriptEnabled = True
converter.ConversionDelay = 2 ' seconds
' For pages behind HTTP authentication, also set
' converter.AuthenticationOptions.Username and .Password
' Convert HTML string
Dim htmlString As String = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; }
</style>
</head>
<body>
<h1>Invoice</h1>
<table>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Price</th>
</tr>
<tr>
<td>Product A</td>
<td>5</td>
<td>$50.00</td>
</tr>
</table>
</body>
</html>"
' Perform conversion
Dim pdfBytes() As Byte = converter.ConvertHtml(htmlString, "")
' Save to file
File.WriteAllBytes("invoice.pdf", pdfBytes)' NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
Imports System.IO
Imports EvoPdf
Dim converter As New HtmlToPdfConverter()
' Production use needs a license key. Leave this line out to evaluate:
' the output then carries an evaluation watermark. An invalid key makes the conversion throw.
' converter.LicenseKey = "<your EvoPdf license key>"
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4
converter.PdfDocumentOptions.PdfPageOrientation = PdfPageOrientation.Portrait
' EvoPdf margins are in points (1/72 inch): 57 pt is about 20 mm, 43 pt about 15 mm,
' matching the IronPDF example above, whose margins are in millimetres
converter.PdfDocumentOptions.TopMargin = 57
converter.PdfDocumentOptions.BottomMargin = 57
converter.PdfDocumentOptions.LeftMargin = 43
converter.PdfDocumentOptions.RightMargin = 43
converter.JavaScriptEnabled = True
converter.ConversionDelay = 2 ' seconds
' For pages behind HTTP authentication, also set
' converter.AuthenticationOptions.Username and .Password
' Convert HTML string
Dim htmlString As String = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; }
</style>
</head>
<body>
<h1>Invoice</h1>
<table>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Price</th>
</tr>
<tr>
<td>Product A</td>
<td>5</td>
<td>$50.00</td>
</tr>
</table>
</body>
</html>"
' Perform conversion
Dim pdfBytes() As Byte = converter.ConvertHtml(htmlString, "")
' Save to file
File.WriteAllBytes("invoice.pdf", pdfBytes)EvoPdf의 변환기는 설정 후 변환 패턴을 따릅니다: PdfDocumentOptions를 설정한 후 ConvertHtml() 또는 ConvertUrl()을 호출합니다. 이 문서의 모든 EvoPdf 예제는 Windows에서 렌더링되는 EvoPdf Classic API(EvoPdf.HtmlToPdf 패키지)를 사용합니다. Chromium 기반의 EvoPdf Next는 자체 API를 가진 별도의 NuGet 패키지 세트입니다: 예를 들어, 라이선스 키는 정적 Licensing.LicenseKey 속성에서 프로세스당 한 번 설정됩니다. Next의 EVO 문서는 flexbox, grid, ES2015+ JavaScript 및 웹 폰트를 지원합니다.
PDF 보안 및 암호화: 민감한 문서 보호
민감한 문서를 다룰 때 보안은 매우 중요합니다. 두 라이브러리는 비밀번호 보호, 권한 제어 및 암호화를 제공합니다.
IronPDF 고급 보안 구현
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Editing;
using IronPdf.Security;
using IronPdf.Signing;
using IronSoftware.Drawing;
using var pdf = PdfDocument.FromFile("confidential-report.pdf");
// Finish every content change before signing
pdf.ApplyWatermark("<h2 style='color:red; opacity:0.5;'>CONFIDENTIAL</h2>",
45, VerticalAlignment.Middle, HorizontalAlignment.Center);
// Descriptive document properties (these do not restrict access)
pdf.MetaData.Author = "Finance Team";
pdf.MetaData.Title = "Quarterly Report (Internal)";
pdf.MetaData.ModifiedDate = DateTime.UtcNow;
// Owner password: required to change the permissions below
pdf.SecuritySettings.OwnerPassword = "owner-password";
// User password: required to open the document
pdf.SecuritySettings.UserPassword = "user-password";
pdf.SecuritySettings.AllowUserCopyPasteContentForAccessibility = false;
pdf.SecuritySettings.AllowUserAnnotations = false;
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserFormData = true;
pdf.SecuritySettings.AllowUserEdits = PdfEditSecurity.NoEdit;
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.PrintLowQuality;
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256;
// Visible digital signature: image file, page index, and position/size on the page
var signature = new PdfSignature("certificate.pfx", "cert-password")
{
SigningReason = "Document Approval",
SigningLocation = "Head Office",
SigningContact = "security@example.com",
SignatureImage = new PdfSignatureImage("signature.png", 0, new Rectangle(100, 100, 200, 50))
};
pdf.Sign(signature);
// The signature is applied when the file is saved
pdf.SaveAs("secured-document.pdf");// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Editing;
using IronPdf.Security;
using IronPdf.Signing;
using IronSoftware.Drawing;
using var pdf = PdfDocument.FromFile("confidential-report.pdf");
// Finish every content change before signing
pdf.ApplyWatermark("<h2 style='color:red; opacity:0.5;'>CONFIDENTIAL</h2>",
45, VerticalAlignment.Middle, HorizontalAlignment.Center);
// Descriptive document properties (these do not restrict access)
pdf.MetaData.Author = "Finance Team";
pdf.MetaData.Title = "Quarterly Report (Internal)";
pdf.MetaData.ModifiedDate = DateTime.UtcNow;
// Owner password: required to change the permissions below
pdf.SecuritySettings.OwnerPassword = "owner-password";
// User password: required to open the document
pdf.SecuritySettings.UserPassword = "user-password";
pdf.SecuritySettings.AllowUserCopyPasteContentForAccessibility = false;
pdf.SecuritySettings.AllowUserAnnotations = false;
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserFormData = true;
pdf.SecuritySettings.AllowUserEdits = PdfEditSecurity.NoEdit;
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.PrintLowQuality;
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256;
// Visible digital signature: image file, page index, and position/size on the page
var signature = new PdfSignature("certificate.pfx", "cert-password")
{
SigningReason = "Document Approval",
SigningLocation = "Head Office",
SigningContact = "security@example.com",
SignatureImage = new PdfSignatureImage("signature.png", 0, new Rectangle(100, 100, 200, 50))
};
pdf.Sign(signature);
// The signature is applied when the file is saved
pdf.SaveAs("secured-document.pdf");' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Imports IronPdf.Editing
Imports IronPdf.Security
Imports IronPdf.Signing
Imports IronSoftware.Drawing
Dim pdf = PdfDocument.FromFile("confidential-report.pdf")
' Finish every content change before signing
pdf.ApplyWatermark("<h2 style='color:red; opacity:0.5;'>CONFIDENTIAL</h2>", 45, VerticalAlignment.Middle, HorizontalAlignment.Center)
' Descriptive document properties (these do not restrict access)
pdf.MetaData.Author = "Finance Team"
pdf.MetaData.Title = "Quarterly Report (Internal)"
pdf.MetaData.ModifiedDate = DateTime.UtcNow
' Owner password: required to change the permissions below
pdf.SecuritySettings.OwnerPassword = "owner-password"
' User password: required to open the document
pdf.SecuritySettings.UserPassword = "user-password"
pdf.SecuritySettings.AllowUserCopyPasteContentForAccessibility = False
pdf.SecuritySettings.AllowUserAnnotations = False
pdf.SecuritySettings.AllowUserCopyPasteContent = False
pdf.SecuritySettings.AllowUserFormData = True
pdf.SecuritySettings.AllowUserEdits = PdfEditSecurity.NoEdit
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.PrintLowQuality
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256
' Visible digital signature: image file, page index, and position/size on the page
Dim signature = New PdfSignature("certificate.pfx", "cert-password") With {
.SigningReason = "Document Approval",
.SigningLocation = "Head Office",
.SigningContact = "security@example.com",
.SignatureImage = New PdfSignatureImage("signature.png", 0, New Rectangle(100, 100, 200, 50))
}
pdf.Sign(signature)
' The signature is applied when the file is saved
pdf.SaveAs("secured-document.pdf")' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Imports IronPdf.Editing
Imports IronPdf.Security
Imports IronPdf.Signing
Imports IronSoftware.Drawing
Dim pdf = PdfDocument.FromFile("confidential-report.pdf")
' Finish every content change before signing
pdf.ApplyWatermark("<h2 style='color:red; opacity:0.5;'>CONFIDENTIAL</h2>", 45, VerticalAlignment.Middle, HorizontalAlignment.Center)
' Descriptive document properties (these do not restrict access)
pdf.MetaData.Author = "Finance Team"
pdf.MetaData.Title = "Quarterly Report (Internal)"
pdf.MetaData.ModifiedDate = DateTime.UtcNow
' Owner password: required to change the permissions below
pdf.SecuritySettings.OwnerPassword = "owner-password"
' User password: required to open the document
pdf.SecuritySettings.UserPassword = "user-password"
pdf.SecuritySettings.AllowUserCopyPasteContentForAccessibility = False
pdf.SecuritySettings.AllowUserAnnotations = False
pdf.SecuritySettings.AllowUserCopyPasteContent = False
pdf.SecuritySettings.AllowUserFormData = True
pdf.SecuritySettings.AllowUserEdits = PdfEditSecurity.NoEdit
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.PrintLowQuality
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256
' Visible digital signature: image file, page index, and position/size on the page
Dim signature = New PdfSignature("certificate.pfx", "cert-password") With {
.SigningReason = "Document Approval",
.SigningLocation = "Head Office",
.SigningContact = "security@example.com",
.SignatureImage = New PdfSignatureImage("signature.png", 0, New Rectangle(100, 100, 200, 50))
}
pdf.Sign(signature)
' The signature is applied when the file is saved
pdf.SaveAs("secured-document.pdf")여기에서 보여지는 IronPDF의 보안 기능:
- 세분화된 권한: 인쇄 품질, 복사, 주석, 양식 채우기 및 편집 제어
- 가시적 디지털 서명: 이유, 위치 및 서명 이미지가 포함된 서명
- 문서 속성: 작성자, 제목 및 날짜 설정(설명 전용)
- 계층화된 보호: 하나의 문서에 워터마크, 암호화 및 서명을 결합
EvoPdf 보안 구성
// NuGet: Install-Package EvoPdf.PdfSecurity
// (licensed with the EVO PDF Toolkit; not included in the standalone HTML to PDF license)
using EvoPdf.PdfSecurity;
PdfSecurityOptions securityOptions = new PdfSecurityOptions();
// Set basic security parameters
securityOptions.UserPassword = "user123";
securityOptions.OwnerPassword = "owner456";
securityOptions.EncryptionAlgorithm = EncryptionAlgorithm.AES;
securityOptions.KeySize = EncryptionKeySize.EncryptKey256Bit;
// Configure permissions
securityOptions.CanPrint = true;
securityOptions.CanCopyContent = false;
securityOptions.CanEditContent = false;
securityOptions.CanEditAnnotations = false;
securityOptions.CanFillFormFields = true;
securityOptions.CanAssembleDocument = false;
// Apply security to existing PDF
PdfSecurityManager securityManager = new PdfSecurityManager(securityOptions);
securityManager.SaveSecuredPdfToFile("input.pdf", "secured-output.pdf");// NuGet: Install-Package EvoPdf.PdfSecurity
// (licensed with the EVO PDF Toolkit; not included in the standalone HTML to PDF license)
using EvoPdf.PdfSecurity;
PdfSecurityOptions securityOptions = new PdfSecurityOptions();
// Set basic security parameters
securityOptions.UserPassword = "user123";
securityOptions.OwnerPassword = "owner456";
securityOptions.EncryptionAlgorithm = EncryptionAlgorithm.AES;
securityOptions.KeySize = EncryptionKeySize.EncryptKey256Bit;
// Configure permissions
securityOptions.CanPrint = true;
securityOptions.CanCopyContent = false;
securityOptions.CanEditContent = false;
securityOptions.CanEditAnnotations = false;
securityOptions.CanFillFormFields = true;
securityOptions.CanAssembleDocument = false;
// Apply security to existing PDF
PdfSecurityManager securityManager = new PdfSecurityManager(securityOptions);
securityManager.SaveSecuredPdfToFile("input.pdf", "secured-output.pdf");' NuGet: Install-Package EvoPdf.PdfSecurity
' (licensed with the EVO PDF Toolkit; not included in the standalone HTML to PDF license)
Imports EvoPdf.PdfSecurity
Dim securityOptions As New PdfSecurityOptions()
' Set basic security parameters
securityOptions.UserPassword = "user123"
securityOptions.OwnerPassword = "owner456"
securityOptions.EncryptionAlgorithm = EncryptionAlgorithm.AES
securityOptions.KeySize = EncryptionKeySize.EncryptKey256Bit
' Configure permissions
securityOptions.CanPrint = True
securityOptions.CanCopyContent = False
securityOptions.CanEditContent = False
securityOptions.CanEditAnnotations = False
securityOptions.CanFillFormFields = True
securityOptions.CanAssembleDocument = False
' Apply security to existing PDF
Dim securityManager As New PdfSecurityManager(securityOptions)
securityManager.SaveSecuredPdfToFile("input.pdf", "secured-output.pdf")' NuGet: Install-Package EvoPdf.PdfSecurity
' (licensed with the EVO PDF Toolkit; not included in the standalone HTML to PDF license)
Imports EvoPdf.PdfSecurity
Dim securityOptions As New PdfSecurityOptions()
' Set basic security parameters
securityOptions.UserPassword = "user123"
securityOptions.OwnerPassword = "owner456"
securityOptions.EncryptionAlgorithm = EncryptionAlgorithm.AES
securityOptions.KeySize = EncryptionKeySize.EncryptKey256Bit
' Configure permissions
securityOptions.CanPrint = True
securityOptions.CanCopyContent = False
securityOptions.CanEditContent = False
securityOptions.CanEditAnnotations = False
securityOptions.CanFillFormFields = True
securityOptions.CanAssembleDocument = False
' Apply security to existing PDF
Dim securityManager As New PdfSecurityManager(securityOptions)
securityManager.SaveSecuredPdfToFile("input.pdf", "secured-output.pdf")EvoPdf는 동일한 핵심 제어를 다룹니다: 사용자 및 소유자 비밀번호, 암호화, 인쇄, 복사, 편집, 주석, 양식 채우기 및 문서 조립에 대한 권한. EVO 문서는 알고리즘(RC4 또는 AES)과 키 크기(40, 128 또는 256 비트)를 별도의 설정으로 문서화합니다; 버전 14.0.2와의 테스트에서 RC4는 40 또는 128 비트 키와 AES는 128 또는 256 비트 키로 예상되는 암호화를 생성했으므로 AES-128 또는 AES-256을 사용하십시오; RC4는 레거시 알고리즘입니다. 독립형 HTML to PDF 라이선스를 사용하면 변환기의 자체 PdfSecurityOptions가 별도의 PDF 보안 구성 요소 없이 PDF가 생성되는 동안 동일한 보호를 적용합니다.
콘텐츠 수정: 민감한 정보 제거
문서가 공유되기 전에 민감한 텍스트를 영구적으로 제거하는 것은 개인 데이터를 처리하는 애플리케이션에서 일반적인 요구 사항입니다. 수정은 그 작업을 위한 하나의 구성 요소입니다; 자체적으로는 GDPR, HIPAA 또는 CCPA와 같은 규정을 준수하는 애플리케이션을 만들지 않습니다.
IronPDF 수정 예제
// NuGet: Install-Package IronPdf
using IronPdf;
using IronSoftware.Drawing;
// This example expects a PDF with at least three pages
using var pdf = PdfDocument.FromFile("customer-records.pdf");
// 1. Redact a specific value on every page
pdf.RedactTextOnAllPages("123-45-6789");
// 2. Redact a word on specific pages (zero-based page indexes)
pdf.RedactTextOnPages(new[] { 0, 1, 2 }, "Confidential", CaseSensitive: false);
// 3. Redact a rectangular region on the first page
pdf.RedactRegionOnPage(0, new RectangleF(100, 200, 300, 50));
// 4. Replace a complete sensitive value with a label instead of drawing a black box
pdf.RedactTextOnAllPages("ACCT-0042-7781", DrawRectangles: false, ReplacementText: "[REDACTED]");
pdf.SaveAs("redacted-records.pdf");// NuGet: Install-Package IronPdf
using IronPdf;
using IronSoftware.Drawing;
// This example expects a PDF with at least three pages
using var pdf = PdfDocument.FromFile("customer-records.pdf");
// 1. Redact a specific value on every page
pdf.RedactTextOnAllPages("123-45-6789");
// 2. Redact a word on specific pages (zero-based page indexes)
pdf.RedactTextOnPages(new[] { 0, 1, 2 }, "Confidential", CaseSensitive: false);
// 3. Redact a rectangular region on the first page
pdf.RedactRegionOnPage(0, new RectangleF(100, 200, 300, 50));
// 4. Replace a complete sensitive value with a label instead of drawing a black box
pdf.RedactTextOnAllPages("ACCT-0042-7781", DrawRectangles: false, ReplacementText: "[REDACTED]");
pdf.SaveAs("redacted-records.pdf");' NuGet: Install-Package IronPdf
Imports IronPdf
Imports IronSoftware.Drawing
' This example expects a PDF with at least three pages
Dim pdf = PdfDocument.FromFile("customer-records.pdf")
' 1. Redact a specific value on every page
pdf.RedactTextOnAllPages("123-45-6789")
' 2. Redact a word on specific pages (zero-based page indexes)
pdf.RedactTextOnPages(New Integer() {0, 1, 2}, "Confidential", CaseSensitive:=False)
' 3. Redact a rectangular region on the first page
pdf.RedactRegionOnPage(0, New RectangleF(100, 200, 300, 50))
' 4. Replace a complete sensitive value with a label instead of drawing a black box
pdf.RedactTextOnAllPages("ACCT-0042-7781", DrawRectangles:=False, ReplacementText:="[REDACTED]")
pdf.SaveAs("redacted-records.pdf")' NuGet: Install-Package IronPdf
Imports IronPdf
Imports IronSoftware.Drawing
' This example expects a PDF with at least three pages
Dim pdf = PdfDocument.FromFile("customer-records.pdf")
' 1. Redact a specific value on every page
pdf.RedactTextOnAllPages("123-45-6789")
' 2. Redact a word on specific pages (zero-based page indexes)
pdf.RedactTextOnPages(New Integer() {0, 1, 2}, "Confidential", CaseSensitive:=False)
' 3. Redact a rectangular region on the first page
pdf.RedactRegionOnPage(0, New RectangleF(100, 200, 300, 50))
' 4. Replace a complete sensitive value with a label instead of drawing a black box
pdf.RedactTextOnAllPages("ACCT-0042-7781", DrawRectangles:=False, ReplacementText:="[REDACTED]")
pdf.SaveAs("redacted-records.pdf")IronPDF의 수정 기능에는 다음이 포함됩니다:
- 콘텐츠 제거: 일치하는 텍스트가 페이지 콘텐츠에서 제거되어 더 이상 추출할 수 없으며, 단순히 덮어씌워지지 않습니다.
- 일치 옵션: 대소문자 구분 및 전체 단어 일치
- 수정 모양: 검은색 상자를 그리거나 대체 텍스트 삽입
- 선택적 수정: 특정 페이지나 영역을 대상으로 함
텍스트 수정은 페이지 콘텐츠의 텍스트와 일치합니다. 스캔된 페이지와 같은 이미지 내부의 텍스트는 일치하지 않으며, 영역 수정은 그 안에 무엇이 있든 상관없이 고정된 영역을 덮습니다. EvoPdf는 수정 기능을 문서화하지 않습니다.
양식 처리: 대화형 PDF 생성
두 라이브러리는 PDF 양식을 지원하지만 생성 및 조작에 대한 접근 방식이 다릅니다.
IronPDF 양식 예제
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
// Create a new PDF with an HTML form
var html = @"
<html>
<body>
<h2>Employee Information Form</h2>
<form>
<label>Full Name:</label>
<input type='text' name='fullName' required />
<label>Email:</label>
<input type='email' name='email' required />
<label>Department:</label>
<select name='department'>
<option value=''>Select Department</option>
<option value='IT'>Information Technology</option>
<option value='HR'>Human Resources</option>
<option value='Sales'>Sales</option>
</select>
<label>Start Date:</label>
<input type='text' name='startDate' placeholder='YYYY-MM-DD' />
<label>
<input type='checkbox' name='agreement' />
I agree to the terms and conditions
</label>
<button type='submit'>Submit</button>
</form>
</body>
</html>";
var renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
CreatePdfFormsFromHtml = true // Enable form field creation
}
};
using var pdf = renderer.RenderHtmlAsPdf(html);
// Programmatically fill form fields
pdf.Form.FindFormField("fullName").Value = "John Doe";
pdf.Form.FindFormField("email").Value = "john.doe@example.com";
// A dropdown takes one of its displayed choices, not the HTML option value ("IT")
pdf.Form.FindFormField("department").Value = "Information Technology";
pdf.Form.FindFormField("startDate").Value = DateTime.Now.ToString("yyyy-MM-dd");
// "Yes" is the checkbox's checked state
pdf.Form.FindFormField("agreement").Value = "Yes";
// Make specific fields read-only
pdf.Form.FindFormField("email").ReadOnly = true;
// Save as fillable form
pdf.SaveAs("fillable-form.pdf");
// Or flatten the form (convert to static content)
pdf.Flatten();
pdf.SaveAs("completed-form.pdf");// NuGet: Install-Package IronPdf
using System;
using IronPdf;
// Create a new PDF with an HTML form
var html = @"
<html>
<body>
<h2>Employee Information Form</h2>
<form>
<label>Full Name:</label>
<input type='text' name='fullName' required />
<label>Email:</label>
<input type='email' name='email' required />
<label>Department:</label>
<select name='department'>
<option value=''>Select Department</option>
<option value='IT'>Information Technology</option>
<option value='HR'>Human Resources</option>
<option value='Sales'>Sales</option>
</select>
<label>Start Date:</label>
<input type='text' name='startDate' placeholder='YYYY-MM-DD' />
<label>
<input type='checkbox' name='agreement' />
I agree to the terms and conditions
</label>
<button type='submit'>Submit</button>
</form>
</body>
</html>";
var renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
CreatePdfFormsFromHtml = true // Enable form field creation
}
};
using var pdf = renderer.RenderHtmlAsPdf(html);
// Programmatically fill form fields
pdf.Form.FindFormField("fullName").Value = "John Doe";
pdf.Form.FindFormField("email").Value = "john.doe@example.com";
// A dropdown takes one of its displayed choices, not the HTML option value ("IT")
pdf.Form.FindFormField("department").Value = "Information Technology";
pdf.Form.FindFormField("startDate").Value = DateTime.Now.ToString("yyyy-MM-dd");
// "Yes" is the checkbox's checked state
pdf.Form.FindFormField("agreement").Value = "Yes";
// Make specific fields read-only
pdf.Form.FindFormField("email").ReadOnly = true;
// Save as fillable form
pdf.SaveAs("fillable-form.pdf");
// Or flatten the form (convert to static content)
pdf.Flatten();
pdf.SaveAs("completed-form.pdf");' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
' Create a new PDF with an HTML form
Dim html = "
<html>
<body>
<h2>Employee Information Form</h2>
<form>
<label>Full Name:</label>
<input type='text' name='fullName' required />
<label>Email:</label>
<input type='email' name='email' required />
<label>Department:</label>
<select name='department'>
<option value=''>Select Department</option>
<option value='IT'>Information Technology</option>
<option value='HR'>Human Resources</option>
<option value='Sales'>Sales</option>
</select>
<label>Start Date:</label>
<input type='text' name='startDate' placeholder='YYYY-MM-DD' />
<label>
<input type='checkbox' name='agreement' />
I agree to the terms and conditions
</label>
<button type='submit'>Submit</button>
</form>
</body>
</html>"
Dim renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.CreatePdfFormsFromHtml = True ' Enable form field creation
}
}
Using pdf = renderer.RenderHtmlAsPdf(html)
' Programmatically fill form fields
pdf.Form.FindFormField("fullName").Value = "John Doe"
pdf.Form.FindFormField("email").Value = "john.doe@example.com"
' A dropdown takes one of its displayed choices, not the HTML option value ("IT")
pdf.Form.FindFormField("department").Value = "Information Technology"
pdf.Form.FindFormField("startDate").Value = DateTime.Now.ToString("yyyy-MM-dd")
' "Yes" is the checkbox's checked state
pdf.Form.FindFormField("agreement").Value = "Yes"
' Make specific fields read-only
pdf.Form.FindFormField("email").ReadOnly = True
' Save as fillable form
pdf.SaveAs("fillable-form.pdf")
' Or flatten the form (convert to static content)
pdf.Flatten()
pdf.SaveAs("completed-form.pdf")
End Using' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
' Create a new PDF with an HTML form
Dim html = "
<html>
<body>
<h2>Employee Information Form</h2>
<form>
<label>Full Name:</label>
<input type='text' name='fullName' required />
<label>Email:</label>
<input type='email' name='email' required />
<label>Department:</label>
<select name='department'>
<option value=''>Select Department</option>
<option value='IT'>Information Technology</option>
<option value='HR'>Human Resources</option>
<option value='Sales'>Sales</option>
</select>
<label>Start Date:</label>
<input type='text' name='startDate' placeholder='YYYY-MM-DD' />
<label>
<input type='checkbox' name='agreement' />
I agree to the terms and conditions
</label>
<button type='submit'>Submit</button>
</form>
</body>
</html>"
Dim renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.CreatePdfFormsFromHtml = True ' Enable form field creation
}
}
Using pdf = renderer.RenderHtmlAsPdf(html)
' Programmatically fill form fields
pdf.Form.FindFormField("fullName").Value = "John Doe"
pdf.Form.FindFormField("email").Value = "john.doe@example.com"
' A dropdown takes one of its displayed choices, not the HTML option value ("IT")
pdf.Form.FindFormField("department").Value = "Information Technology"
pdf.Form.FindFormField("startDate").Value = DateTime.Now.ToString("yyyy-MM-dd")
' "Yes" is the checkbox's checked state
pdf.Form.FindFormField("agreement").Value = "Yes"
' Make specific fields read-only
pdf.Form.FindFormField("email").ReadOnly = True
' Save as fillable form
pdf.SaveAs("fillable-form.pdf")
' Or flatten the form (convert to static content)
pdf.Flatten()
pdf.SaveAs("completed-form.pdf")
End UsingIronPDF의 양식 처리 기능:
- 자동 양식 생성: HTML 양식을 PDF 양식으로 변환
- 프로그래밍적 조작: API를 통해 양식 필드를 채우고, 읽고, 수정
- 필드 유형: 텍스트 입력(이메일, 숫자 및 전화 입력 포함) 및 텍스트 영역은 텍스트 필드가 됩니다; 체크박스, 라디오 버튼 및 드롭다운은 유형을 유지합니다. HTML 날짜 입력은 양식 필드로 변환되지 않으므로 위의 시작 날짜는 텍스트 입력을 사용합니다.
- 필드 속성: 값 및 읽기 전용 플래그 설정
- 양식 평탄화: 채워진 양식을 정적 PDF로 변환
성능 고려 사항
문서 복잡성과 사용 사례에 따라 성능 특성이 크게 달라집니다:
문서 배치의 타이밍
// NuGet: Install-Package IronPdf
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using IronPdf;
// Timing harness for a batch of documents. Replace the generated HTML with your own templates.
const int documentCount = 100;
const int maxConcurrency = 4;
var htmlDocuments = Enumerable.Range(1, documentCount)
.Select(i => $"<html><body><h1>Invoice {i}</h1><p>Amount due: {i * 10} USD</p></body></html>")
.ToList();
var renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
EnableJavaScript = false // static content only
}
};
// Cap how many renders run at once instead of starting every render immediately
using var gate = new SemaphoreSlim(maxConcurrency);
var stopwatch = Stopwatch.StartNew();
var tasks = htmlDocuments.Select(async (html, index) =>
{
await gate.WaitAsync();
try
{
using var pdf = await renderer.RenderHtmlAsPdfAsync(html);
pdf.SaveAs($"invoice-{index + 1}.pdf");
}
finally
{
gate.Release();
}
});
await Task.WhenAll(tasks);
stopwatch.Stop();
Console.WriteLine($"Documents: {documentCount}, at most {maxConcurrency} at a time");
Console.WriteLine($"Total elapsed: {stopwatch.ElapsedMilliseconds} ms");
// Elapsed time divided by the document count: an amortized figure, not the latency of one request
Console.WriteLine($"Amortized time per document: {stopwatch.ElapsedMilliseconds / (double)documentCount:F1} ms");
// Resident memory of this process only, sampled once at the end; the Chromium helper
// processes are not included, and this is not the peak
Console.WriteLine($"Working set of this process at end: {Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024} MB");// NuGet: Install-Package IronPdf
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using IronPdf;
// Timing harness for a batch of documents. Replace the generated HTML with your own templates.
const int documentCount = 100;
const int maxConcurrency = 4;
var htmlDocuments = Enumerable.Range(1, documentCount)
.Select(i => $"<html><body><h1>Invoice {i}</h1><p>Amount due: {i * 10} USD</p></body></html>")
.ToList();
var renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
EnableJavaScript = false // static content only
}
};
// Cap how many renders run at once instead of starting every render immediately
using var gate = new SemaphoreSlim(maxConcurrency);
var stopwatch = Stopwatch.StartNew();
var tasks = htmlDocuments.Select(async (html, index) =>
{
await gate.WaitAsync();
try
{
using var pdf = await renderer.RenderHtmlAsPdfAsync(html);
pdf.SaveAs($"invoice-{index + 1}.pdf");
}
finally
{
gate.Release();
}
});
await Task.WhenAll(tasks);
stopwatch.Stop();
Console.WriteLine($"Documents: {documentCount}, at most {maxConcurrency} at a time");
Console.WriteLine($"Total elapsed: {stopwatch.ElapsedMilliseconds} ms");
// Elapsed time divided by the document count: an amortized figure, not the latency of one request
Console.WriteLine($"Amortized time per document: {stopwatch.ElapsedMilliseconds / (double)documentCount:F1} ms");
// Resident memory of this process only, sampled once at the end; the Chromium helper
// processes are not included, and this is not the peak
Console.WriteLine($"Working set of this process at end: {Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024} MB");' NuGet: Install-Package IronPdf
Imports System
Imports System.Diagnostics
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks
Imports IronPdf
' Timing harness for a batch of documents. Replace the generated HTML with your own templates.
Const documentCount As Integer = 100
Const maxConcurrency As Integer = 4
Dim htmlDocuments = Enumerable.Range(1, documentCount) _
.Select(Function(i) $"<html><body><h1>Invoice {i}</h1><p>Amount due: {i * 10} USD</p></body></html>") _
.ToList()
Dim renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.EnableJavaScript = False ' static content only
}
}
' Cap how many renders run at once instead of starting every render immediately
Using gate As New SemaphoreSlim(maxConcurrency)
Dim stopwatch = Stopwatch.StartNew()
Dim tasks = htmlDocuments.Select(Async Function(html, index)
Await gate.WaitAsync()
Try
Using pdf = Await renderer.RenderHtmlAsPdfAsync(html)
pdf.SaveAs($"invoice-{index + 1}.pdf")
End Using
Finally
gate.Release()
End Try
End Function)
Await Task.WhenAll(tasks)
stopwatch.Stop()
Console.WriteLine($"Documents: {documentCount}, at most {maxConcurrency} at a time")
Console.WriteLine($"Total elapsed: {stopwatch.ElapsedMilliseconds} ms")
' Elapsed time divided by the document count: an amortized figure, not the latency of one request
Console.WriteLine($"Amortized time per document: {stopwatch.ElapsedMilliseconds / CDbl(documentCount):F1} ms")
' Resident memory of this process only, sampled once at the end; the Chromium helper
' processes are not included, and this is not the peak
Console.WriteLine($"Working set of this process at end: {Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024} MB")
End Using' NuGet: Install-Package IronPdf
Imports System
Imports System.Diagnostics
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks
Imports IronPdf
' Timing harness for a batch of documents. Replace the generated HTML with your own templates.
Const documentCount As Integer = 100
Const maxConcurrency As Integer = 4
Dim htmlDocuments = Enumerable.Range(1, documentCount) _
.Select(Function(i) $"<html><body><h1>Invoice {i}</h1><p>Amount due: {i * 10} USD</p></body></html>") _
.ToList()
Dim renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.EnableJavaScript = False ' static content only
}
}
' Cap how many renders run at once instead of starting every render immediately
Using gate As New SemaphoreSlim(maxConcurrency)
Dim stopwatch = Stopwatch.StartNew()
Dim tasks = htmlDocuments.Select(Async Function(html, index)
Await gate.WaitAsync()
Try
Using pdf = Await renderer.RenderHtmlAsPdfAsync(html)
pdf.SaveAs($"invoice-{index + 1}.pdf")
End Using
Finally
gate.Release()
End Try
End Function)
Await Task.WhenAll(tasks)
stopwatch.Stop()
Console.WriteLine($"Documents: {documentCount}, at most {maxConcurrency} at a time")
Console.WriteLine($"Total elapsed: {stopwatch.ElapsedMilliseconds} ms")
' Elapsed time divided by the document count: an amortized figure, not the latency of one request
Console.WriteLine($"Amortized time per document: {stopwatch.ElapsedMilliseconds / CDbl(documentCount):F1} ms")
' Resident memory of this process only, sampled once at the end; the Chromium helper
' processes are not included, and this is not the peak
Console.WriteLine($"Working set of this process at end: {Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024} MB")
End Using이 문서에서는 비교 벤치마크를 제공하지 않습니다: 결과는 템플릿, 하드웨어 및 배포에 따라 다르므로 직접 측정하십시오. 각 라이브러리에 대한 문서화된 레버:
- IronPDF 엔진 버전: 최신, JavaScript가 많은 또는 깊게 중첩된 페이지의 느린 렌더링의 경우, Bootstrap 또는 Tailwind로 구축된 페이지를 포함하여 IronPDF는 Chromium 131을 번들로 제공하는 IronPdf.UpdatedChrome 패키지를 권장합니다. 표준 패키지의 Chromium 109 대신
- IronPDF 동시성:
RenderHtmlAsPdfAsync와 같은 비동기 렌더링 메서드, 동시에 실행되는 렌더 수에 대한 자체 제한 - EvoPdf Next 렌더링 모드:
GlobalSettings.HtmlRendererMode를PersistentProcess(기본값)로 설정하면 변환 간에 하나의 렌더링 엔진 프로세스가 실행됩니다.ProcessPerConversion은 변환당 하나씩 시작합니다; 각 변환은 자체 변환기로 병렬로 실행될 수 있으며,GlobalSettings.MaxParallelConversions는 동시에 실행되는 수를 제한합니다. - EvoPdf Next 비동기 메서드: 비동기/대기 코드를 위한
ConvertHtmlAsync및ConvertUrlAsync
팀 크기에 따른 가격 모델 비교
IronPDF 라이선스 구조
IronPDF는 Lite, Plus 및 Professional 라이선스를 포함한 다양한 가격 옵션을 제공하며, 로열티 없는 재배포 옵션도 제공합니다. 라이선스 모델은 팀과 프로젝트 요구 사항에 맞게 확장되도록 설계되었습니다(2026년 9월 기준 가격):
- Lite 라이선스: $999
- 1 개발자
- 1 위치
- 1 프로젝트
- 이메일 지원
- 이상적: 개인 개발자, 소규모 프로젝트
- Plus 라이선스: $1,499
- 3 개발자
- 3 위치
- 3 프로젝트
- 이메일(48시간 SLA) 및 라이브 채팅 지원
- 이상적: 소규모 팀, 다중 프로젝트
- Professional 라이선스: $2,999
- 10 개발자
- 10 위치
- 10 프로젝트
- 이메일(24시간 SLA), 라이브 채팅, 전화 및 화면 공유 지원
- 이상적: 중대형 팀
- 추가 옵션:
- 로열티 없는 재배포(OEM/SaaS): +$1,999, 최종 사용자가 IronPDF의 API를 직접 호출하지 않는 상용 소프트웨어 및 SaaS의 경우
- SDK 라이선스: 제품이 IronPDF의 기능을 공용 API를 통해 다른 개발자에게 노출할 때 필요
- 지원 및 업데이트 확장: 1, 3 또는 5년, 라이선스 등급에 따라 가격 책정
- Iron Suite: 동일한 등급으로 라이선스된 모든 10개의 Iron Software 제품; Iron Suite Lite(1 개발자, 1 위치, 1 프로젝트)는 $2,998
EvoPdf 라이선스 옵션
EVO PDF Toolkit과 독립형 HTML to PDF 변환기는 각각 두 가지 라이선스 등급, 배포 및 회사(2026년 9월 기준 가격; 현재 가격은 evopdf.com/buy에서 확인). HTML to PDF 라이선스는 Next 및 Classic 변환기를 모두 포함합니다; PDF 보안과 같은 다른 구성 요소는 Toolkit에만 포함됩니다.
- 배포 라이선스:
- EVO HTML to PDF: $450
- EVO PDF Toolkit: $650
- 한 서버에 배포된 하나의 애플리케이션, 자체 회사에서 사용
- 해당 애플리케이션에서 작업하는 무제한 개발자
- 로드 밸런싱, 컨테이너화 또는 자동 확장 배포에는 회사 라이선스가 필요
- 재배포 없음
- 첫 해 표준 지원
- 회사 라이선스:
- EVO HTML to PDF: $1,200
- EVO PDF Toolkit: $1,400
- 무제한 개발자, 애플리케이션 및 서버
- 애플리케이션 내에서 자체 고객에게 재배포; 라이선스 계약은 EVO의 서면 허가 없이 변환 기능을 독립형 서비스, 호스팅 API 또는 SDK로 제공하는 것을 허용하지 않음
- 첫 해 우선 지원
총 소유 비용 분석
아래 각 시나리오는 배포 가정을 명시합니다. 두 공급업체 모두 팀 크기뿐만 아니라 범위에 따라 가격을 책정하기 때문입니다:
시나리오 1: 두 명의 개발자, 한 서버에서 하나의 내부 애플리케이션
- IronPDF Plus: $1,499 (Lite는 1명의 개발자를 커버하므로 2인 팀은 최대 3명을 커버하는 Plus가 필요)
- EvoPdf 배포: $450 (한 서버에서 하나의 애플리케이션; 개발자는 계산되지 않음)
- 애플리케이션이 여러 서버에서 실행되거나 로드 밸런서 뒤에서 또는 자동 확장 컨테이너에서 실행되는 경우 EvoPdf는 회사 라이선스($1,200)가 필요
- 더 낮은 라이선스 비용: EvoPdf
시나리오 2: 다섯 명의 개발자, 여러 내부 애플리케이션
- IronPDF Professional: $2,999 (최대 10명의 개발자, 10개의 위치, 10개의 프로젝트; Plus는 3명의 개발자만 커버)
- EvoPdf 회사: $1,200 (하나 이상의 애플리케이션은 회사 라이선스가 필요)
- 더 낮은 라이선스 비용: EvoPdf
시나리오 3: PDF 및 기타 문서 도구가 필요한 10명의 개발자
- Iron Suite Professional: 10명의 개발자, 10개의 위치, 10개의 프로젝트, 모든 10개의 Iron Software 제품 (현재 가격은 Iron Suite 라이선스 페이지에서 확인)
- EVO PDF Toolkit 회사: $1,400 (무제한 개발자, 애플리케이션 및 서버; OCR 또는 바코드 도구 없이 PDF 중심)
- 다른 범위: EVO PDF Toolkit은 PDF 도구만 필요할 경우 비용이 적게 듭니다. Iron Suite는 OCR, 바코드, Excel, Word 및 기타 라이브러리를 추가하며, Toolkit은 이를 대체하지 않습니다. 개발자 및 배포 범위가 일치할 때만 두 가지를 비교하십시오.
시나리오 4: 고객을 위한 PDF를 생성하는 상업용 SaaS 제품
- IronPDF Professional + OEM 재배포: $2,999 + $1,999 (최종 사용자가 IronPDF의 API를 직접 호출하지 않는 SaaS를 커버)
- EvoPdf 회사: $1,200 (재배포 포함; PDF 생성은 제품의 기능이어야 하며, 일반적인 변환 서비스로 제공할 수 없음)
- 더 낮은 라이선스 비용: EvoPdf, 제품이 변환 기능을 서비스나 공용 API로 노출하지 않는 경우; 두 공급업체 모두 그 경우를 별도로 라이선스함
현대 CSS 프레임워크가 선택에 미치는 영향은?
PDF 라이브러리 선택에서 종종 과소평가되는 요소는 현대 CSS 프레임워크에 대한 지원입니다. 웹 개발에서 널리 사용되는 Bootstrap, Tailwind CSS 및 Foundation과 함께, 이러한 프레임워크를 처리하는 라이브러리의 능력은 개발 효율성과 출력 품질에 직접적인 영향을 미칩니다.
IronPDF: Chromium 버전에 따라 프레임워크 지원
IronPDF의 Chromium 엔진은 번들된 Chromium 버전이 CSS 프레임워크를 렌더링하는 방식에 따라 다르므로, 배포하는 패키지에 대해 프레임워크의 브라우저 요구 사항을 확인하십시오:
- Bootstrap 5: Flexbox 및 CSS Grid 레이아웃이 지원됩니다.
- Tailwind CSS: Tailwind CSS v4는 Chrome 111 이상이 필요하며, 이는 표준 Windows 패키지의 Chromium 109보다 최신이므로, Windows에서 v4 레이아웃은 IronPdf.UpdatedChrome 패키지 (Chromium 131)가 필요합니다. Tailwind는 이전 브라우저를 지원해야 하는 경우 v3.4를 유지할 것을 권장합니다. 배포하는 패키지로 자체 템플릿을 테스트하십시오.
- 현대 CSS3: 변환 및 사용자 정의 속성이 지원됩니다.
- 엔진 버전 중요: 일부 최신 CSS, 예를 들어
subgrid,overflow: hidden가장자리 케이스 및 사용자 정의 줄 높이는 IronPdf.UpdatedChrome에서만 올바르게 렌더링됩니다. IronPDF는 Bootstrap 또는 Tailwind 페이지가 느리게 렌더링될 때도 IronPdf.UpdatedChrome을 권장합니다. IronPdf.UpdatedChrome은 32비트 프로세스, Windows Server 2012 또는SingleProcess모드를 지원하지 않으므로 전환하기 전에 배포 대상을 확인하십시오.
코드 예제: Bootstrap 타임라인 구성 요소
// NuGet: Install-Package IronPdf
using IronPdf;
var renderer = new ChromePdfRenderer();
string bootstrapTimeline = @"
<!DOCTYPE html>
<html>
<head>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<style>
.timeline { position: relative; padding: 20px 0; }
.timeline::before {
content: '';
position: absolute;
left: 50%;
width: 2px;
height: 100%;
background: #dee2e6;
}
.timeline-item { position: relative; margin: 20px 0; }
</style>
</head>
<body>
<div class='container py-5'>
<h2 class='text-center mb-5'>Project Timeline</h2>
<div class='timeline'>
<div class='timeline-item'>
<div class='row'>
<div class='col-md-6'>
<div class='card shadow-sm'>
<div class='card-body'>
<h5 class='card-title'>Phase 1: Planning</h5>
<p class='text-muted'>Q1 2025</p>
<p class='card-text'>Initial project scope and requirements gathering completed.</p>
<span class='badge bg-success'>Completed</span>
</div>
</div>
</div>
</div>
</div>
<!-- Additional timeline items... -->
</div>
</div>
</body>
</html>";
// The page loads Bootstrap from a CDN, so the renderer needs network access
using var pdf = renderer.RenderHtmlAsPdf(bootstrapTimeline);
pdf.SaveAs("project-timeline.pdf");// NuGet: Install-Package IronPdf
using IronPdf;
var renderer = new ChromePdfRenderer();
string bootstrapTimeline = @"
<!DOCTYPE html>
<html>
<head>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<style>
.timeline { position: relative; padding: 20px 0; }
.timeline::before {
content: '';
position: absolute;
left: 50%;
width: 2px;
height: 100%;
background: #dee2e6;
}
.timeline-item { position: relative; margin: 20px 0; }
</style>
</head>
<body>
<div class='container py-5'>
<h2 class='text-center mb-5'>Project Timeline</h2>
<div class='timeline'>
<div class='timeline-item'>
<div class='row'>
<div class='col-md-6'>
<div class='card shadow-sm'>
<div class='card-body'>
<h5 class='card-title'>Phase 1: Planning</h5>
<p class='text-muted'>Q1 2025</p>
<p class='card-text'>Initial project scope and requirements gathering completed.</p>
<span class='badge bg-success'>Completed</span>
</div>
</div>
</div>
</div>
</div>
<!-- Additional timeline items... -->
</div>
</div>
</body>
</html>";
// The page loads Bootstrap from a CDN, so the renderer needs network access
using var pdf = renderer.RenderHtmlAsPdf(bootstrapTimeline);
pdf.SaveAs("project-timeline.pdf");' NuGet: Install-Package IronPdf
Imports IronPdf
Dim renderer As New ChromePdfRenderer()
Dim bootstrapTimeline As String = "
<!DOCTYPE html>
<html>
<head>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<style>
.timeline { position: relative; padding: 20px 0; }
.timeline::before {
content: '';
position: absolute;
left: 50%;
width: 2px;
height: 100%;
background: #dee2e6;
}
.timeline-item { position: relative; margin: 20px 0; }
</style>
</head>
<body>
<div class='container py-5'>
<h2 class='text-center mb-5'>Project Timeline</h2>
<div class='timeline'>
<div class='timeline-item'>
<div class='row'>
<div class='col-md-6'>
<div class='card shadow-sm'>
<div class='card-body'>
<h5 class='card-title'>Phase 1: Planning</h5>
<p class='text-muted'>Q1 2025</p>
<p class='card-text'>Initial project scope and requirements gathering completed.</p>
<span class='badge bg-success'>Completed</span>
</div>
</div>
</div>
</div>
</div>
<!-- Additional timeline items... -->
</div>
</div>
</body>
</html>"
' The page loads Bootstrap from a CDN, so the renderer needs network access
Using pdf = renderer.RenderHtmlAsPdf(bootstrapTimeline)
pdf.SaveAs("project-timeline.pdf")
End Using' NuGet: Install-Package IronPdf
Imports IronPdf
Dim renderer As New ChromePdfRenderer()
Dim bootstrapTimeline As String = "
<!DOCTYPE html>
<html>
<head>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
<style>
.timeline { position: relative; padding: 20px 0; }
.timeline::before {
content: '';
position: absolute;
left: 50%;
width: 2px;
height: 100%;
background: #dee2e6;
}
.timeline-item { position: relative; margin: 20px 0; }
</style>
</head>
<body>
<div class='container py-5'>
<h2 class='text-center mb-5'>Project Timeline</h2>
<div class='timeline'>
<div class='timeline-item'>
<div class='row'>
<div class='col-md-6'>
<div class='card shadow-sm'>
<div class='card-body'>
<h5 class='card-title'>Phase 1: Planning</h5>
<p class='text-muted'>Q1 2025</p>
<p class='card-text'>Initial project scope and requirements gathering completed.</p>
<span class='badge bg-success'>Completed</span>
</div>
</div>
</div>
</div>
</div>
<!-- Additional timeline items... -->
</div>
</div>
</body>
</html>"
' The page loads Bootstrap from a CDN, so the renderer needs network access
Using pdf = renderer.RenderHtmlAsPdf(bootstrapTimeline)
pdf.SaveAs("project-timeline.pdf")
End UsingEvoPdf: Next의 Chromium, Classic의 EVO 자체 엔진
EvoPdf의 두 가지 에디션은 현대 CSS를 다르게 처리합니다:
- EvoPdf Next: Chromium 기반 엔진(Chromium 142, EVO PDF에 따르면)으로 flexbox, grid, ES2015+ JavaScript 및 웹 폰트에 대한 문서화된 지원
- EvoPdf Classic: EVO의 자체 렌더링 엔진으로, EVO는 CSS3, SVG, 웹 폰트 및 JavaScript를 지원한다고 설명합니다; New Edition은 ECMAScript 6, WOFF 2, HTTP/2 및 TLS 1.3을 추가합니다.
개발 영향: 애플리케이션이 Bootstrap 또는 Tailwind를 광범위하게 사용하고 웹 인터페이스와 일치하는 PDF가 필요한 경우, 각 라이브러리의 평가판으로 실제 템플릿을 테스트하십시오. 양쪽 모두 Chromium 기반 엔진(IronPDF 및 EvoPdf Next)이 있는 경우, 차이는 프레임워크 지원보다는 엔진 버전 및 구성에서 발생할 가능성이 더 큽니다.
CSS 프레임워크 호환성에 대한 자세한 내용은 Bootstrap & Flexbox CSS Guide를 참조하십시오.
개발자를 더 잘 지원하는 문서 및 지원 옵션은 무엇입니까?
IronPDF의 개발자 리소스
IronPDF는 문서, 24/5 라이브 채팅, 프리미엄 지원을 통한 24/7 엔지니어링 지원, 비디오 튜토리얼 및 정기 업데이트를 제공합니다. 지원 생태계에는 다음이 포함됩니다:
- 문서 품질:
- API 참조
- 단계별 튜토리얼
- 100개 이상의 코드 예제
- YouTube의 비디오 튜토리얼
- 다른 라이브러리에서의 마이그레이션 가이드
- 지원 채널:
- 프리미엄 지원을 통한 24/7 엔지니어링 지원, 유료 라이선스에 제공; 평가판 사용자는 평가판 동안 프리미엄 지원을 받습니다.
- 이메일 응답 SLA: 48시간(Plus), 24시간(Professional), 12시간(Unlimited)
- 모든 라이선스 등급에서 24/5 라이브 채팅
- Professional 및 Unlimited 라이선스에 대한 전화 지원
- Professional 및 Unlimited 라이선스에 대한 화면 공유
- 학습 리소스:
- 시작 가이드
- 성능 최적화 가이드
- 보안 모범 사례
- 클라우드 배포 가이드
EvoPdf의 지원 구조
EvoPdf는 문서 및 지원을 통해 다음을 제공합니다:
- 문서:
- API 참조 문서
- 일반적인 시나리오에 대한 코드 예제, GitHub의 코드 샘플
- 온라인 데모 애플리케이션
- 라이선스, 배포 및 변환을 다루는 지원 FAQ
- 지원 옵션:
- 첫 해 기술 지원 포함, 평가 중 무료
- 표준 지원(배포 라이선스) 또는 우선 지원(회사 라이선스)
- 첫 해 이후 유지 관리 갱신 가능
주요 차이점은 IronPDF의 교육 콘텐츠(튜토리얼, 사용 방법 가이드 및 비디오)의 폭과 24/5 라이브 채팅 및 프리미엄 지원을 통한 24/7 지원입니다.
각 라이브러리에 가장 적합한 사용 사례는 무엇입니까?
IronPDF를 선택할 때
IronPDF는 다음 시나리오에서 뛰어납니다:
1. 현대 웹 애플리케이션 통합
- 동적 보고서를 생성하는 SaaS 플랫폼
- 차트를 포함한 송장을 생성하는 전자 상거래 사이트
- PDF로 내보낸 비즈니스 인텔리전스 대시보드
- React, Angular 또는 Vue.js를 사용하는 애플리케이션
2. 민감한 콘텐츠가 포함된 문서
- 문서가 공유되기 전에 개인 데이터 제거
- 생성된 보고서 암호화 및 비밀번호 보호
- 문서 서명하여 나중에 변경 사항 감지
- 수정이 포함된 법률 문서 관리
3. 복잡한 문서 처리
- 다중 형식 문서 변환(DOCX, HTML, 이미지)
- IronOCR을 통한 스캔 문서의 OCR
- 병렬 실행을 통한 배치 처리
- 디지털 서명이 포함된 문서
4. 크로스 플랫폼 배포
- Docker 컨테이너화된 애플리케이션
- Linux 기반 클라우드 배포
- 마이크로서비스 아키텍처
- 서버리스 함수(AWS Lambda, Azure Functions)
예제: 생성된 보고서 보호
// NuGet: Install-Package IronPdf
using System.Net;
using IronPdf;
using IronPdf.Security;
using IronPdf.Signing;
var patient = new PatientRecord("P-10442", "Jane <Q.> Doe & Family", "123-45-6789", "open-sesame");
// HTML-encode text values, so characters such as & and < print as text instead of changing the markup
var html = $@"
<html>
<body>
<h1>Patient Summary</h1>
<p>Patient ID: {WebUtility.HtmlEncode(patient.PatientId)}</p>
<p>Name: {WebUtility.HtmlEncode(patient.Name)}</p>
<p>SSN: {WebUtility.HtmlEncode(patient.Ssn)}</p>
</body>
</html>";
var renderer = new ChromePdfRenderer();
using var pdf = renderer.RenderHtmlAsPdf(html);
// Remove the SSN from the page content wherever it appears
pdf.RedactTextOnAllPages(patient.Ssn);
// Descriptive document properties
pdf.MetaData.Author = "Records System";
pdf.MetaData.Title = $"Patient Summary {patient.PatientId}";
// Require a password to open the file, restrict copying, and encrypt with AES-256
pdf.SecuritySettings.UserPassword = patient.AccessCode;
pdf.SecuritySettings.OwnerPassword = "records-owner-password";
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.FullPrintRights;
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256;
// Sign last, so changes made to the file after signing can be detected
pdf.Sign(new PdfSignature("records-cert.pfx", "password")
{
SigningReason = "Record export",
SigningLocation = "Records Department"
});
pdf.SaveAs("patient-summary.pdf");
record PatientRecord(string PatientId, string Name, string Ssn, string AccessCode);// NuGet: Install-Package IronPdf
using System.Net;
using IronPdf;
using IronPdf.Security;
using IronPdf.Signing;
var patient = new PatientRecord("P-10442", "Jane <Q.> Doe & Family", "123-45-6789", "open-sesame");
// HTML-encode text values, so characters such as & and < print as text instead of changing the markup
var html = $@"
<html>
<body>
<h1>Patient Summary</h1>
<p>Patient ID: {WebUtility.HtmlEncode(patient.PatientId)}</p>
<p>Name: {WebUtility.HtmlEncode(patient.Name)}</p>
<p>SSN: {WebUtility.HtmlEncode(patient.Ssn)}</p>
</body>
</html>";
var renderer = new ChromePdfRenderer();
using var pdf = renderer.RenderHtmlAsPdf(html);
// Remove the SSN from the page content wherever it appears
pdf.RedactTextOnAllPages(patient.Ssn);
// Descriptive document properties
pdf.MetaData.Author = "Records System";
pdf.MetaData.Title = $"Patient Summary {patient.PatientId}";
// Require a password to open the file, restrict copying, and encrypt with AES-256
pdf.SecuritySettings.UserPassword = patient.AccessCode;
pdf.SecuritySettings.OwnerPassword = "records-owner-password";
pdf.SecuritySettings.AllowUserCopyPasteContent = false;
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.FullPrintRights;
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256;
// Sign last, so changes made to the file after signing can be detected
pdf.Sign(new PdfSignature("records-cert.pfx", "password")
{
SigningReason = "Record export",
SigningLocation = "Records Department"
});
pdf.SaveAs("patient-summary.pdf");
record PatientRecord(string PatientId, string Name, string Ssn, string AccessCode);' NuGet: Install-Package IronPdf
Imports System.Net
Imports IronPdf
Imports IronPdf.Security
Imports IronPdf.Signing
Dim patient = New PatientRecord("P-10442", "Jane <Q.> Doe & Family", "123-45-6789", "open-sesame")
' HTML-encode text values, so characters such as & and < print as text instead of changing the markup
Dim html = $"
<html>
<body>
<h1>Patient Summary</h1>
<p>Patient ID: {WebUtility.HtmlEncode(patient.PatientId)}</p>
<p>Name: {WebUtility.HtmlEncode(patient.Name)}</p>
<p>SSN: {WebUtility.HtmlEncode(patient.Ssn)}</p>
</body>
</html>"
Dim renderer = New ChromePdfRenderer()
Using pdf = renderer.RenderHtmlAsPdf(html)
' Remove the SSN from the page content wherever it appears
pdf.RedactTextOnAllPages(patient.Ssn)
' Descriptive document properties
pdf.MetaData.Author = "Records System"
pdf.MetaData.Title = $"Patient Summary {patient.PatientId}"
' Require a password to open the file, restrict copying, and encrypt with AES-256
pdf.SecuritySettings.UserPassword = patient.AccessCode
pdf.SecuritySettings.OwnerPassword = "records-owner-password"
pdf.SecuritySettings.AllowUserCopyPasteContent = False
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.FullPrintRights
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256
' Sign last, so changes made to the file after signing can be detected
pdf.Sign(New PdfSignature("records-cert.pfx", "password") With {
.SigningReason = "Record export",
.SigningLocation = "Records Department"
})
pdf.SaveAs("patient-summary.pdf")
End Using
Public Record PatientRecord(PatientId As String, Name As String, Ssn As String, AccessCode As String)' NuGet: Install-Package IronPdf
Imports System.Net
Imports IronPdf
Imports IronPdf.Security
Imports IronPdf.Signing
Dim patient = New PatientRecord("P-10442", "Jane <Q.> Doe & Family", "123-45-6789", "open-sesame")
' HTML-encode text values, so characters such as & and < print as text instead of changing the markup
Dim html = $"
<html>
<body>
<h1>Patient Summary</h1>
<p>Patient ID: {WebUtility.HtmlEncode(patient.PatientId)}</p>
<p>Name: {WebUtility.HtmlEncode(patient.Name)}</p>
<p>SSN: {WebUtility.HtmlEncode(patient.Ssn)}</p>
</body>
</html>"
Dim renderer = New ChromePdfRenderer()
Using pdf = renderer.RenderHtmlAsPdf(html)
' Remove the SSN from the page content wherever it appears
pdf.RedactTextOnAllPages(patient.Ssn)
' Descriptive document properties
pdf.MetaData.Author = "Records System"
pdf.MetaData.Title = $"Patient Summary {patient.PatientId}"
' Require a password to open the file, restrict copying, and encrypt with AES-256
pdf.SecuritySettings.UserPassword = patient.AccessCode
pdf.SecuritySettings.OwnerPassword = "records-owner-password"
pdf.SecuritySettings.AllowUserCopyPasteContent = False
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.FullPrintRights
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256
' Sign last, so changes made to the file after signing can be detected
pdf.Sign(New PdfSignature("records-cert.pfx", "password") With {
.SigningReason = "Record export",
.SigningLocation = "Records Department"
})
pdf.SaveAs("patient-summary.pdf")
End Using
Public Record PatientRecord(PatientId As String, Name As String, Ssn As String, AccessCode As String)이들은 문서 처리의 구성 요소입니다. 애플리케이션이 HIPAA와 같은 규정을 충족하는지 여부는 데이터 저장, 전송 및 접근 제어 방식에 따라 달라지며, 이는 PDF 설정만으로는 결정되지 않습니다.
EvoPdf를 선택할 때
EvoPdf는 다음에 적합합니다:
1. HTML to PDF 변환
- HTML 템플릿에서 생성된 보고서
- Chromium 기반 EvoPdf Next를 사용한 최신 HTML, CSS 및 JavaScript
- 송장 및 영수증
- 일관된 템플릿을 사용한 대량 문서 생성
2. 예산을 고려하는 팀
- 단일 라이선스에서 무제한 개발자
- 낮은 초기 비용($450 배포 라이선스)
- 내장 수정이 필요하지 않은 프로젝트
3. 특정 서버 환경
- 레거시 .NET Framework 애플리케이션(EvoPdf Classic은 .NET Framework 2.0 이상을 지원)
- 단일 애플리케이션에 대한 서버당 라이선스
- 레거시 시스템 통합
4. 대량 변환
- 정적 보고서 생성
- 문서화 시스템
- 인쇄 준비 문서 생성
실제 예제: 송장 생성 시스템
// NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
using System;
using System.Globalization;
using System.IO;
using System.Net;
using EvoPdf;
var invoice = new InvoiceData("INV-2026-0042", new DateTime(2026, 9, 1), "Smith & Sons <Ltd>", 1250.00m);
var converter = new HtmlToPdfConverter
{
// LicenseKey = "<your EvoPdf license key>",
JavaScriptEnabled = false, // not needed for static invoices
ConversionDelay = 0
};
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
converter.PdfDocumentOptions.TopMargin = 30; // points
converter.PdfDocumentOptions.BottomMargin = 30; // points
string template =
"<html><body><h1>Invoice {{InvoiceNumber}}</h1>" +
"<p>Date: {{Date}}</p><p>Customer: {{CustomerName}}</p><p>Total: {{Total}}</p></body></html>";
// HTML-encode text values, so characters such as & and < print as text instead of changing the markup
string html = template
.Replace("{{InvoiceNumber}}", WebUtility.HtmlEncode(invoice.InvoiceNumber))
.Replace("{{Date}}", invoice.Date.ToString("yyyy-MM-dd"))
.Replace("{{CustomerName}}", WebUtility.HtmlEncode(invoice.CustomerName))
.Replace("{{Total}}", invoice.Total.ToString("C", CultureInfo.GetCultureInfo("en-US")));
File.WriteAllBytes($"invoice-{invoice.InvoiceNumber}.pdf", converter.ConvertHtml(html, ""));
record InvoiceData(string InvoiceNumber, DateTime Date, string CustomerName, decimal Total);// NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
using System;
using System.Globalization;
using System.IO;
using System.Net;
using EvoPdf;
var invoice = new InvoiceData("INV-2026-0042", new DateTime(2026, 9, 1), "Smith & Sons <Ltd>", 1250.00m);
var converter = new HtmlToPdfConverter
{
// LicenseKey = "<your EvoPdf license key>",
JavaScriptEnabled = false, // not needed for static invoices
ConversionDelay = 0
};
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
converter.PdfDocumentOptions.TopMargin = 30; // points
converter.PdfDocumentOptions.BottomMargin = 30; // points
string template =
"<html><body><h1>Invoice {{InvoiceNumber}}</h1>" +
"<p>Date: {{Date}}</p><p>Customer: {{CustomerName}}</p><p>Total: {{Total}}</p></body></html>";
// HTML-encode text values, so characters such as & and < print as text instead of changing the markup
string html = template
.Replace("{{InvoiceNumber}}", WebUtility.HtmlEncode(invoice.InvoiceNumber))
.Replace("{{Date}}", invoice.Date.ToString("yyyy-MM-dd"))
.Replace("{{CustomerName}}", WebUtility.HtmlEncode(invoice.CustomerName))
.Replace("{{Total}}", invoice.Total.ToString("C", CultureInfo.GetCultureInfo("en-US")));
File.WriteAllBytes($"invoice-{invoice.InvoiceNumber}.pdf", converter.ConvertHtml(html, ""));
record InvoiceData(string InvoiceNumber, DateTime Date, string CustomerName, decimal Total);' NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
Imports System
Imports System.Globalization
Imports System.IO
Imports System.Net
Imports EvoPdf
Dim invoice = New InvoiceData("INV-2026-0042", New DateTime(2026, 9, 1), "Smith & Sons <Ltd>", 1250.00D)
Dim converter = New HtmlToPdfConverter With {
' LicenseKey = "<your EvoPdf license key>",
.JavaScriptEnabled = False, ' not needed for static invoices
.ConversionDelay = 0
}
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4
converter.PdfDocumentOptions.TopMargin = 30 ' points
converter.PdfDocumentOptions.BottomMargin = 30 ' points
Dim template As String =
"<html><body><h1>Invoice {{InvoiceNumber}}</h1>" +
"<p>Date: {{Date}}</p><p>Customer: {{CustomerName}}</p><p>Total: {{Total}}</p></body></html>"
' HTML-encode text values, so characters such as & and < print as text instead of changing the markup
Dim html As String = template _
.Replace("{{InvoiceNumber}}", WebUtility.HtmlEncode(invoice.InvoiceNumber)) _
.Replace("{{Date}}", invoice.Date.ToString("yyyy-MM-dd")) _
.Replace("{{CustomerName}}", WebUtility.HtmlEncode(invoice.CustomerName)) _
.Replace("{{Total}}", invoice.Total.ToString("C", CultureInfo.GetCultureInfo("en-US")))
File.WriteAllBytes($"invoice-{invoice.InvoiceNumber}.pdf", converter.ConvertHtml(html, ""))
Public Class InvoiceData
Public Property InvoiceNumber As String
Public Property Date As DateTime
Public Property CustomerName As String
Public Property Total As Decimal
Public Sub New(invoiceNumber As String, [date] As DateTime, customerName As String, total As Decimal)
Me.InvoiceNumber = invoiceNumber
Me.Date = [date]
Me.CustomerName = customerName
Me.Total = total
End Sub
End Class' NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
Imports System
Imports System.Globalization
Imports System.IO
Imports System.Net
Imports EvoPdf
Dim invoice = New InvoiceData("INV-2026-0042", New DateTime(2026, 9, 1), "Smith & Sons <Ltd>", 1250.00D)
Dim converter = New HtmlToPdfConverter With {
' LicenseKey = "<your EvoPdf license key>",
.JavaScriptEnabled = False, ' not needed for static invoices
.ConversionDelay = 0
}
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4
converter.PdfDocumentOptions.TopMargin = 30 ' points
converter.PdfDocumentOptions.BottomMargin = 30 ' points
Dim template As String =
"<html><body><h1>Invoice {{InvoiceNumber}}</h1>" +
"<p>Date: {{Date}}</p><p>Customer: {{CustomerName}}</p><p>Total: {{Total}}</p></body></html>"
' HTML-encode text values, so characters such as & and < print as text instead of changing the markup
Dim html As String = template _
.Replace("{{InvoiceNumber}}", WebUtility.HtmlEncode(invoice.InvoiceNumber)) _
.Replace("{{Date}}", invoice.Date.ToString("yyyy-MM-dd")) _
.Replace("{{CustomerName}}", WebUtility.HtmlEncode(invoice.CustomerName)) _
.Replace("{{Total}}", invoice.Total.ToString("C", CultureInfo.GetCultureInfo("en-US")))
File.WriteAllBytes($"invoice-{invoice.InvoiceNumber}.pdf", converter.ConvertHtml(html, ""))
Public Class InvoiceData
Public Property InvoiceNumber As String
Public Property Date As DateTime
Public Property CustomerName As String
Public Property Total As Decimal
Public Sub New(invoiceNumber As String, [date] As DateTime, customerName As String, total As Decimal)
Me.InvoiceNumber = invoiceNumber
Me.Date = [date]
Me.CustomerName = customerName
Me.Total = total
End Sub
End Class고급 구현 패턴 및 모범 사례
프로덕션에서 성능 최적화
두 라이브러리는 적절한 구성 및 사용 패턴에서 이점을 얻습니다:
IronPDF 성능 최적화
// NuGet: Install-Package IronPdf
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using IronPdf;
using IronPdf.Engines.Chrome;
using IronPdf.Rendering;
// Global engine settings: set once at startup, before the first render
Installation.ChromeGpuMode = ChromeGpuModes.Disabled; // servers usually have no GPU
// Set to false only if the Linux dependencies are already installed in your image
Installation.LinuxAndDockerDependenciesAutoConfig = false;
var service = new PdfService(maxConcurrency: 4);
var pages = Enumerable.Range(1, 8).Select(i => $"<h1>Report {i}</h1>");
byte[][] results = await Task.WhenAll(pages.Select(service.GeneratePdfAsync));
for (int i = 0; i < results.Length; i++)
File.WriteAllBytes($"report-{i + 1}.pdf", results[i]);
Console.WriteLine($"Generated {results.Length} PDFs");
public class PdfService
{
private readonly ChromePdfRenderer _renderer;
private readonly SemaphoreSlim _gate;
public PdfService(int maxConcurrency)
{
// Caps how many renders run at once
_gate = new SemaphoreSlim(maxConcurrency);
_renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
EnableJavaScript = false, // only if the pages need no JavaScript
CssMediaType = PdfCssMediaType.Screen,
Timeout = 30 // seconds
}
};
}
public async Task<byte[]> GeneratePdfAsync(string html)
{
await _gate.WaitAsync();
try
{
using var pdf = await _renderer.RenderHtmlAsPdfAsync(html);
return pdf.BinaryData;
}
finally
{
_gate.Release();
}
}
}// NuGet: Install-Package IronPdf
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using IronPdf;
using IronPdf.Engines.Chrome;
using IronPdf.Rendering;
// Global engine settings: set once at startup, before the first render
Installation.ChromeGpuMode = ChromeGpuModes.Disabled; // servers usually have no GPU
// Set to false only if the Linux dependencies are already installed in your image
Installation.LinuxAndDockerDependenciesAutoConfig = false;
var service = new PdfService(maxConcurrency: 4);
var pages = Enumerable.Range(1, 8).Select(i => $"<h1>Report {i}</h1>");
byte[][] results = await Task.WhenAll(pages.Select(service.GeneratePdfAsync));
for (int i = 0; i < results.Length; i++)
File.WriteAllBytes($"report-{i + 1}.pdf", results[i]);
Console.WriteLine($"Generated {results.Length} PDFs");
public class PdfService
{
private readonly ChromePdfRenderer _renderer;
private readonly SemaphoreSlim _gate;
public PdfService(int maxConcurrency)
{
// Caps how many renders run at once
_gate = new SemaphoreSlim(maxConcurrency);
_renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
EnableJavaScript = false, // only if the pages need no JavaScript
CssMediaType = PdfCssMediaType.Screen,
Timeout = 30 // seconds
}
};
}
public async Task<byte[]> GeneratePdfAsync(string html)
{
await _gate.WaitAsync();
try
{
using var pdf = await _renderer.RenderHtmlAsPdfAsync(html);
return pdf.BinaryData;
}
finally
{
_gate.Release();
}
}
}Imports System
Imports System.IO
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks
Imports IronPdf
Imports IronPdf.Engines.Chrome
Imports IronPdf.Rendering
' Global engine settings: set once at startup, before the first render
Installation.ChromeGpuMode = ChromeGpuModes.Disabled ' servers usually have no GPU
' Set to false only if the Linux dependencies are already installed in your image
Installation.LinuxAndDockerDependenciesAutoConfig = False
Dim service = New PdfService(MaxConcurrency:=4)
Dim pages = Enumerable.Range(1, 8).Select(Function(i) $"<h1>Report {i}</h1>")
Dim results As Byte()() = Await Task.WhenAll(pages.Select(Function(page) service.GeneratePdfAsync(page)))
For i As Integer = 0 To results.Length - 1
File.WriteAllBytes($"report-{i + 1}.pdf", results(i))
Next
Console.WriteLine($"Generated {results.Length} PDFs")
Public Class PdfService
Private ReadOnly _renderer As ChromePdfRenderer
Private ReadOnly _gate As SemaphoreSlim
Public Sub New(maxConcurrency As Integer)
' Caps how many renders run at once
_gate = New SemaphoreSlim(maxConcurrency)
_renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.EnableJavaScript = False, ' only if the pages need no JavaScript
.CssMediaType = PdfCssMediaType.Screen,
.Timeout = 30 ' seconds
}
}
End Sub
Public Async Function GeneratePdfAsync(html As String) As Task(Of Byte())
Await _gate.WaitAsync()
Try
Using pdf = Await _renderer.RenderHtmlAsPdfAsync(html)
Return pdf.BinaryData
End Using
Finally
_gate.Release()
End Try
End Function
End ClassImports System
Imports System.IO
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks
Imports IronPdf
Imports IronPdf.Engines.Chrome
Imports IronPdf.Rendering
' Global engine settings: set once at startup, before the first render
Installation.ChromeGpuMode = ChromeGpuModes.Disabled ' servers usually have no GPU
' Set to false only if the Linux dependencies are already installed in your image
Installation.LinuxAndDockerDependenciesAutoConfig = False
Dim service = New PdfService(MaxConcurrency:=4)
Dim pages = Enumerable.Range(1, 8).Select(Function(i) $"<h1>Report {i}</h1>")
Dim results As Byte()() = Await Task.WhenAll(pages.Select(Function(page) service.GeneratePdfAsync(page)))
For i As Integer = 0 To results.Length - 1
File.WriteAllBytes($"report-{i + 1}.pdf", results(i))
Next
Console.WriteLine($"Generated {results.Length} PDFs")
Public Class PdfService
Private ReadOnly _renderer As ChromePdfRenderer
Private ReadOnly _gate As SemaphoreSlim
Public Sub New(maxConcurrency As Integer)
' Caps how many renders run at once
_gate = New SemaphoreSlim(maxConcurrency)
_renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.EnableJavaScript = False, ' only if the pages need no JavaScript
.CssMediaType = PdfCssMediaType.Screen,
.Timeout = 30 ' seconds
}
}
End Sub
Public Async Function GeneratePdfAsync(html As String) As Task(Of Byte())
Await _gate.WaitAsync()
Try
Using pdf = Await _renderer.RenderHtmlAsPdfAsync(html)
Return pdf.BinaryData
End Using
Finally
_gate.Release()
End Try
End Function
End ClassEvoPdf 성능 패턴
// NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EvoPdf;
var service = new EvoPdfService(maxConcurrency: 4);
var pages = Enumerable.Range(1, 8).Select(i => $"<h1>Report {i}</h1>");
byte[][] results = await Task.WhenAll(pages.Select(service.GeneratePdfAsync));
for (int i = 0; i < results.Length; i++)
File.WriteAllBytes($"evopdf-report-{i + 1}.pdf", results[i]);
Console.WriteLine($"Generated {results.Length} PDFs");
public class EvoPdfService
{
private readonly SemaphoreSlim _gate;
// Caps how many conversions run at once
public EvoPdfService(int maxConcurrency) => _gate = new SemaphoreSlim(maxConcurrency);
public async Task<byte[]> GeneratePdfAsync(string html)
{
await _gate.WaitAsync();
try
{
// A new converter for each conversion, as EVO documents for parallel conversions
var converter = new HtmlToPdfConverter
{
// LicenseKey = "<your EvoPdf license key>",
JavaScriptEnabled = false,
ConversionDelay = 0
};
// The Classic API is synchronous, so run it on a thread-pool thread
return await Task.Run(() => converter.ConvertHtml(html, ""));
}
finally
{
_gate.Release();
}
}
}// NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EvoPdf;
var service = new EvoPdfService(maxConcurrency: 4);
var pages = Enumerable.Range(1, 8).Select(i => $"<h1>Report {i}</h1>");
byte[][] results = await Task.WhenAll(pages.Select(service.GeneratePdfAsync));
for (int i = 0; i < results.Length; i++)
File.WriteAllBytes($"evopdf-report-{i + 1}.pdf", results[i]);
Console.WriteLine($"Generated {results.Length} PDFs");
public class EvoPdfService
{
private readonly SemaphoreSlim _gate;
// Caps how many conversions run at once
public EvoPdfService(int maxConcurrency) => _gate = new SemaphoreSlim(maxConcurrency);
public async Task<byte[]> GeneratePdfAsync(string html)
{
await _gate.WaitAsync();
try
{
// A new converter for each conversion, as EVO documents for parallel conversions
var converter = new HtmlToPdfConverter
{
// LicenseKey = "<your EvoPdf license key>",
JavaScriptEnabled = false,
ConversionDelay = 0
};
// The Classic API is synchronous, so run it on a thread-pool thread
return await Task.Run(() => converter.ConvertHtml(html, ""));
}
finally
{
_gate.Release();
}
}
}Imports System
Imports System.IO
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks
Imports EvoPdf
Dim service = New EvoPdfService(maxConcurrency:=4)
Dim pages = Enumerable.Range(1, 8).[Select](Function(i) $"<h1>Report {i}</h1>")
Dim results As Byte()() = Await Task.WhenAll(pages.[Select](Function(page) service.GeneratePdfAsync(page)))
For i As Integer = 0 To results.Length - 1
File.WriteAllBytes($"evopdf-report-{i + 1}.pdf", results(i))
Next
Console.WriteLine($"Generated {results.Length} PDFs")
Public Class EvoPdfService
Private ReadOnly _gate As SemaphoreSlim
' Caps how many conversions run at once
Public Sub New(maxConcurrency As Integer)
_gate = New SemaphoreSlim(maxConcurrency)
End Sub
Public Async Function GeneratePdfAsync(html As String) As Task(Of Byte())
Await _gate.WaitAsync()
Try
' A new converter for each conversion, as EVO documents for parallel conversions
Dim converter = New HtmlToPdfConverter With {
' LicenseKey = "<your EvoPdf license key>",
.JavaScriptEnabled = False,
.ConversionDelay = 0
}
' The Classic API is synchronous, so run it on a thread-pool thread
Return Await Task.Run(Function() converter.ConvertHtml(html, ""))
Finally
_gate.Release()
End Try
End Function
End ClassImports System
Imports System.IO
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks
Imports EvoPdf
Dim service = New EvoPdfService(maxConcurrency:=4)
Dim pages = Enumerable.Range(1, 8).[Select](Function(i) $"<h1>Report {i}</h1>")
Dim results As Byte()() = Await Task.WhenAll(pages.[Select](Function(page) service.GeneratePdfAsync(page)))
For i As Integer = 0 To results.Length - 1
File.WriteAllBytes($"evopdf-report-{i + 1}.pdf", results(i))
Next
Console.WriteLine($"Generated {results.Length} PDFs")
Public Class EvoPdfService
Private ReadOnly _gate As SemaphoreSlim
' Caps how many conversions run at once
Public Sub New(maxConcurrency As Integer)
_gate = New SemaphoreSlim(maxConcurrency)
End Sub
Public Async Function GeneratePdfAsync(html As String) As Task(Of Byte())
Await _gate.WaitAsync()
Try
' A new converter for each conversion, as EVO documents for parallel conversions
Dim converter = New HtmlToPdfConverter With {
' LicenseKey = "<your EvoPdf license key>",
.JavaScriptEnabled = False,
.ConversionDelay = 0
}
' The Classic API is synchronous, so run it on a thread-pool thread
Return Await Task.Run(Function() converter.ConvertHtml(html, ""))
Finally
_gate.Release()
End Try
End Function
End Class세마포어는 두 예제에서 동시성을 제한하는 것입니다. EvoPdf Next에서는 GlobalSettings.MaxParallelConversions가 라이브러리 내부에서 동일한 종류의 제한을 설정하며, 지속적인 렌더러는 변환 간에 렌더링 엔진 프로세스를 실행 상태로 유지합니다.
오류 처리 및 디버깅
프로덕션 코드는 로드할 수 없는 페이지와 같은 렌더링 실패를 처리해야 합니다:
IronPDF 오류 처리
// NuGet: Install-Package IronPdf
using System;
using System.IO;
using System.Threading.Tasks;
using IronPdf;
using IronPdf.Exceptions;
// Write IronPDF's own diagnostics to a log file
IronPdf.Logging.Logger.LogFilePath = "ironpdf.log";
IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.Timeout = 60; // seconds
byte[] page = await TryRenderUrlAsync(renderer, "https://example.com/");
if (page != null)
{
File.WriteAllBytes("example.pdf", page);
Console.WriteLine($"Rendered {page.Length} bytes");
}
// A host that does not resolve makes the render throw, which exercises the error path
byte[] missing = await TryRenderUrlAsync(renderer, "https://nonexistent.invalid/");
Console.WriteLine(missing == null ? "Second render failed and was handled" : "Second render succeeded");
// Returns the PDF bytes, or null if the page could not be rendered
static async Task<byte[]> TryRenderUrlAsync(ChromePdfRenderer renderer, string url)
{
try
{
using var pdf = await renderer.RenderUrlAsPdfAsync(url);
if (pdf.PageCount == 0)
{
Console.Error.WriteLine($"No pages rendered for {url}");
return null;
}
return pdf.BinaryData;
}
catch (IronPdfNativeException ex)
{
// Raised by the rendering engine, for example when the page cannot be loaded
Console.Error.WriteLine($"Rendering failed for {url}: {ex.Message}");
return null;
}
}// NuGet: Install-Package IronPdf
using System;
using System.IO;
using System.Threading.Tasks;
using IronPdf;
using IronPdf.Exceptions;
// Write IronPDF's own diagnostics to a log file
IronPdf.Logging.Logger.LogFilePath = "ironpdf.log";
IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.Timeout = 60; // seconds
byte[] page = await TryRenderUrlAsync(renderer, "https://example.com/");
if (page != null)
{
File.WriteAllBytes("example.pdf", page);
Console.WriteLine($"Rendered {page.Length} bytes");
}
// A host that does not resolve makes the render throw, which exercises the error path
byte[] missing = await TryRenderUrlAsync(renderer, "https://nonexistent.invalid/");
Console.WriteLine(missing == null ? "Second render failed and was handled" : "Second render succeeded");
// Returns the PDF bytes, or null if the page could not be rendered
static async Task<byte[]> TryRenderUrlAsync(ChromePdfRenderer renderer, string url)
{
try
{
using var pdf = await renderer.RenderUrlAsPdfAsync(url);
if (pdf.PageCount == 0)
{
Console.Error.WriteLine($"No pages rendered for {url}");
return null;
}
return pdf.BinaryData;
}
catch (IronPdfNativeException ex)
{
// Raised by the rendering engine, for example when the page cannot be loaded
Console.Error.WriteLine($"Rendering failed for {url}: {ex.Message}");
return null;
}
}' NuGet: Install-Package IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports IronPdf
Imports IronPdf.Exceptions
' Write IronPDF's own diagnostics to a log file
IronPdf.Logging.Logger.LogFilePath = "ironpdf.log"
IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All
Dim renderer = New ChromePdfRenderer()
renderer.RenderingOptions.Timeout = 60 ' seconds
Dim page As Byte() = Await TryRenderUrlAsync(renderer, "https://example.com/")
If page IsNot Nothing Then
File.WriteAllBytes("example.pdf", page)
Console.WriteLine($"Rendered {page.Length} bytes")
End If
' A host that does not resolve makes the render throw, which exercises the error path
Dim missing As Byte() = Await TryRenderUrlAsync(renderer, "https://nonexistent.invalid/")
Console.WriteLine(If(missing Is Nothing, "Second render failed and was handled", "Second render succeeded"))
' Returns the PDF bytes, or null if the page could not be rendered
Private Shared Async Function TryRenderUrlAsync(renderer As ChromePdfRenderer, url As String) As Task(Of Byte())
Try
Using pdf = Await renderer.RenderUrlAsPdfAsync(url)
If pdf.PageCount = 0 Then
Console.Error.WriteLine($"No pages rendered for {url}")
Return Nothing
End If
Return pdf.BinaryData
End Using
Catch ex As IronPdfNativeException
' Raised by the rendering engine, for example when the page cannot be loaded
Console.Error.WriteLine($"Rendering failed for {url}: {ex.Message}")
Return Nothing
End Try
End Function' NuGet: Install-Package IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports IronPdf
Imports IronPdf.Exceptions
' Write IronPDF's own diagnostics to a log file
IronPdf.Logging.Logger.LogFilePath = "ironpdf.log"
IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All
Dim renderer = New ChromePdfRenderer()
renderer.RenderingOptions.Timeout = 60 ' seconds
Dim page As Byte() = Await TryRenderUrlAsync(renderer, "https://example.com/")
If page IsNot Nothing Then
File.WriteAllBytes("example.pdf", page)
Console.WriteLine($"Rendered {page.Length} bytes")
End If
' A host that does not resolve makes the render throw, which exercises the error path
Dim missing As Byte() = Await TryRenderUrlAsync(renderer, "https://nonexistent.invalid/")
Console.WriteLine(If(missing Is Nothing, "Second render failed and was handled", "Second render succeeded"))
' Returns the PDF bytes, or null if the page could not be rendered
Private Shared Async Function TryRenderUrlAsync(renderer As ChromePdfRenderer, url As String) As Task(Of Byte())
Try
Using pdf = Await renderer.RenderUrlAsPdfAsync(url)
If pdf.PageCount = 0 Then
Console.Error.WriteLine($"No pages rendered for {url}")
Return Nothing
End If
Return pdf.BinaryData
End Using
Catch ex As IronPdfNativeException
' Raised by the rendering engine, for example when the page cannot be loaded
Console.Error.WriteLine($"Rendering failed for {url}: {ex.Message}")
Return Nothing
End Try
End Function산업별 구현 예시
금융 보고서: 서명된 읽기 전용 명세서
승인 후 변경되지 않아야 하는 명세서는 워터마크, 제한된 권한 및 서명을 결합할 수 있습니다:
// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Editing;
using IronPdf.Security;
using IronPdf.Signing;
using IronSoftware.Drawing;
var renderer = new ChromePdfRenderer();
using var pdf = renderer.RenderHtmlAsPdf(
"<h1>Quarterly Statement: Q3 2026</h1>" +
"<table><tr><td>Revenue</td><td>1,250,000</td></tr><tr><td>Expenses</td><td>980,000</td></tr></table>");
// Mark the document as a draft
pdf.ApplyWatermark(@"
<div style='color: rgba(0,0,0,0.1); font-size: 120px; transform: rotate(-45deg); font-family: Arial;'>
DRAFT
</div>", 30, VerticalAlignment.Middle, HorizontalAlignment.Center);
// Descriptive document properties: useful for search and filing, not a record of who changed what
pdf.MetaData.Author = "Financial Reporting System";
pdf.MetaData.Keywords = "Quarterly Statement, Q3 2026";
pdf.MetaData.CustomProperties.Add("ReportPeriod", "2026-Q3");
pdf.MetaData.CustomProperties.Add("GeneratedAt", DateTime.UtcNow.ToString("O"));
// Restrict editing; the owner password is needed to change these permissions
pdf.SecuritySettings.OwnerPassword = "finance-owner-password";
pdf.SecuritySettings.AllowUserEdits = PdfEditSecurity.NoEdit;
pdf.SecuritySettings.AllowUserCopyPasteContent = true;
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.FullPrintRights;
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256;
// Sign last: readers can then detect changes made after signing
pdf.Sign(new PdfSignature("financial-cert.pfx", "password")
{
SigningReason = "Statement approval",
SigningLocation = "Finance Department",
// Visible signature image on the first page
SignatureImage = new PdfSignatureImage("cfo-signature.png", 0, new Rectangle(50, 50, 200, 60))
});
pdf.SaveAs("quarterly-statement.pdf");// NuGet: Install-Package IronPdf
using System;
using IronPdf;
using IronPdf.Editing;
using IronPdf.Security;
using IronPdf.Signing;
using IronSoftware.Drawing;
var renderer = new ChromePdfRenderer();
using var pdf = renderer.RenderHtmlAsPdf(
"<h1>Quarterly Statement: Q3 2026</h1>" +
"<table><tr><td>Revenue</td><td>1,250,000</td></tr><tr><td>Expenses</td><td>980,000</td></tr></table>");
// Mark the document as a draft
pdf.ApplyWatermark(@"
<div style='color: rgba(0,0,0,0.1); font-size: 120px; transform: rotate(-45deg); font-family: Arial;'>
DRAFT
</div>", 30, VerticalAlignment.Middle, HorizontalAlignment.Center);
// Descriptive document properties: useful for search and filing, not a record of who changed what
pdf.MetaData.Author = "Financial Reporting System";
pdf.MetaData.Keywords = "Quarterly Statement, Q3 2026";
pdf.MetaData.CustomProperties.Add("ReportPeriod", "2026-Q3");
pdf.MetaData.CustomProperties.Add("GeneratedAt", DateTime.UtcNow.ToString("O"));
// Restrict editing; the owner password is needed to change these permissions
pdf.SecuritySettings.OwnerPassword = "finance-owner-password";
pdf.SecuritySettings.AllowUserEdits = PdfEditSecurity.NoEdit;
pdf.SecuritySettings.AllowUserCopyPasteContent = true;
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.FullPrintRights;
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256;
// Sign last: readers can then detect changes made after signing
pdf.Sign(new PdfSignature("financial-cert.pfx", "password")
{
SigningReason = "Statement approval",
SigningLocation = "Finance Department",
// Visible signature image on the first page
SignatureImage = new PdfSignatureImage("cfo-signature.png", 0, new Rectangle(50, 50, 200, 60))
});
pdf.SaveAs("quarterly-statement.pdf");' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Imports IronPdf.Editing
Imports IronPdf.Security
Imports IronPdf.Signing
Imports IronSoftware.Drawing
Dim renderer = New ChromePdfRenderer()
Using pdf = renderer.RenderHtmlAsPdf(
"<h1>Quarterly Statement: Q3 2026</h1>" &
"<table><tr><td>Revenue</td><td>1,250,000</td></tr><tr><td>Expenses</td><td>980,000</td></tr></table>")
' Mark the document as a draft
pdf.ApplyWatermark("
<div style='color: rgba(0,0,0,0.1); font-size: 120px; transform: rotate(-45deg); font-family: Arial;'>
DRAFT
</div>", 30, VerticalAlignment.Middle, HorizontalAlignment.Center)
' Descriptive document properties: useful for search and filing, not a record of who changed what
pdf.MetaData.Author = "Financial Reporting System"
pdf.MetaData.Keywords = "Quarterly Statement, Q3 2026"
pdf.MetaData.CustomProperties.Add("ReportPeriod", "2026-Q3")
pdf.MetaData.CustomProperties.Add("GeneratedAt", DateTime.UtcNow.ToString("O"))
' Restrict editing; the owner password is needed to change these permissions
pdf.SecuritySettings.OwnerPassword = "finance-owner-password"
pdf.SecuritySettings.AllowUserEdits = PdfEditSecurity.NoEdit
pdf.SecuritySettings.AllowUserCopyPasteContent = True
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.FullPrintRights
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256
' Sign last: readers can then detect changes made after signing
pdf.Sign(New PdfSignature("financial-cert.pfx", "password") With {
.SigningReason = "Statement approval",
.SigningLocation = "Finance Department",
' Visible signature image on the first page
.SignatureImage = New PdfSignatureImage("cfo-signature.png", 0, New Rectangle(50, 50, 200, 60))
})
pdf.SaveAs("quarterly-statement.pdf")
End Using' NuGet: Install-Package IronPdf
Imports System
Imports IronPdf
Imports IronPdf.Editing
Imports IronPdf.Security
Imports IronPdf.Signing
Imports IronSoftware.Drawing
Dim renderer = New ChromePdfRenderer()
Using pdf = renderer.RenderHtmlAsPdf(
"<h1>Quarterly Statement: Q3 2026</h1>" &
"<table><tr><td>Revenue</td><td>1,250,000</td></tr><tr><td>Expenses</td><td>980,000</td></tr></table>")
' Mark the document as a draft
pdf.ApplyWatermark("
<div style='color: rgba(0,0,0,0.1); font-size: 120px; transform: rotate(-45deg); font-family: Arial;'>
DRAFT
</div>", 30, VerticalAlignment.Middle, HorizontalAlignment.Center)
' Descriptive document properties: useful for search and filing, not a record of who changed what
pdf.MetaData.Author = "Financial Reporting System"
pdf.MetaData.Keywords = "Quarterly Statement, Q3 2026"
pdf.MetaData.CustomProperties.Add("ReportPeriod", "2026-Q3")
pdf.MetaData.CustomProperties.Add("GeneratedAt", DateTime.UtcNow.ToString("O"))
' Restrict editing; the owner password is needed to change these permissions
pdf.SecuritySettings.OwnerPassword = "finance-owner-password"
pdf.SecuritySettings.AllowUserEdits = PdfEditSecurity.NoEdit
pdf.SecuritySettings.AllowUserCopyPasteContent = True
pdf.SecuritySettings.AllowUserPrinting = PdfPrintSecurity.FullPrintRights
pdf.SecuritySettings.EncryptionType = PdfEncryptionType.Aes_256
' Sign last: readers can then detect changes made after signing
pdf.Sign(New PdfSignature("financial-cert.pfx", "password") With {
.SigningReason = "Statement approval",
.SigningLocation = "Finance Department",
' Visible signature image on the first page
.SignatureImage = New PdfSignatureImage("cfo-signature.png", 0, New Rectangle(50, 50, 200, 60))
})
pdf.SaveAs("quarterly-statement.pdf")
End Using서명은 이후의 변경을 감지할 수 있게 하고 권한은 편집을 억제합니다. 둘 중 어느 것도 자체적으로는 규정 준수 제어나 누가 무엇을 했는지에 대한 감사 추적이 아닙니다.
전자상거래: 동적 송장 생성
전자상거래 플랫폼은 빠르고 신뢰할 수 있는 송장 생성을 필요로 합니다:
// NuGet: Install-Package IronPdf
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using IronPdf;
using IronPdf.Rendering;
var order = new Order("INV-1001", new DateTime(2026, 9, 1), new[]
{
new OrderItem("Widget <Deluxe> & Co. edition", 2, 19.99m),
new OrderItem("Gift wrap", 1, 4.50m)
});
var renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
MarginTop = 10, // millimetres
MarginBottom = 10,
PaperSize = PdfPaperSize.A4
}
};
var usd = CultureInfo.GetCultureInfo("en-US");
// HTML-encode text values, so characters such as & and < print as text instead of changing the markup
string rows = string.Concat(order.Items.Select(item => $@"
<tr>
<td>{WebUtility.HtmlEncode(item.Name)}</td>
<td>{item.Quantity}</td>
<td>{item.Price.ToString("C", usd)}</td>
<td>{item.Total.ToString("C", usd)}</td>
</tr>"));
var html = $@"
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.invoice-header {{ background: #f0f0f0; padding: 20px; }}
table {{ width: 100%; border-collapse: collapse; }}
th, td {{ padding: 10px; border-bottom: 1px solid #ddd; text-align: left; }}
</style>
</head>
<body>
<div class='invoice-header'>
<h1>Invoice #{WebUtility.HtmlEncode(order.InvoiceNumber)}</h1>
<p>Date: {order.Date:yyyy-MM-dd}</p>
</div>
<table>
<tr><th>Item</th><th>Quantity</th><th>Price</th><th>Total</th></tr>
{rows}
<tr><td colspan='3'><strong>Total</strong></td><td><strong>{order.Total.ToString("C", usd)}</strong></td></tr>
</table>
<p>Thank you for your business!</p>
</body>
</html>";
using var pdf = await renderer.RenderHtmlAsPdfAsync(html);
pdf.SaveAs($"invoice-{order.InvoiceNumber}.pdf");
record OrderItem(string Name, int Quantity, decimal Price)
{
public decimal Total => Price * Quantity;
}
record Order(string InvoiceNumber, DateTime Date, OrderItem[] Items)
{
public decimal Total => Items.Sum(item => item.Total);
}// NuGet: Install-Package IronPdf
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using IronPdf;
using IronPdf.Rendering;
var order = new Order("INV-1001", new DateTime(2026, 9, 1), new[]
{
new OrderItem("Widget <Deluxe> & Co. edition", 2, 19.99m),
new OrderItem("Gift wrap", 1, 4.50m)
});
var renderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
MarginTop = 10, // millimetres
MarginBottom = 10,
PaperSize = PdfPaperSize.A4
}
};
var usd = CultureInfo.GetCultureInfo("en-US");
// HTML-encode text values, so characters such as & and < print as text instead of changing the markup
string rows = string.Concat(order.Items.Select(item => $@"
<tr>
<td>{WebUtility.HtmlEncode(item.Name)}</td>
<td>{item.Quantity}</td>
<td>{item.Price.ToString("C", usd)}</td>
<td>{item.Total.ToString("C", usd)}</td>
</tr>"));
var html = $@"
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.invoice-header {{ background: #f0f0f0; padding: 20px; }}
table {{ width: 100%; border-collapse: collapse; }}
th, td {{ padding: 10px; border-bottom: 1px solid #ddd; text-align: left; }}
</style>
</head>
<body>
<div class='invoice-header'>
<h1>Invoice #{WebUtility.HtmlEncode(order.InvoiceNumber)}</h1>
<p>Date: {order.Date:yyyy-MM-dd}</p>
</div>
<table>
<tr><th>Item</th><th>Quantity</th><th>Price</th><th>Total</th></tr>
{rows}
<tr><td colspan='3'><strong>Total</strong></td><td><strong>{order.Total.ToString("C", usd)}</strong></td></tr>
</table>
<p>Thank you for your business!</p>
</body>
</html>";
using var pdf = await renderer.RenderHtmlAsPdfAsync(html);
pdf.SaveAs($"invoice-{order.InvoiceNumber}.pdf");
record OrderItem(string Name, int Quantity, decimal Price)
{
public decimal Total => Price * Quantity;
}
record Order(string InvoiceNumber, DateTime Date, OrderItem[] Items)
{
public decimal Total => Items.Sum(item => item.Total);
}' NuGet: Install-Package IronPdf
Imports System
Imports System.Globalization
Imports System.Linq
Imports System.Net
Imports IronPdf
Imports IronPdf.Rendering
Dim order = New Order("INV-1001", New DateTime(2026, 9, 1), {
New OrderItem("Widget <Deluxe> & Co. edition", 2, 19.99D),
New OrderItem("Gift wrap", 1, 4.5D)
})
Dim renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.MarginTop = 10, ' millimetres
.MarginBottom = 10,
.PaperSize = PdfPaperSize.A4
}
}
Dim usd = CultureInfo.GetCultureInfo("en-US")
' HTML-encode text values, so characters such as & and < print as text instead of changing the markup
Dim rows As String = String.Concat(order.Items.Select(Function(item) $"
<tr>
<td>{WebUtility.HtmlEncode(item.Name)}</td>
<td>{item.Quantity}</td>
<td>{item.Price.ToString("C", usd)}</td>
<td>{item.Total.ToString("C", usd)}</td>
</tr>"))
Dim html = $"
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.invoice-header {{ background: #f0f0f0; padding: 20px; }}
table {{ width: 100%; border-collapse: collapse; }}
th, td {{ padding: 10px; border-bottom: 1px solid #ddd; text-align: left; }}
</style>
</head>
<body>
<div class='invoice-header'>
<h1>Invoice #{WebUtility.HtmlEncode(order.InvoiceNumber)}</h1>
<p>Date: {order.Date:yyyy-MM-dd}</p>
</div>
<table>
<tr><th>Item</th><th>Quantity</th><th>Price</th><th>Total</th></tr>
{rows}
<tr><td colspan='3'><strong>Total</strong></td><td><strong>{order.Total.ToString("C", usd)}</strong></td></tr>
</table>
<p>Thank you for your business!</p>
</body>
</html>"
Using pdf = Await renderer.RenderHtmlAsPdfAsync(html)
pdf.SaveAs($"invoice-{order.InvoiceNumber}.pdf")
End Using
Public Record OrderItem(Name As String, Quantity As Integer, Price As Decimal)
Public ReadOnly Property Total As Decimal
Get
Return Price * Quantity
End Get
End Property
End Record
Public Record Order(InvoiceNumber As String, [Date] As DateTime, Items As OrderItem())
Public ReadOnly Property Total As Decimal
Get
Return Items.Sum(Function(item) item.Total)
End Get
End Property
End Record' NuGet: Install-Package IronPdf
Imports System
Imports System.Globalization
Imports System.Linq
Imports System.Net
Imports IronPdf
Imports IronPdf.Rendering
Dim order = New Order("INV-1001", New DateTime(2026, 9, 1), {
New OrderItem("Widget <Deluxe> & Co. edition", 2, 19.99D),
New OrderItem("Gift wrap", 1, 4.5D)
})
Dim renderer = New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.MarginTop = 10, ' millimetres
.MarginBottom = 10,
.PaperSize = PdfPaperSize.A4
}
}
Dim usd = CultureInfo.GetCultureInfo("en-US")
' HTML-encode text values, so characters such as & and < print as text instead of changing the markup
Dim rows As String = String.Concat(order.Items.Select(Function(item) $"
<tr>
<td>{WebUtility.HtmlEncode(item.Name)}</td>
<td>{item.Quantity}</td>
<td>{item.Price.ToString("C", usd)}</td>
<td>{item.Total.ToString("C", usd)}</td>
</tr>"))
Dim html = $"
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.invoice-header {{ background: #f0f0f0; padding: 20px; }}
table {{ width: 100%; border-collapse: collapse; }}
th, td {{ padding: 10px; border-bottom: 1px solid #ddd; text-align: left; }}
</style>
</head>
<body>
<div class='invoice-header'>
<h1>Invoice #{WebUtility.HtmlEncode(order.InvoiceNumber)}</h1>
<p>Date: {order.Date:yyyy-MM-dd}</p>
</div>
<table>
<tr><th>Item</th><th>Quantity</th><th>Price</th><th>Total</th></tr>
{rows}
<tr><td colspan='3'><strong>Total</strong></td><td><strong>{order.Total.ToString("C", usd)}</strong></td></tr>
</table>
<p>Thank you for your business!</p>
</body>
</html>"
Using pdf = Await renderer.RenderHtmlAsPdfAsync(html)
pdf.SaveAs($"invoice-{order.InvoiceNumber}.pdf")
End Using
Public Record OrderItem(Name As String, Quantity As Integer, Price As Decimal)
Public ReadOnly Property Total As Decimal
Get
Return Price * Quantity
End Get
End Property
End Record
Public Record Order(InvoiceNumber As String, [Date] As DateTime, Items As OrderItem())
Public ReadOnly Property Total As Decimal
Get
Return Items.Sum(Function(item) item.Total)
End Get
End Property
End Record마이그레이션 전략: 라이브러리 간 이동
라이브러리 간 전환을 고려하고 있다면, 다음은 마이그레이션 접근 방식입니다:
EvoPdf에서 IronPDF로 마이그레이션
// NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
// NuGet: Install-Package IronPdf
using System.IO;
using EvoPdf;
using IronPdf;
using IronPdf.Rendering;
string url = "https://example.com/";
// EvoPdf pattern
HtmlToPdfConverter evoPdfConverter = new HtmlToPdfConverter();
evoPdfConverter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
File.WriteAllBytes("evopdf.pdf", evoPdfConverter.ConvertUrl(url));
// Equivalent IronPDF pattern
var ironPdfRenderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
PaperSize = PdfPaperSize.A4
}
};
using (var pdf = ironPdfRenderer.RenderUrlAsPdf(url))
{
File.WriteAllBytes("ironpdf.pdf", pdf.BinaryData);
}
// During a gradual migration, both libraries can sit behind one interface
IPdfConverter converter = new IronPdfAdapter();
File.WriteAllBytes("adapter.pdf", converter.ConvertUrlToPdf(url));
public interface IPdfConverter
{
byte[] ConvertHtmlToPdf(string html);
byte[] ConvertUrlToPdf(string url);
}
public class IronPdfAdapter : IPdfConverter
{
private readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();
public byte[] ConvertHtmlToPdf(string html)
{
using var pdf = _renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
public byte[] ConvertUrlToPdf(string url)
{
using var pdf = _renderer.RenderUrlAsPdf(url);
return pdf.BinaryData;
}
}
public class EvoPdfAdapter : IPdfConverter
{
private readonly HtmlToPdfConverter _converter = new HtmlToPdfConverter();
public byte[] ConvertHtmlToPdf(string html)
{
return _converter.ConvertHtml(html, "");
}
public byte[] ConvertUrlToPdf(string url)
{
return _converter.ConvertUrl(url);
}
}// NuGet: Install-Package EvoPdf.HtmlToPdf (EvoPdf Classic, Windows)
// NuGet: Install-Package IronPdf
using System.IO;
using EvoPdf;
using IronPdf;
using IronPdf.Rendering;
string url = "https://example.com/";
// EvoPdf pattern
HtmlToPdfConverter evoPdfConverter = new HtmlToPdfConverter();
evoPdfConverter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
File.WriteAllBytes("evopdf.pdf", evoPdfConverter.ConvertUrl(url));
// Equivalent IronPDF pattern
var ironPdfRenderer = new ChromePdfRenderer
{
RenderingOptions = new ChromePdfRenderOptions
{
PaperSize = PdfPaperSize.A4
}
};
using (var pdf = ironPdfRenderer.RenderUrlAsPdf(url))
{
File.WriteAllBytes("ironpdf.pdf", pdf.BinaryData);
}
// During a gradual migration, both libraries can sit behind one interface
IPdfConverter converter = new IronPdfAdapter();
File.WriteAllBytes("adapter.pdf", converter.ConvertUrlToPdf(url));
public interface IPdfConverter
{
byte[] ConvertHtmlToPdf(string html);
byte[] ConvertUrlToPdf(string url);
}
public class IronPdfAdapter : IPdfConverter
{
private readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();
public byte[] ConvertHtmlToPdf(string html)
{
using var pdf = _renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
public byte[] ConvertUrlToPdf(string url)
{
using var pdf = _renderer.RenderUrlAsPdf(url);
return pdf.BinaryData;
}
}
public class EvoPdfAdapter : IPdfConverter
{
private readonly HtmlToPdfConverter _converter = new HtmlToPdfConverter();
public byte[] ConvertHtmlToPdf(string html)
{
return _converter.ConvertHtml(html, "");
}
public byte[] ConvertUrlToPdf(string url)
{
return _converter.ConvertUrl(url);
}
}Imports System.IO
Imports EvoPdf
Imports IronPdf
Imports IronPdf.Rendering
Dim url As String = "https://example.com/"
' EvoPdf pattern
Dim evoPdfConverter As New HtmlToPdfConverter()
evoPdfConverter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4
File.WriteAllBytes("evopdf.pdf", evoPdfConverter.ConvertUrl(url))
' Equivalent IronPDF pattern
Dim ironPdfRenderer As New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.PaperSize = PdfPaperSize.A4
}
}
Using pdf = ironPdfRenderer.RenderUrlAsPdf(url)
File.WriteAllBytes("ironpdf.pdf", pdf.BinaryData)
End Using
' During a gradual migration, both libraries can sit behind one interface
Dim converter As IPdfConverter = New IronPdfAdapter()
File.WriteAllBytes("adapter.pdf", converter.ConvertUrlToPdf(url))
Public Interface IPdfConverter
Function ConvertHtmlToPdf(html As String) As Byte()
Function ConvertUrlToPdf(url As String) As Byte()
End Interface
Public Class IronPdfAdapter
Implements IPdfConverter
Private ReadOnly _renderer As New ChromePdfRenderer()
Public Function ConvertHtmlToPdf(html As String) As Byte() Implements IPdfConverter.ConvertHtmlToPdf
Using pdf = _renderer.RenderHtmlAsPdf(html)
Return pdf.BinaryData
End Using
End Function
Public Function ConvertUrlToPdf(url As String) As Byte() Implements IPdfConverter.ConvertUrlToPdf
Using pdf = _renderer.RenderUrlAsPdf(url)
Return pdf.BinaryData
End Using
End Function
End Class
Public Class EvoPdfAdapter
Implements IPdfConverter
Private ReadOnly _converter As New HtmlToPdfConverter()
Public Function ConvertHtmlToPdf(html As String) As Byte() Implements IPdfConverter.ConvertHtmlToPdf
Return _converter.ConvertHtml(html, "")
End Function
Public Function ConvertUrlToPdf(url As String) As Byte() Implements IPdfConverter.ConvertUrlToPdf
Return _converter.ConvertUrl(url)
End Function
End ClassImports System.IO
Imports EvoPdf
Imports IronPdf
Imports IronPdf.Rendering
Dim url As String = "https://example.com/"
' EvoPdf pattern
Dim evoPdfConverter As New HtmlToPdfConverter()
evoPdfConverter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4
File.WriteAllBytes("evopdf.pdf", evoPdfConverter.ConvertUrl(url))
' Equivalent IronPDF pattern
Dim ironPdfRenderer As New ChromePdfRenderer With {
.RenderingOptions = New ChromePdfRenderOptions With {
.PaperSize = PdfPaperSize.A4
}
}
Using pdf = ironPdfRenderer.RenderUrlAsPdf(url)
File.WriteAllBytes("ironpdf.pdf", pdf.BinaryData)
End Using
' During a gradual migration, both libraries can sit behind one interface
Dim converter As IPdfConverter = New IronPdfAdapter()
File.WriteAllBytes("adapter.pdf", converter.ConvertUrlToPdf(url))
Public Interface IPdfConverter
Function ConvertHtmlToPdf(html As String) As Byte()
Function ConvertUrlToPdf(url As String) As Byte()
End Interface
Public Class IronPdfAdapter
Implements IPdfConverter
Private ReadOnly _renderer As New ChromePdfRenderer()
Public Function ConvertHtmlToPdf(html As String) As Byte() Implements IPdfConverter.ConvertHtmlToPdf
Using pdf = _renderer.RenderHtmlAsPdf(html)
Return pdf.BinaryData
End Using
End Function
Public Function ConvertUrlToPdf(url As String) As Byte() Implements IPdfConverter.ConvertUrlToPdf
Using pdf = _renderer.RenderUrlAsPdf(url)
Return pdf.BinaryData
End Using
End Function
End Class
Public Class EvoPdfAdapter
Implements IPdfConverter
Private ReadOnly _converter As New HtmlToPdfConverter()
Public Function ConvertHtmlToPdf(html As String) As Byte() Implements IPdfConverter.ConvertHtmlToPdf
Return _converter.ConvertHtml(html, "")
End Function
Public Function ConvertUrlToPdf(url As String) As Byte() Implements IPdfConverter.ConvertUrlToPdf
Return _converter.ConvertUrl(url)
End Function
End Class결론: 프로젝트에 적합한 선택
IronPDF와 EvoPdf는 .NET PDF 라이브러리 생태계에서 중요한 역할을 하지만, 서로 다른 사용 사례와 개발 철학을 목표로 합니다.
IronPDF를 선택해야 할 때:
- 최신 웹 표준 지원 (CSS3, JavaScript 프레임워크)
- 병합, 양식, 수정 및 서명과 같은 PDF 생성 이상의 조작
- 암호화, 서명 및 수정과 같은 보안 기능
- 크로스 플랫폼 배포 유연성
- 광범위한 문서화 및 직접적인 엔지니어링 지원
- 다른 문서 처리 도구와의 통합
EvoPdf를 선택해야 할 때:
- 낮은 라이선스 비용의 HTML to PDF 변환
- 애플리케이션당 라이선스(배포) 또는 회사 전체 라이선스(회사), 회사 라이선스에 포함된 자체 애플리케이션 내 재배포
- 개발자를 계산하지 않는 라이선스
- Chromium 기반 엔진(EvoPdf Next)과 .NET Framework 2.0 이상을 지원하는 Windows 엔진(EvoPdf Classic) 중 선택
결정은 궁극적으로 특정 요구 사항, 예산 제약 및 장기적인 확장 가능성 요구에 따라 달라집니다. 생성 이상의 PDF 조작이 필요한 현대 애플리케이션의 경우, 내장 수정 및 DOCX 변환과 같은 IronPDF의 기능 세트와 문서화는 강력한 투자입니다. EvoPdf도 회사 전체 라이선스와 포함된 재배포를 중요시하는 팀에게는 유효한 옵션입니다.
IronPDF의 30일 무료 평가판으로 시작하여 특정 사용 사례에서의 기능을 평가하는 것을 고려해 보세요. 평가판은 IronPDF 기능에 대한 접근을 제공하여 실제 환경에서의 성능을 기반으로 정보에 입각한 결정을 내릴 수 있게 합니다.
PDF 라이브러리의 실제 비용은 라이선스 가격을 넘어 개발 시간, 유지보수 오버헤드 및 애플리케이션이 성장함에 따라 추가 기능의 필요성을 고려해야 한다는 점을 기억하세요. 현재의 요구를 충족할 뿐만 아니라 미래의 요구에 맞춰 확장할 수 있는 라이브러리를 선택하세요.
제이콥 멜러는 Iron Software의 최고 기술 책임자(CTO)이자 C# PDF 기술을 개척한 선구적인 엔지니어입니다. Iron Software의 핵심 코드베이스를 최초로 개발한 그는 창립 초기부터 회사의 제품 아키텍처를 설계해 왔으며, CEO인 캐머런 리밍턴과 함께 회사를 NASA, 테슬라, 그리고 전 세계 정부 기관에 서비스를 제공하는 50명 이상의 직원을 보유한 기업으로 성장시켰습니다.