PDF generation

Lors de la création d’une application, on peut avoir besoin de créer des PDF. Par exemple pour créer des rapports envoyés par mail, pour créer des factures, générer des certificats, des diplômes, des tickets. Ce format est très populaire et disponible sur tous les OS. Donc pouvoir ajouter, la génération de PDF dans une application peut être une fonctionnalité très utile. Il existe de nombreux outils pour créer des PDF avec C#. Par exemple PDFSharp, Aspose.PDF… Dans cet article, je vais parler de QuestPDF. Un outil que j’ai testé et que je trouve très simple à utiliser.

Quest PDF est une librairie open source qui permet de générer facilement des PDF en utilisant uniquement du code C#. L’outil propose plusieurs éléments pour créer sa page. Il est possible d’ajouter facilement texte, images, tableaux… On peut prévisualiser son document, sans avoir à recompiler son code. Très populaire, il a été téléchargé plus de 3 millions de fois. Et pour ne rien gâter, il est gratuit pour les particuliers et les sociétés dont le chiffre d’affaire est inférieur à 1M $

Installation

La librairie est disponible sur NuGet. Il est possible de l’installer à partir de son IDE, par exemple Visual Studio Code.

Pour installer simplement des packages sur VS Code, je conseille l’utilisation de l’extension NuGet Package Manager.

Après son installation, taper Ctrl+Shift+P et entrer ensuite nuget puis appuyer « entrée »

NuGet Package Manager

Entrer ensuite QuestPDF puis appuyer « entrée »

Sélectionner Quest PDF dans les résultats puis sélectionner la version que vous souhaitez installer

Voilà.! Difficile de faire plus simple.

License

Avant de se lancer, il faut configurer les informations de License. C’est un bout que code à écrire au lancement de l’application uniquement. En fonction de votre situation:

QuestPDF.Settings.License = LicenseType.Community;

QuestPDF.Settings.License = LicenseType.Professional;

QuestPDF.Settings.License = LicenseType.Enterprise;

C’est tout! L’application ne fait aucun contrôle. L’éditeur choisit de faire confiance à ses utilisateurs.

Génération du document avec QuestPDF

La génération du PDF implique trois éléments principaux:

  • le modèle : un ensemble de classe qui décrit les données de notre fichier PDF. Il ne contient aucune logique métier. Par exemple si notre PDF contiendra des adresses, on aura une class Adress.
  • la source de données: elle contient les classes qui fournissent les données affichées par les modèles. C’est ici qu’on récupère les données d’une base, d’un fichier plat, d’un webservice… Elle utilise les types définis par les modèles.
  • la présentation: elle décrit comment seront affichées nos données dans le PDF. Un logo à droite, un tableau sur fond rose après le second paragraphe…

Nous pouvons classer les éléments en classes de données et classes de génération. Le modèle et la source sont des classes utilisées pour les données tandis que les classes de présentation sont utilisées pour la mise en forme du document.

Les données

Plutôt qu’un long discours, rien ne vaut un exemple. Nous voulons créer la liste des employés d’une entreprise.

Le modèle

Nous voulons afficher des employés d’une entreprise, donc nous allons créer les classes:

Employee

public class Employee
{
	public string Name{ get; set; }
	public string Email{ get; set; }
	public double Salary{ get; set; }
}

CompanyModel

public class CompanyModel
{
    public string CompanyName { get; set; }
    public List<Employee> Employees { get; set; }
}

La source de données

Nous créons la classe CompanyModelDataSource qui contient des données codées en dur. Dans la réalité, la source de la donnée sera une base de données, un web service, un fichier plat…

public static class CompanyModelDataSource
{
	public static CompanyModel GetCompanyDetails()
    {
        return new CompanyModel
        {
            CompanyName = "ABCD",

            Employees = new List<Employee>
            {
                new Employee
                {
                    Name = "Darrel Schmidt",
                    Email = "quisque@icloud.net",
                    Salary = 2051.68
				},
				new Employee
                {
                    Name = "Leigh Gray",
                    Email = "amet.diam@icloud.c.ouk",
                    Salary = 2285.62
                },
                new Employee
                {
				Name = "Kiara Gamble",
				Email = "elementum@outlook.ca",
				Salary = 3228.34
				},
				new Employee
				{
					Name = "Tatiana Wall",
					Email = "sed.auctor@yahoo.net",
					Salary = 1708.67
				},
				new Employee
				{
					Name = "Jerry Weaver",
					Email = "est.ac@protonmail.com",
					Salary = 4556.29
				}
			}
		};
	}
}

Mise en forme du document QuestPDF

Dans cette partie, nous décrivons la mis en forme de notre document. Il s’agit de la description du contenu de notre pdf.

Généralités

Pour cela, il faut créer une classe qui hérite de l’interface IDocument.

public interface IDocument
{
    DocumentMetadata GetMetadata();
    DocumentSettings GetSettings();
    void Compose(IDocumentContainer container);
}
public class CompanyDocument : IDocument
{	
    CompanyModel Model {get;}

    public CompanyDocument(CompanyModel model)
    {
        Model = model;
    }

    DocumentMetadata GetMetadata()
    {...}

    DocumentSettings GetSettings()
    {...}

    void Compose(IDocumentContainer container)
    {...}
}

Exemple de contenu de Compose

public void Compose(IDocumentContainer container)
{
    container
        .Page(page =>
        {
            page.Margin(50);
        
            page.Header().Height(100).Background(Colors.Grey.Lighten1);
            page.Content().Background(Colors.Grey.Lighten3);
            page.Footer().Height(50).Background(Colors.Grey.Lighten1);
        });
}

Le document QuestPDF est composé de trois parties: l’entête (Header), le corps (Content) et le pied de page (Footer).

Pour faciliter la lecture du code, il est conseillé de créer une méthode pour chaque partie de la page.

public void Compose(IDocumentContainer container)
{
    container.Page(page =>
    {
        page.Margin(50);
        
        page.Header().Element(ComposeHeader);
        page.Content().Element(ComposeContent);
        page.Footer().AlignCenter().Text(
            x =>
            {
                x.CurrentPageNumber();
                x.Span(" / ");
                x.TotalPages();
            });
    });
}

Le code ci-dessus crée le contenu de l’entête avec la méthode ComposeHeader et celui du corps avec ComposeContent.

Pour notre pied de page on affiche simplement la page actuelle et le nombre de page total de notre document.

L’entête

ComposeHeader pourrait ressembler à ceci:

void ComposeHeader(IContainer container)
{
    var titleStyle = TextStyle.Default.FontSize(20).SemiBold().FontColor(QuestPDF.Helpers.Colors.Blue.Medium);

    container.Row(row =>
    {
        row.RelativeItem().AlignMiddle().Text($"Liste des employées de la société #{Model.CompanyName}").Style(titleStyle);

        row.ConstantItem(100, Unit.Point).Image("logo.jpg").FitArea();
    });
}

J’affiche simplement le texte « Liste des employées de la société  » + nom de la société avec son logo.

L’élément Row est un container qui contient plusieurs colonnes. Il est idéal lorsqu’on veut aligner des items horizontalement. En revanche, utilisez Column si vous voulez placer les items verticalement.

Entête de notre fichier
L’entête de notre fichier

Le corps

Notre corps affiche un tableau avec la liste des employés.

void ComposeContent(IContainer container)
{
    container.PaddingVertical(40).Column(column =>
    {
       column.Spacing(5);
       column.Item().Element(ComposeTable);
    });
}

Notre tableau est généré par la méthode ComposeTable.

void ComposeTable(IContainer container)
{
    container.Table(table =>
    {
        // step 1
        table.ColumnsDefinition(columns =>
        {
            columns.RelativeColumn(1);
            columns.RelativeColumn(3);
            columns.RelativeColumn(4);
            columns.RelativeColumn(2);
        });
        
        // step 2
        table.Header(header =>
        {
            header.Cell().Element(CellStyle).Text("#");
            header.Cell().Element(CellStyle).Text("Name");
            header.Cell().Element(CellStyle).AlignLeft().Text("E-mail");
            header.Cell().Element(CellStyle).AlignRight().Text("Salary");
            
            static IContainer CellStyle(IContainer container)
            {
                return container.DefaultTextStyle(x => x.SemiBold()).PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black);
            }
        });
        
        // step 3
        foreach (var item in Model.Employees)
        {
            
            table.Cell().Element(CellStyle).Text((Model.Employees.IndexOf(item) + 1).ToString());
            table.Cell().Element(CellStyle).Text(item.Name);
            table.Cell().Element(CellStyle).AlignLeft().Text(item.Email);
            table.Cell().Element(CellStyle).AlignRight().Text($"{item.Salary} €");

            
            static IContainer CellStyle(IContainer container)
            {
                return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5);
            }
        }
    });
}

La création du tableau se fait en trois étapes

  • étape 1: définition des colonnes
  • étape 2: définition de l’entête, le texte et le style
  • étape 3: insertion des données des employées dans le tableau
Notre tableau
Notre tableau

Création du fichier QuestPDF

Enfin, le code à lancer pour générer notre fichier est très simple

static void Main(string[] args)
{
    var filePath = "employees.pdf";

    var model = CompanyModelDataSource.GetCompanyDetails();
    var document = new CompanyDocument(model);
    document.GeneratePdf(filePath);

    Process.Start("explorer.exe", filePath);
}

Conclusion

QuestPDF C# offre une solution simple et flexible pour la génération de documents PDF avec C#. Vous pouvez ajouter sans difficulté textes, tableaux, images. De plus, la documentation fournie propose de nombreux exemples facilitant son apprentissage et son utilisation. A vous la génération de rapports, factures, bons de commande sans tracas…

Auteur : Daniel MINKO FASSINOU

Laisser un commentaire




Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.