Generovanie PDF pomocou iTextSharp a XmlWorker v MVC

DOWNLOAD

Vždy keď potrebujem v C# generovať nejaké to pdf-ko, si prejdem už štandardne google seriálom, že aká asi knižnica by sa na to najviac hodila, no a na 99% skončím vždy pri použití knižnice iTextSharp. Na NuGet-e je dostupná aj knižnica itextsharp.xmlworker pomocou ktorej môžeme generovať PDF dokumenty z existujúceho HTML kódu, a keď budeme HTML generovať pomocou MVC controller-ov a razor views máme o zábavu postarané.

Ukážeme si teda, ako použiť vyššie zmienené knižnice pre generovanie PDF dokumentov na základe MVC Razor views. Pre tento účel si vytvoríme nový Visual Studio projekt typu ASP.NET Web Application (použijeme Empty template a referenciu na MVC). Do projektu pridáme referencie na iTextSharp knižnice. Obsah súboru packages.config vyzerá nasledovne:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  
<package id="iTextSharp" version="5.5.10" targetFramework="net45" />
  <
package id="itextsharp.xmlworker" version="5.5.10" targetFramework="net45" />
  <
package id="Microsoft.AspNet.Mvc" version="5.0.0" targetFramework="net45" />
  <
package id="Microsoft.AspNet.Razor" version="3.0.0" targetFramework="net45" />
  <
package id="Microsoft.AspNet.WebPages" version="3.0.0" targetFramework="net45" />
</
packages>
Obsah súboru packages.config

Pre testovacie účely si vytvoríme jednoduchý model. Tento model použijeme v Razor view-e pri generovaní výsledného html kódu.

public class Employee
{
    
public int Id
    {
        
get; 
        set;
    
}

    
public string Name
    {
        
get; 
        set;
    
}

    
public string Email
    {
        
get; 
        set;
    
}
}
Trieda reprezentujúca testovací model

Všetku "mágiu" budeme realizovať v controller-i, ktorý si nazveme PdfController. Do controller-a pridáme akciu Index v ktorej si vyrobíme testovací model:

public class PdfController : System.Web.Mvc.Controller
{
    
public ActionResult Index()
    {
        var model 
= new List<Employee>()
        {
            
new Employee()
            {
                Id 
1, Name "Dominic Galloway"
                Email 
"justo@malesuadaIntegerid.edu"
            
},
            
new Employee()
            {
                Id 
2, Name "Austin Hopper"
                Email 
"pede.et.risus@vehicula.ca"
            
},
            
new Employee()
            {
                Id 
3, Name "Amery Carroll"
                Email 
"tincidunt.pede@non.co.uk"
            
},
            
// ...
        
};
        
        return 
View(model);
    
}
}
Základná implementácia PdfController

V adresári Views -> Pdf vytvoríme razor view Index.cshtml:

@model List<RazorPdf.Models.Employee>
@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    
<meta name="viewport" content="width=device-width" />
    <
title>RazorPdf</title>
    
<style>
        body 
{
            font-family
: "Roboto";
            font-size
: 12px;
        
}

        h1 
{
            text-align
: center;
        
}

        a 
{
            color
: #8b0000;
        
}
    </
style>
</head>
<body>
    
<h1>List of Employees</h1>
    @foreach (var emp in Model)
    {
        
<p>
            
<b>@emp.Name</b> (@emp.Id)
            
<br/>
            
<a href="mailto:@emp.Email">@emp.Email</a>
        
</p>
    }
</body>
</html>
Obsah súboru Index.cshtml

Predošlý view okrem toho že vygeneruje zoznam zamestnancov má v hlavičke definovaný aj jednoduchý štýl. Tento štýl bude aplikovaný na výstupný pdf dokument. V štýle definujeme aj použitie fontu Roboto a to z toho dôvodu, aby sme si ukázali, ako použiť pri generovaní vlastný font s požadovaným kódovaním. Samotný font (súbor Roboto-Regular.ttf) sme nahrali do adresára Fonts.

Pre použitie vlastného fontu pri generovaní PDF máme k dispozícii statickú metódu Register triedy FontFactory. Ak však chceme pre generovaní nastaviť aj vlastné kódovanie, musíme si vytvoriť vlastný font provider. Pre tento účel vytvoríme novú triedu DefaultFontProvider:

public sealed class DefaultFontProvider : FontFactoryImp
{
    
private readonly string _fontName;

    public 
DefaultFontProvider(string fontName "Roboto")
    {
        
// Nazov default fontu
        
this._fontName fontName;

        
// Zaregistrujeme nas custom font
        
Register(HostingEnvironment.MapPath("~/Fonts/Roboto-Regular.ttf"), "Roboto");
    
}

    
public override Font GetFont(string fontname, string encoding, bool embedded, float size, int style, BaseColor color, bool cached)
    {
        
// Ak nebol zadefinovany font tak pouzijem 
        // nas defaultny
        
if (string.IsNullOrWhiteSpace(fontname) || (Math.Abs(size) < 0))
        {
            fontname 
= this._fontName;
        
}

        
// Pri pouziti fontu definujeme aj kodovanie "cp1250"
        
return base.GetFont(fontname, "cp1250", embedded, size, style, color, cached);
    
}
}
Trieda DefaultFontProvider

Vďaka vlastnému font provider-u vo výsledku aj "urýchlime" generovanie PDF, pretože iTextSharp nebude musieť zisťovať aké sú dostupné fonty v systémy, čo niekedy zaberie naozaj dosť času.

Pre získanie výstupného html z nášho razor view-u implementujeme v controller-i pomocnú funkciu ActionResultToString, ktorá vstupný action result pomocou "fake" resonse a contextu skonvertuje na html:

private string ActionResultToString(ActionResult result)
{
    
// Vytvorime writer pre zapis html
    
var sb = new StringBuilder();
    using 
(var writer = new StringWriter(sb))
    {
        
// Vytvorime fake http context a response pre
        // renderovanie view
        
var response = new HttpResponse(writer);
        
var context = new HttpContext(System.Web.HttpContext.Current.Request, response);

        
// Vytvorime fake controller context
        
var controllerContext = new ControllerContext(
            
new HttpContextWrapper(context),
            
this.ControllerContext.RouteData,
            
this.ControllerContext.Controller);

        
// Odlozime si povodny context
        
var oldContext System.Web.HttpContext.Current;

        
// Pouzijeme nas fake context
        
System.Web.HttpContext.Current context;

        
// Vyrenderujeme view
        
result.ExecuteResult(controllerContext);

        
// Obnovime povodny context
        
System.Web.HttpContext.Current oldContext;

        
// Flush-neme vystup do nasho writer objektu
        
writer.Flush();
    
}

    
return sb.ToString();
}
Metóda ActionResultToString

V našej akcii Index na konci voláme return View(model), čo pošle na klienta response obsahujúci výstupné html. Toto volanie vymeníme za volanie return ViewPdf(model), čo už pošle na klienta výsledný PDF dokument. Metóda ViewPdf použije výsledok funkcie ActionResultToString a pomocou knižnice iTextSharp a XMLWorker vytvorí PDF:

private ActionResult ViewPdf(object model, string fileName "test.pdf")
{
    
// Vyrenderujeme view ako html string
    
var html ActionResultToString(View(model));

    
// Vytvorime iTextSharp dokument
    
using (var doc = new Document())
    
using (var stream = new MemoryStream())
    
using (var writer PdfWriter.GetInstance(doc, stream))
    {
        writer.CloseStream 
= false;

        
// Nastavime rozmer stranky
        
doc.SetPageSize(PageSize.A4);
        
doc.SetMargins(35.4f, 35.4f, 45.4f, 45.4f);
        
doc.Open();

        
// Skonvertujeme html na stream
        
byte[] htmlBytes Encoding.UTF8.GetBytes(html);
        using 
(var htmlStream = new MemoryStream(htmlBytes))
        {
            
// Sprasujeme html na pdf s pouzitim DefaultFontProvider
            
XMLWorkerHelper.GetInstance().ParseXHtml(writer
                , doc
                , htmlStream
                , 
null
                
, Encoding.UTF8
                , 
new DefaultFontProvider() as IFontProvider);
        
}

        
// Zavrieme dokument
        
doc.Close();

        
// Vytvorime vysledny buffer
        
var buffer = new byte[stream.Position];
        
stream.Position 0;
        
stream.Read(buffer, 0, buffer.Length);

        
// Ako odpoved vratime pdf dokument
        
return File(buffer, "application/pdf", fileName);
    
}
}
Metóda ViewPdf

Výsledný pdf dokument vyzéra nasledovne. Takto môžeme jednoducho generovať pdf dokumenty pomocou MVC, jediné čo treba spraviť je nadefinovať si základnú štruktúru dokumentu v metóde ViewPdf - tu je možné definovať hlavičku, pätičku, embedovanie obrázkov atď, ale o tom až nabudúce.

Kompletné vzorové riešenie je možné nájsť aj na mojom github-e.

Publikované Tuesday, December 06, 2016 10:40 AM xxxmatko
Zaradené do: , , , , , ,

Komentáre

Bez komentárov