SimpleTinyPDF 0.57.0

dotnet add package SimpleTinyPDF --version 0.57.0
                    
NuGet\Install-Package SimpleTinyPDF -Version 0.57.0
                    
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="SimpleTinyPDF" Version="0.57.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SimpleTinyPDF" Version="0.57.0" />
                    
Directory.Packages.props
<PackageReference Include="SimpleTinyPDF" />
                    
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 SimpleTinyPDF --version 0.57.0
                    
#r "nuget: SimpleTinyPDF, 0.57.0"
                    
#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 SimpleTinyPDF@0.57.0
                    
#: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=SimpleTinyPDF&version=0.57.0
                    
Install as a Cake Addin
#tool nuget:?package=SimpleTinyPDF&version=0.57.0
                    
Install as a Cake Tool

SimpleTinyPDF

NuGet

A small-but-mighty zero-dependency PDF generation library for .NET. SimpleTinyPDF is faster and uses less memory than most alternative packages. It doesn't do everything, but likely does what you need.

SimpleTinyPDF lets you create PDF documents from C# with no external packages. It targets .NET Standard 2.0, so it works with .NET Framework 4.6.1+, .NET Core 2.0+, and .NET 5+.

Table of Contents

Features

  • Text — single-line, wrapped text boxes, rich text with mixed fonts/sizes/colors, alignment (left, center, right), underline, hyperlinks, opacity
  • Images — JPEG and PNG (with transparency), EXIF auto-orientation, scaling modes (Stretch, Fit, Fill), opacity
  • Tables — styled headers, column alignment, alternate row shading, auto-pagination with repeated headers, CSV import
  • Barcodes — Code 128, Code 39, EAN-13, UPC-A, and QR Code; pure vector rendering (no images); configurable colors, quiet zones, human-readable text, rotation, and opacity
  • Lists — bullet, numbered, lowercase Roman (i, ii, iii…), and uppercase Roman (I, II, III…); unlimited nesting with per-level style overrides and custom bullet symbols; automatic text wrapping and multi-page flow
  • Shapes — lines, rectangles (stroke and/or fill), with optional rotation
  • Bookmarks — hierarchical outline / table of contents
  • Annotations — text (sticky notes), markup (highlight, underline, strikeout), stamps (Approved, Draft, Confidential, etc.), and internal links (navigate to another page)
  • Encryption — AES-128 and AES-256 password protection with configurable user/owner passwords and granular permission flags (print, copy, modify, annotate, etc.)
  • Fonts — 14 standard PDF Type 1 fonts (Helvetica, Times, Courier, Symbol, ZapfDingbats) plus TrueType (.ttf) and OpenType (.otf) font embedding with full Unicode support including supplementary planes (CJK, Cyrillic, Greek, Arabic, CJK Extension B, enclosed alphanumerics, etc.). TrueType fonts are automatically subsetted to include only the glyphs used in the document
  • Colors — RGB, CMYK, and grayscale
  • Page sizes — A3, A4, A5, Letter, Legal, custom dimensions, landscape
  • Coordinates — top-down (default) or native PDF bottom-up
  • Metadata — document title and author
  • Zero dependencies — no NuGet packages, no native libraries

Quick Start

dotnet add package SimpleTinyPDF
using SimpleTinyPDF;

var doc = new PdfDocument { Title = "Hello World" };
var page = doc.AddPage();

page.DrawText("Hello, World!", 50, 50, PdfFont.HelveticaBold, 24);
page.DrawText("Generated with SimpleTinyPDF.", 50, 80);

doc.Save("hello.pdf");

See the full Invoice example and CSV Table Report example for more realistic usage.

Pros and Cons

Use SimpleTinyPDF when you want to:

  • Generate invoices, reports, receipts, or labels from code
  • Avoid pulling in a large dependency tree for simple PDF output
  • Run in constrained environments (Azure Functions, Docker, CI pipelines) without native libraries
  • Ship a self-contained app with no runtime font or image dependencies

Look elsewhere if you need:

  • HTML-to-PDF conversion
  • Right-to-left text layout (Arabic, Hebrew) or complex script shaping
  • Form fields (AcroForms)
  • PDF reading, parsing, or editing
  • Digital signatures
  • Automatic page layout (headers/footers, page numbers, flowing columns)

Size Comparison

One of SimpleTinyPDF's goals is to stay tiny. Here's how it compares to other popular .NET PDF libraries:

Library NuGet Package Total Footprint vs SimpleTinyPDF Native Binaries?
SimpleTinyPDF ~120 KB ~120 KB 1x No
PDFsharp + MigraDoc 6.2.4 ~6.1 MB ~6.2 MB 52x No
iText 9.6.0 ~5.0 MB ~13–15 MB 130x No (BouncyCastle ~8 MB)
QuestPDF 2026.2.4 ~36 MB ~36 MB 313x Yes (bundled Skia)
IronPDF 2026.5 ~19 MB ~250+ MB 2,174x Yes (bundled Chromium)

PDFsharp is shown with MigraDoc (its document-model layer) since most real-world usage relies on MigraDoc for tables, paragraphs, and auto-pagination — raw PDFsharp alone is ~4.4 MB. IronPDF's footprint includes the Chromium rendering engine downloaded at build/runtime. QuestPDF bundles custom Skia native binaries for cross-platform rendering.

Performance Comparison

Benchmarked with BenchmarkDotNet on Windows 11, Intel Core 7 150U (10 cores), .NET 9.0.16. All output is generated in-memory (byte[]). IronPDF could not be benchmarked (requires a commercial license in Release builds). PDFsharp is benchmarked via MigraDoc (its document-model layer with tables, paragraphs, and auto-pagination) for a fair API-level comparison. See the SimpleTinyPDF.Benchmarks project to run these yourself.

Scenario 1 — 10,000-row CSV table (landscape, auto-paginated with header repeat and alternate row shading):

Library Mean Allocated vs SimpleTinyPDF
SimpleTinyPDF 163 ms 173 MB 1x
PDFsharp + MigraDoc 6.2.4 4,465 ms 845 MB 27x slower
iText 9.6.0 6,987 ms 1,986 MB 43x slower
QuestPDF 2026.5 1,303 ms 284 MB 8x slower
IronPDF 2026.5 N/A N/A

Scenario 2 — 1,000-invoice batch (each invoice: header, details, 3-row line items table, totals, footer):

Library Mean Allocated vs SimpleTinyPDF
SimpleTinyPDF 107 ms 74 MB 1x
PDFsharp + MigraDoc 6.2.4 727 ms 559 MB 7x slower
iText 9.6.0 2,671 ms 1,062 MB 25x slower
QuestPDF 2026.5 975 ms 224 MB 9x slower
IronPDF 2026.5 N/A N/A

SimpleTinyPDF is the fastest library in both scenarios while offering a high-level API (built-in tables, CSV import, auto-pagination). QuestPDF is closest in the invoice scenario but allocates 3x more memory. MigraDoc and iText provide richer layout engines but are 7–43x slower with 5–11x more memory allocation.

API Reference

PdfDocument

The main entry point. Create one, add pages, then save.

var doc = new PdfDocument();
doc.Title = "My Report";
doc.Author = "Jane Doe";

var page = doc.AddPage();                         // A4 by default
var page2 = doc.AddPage(PageSize.Letter);         // US Letter
var page3 = doc.AddPage(PageSize.A4.Landscape()); // A4 landscape
var cover = doc.InsertPage(1);                    // insert at position 1

doc.Save("output.pdf");       // save to file
doc.Save(stream);             // write to stream
byte[] bytes = doc.ToArray(); // get as byte array
Property / Method Description
Title Document title (PDF metadata)
Author Document author (PDF metadata)
Pages Read-only list of all pages
PageCount Number of pages
FirstPage / LastPage First or last page (null if empty)
GetPage(int pageNumber) Get a page by 1-based number
GetPageNumber(PdfPage page) Get the 1-based number of a page
AddPage(PageSize) Append a new page (defaults to A4)
InsertPage(int, PageSize) Insert a page at a 1-based position
AddImage(PdfImage) Register an image (deduplicates identical content)
Save(string) / Save(Stream) Write PDF to file or stream
ToArray() Return PDF as a byte array

PdfPage

All drawing happens on a page. Coordinates are in points. By default the coordinate system is top-down (Y=0 is the top of the page). Set CoordinateOrigin to use native PDF bottom-up coordinates instead.

// Default: Y=0 at top, Y increases downward
page.CoordinateOrigin = CoordinateOrigin.TopDown;

// Native PDF: Y=0 at bottom, Y increases upward
page.CoordinateOrigin = CoordinateOrigin.BottomUp;

In BottomUp mode, Y values map directly to PDF coordinates: text Y is the baseline, and rectangle/image Y is the bottom-left corner. Tables (DrawTable) always use top-down layout internally.

Text
page.DrawText("Hello", 50, 50);
page.DrawText("Bold red", 50, 70, PdfFont.HelveticaBold, 14, PdfColor.Red, TextAlignment.Left, underline: true);

// Hyperlink — text becomes clickable
page.DrawText("Visit Example", 50, 90, PdfFont.Helvetica, 12, PdfColor.Blue,
    underline: true, link: "https://example.com");

// Wrapped text — pass width to enable word wrap, returns Y after last line
float nextY = page.DrawText("Long paragraph...", 50, 110,
    PdfFont.TimesRoman, 11, width: 400, lineSpacing: 1.4f);

// Wrapped text with hyperlink (each wrapped line is clickable)
float nextY1 = page.DrawText("Click here for full terms and conditions", 50, 160,
    link: "https://example.com/terms", width: 200);

// Rich text (mixed fonts/sizes/colors on one line)
page.DrawText(new[] {
    new TextSpan("Normal "),
    new TextSpan("bold", PdfFont.HelveticaBold),
    new TextSpan(" and ", PdfFont.Helvetica, 12, PdfColor.DarkGray),
    new TextSpan("red", PdfFont.Helvetica, 12, PdfColor.Red, underline: true)
}, 50, 200);

// Rich text with hyperlinks — individual spans can be links
page.DrawText(new[] {
    new TextSpan("See "),
    new TextSpan("our docs", PdfFont.Helvetica, 12, PdfColor.Blue,
        underline: true, link: "https://docs.example.com"),
    new TextSpan(" for details.")
}, 50, 220);

// Rich text with word wrap (pass width)
float nextY2 = page.DrawText(spans, 50, 240, width: 400);

// Measure text width
float w = page.MeasureText("Hello", PdfFont.Helvetica, 12);

// Rotated text — angle in degrees, clockwise
page.DrawText("Rotated 45°", 300, 100, fontSize: 16, rotation: 45);
page.DrawText("Rotated text box", 300, 200, width: 150, rotation: 90);
Method Returns Description
DrawText(string, ...) float Text; pass width for word wrap. Returns Y after text
DrawText(spans, ...) float Rich text (mixed formatting); pass width for word wrap
MeasureText(text, font, size) float Width of text in points

All text methods accept an optional rotation parameter — angle in degrees, clockwise (matching CSS convention), rotating around the element's (x, y) position.

TextSpan is used with the spans overload of DrawText for mixed-format text:

new TextSpan("hello")                                          // defaults: Helvetica 12pt black
new TextSpan("bold", PdfFont.HelveticaBold, 14)                // custom font/size
new TextSpan("fancy", PdfFont.TimesItalic, 12, PdfColor.Blue, underline: true, opacity: 0.8f)
new TextSpan("click me", PdfFont.Helvetica, 12, PdfColor.Blue, underline: true,
    link: "https://example.com")                               // hyperlink

TextAlignment: TextAlignment.Left (default), TextAlignment.Center, TextAlignment.Right

Built-in Fonts

SimpleTinyPDF includes the 14 standard PDF Type 1 fonts. These are built into every PDF viewer and require no embedding, keeping output files small.

Family Variants
Helvetica Regular, Bold, Oblique, BoldOblique
Times Roman, Bold, Italic, BoldItalic
Courier Regular, Bold, Oblique, BoldOblique
Symbol (symbol characters)
ZapfDingbats (decorative symbols)

Use them via the PdfFont enum:

page.DrawText("Hello", 50, 50, PdfFont.HelveticaBold, 14);

Custom Fonts (TrueType / OpenType)

Load any .ttf or .otf font file and use it anywhere a built-in font is accepted. The font binary is embedded in the PDF using CID (composite) fonts with Identity-H encoding, enabling full Unicode support — including BMP (CJK, Cyrillic, Greek) and supplementary planes (CJK Extension B, enclosed alphanumerics, etc.). UTF-16 surrogate pairs are handled transparently.

// Load from file, byte array, or stream
var roboto = PdfFontSource.FromFile("Roboto-Regular.ttf");
var mono = PdfFontSource.FromBytes(File.ReadAllBytes("SourceCodePro-Regular.otf"));
var serif = PdfFontSource.FromStream(fontStream);

// Use with any drawing method — Latin, CJK, Cyrillic, etc.
page.DrawText("Custom font text", 50, 50, roboto, 14);
page.DrawText("Кириллица", 50, 70, roboto, 14);      // Cyrillic

var cjkFont = PdfFontSource.FromFile("NotoSansJP-Regular.ttf");
page.DrawText("こんにちは世界", 50, 90, cjkFont, 14); // Japanese

// Mix with built-in fonts in rich text
page.DrawText(new[] {
    new TextSpan("Hello ", PdfFont.HelveticaBold, 14),
    new TextSpan("世界", cjkFont, 14, PdfColor.Red)
}, 50, 120);

// Use in tables
table.HeaderFont = roboto;
table.CellFont = roboto;

Built-in PdfFont values are automatically converted to PdfFontSource, so all existing code continues to work unchanged. The same PdfFontSource instance can be used across multiple pages — the font data is embedded only once in the PDF. TrueType fonts are automatically subsetted to include only the glyphs used in the document. Text rendered with custom fonts is selectable and copyable in PDF viewers (via an embedded ToUnicode CMap).

Character Support

Built-in fonts:

  • WinAnsiEncoding coverage (~256 Latin characters, digits, punctuation)
  • Extended European diacritics (e.g. ą, ć, ę, ł, ň, ř, š, ž, ő, ű) via encoding extensions

Custom fonts (TrueType / OpenType):

  • Full Unicode Basic Multilingual Plane (BMP) — U+0000 to U+FFFF
  • Supplementary Unicode planes — U+10000 to U+10FFFF (CJK Extension B, enclosed alphanumerics, etc.)
  • CJK characters (Chinese, Japanese, Korean)
  • Cyrillic, Greek, Arabic characters, and other scripts
  • Any character the font contains a glyph for

Font Subsetting

TrueType (.ttf) fonts are automatically subsetted when embedded in the PDF. Only the glyphs actually used in the document are included, dramatically reducing file size — especially for large CJK fonts (e.g. a 16 MB font embedding just a few characters produces a PDF well under 1 MB). Composite glyphs (like accented characters built from components) are handled correctly; all referenced component glyphs are automatically retained.

To disable subsetting and embed the full font file, set Subset to false:

var font = PdfFontSource.FromFile("Roboto-Regular.ttf");
font.Subset = false; // embed the full font binary

OpenType (.otf) fonts with CFF outlines are currently embedded in full.

What Is Not Supported

  • Right-to-left text layout (Arabic and Hebrew characters render, but layout is left-to-right)
  • Complex script shaping (ligatures, combining marks)
  • CFF/OpenType font subsetting (the full font file is embedded; TrueType fonts are subsetted)
  • WOFF/WOFF2 web font formats
Shapes
page.DrawLine(50, 100, 500, 100, PdfColor.Black, lineWidth: 0.5f);
page.DrawRectangle(50, 110, 200, 80, PdfColor.DarkGray, lineWidth: 1f);
page.DrawFilledRectangle(50, 200, 200, 80, PdfColor.Rgb(230, 240, 255), PdfColor.Black, lineWidth: 0.5f);

// Rotated shapes — angle in degrees, clockwise
page.DrawFilledRectangle(300, 100, 80, 40, PdfColor.Blue, rotation: 45);
page.DrawRectangle(300, 200, 80, 80, PdfColor.Green, lineWidth: 2, rotation: 30);
page.DrawLine(100, 300, 300, 300, PdfColor.Red, lineWidth: 2, rotation: 90);

All shape methods accept an optional rotation parameter — angle in degrees, clockwise, rotating around the element's (x, y) position.

Images
var img = PdfImage.FromFile("photo.jpg");
var img = PdfImage.FromBytes(byteArray);
var img = PdfImage.FromStream(stream);

int w = img.PixelWidth;   // display width (EXIF-adjusted)
int h = img.PixelHeight;  // display height (EXIF-adjusted)
page.DrawImage(logo, x: 50, y: 30, width: 120, height: 40);
page.DrawImage(logo, x: 50, y: 30, width: 120, height: 40, opacity: 0.5f);
page.DrawImage(logo, x: 50, y: 30, width: 120, height: 40, scaleMode: ImageScaleMode.Fit);

// Rotated image — angle in degrees, clockwise
page.DrawImage(logo, x: 200, y: 100, width: 120, height: 40, rotation: 90);

JPEG and PNG are supported (auto-detected). PNG transparency is preserved. EXIF orientation tags are automatically applied.

The scaleMode parameter controls how the image is scaled to fit the target rectangle:

Mode Aspect Ratio Description
Stretch (default) Ignored Fills the entire area exactly; image may be distorted
Fit Preserved Scales to fit inside the area; image is centered with possible letterboxing
Fill Preserved Scales to cover the entire area; image is centered and overflow is clipped

DrawImage also accepts an optional rotation parameter — angle in degrees, clockwise, rotating around the element's (x, y) position.

Tables

Build tables with a fluent API or import from CSV.

var table = new PdfTable(100, 200, 100, 100)  // column widths in points
    .SetHeaders("ID", "Description", "Qty", "Price")
    .AddRow("1001", "Widget", "5", "$12.50")
    .AddRow("1002", "Gadget", "2", "$24.00");

table.SetColumnAlignment(2, TextAlignment.Right);
table.SetColumnAlignment(3, TextAlignment.Right);
table.AlternateRowShading = true;
Property Default Description
HeaderFont HelveticaBold Font for header row
HeaderFontSize 10 Font size for header row
CellFont Helvetica Font for body cells
CellFontSize 10 Font size for body cells
HeaderBackground LightGray Header row background color
HeaderTextColor Black Header text color
BorderColor Black Border color
BorderWidth 0.5 Border line width in points
CellPadding 4 Padding inside cells in points
TextColor Black Body text color
AlternateRowShading false Enable zebra-stripe rows
AlternateRowColor RGB(0.95, 0.95, 0.95) Alternate row background
LineSpacing 1.2 Line spacing multiplier for cell text

CSV import:

var table = PdfTable.FromCsv("data.csv");
var table = PdfTable.FromCsv("data.csv", firstRowIsHeader: true, delimiter: ',',
    columnWidths: new float[] { 80, 200, 80, 80 });
var table = PdfTable.FromCsvString(csvContent, totalWidth: 500);

Drawing tables:

float endY = page.DrawTable(table, x: 50, y: 200, bottomMargin: 50);

Tables that exceed the page height automatically continue on new pages with repeated headers. By default, continuation pages start the table at the same Y position as the first page. Use continuationY to start higher on subsequent pages:

// Table starts at y=300 on page 1, but at y=50 on continuation pages
page.DrawTable(table, x: 50, y: 300, continuationY: 50);
Lists

DrawList renders a hierarchical list and returns a (PdfPage page, float y) tuple indicating where rendering ended, so you can continue drawing below it — including on a different page if the list overflowed.

var items = new[]
{
    new ListItem("Introduction"),
    new ListItem("Installation",
        new ListItem("Windows"),
        new ListItem("macOS"),
        new ListItem("Linux")),
    new ListItem("Configuration")
};

// Bullet list (default)
var (nextPage, nextY) = page.DrawList(items, x: 50, y: 100, width: 450);

// Numbered list
var (nextPage, nextY) = page.DrawList(items, x: 50, y: 100, width: 450,
    style: ListStyle.Numbered);

// Lowercase Roman numerals (i, ii, iii…)
var (nextPage, nextY) = page.DrawList(items, x: 50, y: 100, width: 450,
    style: ListStyle.RomanLower);

// Uppercase Roman numerals (I, II, III…)
var (nextPage, nextY) = page.DrawList(items, x: 50, y: 100, width: 450,
    style: ListStyle.RomanUpper);

// Multi-page list — automatically flows to new pages
var (lastPage, endY) = page.DrawList(items, x: 50, y: 100, width: 450,
    bottomMargin: 50, continuationY: 50);

Each item can override the style and bullet symbol used for its children:

var items = new[]
{
    // Children use numbered style
    new ListItem("Chapter 1", ListStyle.Numbered,
        new ListItem("Section 1.1"),
        new ListItem("Section 1.2")),

    // Children use Roman numerals with a custom bullet symbol at the next level
    new ListItem("Chapter 2", ListStyle.RomanUpper,
        new ListItem("Part I", ListStyle.RomanLower,
            new ListItem("Sub-part a"))),
};

To use a custom bullet symbol from Symbol or ZapfDingbats, pass a TextSpan as the bullet:

// Custom top-level bullet
var (nextPage, nextY) = page.DrawList(items, x: 50, y: 100, width: 450,
    bullet: new TextSpan("»", PdfFont.Helvetica));

// Per-level custom symbols via ChildrenBullet
new ListItem("Top item", ListStyle.Bullet, new TextSpan("‣", PdfFont.Helvetica),
    new ListItem("Child item"))
Parameter Default Description
items required Array of ListItem to render
x required Left edge in points
y required Top of first item in points
width required Available width for text wrapping
style Bullet Bullet, Numbered, RomanLower, or RomanUpper
bottomMargin 0 Distance from page bottom before a new page is added
font Helvetica Font for item text and numbered markers
fontSize 12 Font size in points
lineSpacing 1.2 Line spacing multiplier
color Black Text and marker color
bullet Bullet symbol (used when style is Bullet)
startNumber 1 Starting counter value (numbered/Roman styles)
indentPerLevel 20 Horizontal indent per nesting level in points
continuationY same as y Y position on continuation pages

PdfColor

var c1 = PdfColor.Rgb(51, 102, 204);        // 0-255 integers
var c2 = PdfColor.Rgb(0.2f, 0.4f, 0.8f);    // 0.0-1.0 floats
var c3 = PdfColor.Cmyk(1f, 0f, 0f, 0f);     // CMYK
var c4 = PdfColor.Gray(0.5f);               // grayscale

Predefined: Black, White, Red, Green, Blue, Yellow, Cyan, Magenta, Orange, Purple, Pink, Brown, Gold, Navy, Teal, Maroon, Olive, Coral, Crimson, Indigo, Silver, MediumGray, LightGray, DarkGray

<details> <summary>Predefined color values</summary>

Name Value Color Space
Black K=1 CMYK
White 255, 255, 255 RGB
Red 255, 0, 0 RGB
Green 0, 255, 0 RGB
Blue 0, 0, 255 RGB
Yellow Y=1 CMYK
Cyan C=1 CMYK
Magenta M=1 CMYK
Orange 255, 165, 0 RGB
Purple 128, 0, 128 RGB
Pink 255, 192, 203 RGB
Brown 139, 69, 19 RGB
Gold 255, 215, 0 RGB
Navy 0, 0, 128 RGB
Teal 0, 128, 128 RGB
Maroon 128, 0, 0 RGB
Olive 128, 128, 0 RGB
Coral 255, 127, 80 RGB
Crimson 220, 20, 60 RGB
Indigo 75, 0, 130 RGB
Silver 192, 192, 192 RGB
MediumGray 128, 128, 128 RGB
LightGray 212, 212, 212 RGB
DarkGray 84, 84, 84 RGB

</details>

PageSize

Predefined: PageSize.A4, A3, A5, Letter, Legal

var landscape = PageSize.A4.Landscape();
var custom = new PageSize(400, 600);  // width x height in points

Bookmarks

Add bookmarks (outlines) that appear in the PDF viewer's navigation panel. Bookmarks can be nested to create a hierarchical table of contents.

// Top-level bookmarks
var ch1 = doc.AddBookmark("Chapter 1", page1);
var ch2 = doc.AddBookmark("Chapter 2", page2);

// Nested bookmarks — point to a specific Y position on the page
ch1.AddBookmark("Installation", page1, y: 150);
ch1.AddBookmark("Configuration", page1, y: 350);

// Deeper nesting
var advanced = ch2.AddBookmark("Advanced Topics", page3);
advanced.AddBookmark("Performance Tuning", page3, y: 200);
Method Description
PdfDocument.AddBookmark(title, page, y?) Add a top-level bookmark. Returns the bookmark for nesting children.
PdfBookmark.AddBookmark(title, page, y?) Add a child bookmark under this one.

When y is omitted the bookmark fits the entire page. When y is provided the viewer scrolls to that vertical position (in the page's coordinate system).

Annotations

Add interactive annotations to pages — sticky notes, text markup, stamps, and internal document links. All annotation types support both TopDown and BottomUp coordinate systems.

Text Annotations (Sticky Notes)

// Basic sticky note
page.AddTextAnnotation(100, 100, "Please review this section.");

// With title, icon, color, and open state
page.AddTextAnnotation(100, 200, "Needs revision", title: "Reviewer",
    icon: TextAnnotationIcon.Note, color: PdfColor.Red, open: true);
Parameter Default Description
x, y required Position of the annotation icon (24x24 points)
contents required Note text shown in the popup
title null Author or title shown in the popup header
icon Comment Icon style: Comment, Note, Key, Help, NewParagraph, Paragraph, Insert
color null Icon color (viewer default if null)
open false Whether the popup starts open

Markup Annotations (Highlight, Underline, StrikeOut)

// Highlight a region
page.AddMarkupAnnotation(50, 50, 200, 14, MarkupAnnotationType.Highlight,
    color: PdfColor.Rgb(1f, 1f, 0f));

// Underline with a comment
page.AddMarkupAnnotation(50, 80, 200, 14, MarkupAnnotationType.Underline,
    color: PdfColor.Green, contents: "Good point");

// Strikeout
page.AddMarkupAnnotation(50, 110, 200, 14, MarkupAnnotationType.StrikeOut,
    color: PdfColor.Red);
Parameter Default Description
x, y required Top-left corner of the marked region
width, height required Size of the marked region
type Highlight Highlight, Underline, or StrikeOut
color null Markup color (viewer default if null)
contents null Comment text shown in a popup
title null Author name

Stamp Annotations

page.AddStampAnnotation(100, 100, 200, 60, stamp: StampType.Approved);
page.AddStampAnnotation(100, 200, 200, 60, stamp: StampType.Confidential,
    contents: "Internal use only");

Available stamp types: Approved, Experimental, NotApproved, AsIs, Expired, NotForPublicRelease, Confidential, Final, Sold, Departmental, ForComment, TopSecret, Draft, ForPublicRelease

Parameter Default Description
x, y required Top-left corner of the stamp
width, height required Size of the stamp
stamp Draft Predefined stamp type
contents null Tooltip text
title null Author name
color null Stamp color

Internal Links (GoTo)

Create clickable regions that navigate to another page in the same document.

var page1 = doc.AddPage();
var page2 = doc.AddPage();

// Link that fits the target page
page1.AddLinkToPage(50, 50, 120, 20, page2);

// Link that scrolls to a specific Y position
page1.AddLinkToPage(50, 80, 120, 20, page2, targetY: 200);
Parameter Default Description
x, y required Top-left corner of the clickable area
width, height required Size of the clickable area
targetPage required The page to navigate to
targetY null Y position on the target page (fits entire page if null)

Encryption

Protect PDF documents with password-based encryption. Supports AES-128 (PDF 1.6) and AES-256 (PDF 2.0) with user passwords, owner passwords, and granular permission flags. All cryptography uses built-in System.Security.Cryptography — no external dependencies.

// Owner password only — opens without a password, but restricts actions
doc.Encryption = new PdfEncryptionOptions
{
    OwnerPassword = "admin-secret",
    Permissions = PdfPermissions.Print | PdfPermissions.ExtractText
};

// User + owner password — requires password to open
doc.Encryption = new PdfEncryptionOptions
{
    UserPassword = "open-me",
    OwnerPassword = "full-access",
    Level = PdfEncryptionLevel.Aes256,
    Permissions = PdfPermissions.Print
};

// All permissions, AES-128 (default level)
doc.Encryption = new PdfEncryptionOptions
{
    UserPassword = "secret",
    OwnerPassword = "owner"
};

PdfEncryptionOptions:

Property Default Description
UserPassword "" Password to open the document. Empty means no open password required
OwnerPassword "" Password for full owner access (change permissions, unrestricted operations)
Level Aes128 Aes128 (PDF 1.6, V4/R4) or Aes256 (PDF 2.0, V5/R6)
Permissions All User access permissions (see below)

PdfPermissions (combine with |):

Flag Description
Print Print the document (may be low-quality without HighQualityPrint)
ModifyContents Modify document contents
ExtractText Copy or extract text and graphics
AnnotateAndForms Add or modify annotations and fill form fields
FillForms Fill in existing form fields
ExtractForAccessibility Extract text and graphics for accessibility
Assemble Insert, rotate, delete pages, and bookmarks
HighQualityPrint Print at high quality
All All permissions granted
None No permissions granted

Barcodes

Draw 1D barcodes and QR codes as crisp vector graphics (PDF rectangles, not images). All encoding, check digits, and error correction are computed internally with zero dependencies.

// EAN-13 barcode (check digit computed automatically)
page.DrawBarcode("590123412345", BarcodeType.Ean13, 50, 100, 200, 80);

// QR code with high error correction
page.DrawBarcode("https://example.com", BarcodeType.QrCode, 50, 200, 150, 150,
    new BarcodeOptions { QrErrorCorrectionLevel = QrErrorCorrection.High });

// Code 128 with human-readable text below
page.DrawBarcode("ABC-12345", BarcodeType.Code128, 50, 400, 250, 60,
    new BarcodeOptions { ShowText = true });

// UPC-A (12-digit North American retail)
page.DrawBarcode("01234567890", BarcodeType.UpcA, 50, 500, 200, 80);

// Custom colors and rotation
page.DrawBarcode("HELLO", BarcodeType.Code39, 300, 100, 200, 60,
    new BarcodeOptions
    {
        ForegroundColor = PdfColor.Navy,
        BackgroundColor = PdfColor.Rgb(245, 245, 255),
        ShowText = true,
        Rotation = 90
    });

Supported barcode types:

Type Characters Use Cases
Code128 ASCII 0-127 Shipping, logistics, general-purpose
Code39 0-9, A-Z, - . $ / + % SPACE Industrial, warehouse, military
Ean13 12-13 digits Retail products (international)
UpcA 11-12 digits Retail products (North America)
QrCode Any text (UTF-8) URLs, payments, WiFi, general data

BarcodeOptions:

Property Default Description
ForegroundColor Black Bar / module color
BackgroundColor White Background color
DrawBackground true Whether to fill the background
IncludeQuietZone true Add required white-space margin (within specified width/height)
ShowText false Human-readable text below 1D barcodes
TextFont Courier Font for human-readable text
TextFontSize 8 Font size for human-readable text
QrErrorCorrectionLevel Medium Low, Medium, Quartile, or High (QR only)
Rotation 0 Clockwise rotation in degrees
Opacity 1.0 0.0 (transparent) to 1.0 (opaque)

Invoice example output

using SimpleTinyPDF;

var doc = new PdfDocument { Title = "Invoice #1042" };
var page = doc.AddPage(PageSize.Letter);

// Company logo
var logo = PdfImage.FromFile("company-logo.png");
page.DrawImage(logo, 50, 40, 150, 50);

// Company info (right-aligned)
page.DrawText("Acme Corp", 562, 40, PdfFont.HelveticaBold, 14, alignment: TextAlignment.Right);
page.DrawText("123 Main Street, Springfield", 562, 58, PdfFont.Helvetica, 9,
    PdfColor.DarkGray, TextAlignment.Right);
page.DrawText("Tel: (555) 123-4567", 562, 70, PdfFont.Helvetica, 9,
    PdfColor.DarkGray, TextAlignment.Right);

// Divider
page.DrawLine(50, 100, 562, 100, PdfColor.LightGray, 1f);

// Invoice title
page.DrawText("INVOICE", 50, 120, PdfFont.HelveticaBold, 24, PdfColor.Rgb(51, 51, 51));

// Invoice details
page.DrawText("Invoice #: 1042", 50, 160, PdfFont.Helvetica, 10);
page.DrawText("Date: April 16, 2026", 50, 175, PdfFont.Helvetica, 10);
page.DrawText("Due: May 16, 2026", 50, 190, PdfFont.Helvetica, 10);

// Bill to
page.DrawText("Bill To:", 350, 160, PdfFont.HelveticaBold, 10);
page.DrawText("John Smith", 350, 175, PdfFont.Helvetica, 10);
page.DrawText("456 Oak Avenue", 350, 190, PdfFont.Helvetica, 10);
page.DrawText("Shelbyville, IL 62565", 350, 205, PdfFont.Helvetica, 10);

// Line items table
var table = new PdfTable(240, 80, 80, 112)
    .SetHeaders("Description", "Quantity", "Unit Price", "Amount")
    .AddRow("Web Development Services", "40 hrs", "$75.00", "$3,000.00")
    .AddRow("UI/UX Design", "16 hrs", "$85.00", "$1,360.00")
    .AddRow("Hosting Setup", "1", "$200.00", "$200.00");

table.SetColumnAlignment(1, TextAlignment.Center);
table.SetColumnAlignment(2, TextAlignment.Right);
table.SetColumnAlignment(3, TextAlignment.Right);
table.HeaderBackground = PdfColor.Rgb(51, 51, 51);
table.HeaderTextColor = PdfColor.White;
table.AlternateRowShading = true;

float tableEndY = page.DrawTable(table, 50, 240);

// Totals
float totalsX = 370;
float totalsY = tableEndY + 15;
page.DrawText("Subtotal:", totalsX, totalsY, PdfFont.Helvetica, 10);
page.DrawText("$4,560.00", 562, totalsY, PdfFont.Helvetica, 10, alignment: TextAlignment.Right);
page.DrawText("Tax (8%):", totalsX, totalsY + 18, PdfFont.Helvetica, 10);
page.DrawText("$364.80", 562, totalsY + 18, PdfFont.Helvetica, 10, alignment: TextAlignment.Right);
page.DrawLine(totalsX, totalsY + 34, 562, totalsY + 34, PdfColor.Black, 0.5f);
page.DrawText("Total Due:", totalsX, totalsY + 42, PdfFont.HelveticaBold, 12);
page.DrawText("$4,924.80", 562, totalsY + 42, PdfFont.HelveticaBold, 12, alignment: TextAlignment.Right);

// QR code for online payment
page.DrawBarcode("https://github.com/gregsalzman/simpletinypdf", BarcodeType.QrCode,
    50, totalsY, 80, 80);
page.DrawText("Scan to pay online.", 90, totalsY + 85, PdfFont.Helvetica, 8,
    PdfColor.DarkGray, TextAlignment.Center);

// Footer note
page.DrawText("Payment is due within 30 days. Thank you for your business!",
    306, 720, PdfFont.HelveticaOblique, 9, PdfColor.DarkGray, TextAlignment.Center);

doc.Save("invoice-1042.pdf");

Example: CSV to Table Report

CSV report example output

using SimpleTinyPDF;

// Suppose "sales-data.csv" contains:
//   Region,Product,Q1,Q2,Q3,Q4
//   North,Widgets,1200,1350,1100,1500
//   South,Widgets,980,1050,1200,1180
//   ...

var doc = new PdfDocument { Title = "Quarterly Sales Report" };
var page = doc.AddPage(PageSize.Letter.Landscape());

// Report header
page.DrawText("Quarterly Sales Report", 50, 40, PdfFont.HelveticaBold, 20);
page.DrawText("Generated: April 16, 2026", 50, 65, PdfFont.Helvetica, 10, PdfColor.DarkGray);
page.DrawLine(50, 85, 742, 85, PdfColor.LightGray, 1f);

// Import CSV directly into a table
var table = PdfTable.FromCsv("sales-data.csv",
    firstRowIsHeader: true,
    columnWidths: new float[] { 100, 120, 90, 90, 90, 90 });

// Style it
table.HeaderBackground = PdfColor.Rgb(0, 51, 102);
table.HeaderTextColor = PdfColor.White;
table.HeaderFont = PdfFont.HelveticaBold;
table.HeaderFontSize = 11;
table.CellFont = PdfFont.Helvetica;
table.CellFontSize = 10;
table.AlternateRowShading = true;
table.AlternateRowColor = PdfColor.Rgb(235, 241, 250);
table.CellPadding = 6;

// Right-align the numeric columns
table.SetColumnAlignment(2, TextAlignment.Right);
table.SetColumnAlignment(3, TextAlignment.Right);
table.SetColumnAlignment(4, TextAlignment.Right);
table.SetColumnAlignment(5, TextAlignment.Right);

// Draw — if the data has many rows, it will automatically
// continue on new pages with the header row repeated.
// Use continuationY to start the table higher on subsequent pages.
page.DrawTable(table, 50, 100, bottomMargin: 50, continuationY: 50);

doc.Save("sales-report.pdf");

Version History

Version Date Changes
0.57 May 31, 2026 Add TrueType font subsetting — only used glyphs are embedded, dramatically reducing PDF size for large fonts (especially CJK). Composite glyph dependencies are resolved automatically. CFF/OpenType fonts continue to embed in full.
0.56 May 29, 2026 Add AES-128 and AES-256 PDF encryption with user/owner passwords and permission flags. Pure C# implementation using built-in System.Security.Cryptography.
0.55 May 22, 2026 Add annotations: text (sticky notes), markup (highlight, underline, strikeout), stamps (14 predefined types), and internal links (GoTo page navigation).
0.54 May 21, 2026 Add TrueType (.ttf) and OpenType (.otf) font embedding via PdfFontSource.FromFile() / FromBytes() / FromStream() with full Unicode support (BMP + supplementary planes) using CID composite fonts with Identity-H encoding. Supports CJK, Cyrillic, Greek, CJK Extension B, enclosed alphanumerics, and any character the font contains. UTF-16 surrogate pairs are handled transparently. Embedded ToUnicode CMap enables text selection/copy in PDF viewers.
0.53 May 14, 2026 Add barcode and QR code support (Code 128, Code 39, EAN-13, UPC-A, QR Code) with vector rendering and configurable options. Also did some code reorganization and optimization. Text methods are now consolidated into a single method with overloads... legacy methods are marked as obsolete.
0.52 May 2026 Add hierarchical lists with nesting, text wrapping, multi-page flow, and four list styles (Bullet, Numbered, RomanLower, RomanUpper)
0.51 May 2026 Add rotation support for text, images, and shapes
0.50 April 2026 Initial beta release

Development Notes

This library was written as an exploration of what my increasingly good friend Claude could accomplish. I continue to be amazed at what generative AI can do.

Sample image assets in the tests project are taken from https://www.publicdomainpictures.net/

License

MIT

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.57.0 40 5/31/2026
0.56.0 37 5/30/2026
0.55.0 120 5/22/2026
0.54.0 106 5/22/2026
0.53.0 98 5/14/2026
0.52.0 102 5/10/2026
0.51.0 99 5/7/2026
0.5.0 98 5/4/2026