Diese Seite kann automatisch übersetzten Text enthalten.

Kurzanleitung zur C#-PDF-Generierung

Lesen Sie, wie Sie in Ihren .NET-Projekten PDF-Dateien wie Berichte, Rechnungen und Quittungen generieren. Erstellen Sie mit C# oder VB.NET ganz einfach PDF-Dokumente, indem Sie Strukturelemente zusammenstellen. Zu den Elementen gehören Kopfzeilen, Fußzeilen, Container, Tabellen, Absätze, Bilder und dergleichen. Die API unterteilt Inhalte automatisch in Seiten.

Einige andere Methoden zum Generieren von PDF-Dateien werden im Artikel PDF-Dokumente in C# und VB.NET erstellen beschrieben.

Einführung in die PDF-Generierungsbibliothek

Wir bieten die Layout-API als kostenloses Add-on für die Docotic.Pdf-Bibliothek an. Sowohl die Bibliothek als auch das Layout-Add-on sind auf NuGet und auf unserer Website verfügbar. Um die Bibliothek ohne Einschränkungen des Testmodus auszuprobieren, holen Sie sich den kostenlosen, zeitlich begrenzten Lizenzschlüssel auf der Bibliotheksseite.

Docotic.Pdf-Bibliothek 9.4.17342 Layout-Add-on 9.4.17342
Regressionstests 14,727 bestanden NuGet-Downloads insgesamt 4,260,602

Installation

Installieren Sie das BitMiracle.Docotic.Pdf.Layout-Paket von NuGet. Dies ist die einfachste und bequemste Möglichkeit, das Layout-Add-on zu installieren.

Alternativ können Sie auf unserer Website die ZIP-Datei mit den Binärdateien der Bibliothek herunterladen. Um die Layout-API zu verwenden, fügen Sie Verweise auf die folgenden DLLs aus dem ZIP-Paket hinzu:

  • BitMiracle.Docotic.Pdf.dll
  • Layout add-on/BitMiracle.Docotic.Pdf.Layout.dll

Die Vorgehensweise

Der Einstiegspunkt ist die Klasse PdfDocumentBuilder. Um mit der Generierung einer PDF-Datei zu beginnen, rufen Sie die Methode Generate der Klasse auf. Die Methode erfordert einen Delegaten vom Typ Action<Document> als einen ihrer Parameter. Die Bibliothek erwartet von Ihnen, dass Sie Dokumentinhalte im Delegaten gestalten.

PdfDocumentBuilder.Create().Generate("output.pdf", (Document doc) =>
{
    // Erstellen Sie hier Dokumentinhalte
});

Bei einem Dokument-Objekt sollte der Delegat das Layout definieren. Die komplette Anlage besteht aus kleineren Bausteinen. Beispiele für solche Blöcke sind Seiten, Kopf- und Fußzeilen, Container und Textblöcke.

Viele Methodenaufrufe sind miteinander verkettet. Die Reihenfolge der Aufrufe in einer Kette ist wichtig. Hier ist ein Beispiel, das zeigt, wie Seiteninhalte in einigen verketteten Aufrufen eingerichtet werden.

PdfDocumentBuilder.Create()
    .Generate("output.pdf", doc => doc.Pages(pages =>
    {
        pages.Content().Padding(24).AlignCenter().Text("Some text");
    }));

Intellisense hilft Ihnen bei der Entdeckung der API. Probieren Sie die API selbst aus. Ich hoffe, Sie finden es einfach zu bedienen.

Hallo Welt!

Lassen Sie uns eine einfache Anwendung entwickeln, die gängige Bausteine in Aktion zeigt. Die App verwendet den oben genannten Ansatz.

Ich habe den vollständigen Anwendungscode in unser Docotic.Pdf-Beispielcode-Repository eingefügt. Der Code für diesen Schritt befindet sich in der Klasse HelloWorld der Beispiel-App.

public void CreatePdf()
{
    PdfDocumentBuilder.Create().Generate("hello.pdf", doc => doc.Pages(page =>
    {
        page.Content().Text("Hello, world!");
    }));
}

Nicht viel Code, aber was passiert hier?

Lange Erklärung für den Funktionscode

Der Code erstellt eine Instanz des Dokument-Builders und fordert den Builder auf, hello.pdf zu generieren. Der Builder delegiert die Aufgabe des Dokumentinhaltslayouts an meinen Code.

Im Code des Delegaten fordere ich das Dokument auf, einige Seiten zu erstellen. Jetzt delegiert das Dokument die Aufgabe, den Inhalt der Seiten an meinen Code zu verteilen.

Im Delegaten für die Seiten greife ich auf den Layout-Container für den primären Inhalt der Seiten zu. Ich mache das, indem ich die Methode Content aufrufe. Der verkettete Aufruf von Text fügt dem Inhalt der Seiten den berühmten Textbereich hinzu.

Jetzt ist es an der Zeit, dass der Builder das Dokument mit einigen Seiten entsprechend dem bereitgestellten Layout generiert. Der Builder erstellt genau die Anzahl an Seiten, die erforderlich ist, um die Daten aufzunehmen, die ich dem Layout-Container für den primären Inhalt hinzugefügt habe. Reicht in diesem Fall eine Seite? Okay.

Bereiten Sie sich auf zukünftige Updates vor

Ich werde dem Code noch einige weitere Funktionen hinzufügen. Um die Weiterentwicklung der App komfortabler zu gestalten, habe ich den Code wie folgt geändert:

public void CreatePdf()
{
    PdfDocumentBuilder.Create().Generate("hello.pdf", BuildDocument);
}

static void BuildDocument(Document doc)
{
    doc.Pages(BuildPages);
}

static void BuildPages(PageLayout pages)
{
    pages.Content().Text("Hello, world!");
}

Es sieht vielleicht unnötig aus, aber die Aufteilung des Codes in Methoden macht ihn zumindest lesbarer.

Der Beispielcode für diesen Schritt befindet sich in der Klasse HelloWorld2 von der Beispiel-App.

Weitere Informationen zu Dokumenten, Seiten und Containern finden Sie in den folgenden Artikeln.

Schriftarten und Farben

Die Standardschriftart ist gut, aber ich werde zeigen, wie man eine andere verwendet. Die Hauptidee besteht darin, mithilfe einer Schriftart einen Textstil zu erstellen. Wenden Sie dann bei Bedarf einige optionale Eigenschaften auf den Stil an und verwenden Sie den Stil schließlich für einen Textbereich.

Sie können eine Schriftart aus der im Betriebssystem installierten Schriftartensammlung verwenden oder eine Schriftart aus einer Datei oder einem Stream laden.

Ich empfehle, die vordefinierten Stile zu überschreiben, indem Sie Typografie für das Dokument einrichten. Dies ist jedoch nicht erforderlich und Sie können Textstile direkt verwenden.

Der Beispielcode für diesen Schritt befindet sich in der Klasse HelloWorld3 von der Beispiel-App. Nachfolgend sind die Teile aufgeführt, die sich gegenüber dem vorherigen Schritt geändert haben.

static void BuildDocument(Document doc)
{
    doc.Typography(t =>
    {
        t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
    });
    doc.Pages(BuildPages);
}

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildTextContent(pages.Content());
}

static void BuildTextContent(LayoutContainer content)
{
    var colorAccented = new PdfRgbColor(56, 194, 10);
    var styleAccented = TextStyle.Parent.FontColor(colorAccented);

    content.Text(t =>
    {
        t.Span("Hello, ").Style(styleAccented);
        t.Span("world!").Style(styleAccented.Underline());
    });
}

Wie Sie sehen können, habe ich den Code für BuildDocument und BuildPages aktualisiert. Ich habe auch eine neue Methode namens BuildTextContent hinzugefügt.

In BuildDocument erstelle ich einen Textstil aus der Systemschriftart namens „Calibri“. Dann lege ich diesen Textstil als Standardtextstil für das Dokument fest.

Die Methode BuildPages enthält jetzt Code zum Festlegen von Größe und Rand für alle Seiten. Außerdem ruft die Methode BuildTextContent auf und übergibt den Container für den primären Inhalt der Seiten als Parameter.

Die Art und Weise, wie ich den Hauptinhalt aufbaue, ist jetzt anders. Ich verwende zwei Textbereiche und wende auf jeden Bereich unterschiedliche Textstile an. Tatsächlich verwenden beide Bereiche die Akzentfarbe, aber der zweite Bereich verfügt auch über eine Unterstreichung.

Der Code erzeugt ein PDF mit der benutzerdefinierten Textschriftart und -farbe. Wenn Ihr Ergebnis eine Testwarnung enthält, holen Sie sich bitte eine kostenlose, zeitlich begrenzte Lizenz auf der Bibliotheksseite Docotic.Pdf.

Viele reale PDF-Dokumente wie Berichte oder Rechnungen enthalten Kopf- und Fußzeilen. Lassen Sie mich zeigen, wie man einer PDF-Datei Kopf- und Fußzeilen hinzufügt.

Der Beispielcode für diesen Schritt befindet sich in der Klasse HelloWorld4 von der Beispiel-App. Nachfolgend sind die Teile aufgeführt, die sich gegenüber dem vorherigen Schritt geändert haben.

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildPagesHeader(pages.Header());
    BuildPagesFooter(pages.Footer());

    BuildTextContent(pages.Content());
}

static void BuildPagesHeader(LayoutContainer header)
{
    header.TextStyle(TextStyle.Parent.FontSize(8))
        .AlignRight()
        .Text(t =>
        {
            t.Line($"Created by: {Environment.UserName}");
            t.Line($"Date: {DateTime.Now}");
        });
}

static void BuildPagesFooter(LayoutContainer footer)
{
    footer.Height(20)
        .Background(new PdfGrayColor(95))
        .AlignCenter()
        .Text(t => t.CurrentPageNumber());
}

Ich habe die Methode BuildPages so geändert, dass sie BuildPagesHeader mit dem Header-Container als Parameter aufruft. Die Methode ruft auch BuildPagesFooter auf und übergibt ihr den Fußzeilencontainer.

Das resultierende PDF mit Kopf- und Fußzeile sieht definitiv eher wie ein professionelles PDF-Dokument aus.

Die Methode BuildPagesHeader legt eine kleinere Schriftgröße für den Text fest. Ich verwende zwei Zeilen für den Header-Inhalt: eine mit dem Namen des aktuellen Benutzers und die zweite mit dem aktuellen Datum. Der Text ist rechtsbündig.

Bitte beachten Sie, dass ich keine explizite Größe für den Header vorgebe. Es nimmt die gesamte Seitenbreite abzüglich des linken und rechten Rands ein. Die Höhe hängt von der Höhe der Textzeilen ab.

Die Fußzeile ist ähnlich, außer dass ihre Höhe explizit angegeben wird. Und es wird eine Hintergrundfarbe darauf angewendet. Das Interessante daran ist, dass ich zum Einfügen der aktuellen Seitenzahl in die PDF-Fußzeile einfach die CurrentPageNumber-Methode verwenden kann. Alle Berechnungen erfolgen innerhalb der Bibliothek.

Bilder

Wie man so schön sagt, kann man durch die Verwendung eines Bildes viele Wörter einsparen. In einem Angebot oder einer Quittung werden Sie wahrscheinlich ein Logo Ihres Unternehmens oder ein anderes wichtiges Bild verwenden. Für diesen Beispielcode verwende ich nur einen gut aussehenden Code.

Die Datei befindet sich auf unserer Website, damit Sie sehen können, wie das resultierende PDF mit dem schönen Bild aussieht.

Um ein Bild zu verwenden, müssen Sie es zunächst dem Dokument hinzufügen. Die Bibliothek kann das Bild aus einer Datei oder einem Stream lesen. Ich habe die Methode BuildDocument geändert, um zu zeigen, wie man dem Dokument ein Bild hinzufügt.

Der Beispielcode für diesen Schritt befindet sich in der Klasse HelloWorld5 von der Beispiel-App. Nachfolgend sind die Teile aufgeführt, die sich gegenüber dem vorherigen Schritt geändert haben.

static void BuildDocument(Document doc)
{
    doc.Typography(t =>
    {
        t.Document = doc.TextStyleWithFont(SystemFont.Family("Calibri"));
    });

    var imageFile = new FileInfo("red-flowers-at-butterfly-world.jpg");
    var image = doc.Image(imageFile);

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
        });
    });
}

static void BuildPages(PageLayout pages)
{
    pages.Size(PdfPaperSize.A6);
    pages.Margin(10);

    BuildPagesHeader(pages.Header());
    BuildPagesFooter(pages.Footer());
}

static void BuildImageContent(LayoutContainer content, Image image)
{
    content.AlignCenter()
        .PaddingVertical(20)
        .Image(image);
}

Wie Sie sehen können, gibt es weitere Änderungen in der Methode BuildDocument. Zuvor verwendete der Code die Methode Text, um Text als primären Inhalt für Seiten festzulegen. Dies geschieht immer noch in der Methode BuildTextContent. Jetzt möchte ich aber auch ein Bild im Inhalt.

Um sowohl den Text als auch das Bild im Hauptinhalt der Seiten zu haben, benötige ich einen Container. Ich verwende den Container Column, um den Text und das Bild vertikal nacheinander hinzuzufügen. Die Methode Item des Spaltencontainers stellt einen Untercontainer bereit. Ich verwende einen Aufruf der Methode Item, um einen Container für den Text abzurufen, und einen weiteren Aufruf, um einen Container für das Bild abzurufen.

Wie Sie sehen, habe ich BuildTextContent kein bisschen verändert. Aber natürlich musste ich einen Aufruf von BuildTextContent aus der Methode BuildPages entfernen.

BuildImageContent erledigt in drei Zeilen eine wichtige Aufgabe. Es fügt das Bild mit etwas Polsterung oben und unten hinzu. Außerdem wird das Bild auf der Seite zentriert.

Listen

Was ist eine Liste? Stellen wir es uns als eine Gruppe nummerierter Elemente vor, die untereinander geschrieben sind. Diese Idee schlägt eine Möglichkeit vor, eine Liste zu implementieren.

Der Beispielcode für diesen Schritt befindet sich in der Klasse HelloWorld6 von der Beispiel-App. Nachfolgend sind die Teile aufgeführt, die sich gegenüber dem vorherigen Schritt geändert haben.

static void BuildDocument(Document doc)
{
    ....

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
            BuildListContent(c.Item());
        });
    });
}

static void BuildListContent(LayoutContainer content)
{
    var dayNames = DateTimeFormatInfo.InvariantInfo.DayNames;
    var dayNamesSpain = DateTimeFormatInfo.GetInstance(new CultureInfo("es-ES")).DayNames;

    content.Column(column =>
    {
        for (int i = 0; i < dayNames.Length; i++)
        {
            column.Item().Row(row =>
            {
                row.Spacing(5);
                row.AutoItem().Text($"{i + 1}.");
                row.RelativeItem().Text(t => {
                    t.Line(dayNames[i]);
                    t.Line($"In Spain they call it {dayNamesSpain[i]}");
                });
            });
        }
    });
}

Die einzige Änderung in BuildDocument ist der neue Methodenaufruf. Ich habe den Aufruf von BuildListContent nach dem BuildImageContent-Aufruf hinzugefügt.

BuildListContent erstellt die Liste als Zeilenspalte. In jeder Zeile gibt es zwei Elemente. Die erste (links) ist für die Artikelnummer. Eine weitere (rechte) ist für den Artikeltext. Der Code legt explizit den Abstand zwischen den Elementen in der Zeile fest.

Um Elemente anzuordnen, muss der Container Row vorher die Größe jedes Elements kennen oder berechnen. In diesem Beispiel verwende ich die Methoden AutoItem und RelativeItem. Als Ergebnis berechnet der Zeilencontainer die Breite, die erforderlich ist, um das erste Element aufzunehmen. Dann nutzt der Container die verbleibende verfügbare Breite für das zweite Element.

Nach dem Hinzufügen der Liste passt der Inhalt der resultierenden PDF-Datei nicht mehr auf eine Seite. Die Layout-API fügt automatisch die zweite Seite hinzu und platziert die Liste darauf. Sie können sehen, dass die API die Kopf- und Fußzeile auf der neuen Seite im PDF-Dokument mit mehreren Seiten wiederholt hat.

Tabellen

Viele PDF-Dokumente enthalten Tabellen. Dies ist keine Überraschung, da Tabellen die Klarheit und Organisation der Daten verbessern. Lassen Sie mich zeigen, wie Sie mit der Layout-API eine Tabelle im PDF-Format erstellen.

Der Beispielcode für diesen Schritt befindet sich in der Klasse HelloWorld7 von der Beispiel-App. Nachfolgend sind die Teile aufgeführt, die sich gegenüber dem vorherigen Schritt geändert haben.

static void BuildDocument(Document doc)
{
    ....

    doc.Pages(pages => {
        BuildPages(pages);

        pages.Content().Column(c =>
        {
            BuildTextContent(c.Item());
            BuildImageContent(c.Item(), image);
            BuildListContent(c.Item());
            BuildTableContent(c.Item());
        });
    });
}

static void BuildTableContent(LayoutContainer content)
{
    var color = new PdfGrayColor(75);

    content.PaddingTop(20).Table(t =>
    {
        t.Columns(c =>
        {
            c.RelativeColumn(4);
            c.RelativeColumn(1);
            c.RelativeColumn(4);
        });

        t.Header(h =>
        {
            h.Cell().Background(color).Text("Month in 2024");
            h.Cell().Background(color).Text("Days");
            h.Cell().Background(color).Text("First Day");
        });

        for (int i = 0; i < 12; i++)
        {
            var stats = GetMonthStats(2024, i);

            t.Cell().Text(stats.Item1);
            t.Cell().Text(stats.Item2);
            t.Cell().Text(stats.Item3);
        }
    });
}

static (string, string, string) GetMonthStats(int year, int monthIndex)
{
    return (
        DateTimeFormatInfo.InvariantInfo.MonthNames[monthIndex],
        DateTime.DaysInMonth(year, monthIndex + 1).ToString(),
        new DateTime(year, monthIndex + 1, 1).DayOfWeek.ToString()
    );
}

Der Aufruf von BuildTableContent ist die einzige Änderung in BuildDocument.

In BuildTableContent erstelle ich eine einfache Tabelle. Die Tabelle zeigt triviale Informationen über Monate im Jahr 2024. Zunächst definiere ich drei Spalten mit relativen Breiten. Die Spalten ganz links und ganz rechts sind viermal breiter als die Spalte in der Mitte.

Der Code definiert auch den Header für die Tabelle. Dies geschieht durch das Hinzufügen von Kopfzellen und das Festlegen von Text und Hintergrundfarbe für jede Zelle. Wenn die Tabelle nicht auf eine Seite passt, wird die Kopfzeile auf jeder von der Tabelle belegten Seite wiederholt. Sie können dies im resultierenden PDF-Dokument mit Tabelle sehen. Im Dokument beginnt die Tabelle auf der zweiten Seite und wird auf der dritten Seite fortgesetzt.

Ich verwende die einfache Schleife, um Zeilen zu bilden. Der Code in der Schleife beginnt mit dem Abrufen der Informationen zu jedem Monat. Anschließend werden drei Zellen hinzugefügt, die eine Informationszeile bilden.

Beispielcode

Oben habe ich einige der beliebtesten Funktionen der PDF-Generierung in C# vorgestellt. Ich empfehle Ihnen, die API weiter zu entdecken, indem Sie den folgenden Artikel lesen. Probieren Sie auch den Beispielcode aus dem Docotic.Pdf.Samples-Repository auf GitHub aus. Der Beispielcode für die Layout-API befindet sich im Layout-Ordner des Repositorys.

Sie können den Beispielcode als Codespielplatz verwenden. Auf diese Weise können Sie einige Ideen ausprobieren, ohne jedes Mal bei Null anfangen zu müssen.

Der gleiche Beispielcode befindet sich im Ordner Samples des ZIP-Pakets. Es gibt zwei Lösungsdateien. SamplesCSharp ist für Beispielprojekte gedacht, die die Sprache C# verwenden. Und SamplesVB.NET ist für die VB.NET-Versionen.