TerraPDF 1.2.3

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package TerraPDF --version 1.2.3
                    
NuGet\Install-Package TerraPDF -Version 1.2.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TerraPDF" Version="1.2.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TerraPDF" Version="1.2.3" />
                    
Directory.Packages.props
<PackageReference Include="TerraPDF" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TerraPDF --version 1.2.3
                    
#r "nuget: TerraPDF, 1.2.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TerraPDF@1.2.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TerraPDF&version=1.2.3
                    
Install as a Cake Addin
#tool nuget:?package=TerraPDF&version=1.2.3
                    
Install as a Cake Tool

TerraPDF

A free, pure C# library designed for fast and reliable PDF generation.

TerraPDF

NuGet License: MIT

πŸ“š Documentation: https://github.com/sahebansari/TerraPDF/tree/master/docs

New in 1.2.3: Unicode & Character Encoding guide β€” WinAnsiEncoding coverage, Windows-1252 specials, Latin-1 Supplement, and how to avoid ? characters. Vector Graphics guide β€” Canvas API, lines, shapes, BΓ©zier paths, polygons, grids, charts.

TerraPDF is a lightweight, zero-dependency, pure C# library for generating professional PDF 1.7 documents programmatically. It provides a fluent, composable API that covers the full document-authoring lifecycle β€” from page layout and rich text to tables, images, hyperlinks, and multi-page pagination β€” with no native binaries, no third-party runtime packages, and no licensing restrictions.

  • No native dependencies, no third-party packages
  • Targets .NET 8 and .NET 9
  • Text styling β€” bold, italic, bold-italic, strikethrough, underline, font size, colour
  • Configurable line-height multiplier per text block
  • Per-span formatting inside mixed-style text blocks
  • Margin and padding decorators with full unit support
  • Background fills and borders (full, rounded, and per-edge)
  • Rounded-corner borders and filled rounded boxes
  • Per-edge borders β€” BorderTop, BorderBottom, BorderLeft, BorderRight
  • Horizontal and vertical alignment
  • Column, Row, and Table layouts
  • PNG and JPEG image embedding
  • Horizontal and vertical rule lines
  • Explicit page breaks via PageBreak()
  • Clickable hyperlink (URI) annotations via Hyperlink()
  • Internal document links (GoTo) via InternalLink()
  • Automatic Table of Contents generation from H1–H6 headings
  • PDF bookmarks / outlines with hierarchical nesting
  • Document metadata (Title, Author, Subject, Keywords, Creator)
  • Conditional rendering via ShowIf
  • Reusable components via IComponent
  • Headers, footers, and page numbers
  • Full WinAnsiEncoding character coverage β€” Latin-1 Supplement (U+00A0–U+00FF) and all 27 Windows-1252 typographic specials (€ β„’ β€˜ ’ β€œ ” – β€” … β€’) with exact Adobe AFM glyph widths
  • Vector graphics canvas β€” lines, rectangles, rounded rectangles, circles, ellipses, arbitrary BΓ©zier paths, polygons, and grid helpers via container.Canvas()
  • Fluent, composable API

Installation

dotnet add package TerraPDF

Quick Start

using TerraPDF.Core;
using TerraPDF.Helpers;

Document.Create(container =>
{
    container.Page(page =>
    {
        page.Size(PageSize.A4);
        page.Margin(2, Unit.Centimetre);
        page.PageColor(Color.White);
        page.DefaultTextStyle(s => s.FontSize(11));

        page.Header()
            .Text("My Report")
            .Bold()
            .FontSize(24)
            .FontColor(Color.Blue.Darken2);

        page.Content()
            .Column(col =>
            {
                col.Spacing(8);
                col.Item().Text("Hello, TerraPDF!");
                col.Item().Text("This paragraph is italic.").Italic();
                col.Item()
                   .Margin(6).Background(Color.Grey.Lighten4).Padding(10)
                   .Text("Indented callout box").Bold();
            });

        page.Footer()
            .AlignCenter()
            .Text(t =>
            {
                t.Span("Page ");
                t.CurrentPageNumber().FontSize(9);
                t.Span(" / ");
                t.TotalPages().FontSize(9);
            });
    });
})
.PublishPdf("output.pdf");

API Overview

Document entry points

Method Description
Document.Create(Action<IDocumentContainer>) Inline composition callback
Document.Create(IDocument) Reusable IDocument class

Page configuration

page.Size(PageSize.A4);
page.Margin(2, Unit.Centimetre);   // page margin
page.PageColor(Color.White);
page.DefaultTextStyle(s => s.FontSize(11));
page.Header()   // returns IContainer
page.Content()  // returns IContainer
page.Footer()   // returns IContainer

Text

Single-string text

container.Text("Hello, world!")
    .Bold()
    .SemiBold()
    .Italic()
    .Strikethrough()
    .Underline()
    .FontSize(18)
    .LineHeight(1.5)  // line-height multiplier (default β‰ˆ 1.4)
    .FontColor(Color.Blue.Darken2)
    .AlignLeft()      // default
    .AlignCenter()
    .AlignRight()
    .Justify();

Multi-span text

Each Span(), CurrentPageNumber(), and TotalPages() returns a SpanDescriptor so formatting applies only to that span, not to the whole block.

container.Text(t =>
{
    t.Span("Normal  ");
    t.Span("Bold  ").Bold();
    t.Span("Italic  ").Italic();
    t.Span("Struck  ").Strikethrough();
    t.Span("Coloured  ").FontColor(Color.Red.Medium);
    t.Span("Large").FontSize(16).FontColor("#1a4a8a");
});

Page numbers in footers

container.Text(t =>
{
    t.Span("Page ").FontSize(9).FontColor(Color.Grey.Medium);
    t.CurrentPageNumber().FontSize(9).FontColor(Color.Grey.Medium);
    t.Span(" / ").FontSize(9);
    t.TotalPages().FontSize(9);
});

Layout

Column β€” stacks children vertically

container.Column(col =>
{
    col.Spacing(6);                       // gap between items in points
    col.AlignItemsLeft();                 // default
    col.AlignItemsCenter();
    col.AlignItemsRight();

    col.Item().Text("First");
    col.Item().Text("Second");
});

Row β€” arranges children horizontally

container.Row(row =>
{
    row.Spacing(8);                       // gap between items in points

    row.RelativeItem(2).Text("2x wide"); // proportional share of remaining space
    row.RelativeItem(1).Text("1x wide");
    row.AutoItem().Text("Auto width");   // natural content width
    row.ConstantItem(80).Text("80 pt");  // fixed width in points
});

Table β€” grid with repeating header rows

container.Table(table =>
{
    table.ColumnsDefinition(cols =>
    {
        cols.RelativeColumn(4);   // proportional
        cols.RelativeColumn(1);
        cols.ConstantColumn(60);  // fixed width in points
    });

    // HeaderRow repeats on every continuation page
    table.HeaderRow(row =>
    {
        row.Cell().Background("#1a4a8a").Padding(6).Text("Description").Bold().FontColor(Color.White);
        row.Cell().Background("#1a4a8a").Padding(6).Text("Qty").Bold().FontColor(Color.White);
        row.Cell().Background("#1a4a8a").Padding(6).AlignRight().Text("Amount").Bold().FontColor(Color.White);
    });

    table.Row(row =>
    {
        row.Cell().Padding(6).Text("Web Development");
        row.Cell().Padding(6).Text("1");
        row.Cell().Padding(6).AlignRight().Text("$2,500.00");
    });
});

Decorators

Decorators are chainable and compose from the outside in: .Margin() β†’ .Background() β†’ .Padding() β†’ content

Padding β€” inner spacing (inside background / border)

container.Padding(10)                    // all sides
container.PaddingVertical(8)             // top + bottom
container.PaddingHorizontal(12)          // left + right
container.PaddingTop(4)
container.PaddingBottom(4)
container.PaddingLeft(6)
container.PaddingRight(6)

// All padding methods also accept a Unit:
container.Padding(0.5, Unit.Centimetre)

Margin β€” outer spacing (outside background / border)

container.Margin(10)                     // all sides
container.MarginVertical(8)              // top + bottom
container.MarginHorizontal(12)           // left + right
container.MarginTop(4)
container.MarginBottom(4)
container.MarginLeft(6)
container.MarginRight(6)

// All margin methods also accept a Unit:
container.Margin(0.5, Unit.Centimetre)

Padding vs Margin .Margin(10).Background("red").Padding(5).Text("Hi") β€” the red background starts after the 10 pt margin gap; the text is inset 5 pt from the red edge.

Background and Border

container.Background("#1a4a8a")
container.Border(1.5, "#1a4a8a")        // line width + colour
container.Border(1)                      // black by default

// Rounded-corner border
container.RoundedBorder(radius: 8, lineWidth: 1, hexColor: "#1a4a8a")

// Filled rounded box (background + border)
container.RoundedBox(radius: 8, fillHexColor: "#E3F2FD", borderHexColor: "#1a4a8a")

// Per-edge borders
container.BorderTop(1.5, "#1a4a8a")
container.BorderBottom(1)
container.BorderLeft(3, Color.Red.Medium)
container.BorderRight(1, Color.Grey.Lighten2)

Page Break

// Force a new page at this position inside a Column
container.PageBreak();
// Wrap any content in a clickable URI annotation
container.Hyperlink("https://example.com").Text("Click here");
container.Hyperlink("https://example.com").Image("logo.png", 120);

Wrap content in a clickable internal hyperlink that jumps to a specific page and optional vertical position:

container.InternalLink(pageNumber, topPosition).Text("Go to section");

This creates a /GoTo action in the PDF. The automatic Table of Contents uses internal links to make each entry clickable.

Alignment

// Horizontal
container.AlignLeft()
container.AlignCenter()
container.AlignRight()

// Vertical
container.AlignMiddle()
container.AlignBottom()

Lines

container.LineHorizontal(1.5, "#1a4a8a")   // horizontal rule
container.LineVertical(1)                   // vertical rule (black)

Conditional rendering

container.ShowIf(isAdmin).Text("Admin panel");

Vector Graphics

Place a drawing canvas anywhere in your layout with container.Canvas(height, draw). The callback receives a VectorCanvas with primitives for every common shape:

container.Canvas(120, c =>
{
    // Shapes
    c.FillRect(0, 0, 80, 60, Color.Blue.Lighten4);
    c.StrokeRect(0, 0, 80, 60, Color.Blue.Darken2, 1.5);
    c.FillCircle(140, 30, 25, Color.Orange.Medium);
    c.DrawRoundedRect(200, 5, 100, 50, 8, Color.Green.Lighten4, Color.Green.Darken2);

    // Arbitrary path (triangle)
    c.Path(p => p
        .MoveTo(340, 60)
        .LineTo(380, 10)
        .LineTo(420, 60)
        .Close()
        .Fill(Color.Purple.Lighten3)
        .Stroke(Color.Purple.Darken2, 1));

    // Grid background
    c.Grid(20, hexColor: "#E8EDF2", lineWidth: 0.3);
});

VectorCanvas methods

Method Description
Line(x1,y1,x2,y2, color, lw) Straight line
FillRect / StrokeRect / DrawRect Rectangle (fill / stroke / both)
FillRoundedRect / StrokeRoundedRect / DrawRoundedRect Rounded rectangle
FillCircle / StrokeCircle / DrawCircle Circle
FillEllipse / StrokeEllipse / DrawEllipse Ellipse
Path(Action<PathDescriptor>) Arbitrary path with BΓ©zier curves, polygons, holes
Grid(cellWidth, cellHeight?, color, lw) Full-canvas rectangular grid

PathDescriptor

canvas.Path(p => p
    .MoveTo(10, 10)
    .LineTo(90, 10)
    .CurveTo(90, 10, 90, 80, 50, 80)  // cubic BΓ©zier
    .Close()
    .Fill(Color.Blue.Lighten4)
    .Stroke(Color.Blue.Darken2, 1.5));

// Convenience: Polygon, Polyline, Rect, Ellipse, Circle
canvas.Path(p => p
    .Polygon((50,10),(90,80),(10,80))
    .Fill(Color.Orange.Medium));

// Shapes with holes β€” even-odd fill
canvas.Path(p => p
    .Circle(60, 60, 50)  // outer
    .Circle(60, 60, 25)  // inner (hole)
    .Fill(Color.Blue.Medium)
    .UseEvenOddFill());

See docs/vector-graphics.md for the full reference.


Images

// Fill available width, preserve aspect ratio (PNG or JPEG)
container.Image("path/to/image.png");
container.Image("path/to/photo.jpg");

// Fixed width in points, can be positioned with alignment
container.AlignCenter().Image("logo.png", 120);

Headings

TerraPDF provides six levels of section headings: .H1() through .H6(). Each heading uses sensible defaults (size + weight), which you can further customise via the fluent TextDescriptor API.

container.H1("Chapter Title").FontColor(Color.Blue.Darken2);
container.H2("Section Title").Underline();
container.H3("Subsection");

Default heading styles

Level Font size Style
H1 24 pt Bold
H2 20 pt Bold
H3 16 pt Bold
H4 14 pt Italic
H5 12 pt Bold
H6 11 pt Regular

Table of Contents

Call container.TableOfContents() to add a contents page that is automatically populated with all headings in the document. Headings appear with correct logical page numbers (excluding the TOC page itself) and clickable internal links that jump to the exact physical page.

Document.Create(container =>
{
    // Create a Table of Contents page
    container.TableOfContents(p =>
    {
        p.Size(PageSize.A4);
        p.Margin(2, Unit.Centimetre);
        p.DefaultTextStyle(s => s.FontSize(11));
    });

    // Chapters with headings
    container.Page(p =>
    {
        p.Content()
            .H1("Introduction")
            .H2("Getting Started")
            .H3("Installation")
            .H4("NuGet Package");
    });

    container.Page(p =>
    {
        p.Content()
            .H1("Core Features")
            .H2("Text & Typography")
            .H2("Layout Containers")
            .H2("Tables");
    });
})
.PublishPdf("toc_demo.pdf");

Note: Page numbers displayed in the TOC start from 1 for the first page after the TOC (i.e., the TOC page is treated as page 0). The internal links point to the correct physical pages, accounting for the TOC's actual page count.

Two-pass rendering TerraPDF performs an initial measurement pass to collect heading positions and page numbers, then emits the final PDF with accurate TOC links.


Reusable Components

public class CalloutBox : IComponent
{
    private readonly string _text;
    public CalloutBox(string text) => _text = text;

    public void Compose(IContainer container) =>
        container
            .Margin(6).Background(Color.Blue.Lighten4).Padding(10)
            .Text(_text).Italic();
}

// Usage
container.Component(new CalloutBox("Note: this is important."));

Reusable Document Template

public class InvoiceDocument : IDocument
{
    private readonly InvoiceData _data;
    public InvoiceDocument(InvoiceData data) => _data = data;

    public void Compose(IDocumentContainer container)
    {
        container.Page(page =>
        {
            page.Size(PageSize.A4);
            page.Margin(2, Unit.Centimetre);
            page.Content().Text($"Invoice #{_data.Number}").Bold().FontSize(24);
        });
    }
}
// Usage
Document.Create(new InvoiceDocument(data)).PublishPdf("invoice.pdf");

Output Methods

var composer = Document.Create(...);
// To file
composer.PublishPdf("output.pdf");
// To byte array (HTTP responses, email attachments, etc.)
byte[] bytes = composer.PublishPdf();
// To any writable stream
composer.PublishPdf(stream);

Reusable Document Template

public class InvoiceDocument : IDocument
{
    private readonly InvoiceData _data;
    public InvoiceDocument(InvoiceData data) => _data = data;

    public void Compose(IDocumentContainer container)
    {
        container.Page(page =>
        {
            page.Size(PageSize.A4);
            page.Margin(2, Unit.Centimetre);
            page.Content().Text($"Invoice #{_data.Number}").Bold().FontSize(24);
        });
    }
}

// Usage
Document.Create(new InvoiceDocument(data)).PublishPdf("invoice.pdf");

Output Methods

var composer = Document.Create(...);

// To file
composer.PublishPdf("output.pdf");

// To byte array (HTTP responses, email attachments, etc.)
byte[] bytes = composer.PublishPdf();

// To any writable stream
composer.PublishPdf(stream);

Built-in Page Sizes

Constant Width Γ— Height (pt)
PageSize.A0 2383.94 Γ— 3370.39
PageSize.A1 1683.78 Γ— 2383.94
PageSize.A2 1190.55 Γ— 1683.78
PageSize.A3 841.89 Γ— 1190.55
PageSize.A4 595.28 Γ— 841.89
PageSize.A5 419.53 Γ— 595.28
PageSize.A6 297.64 Γ— 419.53
PageSize.Letter 612.00 Γ— 792.00
PageSize.Legal 612.00 Γ— 1008.00
PageSize.Tabloid 792.00 Γ— 1224.00
PageSize.Executive 521.86 Γ— 756.00
// Landscape variant of any size
page.Size(PageSize.Landscape(PageSize.A4));

Built-in Units

Enum value Description
Unit.Point PDF native unit (1 pt = 1/72 inch) β€” default
Unit.Millimetre 1 mm β‰ˆ 2.835 pt
Unit.Centimetre 1 cm β‰ˆ 28.35 pt
Unit.Inch 1 in = 72 pt

Built-in Colours

// Named shades (Material Design palette)
Color.Red.Darken2      Color.Red.Darken1
Color.Red.Medium       Color.Red.Lighten1  ...  Color.Red.Lighten4
Color.Blue.Darken2     Color.Blue.Medium   ...
Color.Green.Darken2    Color.Green.Medium  ...
Color.Grey.Darken2     Color.Grey.Medium   Color.Grey.Lighten1 ...
Color.Orange.Medium
Color.Purple.Medium
// Convenience constants
Color.White            // "#FFFFFF"
Color.Black            // "#000000"

Building from Source

git clone https://github.com/sahebansari/TerraPDF.git
cd TerraPDF
dotnet build
dotnet test

Requires .NET 8 SDK or later.


Contributing

Contributions are welcome! Please read CONTRIBUTING.md for coding standards, project structure, how to run tests, and the pull-request process.


Changelog

All notable changes are documented in CHANGELOG.md, following the Keep a Changelog format.


Security

To report a vulnerability, please follow the responsible-disclosure process described in SECURITY.md. Do not open a public issue for security problems.


License

MIT β€” see LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on TerraPDF:

Package Downloads
PdfSharpDslCore

A DSL using TerraPDF to generate PDF

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.2.0 0 9/20/2026
2.1.0 133 9/2/2026
2.0.1 124 8/28/2026
2.0.0 1,380 7/23/2026
1.5.1 256 7/10/2026
1.5.0 142 7/9/2026
1.4.0 434 7/5/2026
1.3.0 448 5/22/2026
1.2.3 115 5/18/2026
1.2.2 123 5/3/2026
1.2.1 127 5/2/2026
1.2.0 128 4/30/2026