IRONPDF 사용 How to Generate a PDF in ASP.NET Using C# 커티스 차우 업데이트됨:10월 26, 2025 다운로드 IronPDF NuGet 다운로드 DLL 다운로드 윈도우 설치 프로그램 무료 체험 시작하기 LLM용 사본 LLM용 사본 LLM용 마크다운 형식으로 페이지를 복사하세요 ChatGPT에서 열기 ChatGPT에 이 페이지에 대해 문의하세요 제미니에서 열기 제미니에게 이 페이지에 대해 문의하세요 Grok에서 열기 Grok에게 이 페이지에 대해 문의하세요 혼란 속에서 열기 Perplexity에게 이 페이지에 대해 문의하세요 공유하다 페이스북에 공유하기 트위터에 공유하기 LinkedIn에 공유하기 URL 복사 이메일로 기사 보내기 Generating PDFs programmatically is a critical requirement for modern web applications, whether you're creating invoices, reports, certificates, or tickets. If you're an ASP.NET Core .NET developer looking to implement robust PDF generation with pixel-perfect rendering and enterprise features, you're in the right place. This comprehensive guide will walk you through professional PDF file generation using IronPDF, a powerful .NET library that enables the creation of PDF documents with ease. We'll explore everything from basic setup to advanced batch processing, showcasing how efficient and easily integrated it can be. By the end, you’ll have a powerful PDF converter for generating PDF documents in your ASP.NET app using IronPDF like a pro. Why Generate PDFs in ASP.NET Core? Server-side PDF generation offers significant advantages over client-side alternatives. When you generate PDFs on the server, you ensure consistent output across all browsers and devices, eliminate dependency on client-side resources, and maintain better control over sensitive data. Common business scenarios for HTML to PDF conversion include: Financial documents: Invoices, statements, and transaction receipts Compliance reports: Regulatory filings and audit documentation User certificates: Training completions and professional certifications Event tickets: QR-coded entry passes and boarding passes Data exports: Analytics reports and dashboard snapshots Furthermore, the server-side approach ensures that the PDFs are consistent across all browsers and operating systems. Making it highly regarded in terms of legal and financial documents. How Does IronPDF Transform Your PDF Generation? IronPDF is a PDF library that stands out in the .NET ecosystem thanks to its HTML converter, which uses a complete Chrome rendering engine under the hood. This means your PDF documents render exactly as they would in Google Chrome, with full support for modern CSS3, JavaScript execution, and web fonts. Unlike other libraries, IronPDF enables you to directly convert your existing HTML, CSS, and JavaScript knowledge into PDF generation capabilities within your ASP.NET Core applications. Key advantages that transform your development: Chrome-based rendering for pixel-perfect accuracy, matching what you see in the browser when carrying out HTML to PDF conversion tasks (often in just a few lines of code) Full HTML5, CSS3, and JavaScript support, including modern frameworks like Bootstrap Native .NET integration without external dependencies or command-line tools Cross-platform compatibility supporting .NET 6/7/8, .NET Core, and even .NET Framework 4.6.2+ for legacy applications Comprehensive API for post-generation manipulation, including merging, watermarking, and digital signatures of your PDF pages. NuGet을 사용하여 설치하세요 PM > Install-Package IronPdf 빠른 설치를 원하시면 NuGet 에서 https://www.NuGet.org/packages/IronPdf를 검색해 보세요. 1천만 건 이상의 다운로드를 기록하며 C#을 이용한 PDF 개발 방식을 혁신하고 있습니다. DLL 파일 이나 윈도우 설치 프로그램을 다운로드할 수도 있습니다. Setting Up Your ASP.NET Core Project Let's create a new ASP.NET Core MVC application configured for PDF generation. We'll build a production-ready setup with proper dependency injection and error handling. This will be a new .NET project in Visual Studio; however, you are also welcome to use an existing project. Creating the Project We will create this project and give it a fitting project name by running the following command: dotnet new mvc -n PdfGeneratorApp cd PdfGeneratorApp Installing IronPDF Before we begin generating PDF documents in ASP.NET, add IronPDF to your project by running the following lines in the NuGet Package Manager Console: Install-Package IronPdf Install-Package IronPdf.Extensions.Mvc.Core For detailed installation options, including NuGet packages configuration and Windows installer, refer to the official documentation. Configuring Services in Program.cs using IronPdf; using IronPdf.Extensions.Mvc.Core; using Microsoft.AspNetCore.Mvc.ViewFeatures; var builder = WebApplication.CreateBuilder(args); // Configure IronPDF license (use your license key) License.LicenseKey = "your-license-key"; // Add MVC services builder.Services.AddControllersWithViews(); // Register IronPDF services builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); builder.Services.AddSingleton<ITempDataProvider, CookieTempDataProvider>(); builder.Services.AddSingleton<IRazorViewRenderer, RazorViewRenderer>(); // Configure ChromePdfRenderer as a service builder.Services.AddSingleton<ChromePdfRenderer>(provider => { var renderer = new ChromePdfRenderer(); // Configure rendering options renderer.RenderingOptions.MarginTop = 25; renderer.RenderingOptions.MarginBottom = 25; renderer.RenderingOptions.MarginLeft = 20; renderer.RenderingOptions.MarginRight = 20; renderer.RenderingOptions.EnableJavaScript = true; renderer.RenderingOptions.RenderDelay = 500; // Wait for JS execution return renderer; }); var app = builder.Build(); // Configure middleware pipeline if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run(); using IronPdf; using IronPdf.Extensions.Mvc.Core; using Microsoft.AspNetCore.Mvc.ViewFeatures; var builder = WebApplication.CreateBuilder(args); // Configure IronPDF license (use your license key) License.LicenseKey = "your-license-key"; // Add MVC services builder.Services.AddControllersWithViews(); // Register IronPDF services builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); builder.Services.AddSingleton<ITempDataProvider, CookieTempDataProvider>(); builder.Services.AddSingleton<IRazorViewRenderer, RazorViewRenderer>(); // Configure ChromePdfRenderer as a service builder.Services.AddSingleton<ChromePdfRenderer>(provider => { var renderer = new ChromePdfRenderer(); // Configure rendering options renderer.RenderingOptions.MarginTop = 25; renderer.RenderingOptions.MarginBottom = 25; renderer.RenderingOptions.MarginLeft = 20; renderer.RenderingOptions.MarginRight = 20; renderer.RenderingOptions.EnableJavaScript = true; renderer.RenderingOptions.RenderDelay = 500; // Wait for JS execution return renderer; }); var app = builder.Build(); // Configure middleware pipeline if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run(); $vbLabelText $csharpLabel This configuration establishes IronPDF as a singleton service, ensuring efficient resource usage across your application. You can further modify the `RenderingOptions` to suit your specific requirements. At this point, your folder structure in the Solution Explorer in Visual Studio should look like: Generating PDFs from Razor Views The most powerful approach to generating new PDF documents in ASP.NET Core involves leveraging Razor views for PDF conversion. This allows you to utilize familiar MVC patterns, strongly typed models, and Razor syntax to create dynamic PDFs from custom HTML files, web pages, and other sources. According to Microsoft's documentation on Razor Pages, this approach provides the cleanest separation of concerns for page-focused scenarios. Creating the Data Model First, let's create a comprehensive model that represents a typical business document: namespace PdfGeneratorApp.Models { public class InvoiceModel { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public CompanyInfo Vendor { get; set; } public CompanyInfo Customer { get; set; } public List<InvoiceItem> Items { get; set; } public decimal Subtotal => Items?.Sum(x => x.Total) ?? 0; public decimal TaxRate { get; set; } public decimal TaxAmount => Subtotal * (TaxRate / 100); public decimal Total => Subtotal + TaxAmount; public string Notes { get; set; } public string PaymentTerms { get; set; } } public class CompanyInfo { public string Name { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } public string Email { get; set; } public string Phone { get; set; } } public class InvoiceItem { public string Description { get; set; } public int Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total => Quantity * UnitPrice; } } namespace PdfGeneratorApp.Models { public class InvoiceModel { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public CompanyInfo Vendor { get; set; } public CompanyInfo Customer { get; set; } public List<InvoiceItem> Items { get; set; } public decimal Subtotal => Items?.Sum(x => x.Total) ?? 0; public decimal TaxRate { get; set; } public decimal TaxAmount => Subtotal * (TaxRate / 100); public decimal Total => Subtotal + TaxAmount; public string Notes { get; set; } public string PaymentTerms { get; set; } } public class CompanyInfo { public string Name { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } public string Email { get; set; } public string Phone { get; set; } } public class InvoiceItem { public string Description { get; set; } public int Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total => Quantity * UnitPrice; } } $vbLabelText $csharpLabel Building the Razor View Create a view at Views/Invoice/InvoiceTemplate.cshtml that renders your PDF content: @model PdfGeneratorApp.Models.InvoiceModel @{ Layout = null; // PDFs should not use the site layout } <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Invoice @Model.InvoiceNumber</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: #333; } .invoice-container { max-width: 800px; margin: 0 auto; padding: 30px; } .invoice-header { display: flex; justify-content: space-between; margin-bottom: 40px; padding-bottom: 20px; border-bottom: 2px solid #2c3e50; } .company-details { flex: 1; } .company-details h1 { color: #2c3e50; margin-bottom: 10px; } .invoice-details { text-align: right; } .invoice-details h2 { color: #2c3e50; font-size: 28px; margin-bottom: 10px; } .invoice-details p { margin: 5px 0; font-size: 14px; } .billing-details { display: flex; justify-content: space-between; margin-bottom: 40px; } .billing-section { flex: 1; } .billing-section h3 { color: #2c3e50; margin-bottom: 10px; font-size: 16px; text-transform: uppercase; } .items-table { width: 100%; border-collapse: collapse; margin-bottom: 40px; } .items-table thead { background-color: #2c3e50; color: white; } .items-table th, .items-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; } .items-table tbody tr:hover { background-color: #f5f5f5; } .text-right { text-align: right; } .invoice-summary { display: flex; justify-content: flex-end; margin-bottom: 40px; } .summary-table { width: 300px; } .summary-table tr { border-bottom: 1px solid #ddd; } .summary-table td { padding: 8px; } .summary-table .total-row { font-weight: bold; font-size: 18px; color: #2c3e50; border-top: 2px solid #2c3e50; } .invoice-footer { margin-top: 60px; padding-top: 20px; border-top: 1px solid #ddd; } .footer-notes { margin-bottom: 20px; } .footer-notes h4 { color: #2c3e50; margin-bottom: 10px; } @@media print { .invoice-container { padding: 0; } } </style> </head> <body> <div class="invoice-container"> <div class="invoice-header"> <div class="company-details"> <h1>@Model.Vendor.Name</h1> <p>@Model.Vendor.Address</p> <p>@Model.Vendor.City, @Model.Vendor.State @Model.Vendor.ZipCode</p> <p>Email: @Model.Vendor.Email</p> <p>Phone: @Model.Vendor.Phone</p> </div> <div class="invoice-details"> <h2>INVOICE</h2> <p><strong>Invoice #:</strong> @Model.InvoiceNumber</p> <p><strong>Date:</strong> @Model.InvoiceDate.ToString("MMM dd, yyyy")</p> <p><strong>Due Date:</strong> @Model.DueDate.ToString("MMM dd, yyyy")</p> </div> </div> <div class="billing-details"> <div class="billing-section"> <h3>Bill To:</h3> <p><strong>@Model.Customer.Name</strong></p> <p>@Model.Customer.Address</p> <p>@Model.Customer.City, @Model.Customer.State @Model.Customer.ZipCode</p> <p>@Model.Customer.Email</p> </div> </div> <table class="items-table"> <thead> <tr> <th>Description</th> <th class="text-right">Quantity</th> <th class="text-right">Unit Price</th> <th class="text-right">Total</th> </tr> </thead> <tbody> @foreach (var item in Model.Items) { <tr> <td>@item.Description</td> <td class="text-right">@item.Quantity</td> <td class="text-right">@item.UnitPrice.ToString("C")</td> <td class="text-right">@item.Total.ToString("C")</td> </tr> } </tbody> </table> <div class="invoice-summary"> <table class="summary-table"> <tr> <td>Subtotal:</td> <td class="text-right">@Model.Subtotal.ToString("C")</td> </tr> <tr> <td>Tax (@Model.TaxRate%):</td> <td class="text-right">@Model.TaxAmount.ToString("C")</td> </tr> <tr class="total-row"> <td>Total:</td> <td class="text-right">@Model.Total.ToString("C")</td> </tr> </table> </div> @if (!string.IsNullOrEmpty(Model.Notes)) { <div class="invoice-footer"> <div class="footer-notes"> <h4>Notes</h4> <p>@Model.Notes</p> </div> </div> } @if (!string.IsNullOrEmpty(Model.PaymentTerms)) { <div class="footer-notes"> <h4>Payment Terms</h4> <p>@Model.PaymentTerms</p> </div> } </div> </body> </html> @model PdfGeneratorApp.Models.InvoiceModel @{ Layout = null; // PDFs should not use the site layout } <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Invoice @Model.InvoiceNumber</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: #333; } .invoice-container { max-width: 800px; margin: 0 auto; padding: 30px; } .invoice-header { display: flex; justify-content: space-between; margin-bottom: 40px; padding-bottom: 20px; border-bottom: 2px solid #2c3e50; } .company-details { flex: 1; } .company-details h1 { color: #2c3e50; margin-bottom: 10px; } .invoice-details { text-align: right; } .invoice-details h2 { color: #2c3e50; font-size: 28px; margin-bottom: 10px; } .invoice-details p { margin: 5px 0; font-size: 14px; } .billing-details { display: flex; justify-content: space-between; margin-bottom: 40px; } .billing-section { flex: 1; } .billing-section h3 { color: #2c3e50; margin-bottom: 10px; font-size: 16px; text-transform: uppercase; } .items-table { width: 100%; border-collapse: collapse; margin-bottom: 40px; } .items-table thead { background-color: #2c3e50; color: white; } .items-table th, .items-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; } .items-table tbody tr:hover { background-color: #f5f5f5; } .text-right { text-align: right; } .invoice-summary { display: flex; justify-content: flex-end; margin-bottom: 40px; } .summary-table { width: 300px; } .summary-table tr { border-bottom: 1px solid #ddd; } .summary-table td { padding: 8px; } .summary-table .total-row { font-weight: bold; font-size: 18px; color: #2c3e50; border-top: 2px solid #2c3e50; } .invoice-footer { margin-top: 60px; padding-top: 20px; border-top: 1px solid #ddd; } .footer-notes { margin-bottom: 20px; } .footer-notes h4 { color: #2c3e50; margin-bottom: 10px; } @@media print { .invoice-container { padding: 0; } } </style> </head> <body> <div class="invoice-container"> <div class="invoice-header"> <div class="company-details"> <h1>@Model.Vendor.Name</h1> <p>@Model.Vendor.Address</p> <p>@Model.Vendor.City, @Model.Vendor.State @Model.Vendor.ZipCode</p> <p>Email: @Model.Vendor.Email</p> <p>Phone: @Model.Vendor.Phone</p> </div> <div class="invoice-details"> <h2>INVOICE</h2> <p><strong>Invoice #:</strong> @Model.InvoiceNumber</p> <p><strong>Date:</strong> @Model.InvoiceDate.ToString("MMM dd, yyyy")</p> <p><strong>Due Date:</strong> @Model.DueDate.ToString("MMM dd, yyyy")</p> </div> </div> <div class="billing-details"> <div class="billing-section"> <h3>Bill To:</h3> <p><strong>@Model.Customer.Name</strong></p> <p>@Model.Customer.Address</p> <p>@Model.Customer.City, @Model.Customer.State @Model.Customer.ZipCode</p> <p>@Model.Customer.Email</p> </div> </div> <table class="items-table"> <thead> <tr> <th>Description</th> <th class="text-right">Quantity</th> <th class="text-right">Unit Price</th> <th class="text-right">Total</th> </tr> </thead> <tbody> @foreach (var item in Model.Items) { <tr> <td>@item.Description</td> <td class="text-right">@item.Quantity</td> <td class="text-right">@item.UnitPrice.ToString("C")</td> <td class="text-right">@item.Total.ToString("C")</td> </tr> } </tbody> </table> <div class="invoice-summary"> <table class="summary-table"> <tr> <td>Subtotal:</td> <td class="text-right">@Model.Subtotal.ToString("C")</td> </tr> <tr> <td>Tax (@Model.TaxRate%):</td> <td class="text-right">@Model.TaxAmount.ToString("C")</td> </tr> <tr class="total-row"> <td>Total:</td> <td class="text-right">@Model.Total.ToString("C")</td> </tr> </table> </div> @if (!string.IsNullOrEmpty(Model.Notes)) { <div class="invoice-footer"> <div class="footer-notes"> <h4>Notes</h4> <p>@Model.Notes</p> </div> </div> } @if (!string.IsNullOrEmpty(Model.PaymentTerms)) { <div class="footer-notes"> <h4>Payment Terms</h4> <p>@Model.PaymentTerms</p> </div> } </div> </body> </html> $vbLabelText $csharpLabel Implementing the Controller with Error Handling Now let's create a robust controller that generates PDFs with comprehensive error handling: using IronPdf; using IronPdf.Extensions.Mvc.Core; using Microsoft.AspNetCore.Mvc; using PdfGeneratorApp.Models; using System.Diagnostics; namespace PdfGeneratorApp.Controllers { public class InvoiceController : Controller { private readonly ILogger<InvoiceController> _logger; private readonly IRazorViewRenderer _viewRenderer; private readonly ChromePdfRenderer _pdfRenderer; private readonly IWebHostEnvironment _environment; public InvoiceController( ILogger<InvoiceController> logger, IRazorViewRenderer viewRenderer, ChromePdfRenderer pdfRenderer, IWebHostEnvironment environment) { _logger = logger; _viewRenderer = viewRenderer; _pdfRenderer = pdfRenderer; _environment = environment; } [HttpGet] public IActionResult Index() { // Display a form or list of invoices return View(); } [HttpGet] public async Task<IActionResult> GenerateInvoice(string invoiceNumber) { var stopwatch = Stopwatch.StartNew(); try { // Validate input if (string.IsNullOrEmpty(invoiceNumber)) { _logger.LogWarning("Invoice generation attempted without invoice number"); return BadRequest("Invoice number is required"); } // Generate sample data (in production, fetch from database) var invoice = CreateSampleInvoice(invoiceNumber); // Log the generation attempt _logger.LogInformation($"Generating PDF for invoice {invoiceNumber}"); // Configure PDF rendering options _pdfRenderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait; _pdfRenderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4; _pdfRenderer.RenderingOptions.PrintHtmlBackgrounds = true; _pdfRenderer.RenderingOptions.CreatePdfFormsFromHtml = false; // Add custom header with page numbers _pdfRenderer.RenderingOptions.TextHeader = new TextHeaderFooter { CenterText = $"Invoice {invoice.InvoiceNumber}", DrawDividerLine = true, Font = IronSoftware.Drawing.FontTypes.Helvetica, FontSize = 10 }; // Add footer with page numbers _pdfRenderer.RenderingOptions.TextFooter = new TextHeaderFooter { LeftText = "{date} {time}", RightText = "Page {page} of {total-pages}", DrawDividerLine = true, Font = IronSoftware.Drawing.FontTypes.Helvetica, FontSize = 8 }; // Render the view to PDF PdfDocument pdf; try { pdf = _pdfRenderer.RenderRazorViewToPdf( _viewRenderer, "Views/Invoice/InvoiceTemplate.cshtml", invoice); } catch (Exception renderEx) { _logger.LogError(renderEx, "Failed to render Razor view to PDF"); throw new InvalidOperationException("PDF rendering failed. Please check the template.", renderEx); } // Apply metadata pdf.MetaData.Author = "PdfGeneratorApp"; pdf.MetaData.Title = $"Invoice {invoice.InvoiceNumber}"; pdf.MetaData.Subject = $"Invoice for {invoice.Customer.Name}"; pdf.MetaData.Keywords = "invoice, billing, payment"; pdf.MetaData.CreationDate = DateTime.UtcNow; pdf.MetaData.ModifiedDate = DateTime.UtcNow; // Optional: Add password protection // pdf.SecuritySettings.UserPassword = "user123"; // pdf.SecuritySettings.OwnerPassword = "owner456"; // pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights; // Log performance metrics stopwatch.Stop(); _logger.LogInformation($"PDF generated successfully for invoice {invoiceNumber} in {stopwatch.ElapsedMilliseconds}ms"); // Return the PDF file Response.Headers.Add("Content-Disposition", $"inline; filename=Invoice_{invoiceNumber}.pdf"); return File(pdf.BinaryData, "application/pdf", $"Invoice_{invoiceNumber}.pdf"); } catch (Exception ex) { _logger.LogError(ex, $"Error generating PDF for invoice {invoiceNumber}"); // In development, return detailed error if (_environment.IsDevelopment()) { return StatusCode(500, new { error = "PDF generation failed", message = ex.Message, stackTrace = ex.StackTrace }); } // In production, return generic error return StatusCode(500, "An error occurred while generating the PDF"); } } private InvoiceModel CreateSampleInvoice(string invoiceNumber) { return new InvoiceModel { InvoiceNumber = invoiceNumber, InvoiceDate = DateTime.Now, DueDate = DateTime.Now.AddDays(30), Vendor = new CompanyInfo { Name = "Tech Solutions Inc.", Address = "123 Business Ave", City = "New York", State = "NY", ZipCode = "10001", Email = "billing@techsolutions.com", Phone = "(555) 123-4567" }, Customer = new CompanyInfo { Name = "Acme Corporation", Address = "456 Commerce St", City = "Los Angeles", State = "CA", ZipCode = "90001", Email = "accounts@acmecorp.com", Phone = "(555) 987-6543" }, Items = new List<InvoiceItem> { new InvoiceItem { Description = "Software Development Services - 40 hours", Quantity = 40, UnitPrice = 150.00m }, new InvoiceItem { Description = "Project Management - 10 hours", Quantity = 10, UnitPrice = 120.00m }, new InvoiceItem { Description = "Quality Assurance Testing", Quantity = 1, UnitPrice = 2500.00m } }, TaxRate = 8.875m, Notes = "Payment is due within 30 days. Late payments subject to 1.5% monthly interest.", PaymentTerms = "Net 30" }; } } } using IronPdf; using IronPdf.Extensions.Mvc.Core; using Microsoft.AspNetCore.Mvc; using PdfGeneratorApp.Models; using System.Diagnostics; namespace PdfGeneratorApp.Controllers { public class InvoiceController : Controller { private readonly ILogger<InvoiceController> _logger; private readonly IRazorViewRenderer _viewRenderer; private readonly ChromePdfRenderer _pdfRenderer; private readonly IWebHostEnvironment _environment; public InvoiceController( ILogger<InvoiceController> logger, IRazorViewRenderer viewRenderer, ChromePdfRenderer pdfRenderer, IWebHostEnvironment environment) { _logger = logger; _viewRenderer = viewRenderer; _pdfRenderer = pdfRenderer; _environment = environment; } [HttpGet] public IActionResult Index() { // Display a form or list of invoices return View(); } [HttpGet] public async Task<IActionResult> GenerateInvoice(string invoiceNumber) { var stopwatch = Stopwatch.StartNew(); try { // Validate input if (string.IsNullOrEmpty(invoiceNumber)) { _logger.LogWarning("Invoice generation attempted without invoice number"); return BadRequest("Invoice number is required"); } // Generate sample data (in production, fetch from database) var invoice = CreateSampleInvoice(invoiceNumber); // Log the generation attempt _logger.LogInformation($"Generating PDF for invoice {invoiceNumber}"); // Configure PDF rendering options _pdfRenderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait; _pdfRenderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4; _pdfRenderer.RenderingOptions.PrintHtmlBackgrounds = true; _pdfRenderer.RenderingOptions.CreatePdfFormsFromHtml = false; // Add custom header with page numbers _pdfRenderer.RenderingOptions.TextHeader = new TextHeaderFooter { CenterText = $"Invoice {invoice.InvoiceNumber}", DrawDividerLine = true, Font = IronSoftware.Drawing.FontTypes.Helvetica, FontSize = 10 }; // Add footer with page numbers _pdfRenderer.RenderingOptions.TextFooter = new TextHeaderFooter { LeftText = "{date} {time}", RightText = "Page {page} of {total-pages}", DrawDividerLine = true, Font = IronSoftware.Drawing.FontTypes.Helvetica, FontSize = 8 }; // Render the view to PDF PdfDocument pdf; try { pdf = _pdfRenderer.RenderRazorViewToPdf( _viewRenderer, "Views/Invoice/InvoiceTemplate.cshtml", invoice); } catch (Exception renderEx) { _logger.LogError(renderEx, "Failed to render Razor view to PDF"); throw new InvalidOperationException("PDF rendering failed. Please check the template.", renderEx); } // Apply metadata pdf.MetaData.Author = "PdfGeneratorApp"; pdf.MetaData.Title = $"Invoice {invoice.InvoiceNumber}"; pdf.MetaData.Subject = $"Invoice for {invoice.Customer.Name}"; pdf.MetaData.Keywords = "invoice, billing, payment"; pdf.MetaData.CreationDate = DateTime.UtcNow; pdf.MetaData.ModifiedDate = DateTime.UtcNow; // Optional: Add password protection // pdf.SecuritySettings.UserPassword = "user123"; // pdf.SecuritySettings.OwnerPassword = "owner456"; // pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights; // Log performance metrics stopwatch.Stop(); _logger.LogInformation($"PDF generated successfully for invoice {invoiceNumber} in {stopwatch.ElapsedMilliseconds}ms"); // Return the PDF file Response.Headers.Add("Content-Disposition", $"inline; filename=Invoice_{invoiceNumber}.pdf"); return File(pdf.BinaryData, "application/pdf", $"Invoice_{invoiceNumber}.pdf"); } catch (Exception ex) { _logger.LogError(ex, $"Error generating PDF for invoice {invoiceNumber}"); // In development, return detailed error if (_environment.IsDevelopment()) { return StatusCode(500, new { error = "PDF generation failed", message = ex.Message, stackTrace = ex.StackTrace }); } // In production, return generic error return StatusCode(500, "An error occurred while generating the PDF"); } } private InvoiceModel CreateSampleInvoice(string invoiceNumber) { return new InvoiceModel { InvoiceNumber = invoiceNumber, InvoiceDate = DateTime.Now, DueDate = DateTime.Now.AddDays(30), Vendor = new CompanyInfo { Name = "Tech Solutions Inc.", Address = "123 Business Ave", City = "New York", State = "NY", ZipCode = "10001", Email = "billing@techsolutions.com", Phone = "(555) 123-4567" }, Customer = new CompanyInfo { Name = "Acme Corporation", Address = "456 Commerce St", City = "Los Angeles", State = "CA", ZipCode = "90001", Email = "accounts@acmecorp.com", Phone = "(555) 987-6543" }, Items = new List<InvoiceItem> { new InvoiceItem { Description = "Software Development Services - 40 hours", Quantity = 40, UnitPrice = 150.00m }, new InvoiceItem { Description = "Project Management - 10 hours", Quantity = 10, UnitPrice = 120.00m }, new InvoiceItem { Description = "Quality Assurance Testing", Quantity = 1, UnitPrice = 2500.00m } }, TaxRate = 8.875m, Notes = "Payment is due within 30 days. Late payments subject to 1.5% monthly interest.", PaymentTerms = "Net 30" }; } } } $vbLabelText $csharpLabel Code Explanation To test and run the PDF generator above, launch the project and enter the following URL: https://localhost:[port]/Invoice/GenerateInvoice?invoiceNumber=055 to generate the invoice. Please remember to replace the port with the actual port number on which you hosted the application. Output How to Return PDFs from Web API Endpoints for Maximum Efficiency? For applications that require serving PDFs through RESTful APIs, here's how to implement efficient PDF delivery with proper memory management. This approach is particularly useful when building microservices or when you need to generate PDFs in ASP.NET Core Web API controllers: using Microsoft.AspNetCore.Mvc; using IronPdf; using System.IO; namespace PdfGeneratorApp.Controllers.Api { [ApiController] [Route("api/[controller]")] public class PdfApiController : ControllerBase { private readonly ChromePdfRenderer _pdfRenderer; private readonly ILogger<PdfApiController> _logger; public PdfApiController( ChromePdfRenderer pdfRenderer, ILogger<PdfApiController> logger) { _pdfRenderer = pdfRenderer; _logger = logger; } [HttpPost("generate-report")] public async Task<IActionResult> GenerateReport([FromBody] ReportRequest request) { try { // Validate request if (!ModelState.IsValid) { return BadRequest(ModelState); } // Build HTML content dynamically var htmlContent = BuildReportHtml(request); // Generate PDF with memory-efficient streaming using var pdfDocument = _pdfRenderer.RenderHtmlAsPdf(htmlContent); // Apply compression for smaller file size pdfDocument.CompressImages(60); // 60% quality // Stream the PDF directly to response var stream = new MemoryStream(); pdfDocument.SaveAs(stream); stream.Position = 0; _logger.LogInformation($"Report generated for {request.ReportType}"); return new FileStreamResult(stream, "application/pdf") { FileDownloadName = $"Report_{DateTime.Now:yyyyMMdd_HHmmss}.pdf" }; } catch (Exception ex) { _logger.LogError(ex, "Failed to generate report"); return StatusCode(500, new { error = "Report generation failed" }); } } [HttpGet("download/{documentId}")] public async Task<IActionResult> DownloadDocument(string documentId) { try { // In production, retrieve document from database or storage var documentPath = Path.Combine("wwwroot", "documents", $"{documentId}.pdf"); if (!System.IO.File.Exists(documentPath)) { return NotFound(new { error = "Document not found" }); } var memory = new MemoryStream(); using (var stream = new FileStream(documentPath, FileMode.Open)) { await stream.CopyToAsync(memory); } memory.Position = 0; return File(memory, "application/pdf", $"Document_{documentId}.pdf"); } catch (Exception ex) { _logger.LogError(ex, $"Failed to download document {documentId}"); return StatusCode(500, new { error = "Download failed" }); } } private string BuildReportHtml(ReportRequest request) { return $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; margin: 40px; }} h1 {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }} .report-date {{ color: #7f8c8d; font-size: 14px; }} .data-table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }} .data-table th, .data-table td {{ border: 1px solid #ddd; padding: 12px; text-align: left; }} .data-table th {{ background-color: #3498db; color: white; }} </style> </head> <body> <h1>{request.ReportType} Report</h1> <p class='report-date'>Generated: {DateTime.Now:MMMM dd, yyyy HH:mm}</p> <p>{request.Description}</p> {GenerateDataTable(request.Data)} </body> </html>"; } private string GenerateDataTable(List<ReportDataItem> data) { if (data == null || !data.Any()) return "<p>No data available</p>"; var table = "<table class='data-table'><thead><tr>"; // Add headers foreach (var prop in typeof(ReportDataItem).GetProperties()) { table += $"<th>{prop.Name}</th>"; } table += "</tr></thead><tbody>"; // Add data rows foreach (var item in data) { table += "<tr>"; foreach (var prop in typeof(ReportDataItem).GetProperties()) { var value = prop.GetValue(item) ?? ""; table += $"<td>{value}</td>"; } table += "</tr>"; } table += "</tbody></table>"; return table; } } public class ReportRequest { public string ReportType { get; set; } public string Description { get; set; } public List<ReportDataItem> Data { get; set; } } public class ReportDataItem { public string Name { get; set; } public string Category { get; set; } public decimal Value { get; set; } public DateTime Date { get; set; } } } using Microsoft.AspNetCore.Mvc; using IronPdf; using System.IO; namespace PdfGeneratorApp.Controllers.Api { [ApiController] [Route("api/[controller]")] public class PdfApiController : ControllerBase { private readonly ChromePdfRenderer _pdfRenderer; private readonly ILogger<PdfApiController> _logger; public PdfApiController( ChromePdfRenderer pdfRenderer, ILogger<PdfApiController> logger) { _pdfRenderer = pdfRenderer; _logger = logger; } [HttpPost("generate-report")] public async Task<IActionResult> GenerateReport([FromBody] ReportRequest request) { try { // Validate request if (!ModelState.IsValid) { return BadRequest(ModelState); } // Build HTML content dynamically var htmlContent = BuildReportHtml(request); // Generate PDF with memory-efficient streaming using var pdfDocument = _pdfRenderer.RenderHtmlAsPdf(htmlContent); // Apply compression for smaller file size pdfDocument.CompressImages(60); // 60% quality // Stream the PDF directly to response var stream = new MemoryStream(); pdfDocument.SaveAs(stream); stream.Position = 0; _logger.LogInformation($"Report generated for {request.ReportType}"); return new FileStreamResult(stream, "application/pdf") { FileDownloadName = $"Report_{DateTime.Now:yyyyMMdd_HHmmss}.pdf" }; } catch (Exception ex) { _logger.LogError(ex, "Failed to generate report"); return StatusCode(500, new { error = "Report generation failed" }); } } [HttpGet("download/{documentId}")] public async Task<IActionResult> DownloadDocument(string documentId) { try { // In production, retrieve document from database or storage var documentPath = Path.Combine("wwwroot", "documents", $"{documentId}.pdf"); if (!System.IO.File.Exists(documentPath)) { return NotFound(new { error = "Document not found" }); } var memory = new MemoryStream(); using (var stream = new FileStream(documentPath, FileMode.Open)) { await stream.CopyToAsync(memory); } memory.Position = 0; return File(memory, "application/pdf", $"Document_{documentId}.pdf"); } catch (Exception ex) { _logger.LogError(ex, $"Failed to download document {documentId}"); return StatusCode(500, new { error = "Download failed" }); } } private string BuildReportHtml(ReportRequest request) { return $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; margin: 40px; }} h1 {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }} .report-date {{ color: #7f8c8d; font-size: 14px; }} .data-table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }} .data-table th, .data-table td {{ border: 1px solid #ddd; padding: 12px; text-align: left; }} .data-table th {{ background-color: #3498db; color: white; }} </style> </head> <body> <h1>{request.ReportType} Report</h1> <p class='report-date'>Generated: {DateTime.Now:MMMM dd, yyyy HH:mm}</p> <p>{request.Description}</p> {GenerateDataTable(request.Data)} </body> </html>"; } private string GenerateDataTable(List<ReportDataItem> data) { if (data == null || !data.Any()) return "<p>No data available</p>"; var table = "<table class='data-table'><thead><tr>"; // Add headers foreach (var prop in typeof(ReportDataItem).GetProperties()) { table += $"<th>{prop.Name}</th>"; } table += "</tr></thead><tbody>"; // Add data rows foreach (var item in data) { table += "<tr>"; foreach (var prop in typeof(ReportDataItem).GetProperties()) { var value = prop.GetValue(item) ?? ""; table += $"<td>{value}</td>"; } table += "</tr>"; } table += "</tbody></table>"; return table; } } public class ReportRequest { public string ReportType { get; set; } public string Description { get; set; } public List<ReportDataItem> Data { get; set; } } public class ReportDataItem { public string Name { get; set; } public string Category { get; set; } public decimal Value { get; set; } public DateTime Date { get; set; } } } $vbLabelText $csharpLabel Adding Professional Headers, Footers, and Styling Professional PDFs require consistent headers, footers, and styling. IronPDF provides both simple text-based and advanced HTML-based options. By using CSS styles to style the HTML markup, we can create custom PDF headers and footers. The following code snippet explores how we could use this in our current project: using IronPdf; using IronPdf.Extensions.Mvc.Core; using Microsoft.AspNetCore.Mvc; using PdfGeneratorApp.Models; using PdfGeneratorApp.Services; using System.Diagnostics; namespace PdfGeneratorApp.Controllers { public class InvoiceController : Controller { private readonly ILogger<InvoiceController> _logger; private readonly IRazorViewRenderer _viewRenderer; private readonly ChromePdfRenderer _pdfRenderer; private readonly PdfFormattingService _pdfFormattingService; private readonly IWebHostEnvironment _environment; public InvoiceController( ILogger<InvoiceController> logger, IRazorViewRenderer viewRenderer, ChromePdfRenderer pdfRenderer, PdfFormattingService pdfFormattingService, IWebHostEnvironment environment) { _logger = logger; _viewRenderer = viewRenderer; _pdfRenderer = pdfRenderer; _pdfFormattingService = pdfFormattingService; _environment = environment; } [HttpGet] public IActionResult Index() { // Display a form or list of invoices return View(); } private void ConfigurePdfRendererOptions(ChromePdfRenderer renderer, InvoiceModel invoice, PdfStylingOptions options) { // Margins renderer.RenderingOptions.MarginTop = options.MarginTop; renderer.RenderingOptions.MarginBottom = options.MarginBottom; renderer.RenderingOptions.MarginLeft = options.MarginLeft; renderer.RenderingOptions.MarginRight = options.MarginRight; // Header if (options.UseHtmlHeader) { renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter { MaxHeight = 50, HtmlFragment = $@" <div style='width: 100%; font-size: 12px; font-family: Arial;'> <div style='float: left; width: 50%;'> <img src='https://ironpdf.com/img/products/ironpdf-logo-text-dotnet.svg' height='40' /> </div> <div style='float: right; width: 50%; text-align: right;'> <strong>Invoice {invoice.InvoiceNumber}</strong><br/> Generated: {DateTime.Now:yyyy-MM-dd} </div> </div>", LoadStylesAndCSSFromMainHtmlDocument = true }; } else { renderer.RenderingOptions.TextHeader = new TextHeaderFooter { CenterText = options.HeaderText, Font = IronSoftware.Drawing.FontTypes.Arial, FontSize = 12, DrawDividerLine = true }; } // Footer renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter { MaxHeight = 30, HtmlFragment = @" <div style='width: 100%; font-size: 10px; color: #666;'> <div style='float: left; width: 33%;'> © 2025 Your Company </div> <div style='float: center; width: 33%; text-align: center;'> yourwebsite.com </div> <div style='float: right; width: 33%; text-align: right;'> Page {page} of {total-pages} </div> </div>" }; // Optional: Add watermark here (IronPDF supports adding after PDF is generated, so keep it as-is) // Margins, paper size etc., can also be set here if needed renderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait; renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4; renderer.RenderingOptions.PrintHtmlBackgrounds = true; } [HttpGet] public async Task<IActionResult> GenerateInvoice(string invoiceNumber) { var stopwatch = Stopwatch.StartNew(); try { // Validate input if (string.IsNullOrEmpty(invoiceNumber)) { _logger.LogWarning("Invoice generation attempted without invoice number"); return BadRequest("Invoice number is required"); } // Generate sample data (in production, fetch from database) var invoice = CreateSampleInvoice(invoiceNumber); // Log the generation attempt _logger.LogInformation($"Generating PDF for invoice {invoiceNumber}"); // Configure PDF rendering options _pdfRenderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait; _pdfRenderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4; _pdfRenderer.RenderingOptions.PrintHtmlBackgrounds = true; _pdfRenderer.RenderingOptions.CreatePdfFormsFromHtml = false; var options = new PdfStylingOptions { MarginTop = 25, MarginBottom = 25, MarginLeft = 20, MarginRight = 20, UseHtmlHeader = true, HeaderText = $"Invoice {invoice.InvoiceNumber}", AddWatermark = false, ForcePageBreaks = false }; // Apply the styling to the renderer BEFORE rendering PDF ConfigurePdfRendererOptions(_pdfRenderer, invoice, options); // Render the view to PDF PdfDocument pdf; try { pdf = _pdfRenderer.RenderRazorViewToPdf( _viewRenderer, "Views/Invoice/InvoiceTemplate.cshtml", invoice); } catch (Exception renderEx) { _logger.LogError(renderEx, "Failed to render Razor view to PDF"); throw new InvalidOperationException("PDF rendering failed. Please check the template.", renderEx); } // Apply metadata pdf.MetaData.Author = "PdfGeneratorApp"; pdf.MetaData.Title = $"Invoice {invoice.InvoiceNumber}"; pdf.MetaData.Subject = $"Invoice for {invoice.Customer.Name}"; pdf.MetaData.Keywords = "invoice, billing, payment"; pdf.MetaData.CreationDate = DateTime.UtcNow; pdf.MetaData.ModifiedDate = DateTime.UtcNow; // Optional: Add password protection // pdf.SecuritySettings.UserPassword = "user123"; // pdf.SecuritySettings.OwnerPassword = "owner456"; // pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights; // Log performance metrics stopwatch.Stop(); _logger.LogInformation($"PDF generated successfully for invoice {invoiceNumber} in {stopwatch.ElapsedMilliseconds}ms"); // Return the PDF file Response.Headers.Add("Content-Disposition", $"inline; filename=Invoice_{invoiceNumber}.pdf"); return File(pdf.BinaryData, "application/pdf", $"Invoice_{invoiceNumber}.pdf"); } catch (Exception ex) { _logger.LogError(ex, $"Error generating PDF for invoice {invoiceNumber}"); // In development, return detailed error if (_environment.IsDevelopment()) { return StatusCode(500, new { error = "PDF generation failed", message = ex.Message, stackTrace = ex.StackTrace }); } // In production, return generic error return StatusCode(500, "An error occurred while generating the PDF"); } } private InvoiceModel CreateSampleInvoice(string invoiceNumber) { return new InvoiceModel { InvoiceNumber = invoiceNumber, InvoiceDate = DateTime.Now, DueDate = DateTime.Now.AddDays(30), Vendor = new CompanyInfo { Name = "Tech Solutions Inc.", Address = "123 Business Ave", City = "New York", State = "NY", ZipCode = "10001", Email = "billing@techsolutions.com", Phone = "(555) 123-4567" }, Customer = new CompanyInfo { Name = "Acme Corporation", Address = "456 Commerce St", City = "Los Angeles", State = "CA", ZipCode = "90001", Email = "accounts@acmecorp.com", Phone = "(555) 987-6543" }, Items = new List<InvoiceItem> { new InvoiceItem { Description = "Software Development Services - 40 hours", Quantity = 40, UnitPrice = 150.00m }, new InvoiceItem { Description = "Project Management - 10 hours", Quantity = 10, UnitPrice = 120.00m }, new InvoiceItem { Description = "Quality Assurance Testing", Quantity = 1, UnitPrice = 2500.00m } }, TaxRate = 8.875m, Notes = "Payment is due within 30 days. Late payments subject to 1.5% monthly interest.", PaymentTerms = "Net 30" }; } } } using IronPdf; using IronPdf.Extensions.Mvc.Core; using Microsoft.AspNetCore.Mvc; using PdfGeneratorApp.Models; using PdfGeneratorApp.Services; using System.Diagnostics; namespace PdfGeneratorApp.Controllers { public class InvoiceController : Controller { private readonly ILogger<InvoiceController> _logger; private readonly IRazorViewRenderer _viewRenderer; private readonly ChromePdfRenderer _pdfRenderer; private readonly PdfFormattingService _pdfFormattingService; private readonly IWebHostEnvironment _environment; public InvoiceController( ILogger<InvoiceController> logger, IRazorViewRenderer viewRenderer, ChromePdfRenderer pdfRenderer, PdfFormattingService pdfFormattingService, IWebHostEnvironment environment) { _logger = logger; _viewRenderer = viewRenderer; _pdfRenderer = pdfRenderer; _pdfFormattingService = pdfFormattingService; _environment = environment; } [HttpGet] public IActionResult Index() { // Display a form or list of invoices return View(); } private void ConfigurePdfRendererOptions(ChromePdfRenderer renderer, InvoiceModel invoice, PdfStylingOptions options) { // Margins renderer.RenderingOptions.MarginTop = options.MarginTop; renderer.RenderingOptions.MarginBottom = options.MarginBottom; renderer.RenderingOptions.MarginLeft = options.MarginLeft; renderer.RenderingOptions.MarginRight = options.MarginRight; // Header if (options.UseHtmlHeader) { renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter { MaxHeight = 50, HtmlFragment = $@" <div style='width: 100%; font-size: 12px; font-family: Arial;'> <div style='float: left; width: 50%;'> <img src='https://ironpdf.com/img/products/ironpdf-logo-text-dotnet.svg' height='40' /> </div> <div style='float: right; width: 50%; text-align: right;'> <strong>Invoice {invoice.InvoiceNumber}</strong><br/> Generated: {DateTime.Now:yyyy-MM-dd} </div> </div>", LoadStylesAndCSSFromMainHtmlDocument = true }; } else { renderer.RenderingOptions.TextHeader = new TextHeaderFooter { CenterText = options.HeaderText, Font = IronSoftware.Drawing.FontTypes.Arial, FontSize = 12, DrawDividerLine = true }; } // Footer renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter { MaxHeight = 30, HtmlFragment = @" <div style='width: 100%; font-size: 10px; color: #666;'> <div style='float: left; width: 33%;'> © 2025 Your Company </div> <div style='float: center; width: 33%; text-align: center;'> yourwebsite.com </div> <div style='float: right; width: 33%; text-align: right;'> Page {page} of {total-pages} </div> </div>" }; // Optional: Add watermark here (IronPDF supports adding after PDF is generated, so keep it as-is) // Margins, paper size etc., can also be set here if needed renderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait; renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4; renderer.RenderingOptions.PrintHtmlBackgrounds = true; } [HttpGet] public async Task<IActionResult> GenerateInvoice(string invoiceNumber) { var stopwatch = Stopwatch.StartNew(); try { // Validate input if (string.IsNullOrEmpty(invoiceNumber)) { _logger.LogWarning("Invoice generation attempted without invoice number"); return BadRequest("Invoice number is required"); } // Generate sample data (in production, fetch from database) var invoice = CreateSampleInvoice(invoiceNumber); // Log the generation attempt _logger.LogInformation($"Generating PDF for invoice {invoiceNumber}"); // Configure PDF rendering options _pdfRenderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Portrait; _pdfRenderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4; _pdfRenderer.RenderingOptions.PrintHtmlBackgrounds = true; _pdfRenderer.RenderingOptions.CreatePdfFormsFromHtml = false; var options = new PdfStylingOptions { MarginTop = 25, MarginBottom = 25, MarginLeft = 20, MarginRight = 20, UseHtmlHeader = true, HeaderText = $"Invoice {invoice.InvoiceNumber}", AddWatermark = false, ForcePageBreaks = false }; // Apply the styling to the renderer BEFORE rendering PDF ConfigurePdfRendererOptions(_pdfRenderer, invoice, options); // Render the view to PDF PdfDocument pdf; try { pdf = _pdfRenderer.RenderRazorViewToPdf( _viewRenderer, "Views/Invoice/InvoiceTemplate.cshtml", invoice); } catch (Exception renderEx) { _logger.LogError(renderEx, "Failed to render Razor view to PDF"); throw new InvalidOperationException("PDF rendering failed. Please check the template.", renderEx); } // Apply metadata pdf.MetaData.Author = "PdfGeneratorApp"; pdf.MetaData.Title = $"Invoice {invoice.InvoiceNumber}"; pdf.MetaData.Subject = $"Invoice for {invoice.Customer.Name}"; pdf.MetaData.Keywords = "invoice, billing, payment"; pdf.MetaData.CreationDate = DateTime.UtcNow; pdf.MetaData.ModifiedDate = DateTime.UtcNow; // Optional: Add password protection // pdf.SecuritySettings.UserPassword = "user123"; // pdf.SecuritySettings.OwnerPassword = "owner456"; // pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights; // Log performance metrics stopwatch.Stop(); _logger.LogInformation($"PDF generated successfully for invoice {invoiceNumber} in {stopwatch.ElapsedMilliseconds}ms"); // Return the PDF file Response.Headers.Add("Content-Disposition", $"inline; filename=Invoice_{invoiceNumber}.pdf"); return File(pdf.BinaryData, "application/pdf", $"Invoice_{invoiceNumber}.pdf"); } catch (Exception ex) { _logger.LogError(ex, $"Error generating PDF for invoice {invoiceNumber}"); // In development, return detailed error if (_environment.IsDevelopment()) { return StatusCode(500, new { error = "PDF generation failed", message = ex.Message, stackTrace = ex.StackTrace }); } // In production, return generic error return StatusCode(500, "An error occurred while generating the PDF"); } } private InvoiceModel CreateSampleInvoice(string invoiceNumber) { return new InvoiceModel { InvoiceNumber = invoiceNumber, InvoiceDate = DateTime.Now, DueDate = DateTime.Now.AddDays(30), Vendor = new CompanyInfo { Name = "Tech Solutions Inc.", Address = "123 Business Ave", City = "New York", State = "NY", ZipCode = "10001", Email = "billing@techsolutions.com", Phone = "(555) 123-4567" }, Customer = new CompanyInfo { Name = "Acme Corporation", Address = "456 Commerce St", City = "Los Angeles", State = "CA", ZipCode = "90001", Email = "accounts@acmecorp.com", Phone = "(555) 987-6543" }, Items = new List<InvoiceItem> { new InvoiceItem { Description = "Software Development Services - 40 hours", Quantity = 40, UnitPrice = 150.00m }, new InvoiceItem { Description = "Project Management - 10 hours", Quantity = 10, UnitPrice = 120.00m }, new InvoiceItem { Description = "Quality Assurance Testing", Quantity = 1, UnitPrice = 2500.00m } }, TaxRate = 8.875m, Notes = "Payment is due within 30 days. Late payments subject to 1.5% monthly interest.", PaymentTerms = "Net 30" }; } } } $vbLabelText $csharpLabel Code Explanation You can see the brief differences between basic and styled headers. With IronPDF, you could even add customized HTML headers and footers to your invoice to further stylize it and truly make it your own. For more in-depth information on adding headers and footers, please refer to this how-to guide. Output How to Implement High-Performance Batch PDF Processing? Need to generate hundreds or thousands of PDFs? Here's how to achieve optimal performance with parallel processing while efficiently managing memory. Download our complete working example to see this in action. For applications that need to generate multiple PDFs efficiently, here's an optimized batch processing implementation using async and multithreading techniques. This approach follows Microsoft's parallel programming best practices for optimal performance when generating PDFs in ASP.NET Core using C#: using System.Collections.Concurrent; using System.Diagnostics; public class BatchPdfProcessor { private readonly ChromePdfRenderer _renderer; private readonly ILogger<BatchPdfProcessor> _logger; private readonly SemaphoreSlim _semaphore; public BatchPdfProcessor( ChromePdfRenderer renderer, ILogger<BatchPdfProcessor> logger) { _renderer = renderer; _logger = logger; // Limit concurrent PDF generation to prevent memory exhaustion _semaphore = new SemaphoreSlim(Environment.ProcessorCount); } public async Task<BatchProcessingResult> ProcessBatchAsync( List<BatchPdfRequest> requests, IProgress<BatchProgressReport> progress = null) { var result = new BatchProcessingResult { StartTime = DateTime.UtcNow, TotalRequests = requests.Count }; var successfulPdfs = new ConcurrentBag<GeneratedPdf>(); var errors = new ConcurrentBag<ProcessingError>(); var stopwatch = Stopwatch.StartNew(); // Process PDFs in parallel with controlled concurrency var tasks = requests.Select(async (request, index) => { await _semaphore.WaitAsync(); try { var taskStopwatch = Stopwatch.StartNew(); // Generate PDF var pdf = await GeneratePdfAsync(request); taskStopwatch.Stop(); successfulPdfs.Add(new GeneratedPdf { Id = request.Id, FileName = request.FileName, Data = pdf.BinaryData, GenerationTime = taskStopwatch.ElapsedMilliseconds, PageCount = pdf.PageCount }); // Report progress progress?.Report(new BatchProgressReport { ProcessedCount = successfulPdfs.Count + errors.Count, TotalCount = requests.Count, CurrentFile = request.FileName }); _logger.LogDebug($"Generated PDF {request.Id} in {taskStopwatch.ElapsedMilliseconds}ms"); } catch (Exception ex) { errors.Add(new ProcessingError { RequestId = request.Id, FileName = request.FileName, Error = ex.Message, StackTrace = ex.StackTrace }); _logger.LogError(ex, $"Failed to generate PDF for request {request.Id}"); } finally { _semaphore.Release(); } }); await Task.WhenAll(tasks); stopwatch.Stop(); // Compile results result.EndTime = DateTime.UtcNow; result.TotalProcessingTime = stopwatch.ElapsedMilliseconds; result.SuccessfulPdfs = successfulPdfs.ToList(); result.Errors = errors.ToList(); result.SuccessCount = successfulPdfs.Count; result.ErrorCount = errors.Count; result.AverageGenerationTime = successfulPdfs.Any() ? successfulPdfs.Average(p => p.GenerationTime) : 0; result.TotalPages = successfulPdfs.Sum(p => p.PageCount); result.TotalSizeBytes = successfulPdfs.Sum(p => p.Data.Length); // Log summary _logger.LogInformation($"Batch processing completed: {result.SuccessCount} successful, " + $"{result.ErrorCount} errors, {result.TotalProcessingTime}ms total time"); // Clean up memory after large batch if (requests.Count > 100) { GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } return result; } private async Task<PdfDocument> GeneratePdfAsync(BatchPdfRequest request) { return await Task.Run(() => { // Configure renderer for this specific request var localRenderer = new ChromePdfRenderer(); localRenderer.RenderingOptions.PaperSize = request.PaperSize; localRenderer.RenderingOptions.MarginTop = request.MarginTop; localRenderer.RenderingOptions.MarginBottom = request.MarginBottom; // Generate PDF var pdf = localRenderer.RenderHtmlAsPdf(request.HtmlContent); // Apply compression if requested if (request.CompressImages) { pdf.CompressImages(request.CompressionQuality); } return pdf; }); } } public class BatchPdfRequest { public string Id { get; set; } public string FileName { get; set; } public string HtmlContent { get; set; } public IronPdf.Rendering.PdfPaperSize PaperSize { get; set; } = IronPdf.Rendering.PdfPaperSize.A4; public int MarginTop { get; set; } = 25; public int MarginBottom { get; set; } = 25; public bool CompressImages { get; set; } = true; public int CompressionQuality { get; set; } = 80; } public class BatchProcessingResult { public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public long TotalProcessingTime { get; set; } public int TotalRequests { get; set; } public int SuccessCount { get; set; } public int ErrorCount { get; set; } public double AverageGenerationTime { get; set; } public int TotalPages { get; set; } public long TotalSizeBytes { get; set; } public List<GeneratedPdf> SuccessfulPdfs { get; set; } public List<ProcessingError> Errors { get; set; } } public class GeneratedPdf { public string Id { get; set; } public string FileName { get; set; } public byte[] Data { get; set; } public long GenerationTime { get; set; } public int PageCount { get; set; } } public class ProcessingError { public string RequestId { get; set; } public string FileName { get; set; } public string Error { get; set; } public string StackTrace { get; set; } } public class BatchProgressReport { public int ProcessedCount { get; set; } public int TotalCount { get; set; } public string CurrentFile { get; set; } public double PercentComplete => (double)ProcessedCount / TotalCount * 100; } using System.Collections.Concurrent; using System.Diagnostics; public class BatchPdfProcessor { private readonly ChromePdfRenderer _renderer; private readonly ILogger<BatchPdfProcessor> _logger; private readonly SemaphoreSlim _semaphore; public BatchPdfProcessor( ChromePdfRenderer renderer, ILogger<BatchPdfProcessor> logger) { _renderer = renderer; _logger = logger; // Limit concurrent PDF generation to prevent memory exhaustion _semaphore = new SemaphoreSlim(Environment.ProcessorCount); } public async Task<BatchProcessingResult> ProcessBatchAsync( List<BatchPdfRequest> requests, IProgress<BatchProgressReport> progress = null) { var result = new BatchProcessingResult { StartTime = DateTime.UtcNow, TotalRequests = requests.Count }; var successfulPdfs = new ConcurrentBag<GeneratedPdf>(); var errors = new ConcurrentBag<ProcessingError>(); var stopwatch = Stopwatch.StartNew(); // Process PDFs in parallel with controlled concurrency var tasks = requests.Select(async (request, index) => { await _semaphore.WaitAsync(); try { var taskStopwatch = Stopwatch.StartNew(); // Generate PDF var pdf = await GeneratePdfAsync(request); taskStopwatch.Stop(); successfulPdfs.Add(new GeneratedPdf { Id = request.Id, FileName = request.FileName, Data = pdf.BinaryData, GenerationTime = taskStopwatch.ElapsedMilliseconds, PageCount = pdf.PageCount }); // Report progress progress?.Report(new BatchProgressReport { ProcessedCount = successfulPdfs.Count + errors.Count, TotalCount = requests.Count, CurrentFile = request.FileName }); _logger.LogDebug($"Generated PDF {request.Id} in {taskStopwatch.ElapsedMilliseconds}ms"); } catch (Exception ex) { errors.Add(new ProcessingError { RequestId = request.Id, FileName = request.FileName, Error = ex.Message, StackTrace = ex.StackTrace }); _logger.LogError(ex, $"Failed to generate PDF for request {request.Id}"); } finally { _semaphore.Release(); } }); await Task.WhenAll(tasks); stopwatch.Stop(); // Compile results result.EndTime = DateTime.UtcNow; result.TotalProcessingTime = stopwatch.ElapsedMilliseconds; result.SuccessfulPdfs = successfulPdfs.ToList(); result.Errors = errors.ToList(); result.SuccessCount = successfulPdfs.Count; result.ErrorCount = errors.Count; result.AverageGenerationTime = successfulPdfs.Any() ? successfulPdfs.Average(p => p.GenerationTime) : 0; result.TotalPages = successfulPdfs.Sum(p => p.PageCount); result.TotalSizeBytes = successfulPdfs.Sum(p => p.Data.Length); // Log summary _logger.LogInformation($"Batch processing completed: {result.SuccessCount} successful, " + $"{result.ErrorCount} errors, {result.TotalProcessingTime}ms total time"); // Clean up memory after large batch if (requests.Count > 100) { GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } return result; } private async Task<PdfDocument> GeneratePdfAsync(BatchPdfRequest request) { return await Task.Run(() => { // Configure renderer for this specific request var localRenderer = new ChromePdfRenderer(); localRenderer.RenderingOptions.PaperSize = request.PaperSize; localRenderer.RenderingOptions.MarginTop = request.MarginTop; localRenderer.RenderingOptions.MarginBottom = request.MarginBottom; // Generate PDF var pdf = localRenderer.RenderHtmlAsPdf(request.HtmlContent); // Apply compression if requested if (request.CompressImages) { pdf.CompressImages(request.CompressionQuality); } return pdf; }); } } public class BatchPdfRequest { public string Id { get; set; } public string FileName { get; set; } public string HtmlContent { get; set; } public IronPdf.Rendering.PdfPaperSize PaperSize { get; set; } = IronPdf.Rendering.PdfPaperSize.A4; public int MarginTop { get; set; } = 25; public int MarginBottom { get; set; } = 25; public bool CompressImages { get; set; } = true; public int CompressionQuality { get; set; } = 80; } public class BatchProcessingResult { public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public long TotalProcessingTime { get; set; } public int TotalRequests { get; set; } public int SuccessCount { get; set; } public int ErrorCount { get; set; } public double AverageGenerationTime { get; set; } public int TotalPages { get; set; } public long TotalSizeBytes { get; set; } public List<GeneratedPdf> SuccessfulPdfs { get; set; } public List<ProcessingError> Errors { get; set; } } public class GeneratedPdf { public string Id { get; set; } public string FileName { get; set; } public byte[] Data { get; set; } public long GenerationTime { get; set; } public int PageCount { get; set; } } public class ProcessingError { public string RequestId { get; set; } public string FileName { get; set; } public string Error { get; set; } public string StackTrace { get; set; } } public class BatchProgressReport { public int ProcessedCount { get; set; } public int TotalCount { get; set; } public string CurrentFile { get; set; } public double PercentComplete => (double)ProcessedCount / TotalCount * 100; } $vbLabelText $csharpLabel Real-World Healthcare Report Example Here's a specific implementation for generating HIPAA-compliant medical reports: public class MedicalReportGenerator { private readonly ChromePdfRenderer _renderer; private readonly ILogger<MedicalReportGenerator> _logger; public MedicalReportGenerator( ChromePdfRenderer renderer, ILogger<MedicalReportGenerator> logger) { _renderer = renderer; _logger = logger; } public async Task<PdfDocument> GeneratePatientReport(PatientReportModel model) { var stopwatch = Stopwatch.StartNew(); // Configure for medical document standards _renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter; _renderer.RenderingOptions.MarginTop = 50; _renderer.RenderingOptions.MarginBottom = 40; // HIPAA-compliant header _renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter { Height = 45, HtmlFragment = $@" <div style='width: 100%; font-size: 10px;'> <div style='float: left;'> <strong>CONFIDENTIAL MEDICAL RECORD</strong><br/> Patient: {model.PatientName} | MRN: {model.MedicalRecordNumber} </div> <div style='float: right; text-align: right;'> Generated: {{date}} {{time}}<br/> Provider: {model.ProviderName} </div> </div>" }; // Generate report HTML var html = GenerateMedicalReportHtml(model); // Create PDF with encryption for HIPAA compliance var pdf = _renderer.RenderHtmlAsPdf(html); // Apply 256-bit AES encryption pdf.SecuritySettings.UserPassword = GenerateSecurePassword(); pdf.SecuritySettings.OwnerPassword = GenerateOwnerPassword(); pdf.SecuritySettings.AllowUserCopyPasteContent = false; pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint; pdf.SecuritySettings.AllowUserFormData = false; pdf.SecuritySettings.AllowUserAnnotations = false; // Add audit metadata pdf.MetaData.Author = model.ProviderName; pdf.MetaData.Title = $"Medical Report - {model.PatientName}"; pdf.MetaData.Keywords = "medical,confidential,hipaa"; pdf.MetaData.CustomProperties.Add("ReportType", model.ReportType); pdf.MetaData.CustomProperties.Add("GeneratedBy", model.UserId); pdf.MetaData.CustomProperties.Add("Timestamp", DateTime.UtcNow.ToString("O")); stopwatch.Stop(); _logger.LogInformation($"Medical report generated in {stopwatch.ElapsedMilliseconds}ms for patient {model.MedicalRecordNumber}"); return pdf; } private string GenerateMedicalReportHtml(PatientReportModel model) { // Generate comprehensive medical report HTML // This would typically use a Razor view in production return $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: 'Segoe UI', Arial, sans-serif; margin: 0; padding: 20px; }} .header {{ background: #f0f4f8; padding: 20px; margin-bottom: 30px; }} .patient-info {{ display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; }} .section {{ margin-bottom: 30px; }} .section h2 {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }} .vital-signs {{ display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }} .vital-box {{ background: #ecf0f1; padding: 15px; border-radius: 5px; }} .medication-table {{ width: 100%; border-collapse: collapse; }} .medication-table th, .medication-table td {{ border: 1px solid #ddd; padding: 10px; text-align: left; }} .medication-table th {{ background: #3498db; color: white; }} .alert {{ background: #e74c3c; color: white; padding: 10px; border-radius: 5px; margin-bottom: 20px; }} </style> </head> <body> <div class='header'> <h1>Patient Medical Report</h1> <p>Report Date: {DateTime.Now:MMMM dd, yyyy}</p> </div> {(model.HasAllergies ? "<div class='alert'>⚠ PATIENT HAS KNOWN ALLERGIES - SEE ALLERGY SECTION</div>" : "")} <div class='patient-info'> <div> <strong>Patient Name:</strong> {model.PatientName}<br/> <strong>Date of Birth:</strong> {model.DateOfBirth:MM/dd/yyyy}<br/> <strong>Age:</strong> {model.Age} years<br/> <strong>Gender:</strong> {model.Gender} </div> <div> <strong>MRN:</strong> {model.MedicalRecordNumber}<br/> <strong>Admission Date:</strong> {model.AdmissionDate:MM/dd/yyyy}<br/> <strong>Provider:</strong> {model.ProviderName}<br/> <strong>Department:</strong> {model.Department} </div> </div> <div class='section'> <h2>Vital Signs</h2> <div class='vital-signs'> <div class='vital-box'> <strong>Blood Pressure</strong><br/> {model.BloodPressure} </div> <div class='vital-box'> <strong>Heart Rate</strong><br/> {model.HeartRate} bpm </div> <div class='vital-box'> <strong>Temperature</strong><br/> {model.Temperature}°F </div> <div class='vital-box'> <strong>Respiratory Rate</strong><br/> {model.RespiratoryRate} /min </div> <div class='vital-box'> <strong>O2 Saturation</strong><br/> {model.OxygenSaturation}% </div> <div class='vital-box'> <strong>Weight</strong><br/> {model.Weight} lbs </div> </div> </div> <div class='section'> <h2>Current Medications</h2> <table class='medication-table'> <thead> <tr> <th>Medication</th> <th>Dosage</th> <th>Frequency</th> <th>Route</th> <th>Start Date</th> </tr> </thead> <tbody> {string.Join("", model.Medications.Select(m => $@" <tr> <td>{m.Name}</td> <td>{m.Dosage}</td> <td>{m.Frequency}</td> <td>{m.Route}</td> <td>{m.StartDate:MM/dd/yyyy}</td> </tr> "))} </tbody> </table> </div> <div class='section'> <h2>Clinical Notes</h2> <p>{model.ClinicalNotes}</p> </div> <div class='section'> <h2>Treatment Plan</h2> <p>{model.TreatmentPlan}</p> </div> </body> </html>"; } private string GenerateSecurePassword() { // Generate cryptographically secure password using var rng = System.Security.Cryptography.RandomNumberGenerator.Create(); var bytes = new byte[32]; rng.GetBytes(bytes); return Convert.ToBase64String(bytes); } private string GenerateOwnerPassword() { // In production, retrieve from secure configuration return "SecureOwnerPassword123!"; } } public class PatientReportModel { public string PatientName { get; set; } public string MedicalRecordNumber { get; set; } public DateTime DateOfBirth { get; set; } public int Age { get; set; } public string Gender { get; set; } public DateTime AdmissionDate { get; set; } public string ProviderName { get; set; } public string Department { get; set; } public string ReportType { get; set; } public string UserId { get; set; } // Vital Signs public string BloodPressure { get; set; } public int HeartRate { get; set; } public decimal Temperature { get; set; } public int RespiratoryRate { get; set; } public int OxygenSaturation { get; set; } public decimal Weight { get; set; } // Medical Information public bool HasAllergies { get; set; } public List<Medication> Medications { get; set; } public string ClinicalNotes { get; set; } public string TreatmentPlan { get; set; } } public class Medication { public string Name { get; set; } public string Dosage { get; set; } public string Frequency { get; set; } public string Route { get; set; } public DateTime StartDate { get; set; } } public class MedicalReportGenerator { private readonly ChromePdfRenderer _renderer; private readonly ILogger<MedicalReportGenerator> _logger; public MedicalReportGenerator( ChromePdfRenderer renderer, ILogger<MedicalReportGenerator> logger) { _renderer = renderer; _logger = logger; } public async Task<PdfDocument> GeneratePatientReport(PatientReportModel model) { var stopwatch = Stopwatch.StartNew(); // Configure for medical document standards _renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter; _renderer.RenderingOptions.MarginTop = 50; _renderer.RenderingOptions.MarginBottom = 40; // HIPAA-compliant header _renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter { Height = 45, HtmlFragment = $@" <div style='width: 100%; font-size: 10px;'> <div style='float: left;'> <strong>CONFIDENTIAL MEDICAL RECORD</strong><br/> Patient: {model.PatientName} | MRN: {model.MedicalRecordNumber} </div> <div style='float: right; text-align: right;'> Generated: {{date}} {{time}}<br/> Provider: {model.ProviderName} </div> </div>" }; // Generate report HTML var html = GenerateMedicalReportHtml(model); // Create PDF with encryption for HIPAA compliance var pdf = _renderer.RenderHtmlAsPdf(html); // Apply 256-bit AES encryption pdf.SecuritySettings.UserPassword = GenerateSecurePassword(); pdf.SecuritySettings.OwnerPassword = GenerateOwnerPassword(); pdf.SecuritySettings.AllowUserCopyPasteContent = false; pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint; pdf.SecuritySettings.AllowUserFormData = false; pdf.SecuritySettings.AllowUserAnnotations = false; // Add audit metadata pdf.MetaData.Author = model.ProviderName; pdf.MetaData.Title = $"Medical Report - {model.PatientName}"; pdf.MetaData.Keywords = "medical,confidential,hipaa"; pdf.MetaData.CustomProperties.Add("ReportType", model.ReportType); pdf.MetaData.CustomProperties.Add("GeneratedBy", model.UserId); pdf.MetaData.CustomProperties.Add("Timestamp", DateTime.UtcNow.ToString("O")); stopwatch.Stop(); _logger.LogInformation($"Medical report generated in {stopwatch.ElapsedMilliseconds}ms for patient {model.MedicalRecordNumber}"); return pdf; } private string GenerateMedicalReportHtml(PatientReportModel model) { // Generate comprehensive medical report HTML // This would typically use a Razor view in production return $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: 'Segoe UI', Arial, sans-serif; margin: 0; padding: 20px; }} .header {{ background: #f0f4f8; padding: 20px; margin-bottom: 30px; }} .patient-info {{ display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; }} .section {{ margin-bottom: 30px; }} .section h2 {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }} .vital-signs {{ display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }} .vital-box {{ background: #ecf0f1; padding: 15px; border-radius: 5px; }} .medication-table {{ width: 100%; border-collapse: collapse; }} .medication-table th, .medication-table td {{ border: 1px solid #ddd; padding: 10px; text-align: left; }} .medication-table th {{ background: #3498db; color: white; }} .alert {{ background: #e74c3c; color: white; padding: 10px; border-radius: 5px; margin-bottom: 20px; }} </style> </head> <body> <div class='header'> <h1>Patient Medical Report</h1> <p>Report Date: {DateTime.Now:MMMM dd, yyyy}</p> </div> {(model.HasAllergies ? "<div class='alert'>⚠ PATIENT HAS KNOWN ALLERGIES - SEE ALLERGY SECTION</div>" : "")} <div class='patient-info'> <div> <strong>Patient Name:</strong> {model.PatientName}<br/> <strong>Date of Birth:</strong> {model.DateOfBirth:MM/dd/yyyy}<br/> <strong>Age:</strong> {model.Age} years<br/> <strong>Gender:</strong> {model.Gender} </div> <div> <strong>MRN:</strong> {model.MedicalRecordNumber}<br/> <strong>Admission Date:</strong> {model.AdmissionDate:MM/dd/yyyy}<br/> <strong>Provider:</strong> {model.ProviderName}<br/> <strong>Department:</strong> {model.Department} </div> </div> <div class='section'> <h2>Vital Signs</h2> <div class='vital-signs'> <div class='vital-box'> <strong>Blood Pressure</strong><br/> {model.BloodPressure} </div> <div class='vital-box'> <strong>Heart Rate</strong><br/> {model.HeartRate} bpm </div> <div class='vital-box'> <strong>Temperature</strong><br/> {model.Temperature}°F </div> <div class='vital-box'> <strong>Respiratory Rate</strong><br/> {model.RespiratoryRate} /min </div> <div class='vital-box'> <strong>O2 Saturation</strong><br/> {model.OxygenSaturation}% </div> <div class='vital-box'> <strong>Weight</strong><br/> {model.Weight} lbs </div> </div> </div> <div class='section'> <h2>Current Medications</h2> <table class='medication-table'> <thead> <tr> <th>Medication</th> <th>Dosage</th> <th>Frequency</th> <th>Route</th> <th>Start Date</th> </tr> </thead> <tbody> {string.Join("", model.Medications.Select(m => $@" <tr> <td>{m.Name}</td> <td>{m.Dosage}</td> <td>{m.Frequency}</td> <td>{m.Route}</td> <td>{m.StartDate:MM/dd/yyyy}</td> </tr> "))} </tbody> </table> </div> <div class='section'> <h2>Clinical Notes</h2> <p>{model.ClinicalNotes}</p> </div> <div class='section'> <h2>Treatment Plan</h2> <p>{model.TreatmentPlan}</p> </div> </body> </html>"; } private string GenerateSecurePassword() { // Generate cryptographically secure password using var rng = System.Security.Cryptography.RandomNumberGenerator.Create(); var bytes = new byte[32]; rng.GetBytes(bytes); return Convert.ToBase64String(bytes); } private string GenerateOwnerPassword() { // In production, retrieve from secure configuration return "SecureOwnerPassword123!"; } } public class PatientReportModel { public string PatientName { get; set; } public string MedicalRecordNumber { get; set; } public DateTime DateOfBirth { get; set; } public int Age { get; set; } public string Gender { get; set; } public DateTime AdmissionDate { get; set; } public string ProviderName { get; set; } public string Department { get; set; } public string ReportType { get; set; } public string UserId { get; set; } // Vital Signs public string BloodPressure { get; set; } public int HeartRate { get; set; } public decimal Temperature { get; set; } public int RespiratoryRate { get; set; } public int OxygenSaturation { get; set; } public decimal Weight { get; set; } // Medical Information public bool HasAllergies { get; set; } public List<Medication> Medications { get; set; } public string ClinicalNotes { get; set; } public string TreatmentPlan { get; set; } } public class Medication { public string Name { get; set; } public string Dosage { get; set; } public string Frequency { get; set; } public string Route { get; set; } public DateTime StartDate { get; set; } } $vbLabelText $csharpLabel Security Considerations When generating PDFs in production environments or when working with sensitive information, security is paramount. IronPDF provides several security features: Applying Security Settings using System.Text.RegularExpressions; namespace PdfGeneratorApp.Utilities { public static class PdfSecurityHelper { public static void ApplySecuritySettings(PdfDocument pdf, SecurityLevel level) { switch (level) { case SecurityLevel.Low: // Basic protection pdf.SecuritySettings.AllowUserCopyPasteContent = true; pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights; break; case SecurityLevel.Medium: // Restricted copying pdf.SecuritySettings.UserPassword = GeneratePassword(8); pdf.SecuritySettings.AllowUserCopyPasteContent = false; pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.PrintLowQuality; break; case SecurityLevel.High: // Maximum security pdf.SecuritySettings.UserPassword = GeneratePassword(16); pdf.SecuritySettings.OwnerPassword = GeneratePassword(16); pdf.SecuritySettings.AllowUserCopyPasteContent = false; pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint; pdf.SecuritySettings.AllowUserAnnotations = false; pdf.SecuritySettings.AllowUserFormData = false; break; } } public enum SecurityLevel { Low, Medium, High } } using System.Text.RegularExpressions; namespace PdfGeneratorApp.Utilities { public static class PdfSecurityHelper { public static void ApplySecuritySettings(PdfDocument pdf, SecurityLevel level) { switch (level) { case SecurityLevel.Low: // Basic protection pdf.SecuritySettings.AllowUserCopyPasteContent = true; pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.FullPrintRights; break; case SecurityLevel.Medium: // Restricted copying pdf.SecuritySettings.UserPassword = GeneratePassword(8); pdf.SecuritySettings.AllowUserCopyPasteContent = false; pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.PrintLowQuality; break; case SecurityLevel.High: // Maximum security pdf.SecuritySettings.UserPassword = GeneratePassword(16); pdf.SecuritySettings.OwnerPassword = GeneratePassword(16); pdf.SecuritySettings.AllowUserCopyPasteContent = false; pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint; pdf.SecuritySettings.AllowUserAnnotations = false; pdf.SecuritySettings.AllowUserFormData = false; break; } } public enum SecurityLevel { Low, Medium, High } } $vbLabelText $csharpLabel In the helper class above, we use the SecurityLevel enum to set different security options for the PDF. For example, the low SecurityLevel applies basic protection but still allows FullPrintRights and copying and pasting from the PDF by setting the property AllowUserCopyPasteContent to true. These settings are enabled by adjusting the PDF class properties with IronPDF. For a complete list of properties and options for security, please consult the how-to guide. Error Handling and Troubleshooting Common issues and their solutions for generating PDFs in ASP.NET Core have been extensively discussed in the developer community. Here are proven solutions for the most frequent challenges: Memory Management For detailed guidance on handling memory leaks in IronPDF, implement the following pattern: public class PdfMemoryManager : IDisposable { private readonly List<PdfDocument> _openDocuments = new(); private readonly ILogger<PdfMemoryManager> _logger; private bool _disposed; public PdfMemoryManager(ILogger<PdfMemoryManager> logger) { _logger = logger; } public PdfDocument CreateDocument(ChromePdfRenderer renderer, string html) { try { var pdf = renderer.RenderHtmlAsPdf(html); _openDocuments.Add(pdf); return pdf; } catch (OutOfMemoryException ex) { _logger.LogError(ex, "Out of memory while generating PDF"); // Force garbage collection CleanupMemory(); // Retry with reduced quality renderer.RenderingOptions.JpegQuality = 50; var pdf = renderer.RenderHtmlAsPdf(html); _openDocuments.Add(pdf); return pdf; } } private void CleanupMemory() { // Dispose all open documents foreach (var doc in _openDocuments) { doc?.Dispose(); } _openDocuments.Clear(); // Force garbage collection GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); _logger.LogInformation("Memory cleanup performed"); } public void Dispose() { if (!_disposed) { CleanupMemory(); _disposed = true; } } } public class PdfMemoryManager : IDisposable { private readonly List<PdfDocument> _openDocuments = new(); private readonly ILogger<PdfMemoryManager> _logger; private bool _disposed; public PdfMemoryManager(ILogger<PdfMemoryManager> logger) { _logger = logger; } public PdfDocument CreateDocument(ChromePdfRenderer renderer, string html) { try { var pdf = renderer.RenderHtmlAsPdf(html); _openDocuments.Add(pdf); return pdf; } catch (OutOfMemoryException ex) { _logger.LogError(ex, "Out of memory while generating PDF"); // Force garbage collection CleanupMemory(); // Retry with reduced quality renderer.RenderingOptions.JpegQuality = 50; var pdf = renderer.RenderHtmlAsPdf(html); _openDocuments.Add(pdf); return pdf; } } private void CleanupMemory() { // Dispose all open documents foreach (var doc in _openDocuments) { doc?.Dispose(); } _openDocuments.Clear(); // Force garbage collection GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); _logger.LogInformation("Memory cleanup performed"); } public void Dispose() { if (!_disposed) { CleanupMemory(); _disposed = true; } } } $vbLabelText $csharpLabel Font Rendering Issues public class FontTroubleshooter { public static void EnsureFontsAvailable(ChromePdfRenderer renderer) { // Embed fonts in the PDF renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print; // Use web-safe fonts as fallback var fontFallback = @" <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } @font-face { font-family: 'CustomFont'; src: url('data:font/woff2;base64,YOUR_BASE64_FONT_HERE') format('woff2'); } </style>"; // Add to HTML head renderer.RenderingOptions.CustomCssUrl = "https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap"; } } public class FontTroubleshooter { public static void EnsureFontsAvailable(ChromePdfRenderer renderer) { // Embed fonts in the PDF renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print; // Use web-safe fonts as fallback var fontFallback = @" <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } @font-face { font-family: 'CustomFont'; src: url('data:font/woff2;base64,YOUR_BASE64_FONT_HERE') format('woff2'); } </style>"; // Add to HTML head renderer.RenderingOptions.CustomCssUrl = "https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap"; } } $vbLabelText $csharpLabel Common Exceptions and Solutions ExceptionCauseSolutionIronPdf.Exceptions.IronPdfNativeExceptionChrome engine initialization failedEnsure Visual C++ Redistributables are installedSystem.UnauthorizedAccessExceptionInsufficient permissionsGrant write access to the temp folderSystem.TimeoutExceptionJavaScript is taking too longIncrease RenderDelay or disable JavaScriptSystem.OutOfMemoryExceptionLarge PDF or batch processingImplement pagination or reduce image qualityIronPdf.Exceptions.IronPdfLicensingExceptionInvalid or expired licenseVerify the license key in the configuration Debugging Tips With IronPDF, you can easily debug and log all processes run by your application to search and verify any inputs and outputs. To enable logging, set `EnableDebugging` to true and specify a file path for the log by assigning a value to the LogFilePath property. Here's a quick code snippet showing how to do that. public class PdfDebugger { private readonly ILogger<PdfDebugger> _logger; public PdfDebugger(ILogger<PdfDebugger> logger) { _logger = logger; } public void EnableDebugging(ChromePdfRenderer renderer) { // Enable detailed logging IronPdf.Logging.Logger.EnableDebugging = true; IronPdf.Logging.Logger.LogFilePath = "IronPdf.log"; IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All; // Log rendering settings _logger.LogDebug($"Paper Size: {renderer.RenderingOptions.PaperSize}"); _logger.LogDebug($"Margins: T{renderer.RenderingOptions.MarginTop} " + $"B{renderer.RenderingOptions.MarginBottom} " + $"L{renderer.RenderingOptions.MarginLeft} " + $"R{renderer.RenderingOptions.MarginRight}"); _logger.LogDebug($"JavaScript Enabled: {renderer.RenderingOptions.EnableJavaScript}"); _logger.LogDebug($"Render Delay: {renderer.RenderingOptions.RenderDelay}ms"); } public void SaveDebugHtml(string html, string fileName) { // Save HTML for inspection var debugPath = Path.Combine("debug", $"{fileName}_{DateTime.Now:yyyyMMdd_HHmmss}.html"); Directory.CreateDirectory("debug"); File.WriteAllText(debugPath, html); _logger.LogDebug($"Debug HTML saved to: {debugPath}"); } } public class PdfDebugger { private readonly ILogger<PdfDebugger> _logger; public PdfDebugger(ILogger<PdfDebugger> logger) { _logger = logger; } public void EnableDebugging(ChromePdfRenderer renderer) { // Enable detailed logging IronPdf.Logging.Logger.EnableDebugging = true; IronPdf.Logging.Logger.LogFilePath = "IronPdf.log"; IronPdf.Logging.Logger.LoggingMode = IronPdf.Logging.Logger.LoggingModes.All; // Log rendering settings _logger.LogDebug($"Paper Size: {renderer.RenderingOptions.PaperSize}"); _logger.LogDebug($"Margins: T{renderer.RenderingOptions.MarginTop} " + $"B{renderer.RenderingOptions.MarginBottom} " + $"L{renderer.RenderingOptions.MarginLeft} " + $"R{renderer.RenderingOptions.MarginRight}"); _logger.LogDebug($"JavaScript Enabled: {renderer.RenderingOptions.EnableJavaScript}"); _logger.LogDebug($"Render Delay: {renderer.RenderingOptions.RenderDelay}ms"); } public void SaveDebugHtml(string html, string fileName) { // Save HTML for inspection var debugPath = Path.Combine("debug", $"{fileName}_{DateTime.Now:yyyyMMdd_HHmmss}.html"); Directory.CreateDirectory("debug"); File.WriteAllText(debugPath, html); _logger.LogDebug($"Debug HTML saved to: {debugPath}"); } } $vbLabelText $csharpLabel Local Deployment Best Practices IIS Configuration For deploying to IIS, ensure proper configuration: <configuration> <system.webServer> <applicationPool> <processModel enable32BitAppOnWin64="false" /> </applicationPool> <httpRuntime executionTimeout="300" maxRequestLength="51200" /> <system.web> <compilation tempDirectory="~/App_Data/Temp/" /> </system.web> </system.webServer> </configuration> <configuration> <system.webServer> <applicationPool> <processModel enable32BitAppOnWin64="false" /> </applicationPool> <httpRuntime executionTimeout="300" maxRequestLength="51200" /> <system.web> <compilation tempDirectory="~/App_Data/Temp/" /> </system.web> </system.webServer> </configuration> XML Required Dependencies Ensure these components are installed on your deployment server: .NET Runtime - Version 6.0 or later Visual C++ Redistributables - 2015-2022 (x64) Windows Server - 2012 R2 or later recommended File System Permissions # Grant IIS_IUSRS write access to temp folder icacls "C:\inetpub\wwwroot\YourApp\App_Data\Temp" /grant "IIS_IUSRS:(OI)(CI)M" /T # Grant access to IronPDF cache folder icacls "C:\Windows\Temp\IronPdf" /grant "IIS_IUSRS:(OI)(CI)M" /T # Grant IIS_IUSRS write access to temp folder icacls "C:\inetpub\wwwroot\YourApp\App_Data\Temp" /grant "IIS_IUSRS:(OI)(CI)M" /T # Grant access to IronPDF cache folder icacls "C:\Windows\Temp\IronPdf" /grant "IIS_IUSRS:(OI)(CI)M" /T SHELL Performance Tuning // Startup.cs or Program.cs public void ConfigureServices(IServiceCollection services) { // Configure IronPDF for production services.AddSingleton<ChromePdfRenderer>(provider => { var renderer = new ChromePdfRenderer(); // Production optimizations renderer.RenderingOptions.RenderDelay = 50; // Minimize delay renderer.RenderingOptions.Timeout = 120; // 2 minutes max renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print; // Enable caching for static resources Installation.ChromeGpuMode = IronPdf.Engines.Chrome.ChromeGpuModes.Disabled; Installation.LinuxAndDockerDependenciesAutoConfig = false; return renderer; }); // Configure memory cache for generated PDFs services.AddMemoryCache(options => { options.SizeLimit = 100_000_000; // 100 MB cache }); } // Startup.cs or Program.cs public void ConfigureServices(IServiceCollection services) { // Configure IronPDF for production services.AddSingleton<ChromePdfRenderer>(provider => { var renderer = new ChromePdfRenderer(); // Production optimizations renderer.RenderingOptions.RenderDelay = 50; // Minimize delay renderer.RenderingOptions.Timeout = 120; // 2 minutes max renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print; // Enable caching for static resources Installation.ChromeGpuMode = IronPdf.Engines.Chrome.ChromeGpuModes.Disabled; Installation.LinuxAndDockerDependenciesAutoConfig = false; return renderer; }); // Configure memory cache for generated PDFs services.AddMemoryCache(options => { options.SizeLimit = 100_000_000; // 100 MB cache }); } $vbLabelText $csharpLabel Best Practices Summary Always dispose of PdfDocument objects to prevent memory leaks Reuse ChromePdfRenderer instances for better performance Implement proper error handling with detailed logging Sanitize all user input before PDF generation Use async/await patterns for better scalability Configure appropriate timeouts for JavaScript rendering Compress images to reduce file size Cache generated PDFs when the content is static Monitor memory usage during batch processing Test with production-like data volumes before deployment Transform Your ASP.NET Application Today You now have a comprehensive understanding of generating PDFs in ASP.NET Core using C# with IronPDF. From basic Razor view conversion to advanced batch processing with performance optimization, you're equipped to implement professional PDF generation that matches precisely what you see in Chrome. Key achievements you've unlocked: Pixel-perfect rendering using IronPDF's Chrome engine that eliminates formatting surprises when you create a PDF from HTML pages, web pages, etc. Production-ready templates with Razor views providing familiar, maintainable PDF generation Enterprise-grade error handling and memory management for reliable operation at scale Optimized batch processing with parallel generation, handling thousands of documents Professional security features protecting sensitive documents with 256-bit encryption Start Building Professional PDFs Now 지금 바로 IronPDF으로 시작하세요. 무료로 시작하세요 Ready to implement PDF generation in your ASP.NET Core application? Download IronPDF's free trial today and experience the power of professional PDF generation with no watermarks or limitations for 30 days. With comprehensive documentation, responsive engineering support, and a 30-day money-back guarantee, you can confidently build production-ready PDF solutions. Essential Resources for Your Journey 📚 Explore the complete API documentation for detailed method references 👥 Join the developer community for real-time assistance 🚀 Purchase a commercial license starting at $799 for production deployment Transform your ASP.NET Core applications with enterprise-grade PDF generation that works as expected and start building with IronPDF today! 자주 묻는 질문 ASP.NET 애플리케이션에서 IronPDF의 주요 용도는 무엇인가요? IronPDF는 주로 PDF 문서에서 콘텐츠를 생성, 편집 및 추출하는 데 사용되며, ASP.NET 애플리케이션에서 송장, 보고서, 인증서 또는 티켓을 만드는 데 필수적인 툴입니다. IronPDF는 어떻게 픽셀 단위까지 완벽한 PDF 렌더링을 보장하나요? IronPDF는 고급 렌더링 엔진과 엔터프라이즈 기능을 활용하여 HTML, 이미지 또는 기타 문서 형식을 고품질 PDF로 정확하게 변환함으로써 픽셀 단위의 완벽한 렌더링을 보장합니다. IronPDF를 ASP.NET Core 애플리케이션과 통합할 수 있나요? 예, IronPDF는 ASP.NET Core 애플리케이션과 원활하게 통합할 수 있어 개발자에게 다양한 PDF 작업을 효율적으로 처리할 수 있는 강력한 라이브러리를 제공합니다. PDF 생성에 IronPDF를 사용하면 어떤 이점이 있나요? PDF 생성에 IronPDF를 사용하면 사용 편의성, 고품질 렌더링, 복잡한 문서 기능 지원, 애플리케이션 내에서 PDF 작업을 자동화하는 기능 등의 이점을 누릴 수 있습니다. IronPDF는 기존 PDF 문서 편집을 지원하나요? 예, IronPDF는 기존 PDF 문서 편집을 지원하므로 개발자가 프로그래밍 방식으로 콘텐츠를 수정하고, 주석을 추가하고, PDF 메타데이터를 업데이트할 수 있습니다. IronPDF는 엔터프라이즈급 PDF 문서를 만드는 데 적합하나요? IronPDF는 복잡한 문서 구조 지원, 암호화 및 디지털 서명과 같은 보안 기능 등 강력한 기능을 갖추고 있어 엔터프라이즈급 PDF 문서를 만드는 데 이상적입니다. IronPDF는 어떤 파일 형식을 PDF로 변환할 수 있나요? IronPDF는 HTML, 이미지 및 기타 문서 유형을 포함한 다양한 파일 형식을 PDF로 변환할 수 있어 다양한 데이터 소스와의 유연성과 호환성을 보장합니다. IronPDF는 PDF 콘텐츠 추출을 어떻게 처리하나요? IronPDF는 텍스트, 이미지, 메타데이터를 추출하는 API를 제공하여 PDF 문서에서 데이터를 쉽게 검색하고 조작할 수 있도록 PDF 콘텐츠 추출을 처리합니다. PDF 문서 워크플로우를 자동화하는 데 IronPDF를 사용할 수 있나요? 예, IronPDF는 PDF 문서 워크플로우를 자동화하여 웹 애플리케이션에서 PDF 파일의 일괄 생성, 변환 및 배포와 같은 프로세스를 간소화하는 데 사용할 수 있습니다. IronPDF는 개발자를 위해 어떤 지원을 제공하나요? IronPDF는 자세한 문서, 샘플 코드, 통합 및 문제 해결을 지원하는 신속한 고객 서비스 등 개발자를 위한 광범위한 지원을 제공합니다. IronPDF는 .NET 10을 바로 지원하나요? IronPDF는 .NET 10에 대한 사전 릴리스 지원을 제공하며 2025년 11월에 예정된 .NET 10 릴리스를 이미 준수하고 있습니다. 개발자는 특별한 구성 없이도 .NET 10 프로젝트에서 IronPDF를 사용할 수 있습니다. 커티스 차우 지금 바로 엔지니어링 팀과 채팅하세요 기술 문서 작성자 커티스 차우는 칼턴 대학교에서 컴퓨터 과학 학사 학위를 취득했으며, Node.js, TypeScript, JavaScript, React를 전문으로 하는 프론트엔드 개발자입니다. 직관적이고 미적으로 뛰어난 사용자 인터페이스를 만드는 데 열정을 가진 그는 최신 프레임워크를 활용하고, 잘 구성되고 시각적으로 매력적인 매뉴얼을 제작하는 것을 즐깁니다. 커티스는 개발 분야 외에도 사물 인터넷(IoT)에 깊은 관심을 가지고 있으며, 하드웨어와 소프트웨어를 통합하는 혁신적인 방법을 연구합니다. 여가 시간에는 게임을 즐기거나 디스코드 봇을 만들면서 기술에 대한 애정과 창의성을 결합합니다. 관련 기사 업데이트됨 1월 22, 2026 How to Create PDF Documents in .NET with IronPDF: Complete Guide Discover effective methods to create PDF files in C# for developers. Enhance your coding skills and streamline your projects. Read the article now! 더 읽어보기 업데이트됨 1월 21, 2026 How to Merge PDF Files in VB.NET: Complete Tutorial Merge PDF VB NET with IronPDF. Learn to combine multiple PDF files into one document using simple VB.NET code. Step-by-step examples included. 더 읽어보기 업데이트됨 1월 21, 2026 C# PDFWriter Tutorial: Create PDF Documents in .NET Learn to create PDFs efficiently using C# PDFWriter with this step-by-step guide for developers. Read the article to enhance your skills today! 더 읽어보기 How to Extract Images from a PDF in C#How to Extract Data from a PDF in .NET
업데이트됨 1월 22, 2026 How to Create PDF Documents in .NET with IronPDF: Complete Guide Discover effective methods to create PDF files in C# for developers. Enhance your coding skills and streamline your projects. Read the article now! 더 읽어보기
업데이트됨 1월 21, 2026 How to Merge PDF Files in VB.NET: Complete Tutorial Merge PDF VB NET with IronPDF. Learn to combine multiple PDF files into one document using simple VB.NET code. Step-by-step examples included. 더 읽어보기
업데이트됨 1월 21, 2026 C# PDFWriter Tutorial: Create PDF Documents in .NET Learn to create PDFs efficiently using C# PDFWriter with this step-by-step guide for developers. Read the article to enhance your skills today! 더 읽어보기