SimpleTinyPDF 0.55.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package SimpleTinyPDF --version 0.55.0
                    
NuGet\Install-Package SimpleTinyPDF -Version 0.55.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.55.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SimpleTinyPDF" Version="0.55.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.55.0
                    
#r "nuget: SimpleTinyPDF, 0.55.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.55.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.55.0
                    
Install as a Cake Addin
#tool nuget:?package=SimpleTinyPDF&version=0.55.0
                    
Install as a Cake Tool

SimpleTinyPDF

A small-but-mighty zero-dependency PDF generation library for .NET.

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+.

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)
  • Rotation — rotate any text, image, or shape element by an arbitrary angle
  • 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)
  • 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.)
  • 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

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
  • Encryption or 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 ~106 KB ~106 KB 1x No
PDFsharp 6.2.4 ~4.4 MB ~4.5 MB 45x No
iText 9.6.0 ~5.0 MB ~13–15 MB 149x No (BouncyCastle ~8 MB)
QuestPDF 2026.2.4 ~36 MB ~36 MB 356x Yes (bundled Skia)
IronPDF 2026.5 ~19 MB ~250+ MB 2,475x Yes (bundled Chromium)

IronPDF's footprint includes the Chromium rendering engine downloaded at build/runtime. QuestPDF bundles custom Skia native binaries for cross-platform rendering.

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

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

Custom Fonts (TrueType / OpenType) — Load .ttf / .otf files via PdfFontSource.FromFile() and use them anywhere a built-in font is accepted. Full Unicode support including CJK, Cyrillic, and supplementary planes. See Fonts for details and examples.

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

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);

Images

var logo = PdfImage.FromFile("logo.png");
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

Rotation

All drawing methods (DrawText, DrawImage, DrawLine, DrawRectangle, DrawFilledRectangle) accept an optional rotation parameter:

  • Angle is in degrees, clockwise (matching CSS convention)
  • Origin is the element's (x, y) position
  • Default is 0 (no rotation)
// Watermark-style diagonal text
page.DrawText("DRAFT", 300, 400, PdfFont.HelveticaBold, 72,
    PdfColor.Rgb(200, 200, 200), TextAlignment.Center, opacity: 0.3f, rotation: -45);

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

PdfTable

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);

PdfImage

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)

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 colors:

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

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)

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");

Fonts

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. 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

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)
  • Font subsetting (the full font file is embedded)
  • WOFF/WOFF2 web font formats

Version History

Version Date Changes
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 acomplish. 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.60.0 57 6/11/2026
0.58.0 76 6/5/2026
0.57.0 100 5/31/2026
0.56.0 92 5/30/2026
0.55.0 123 5/22/2026
0.54.0 108 5/22/2026
0.53.0 100 5/14/2026
0.52.0 104 5/10/2026
0.51.0 101 5/7/2026
0.5.0 100 5/4/2026