The minimum: one page, one line of text, one file on disk.
using JetsonPDF;
var doc = new PdfDocument { Title = "Hello" };
var page = doc.AddPage(PageSize.Letter);
page.DrawText("Hello, PDF!",
new PdfFont(FontFamily.Helvetica, 24),
x: 72, y: 700);
doc.Save("hello.pdf");
PDF coordinates start at the bottom-left, in points (1 point = 1/72 inch). A US Letter page is 612×792 points; y: 700 is near the top.
2Text, image, and a clickable link
Drop in a JPEG, draw a heading, and make the heading itself clickable.
using JetsonPDF;
var doc = new PdfDocument();
var page = doc.AddPage(PageSize.Letter);
var heading = new PdfFont(FontFamily.Helvetica, 22, FontStyle.Bold);
page.DrawText("Annotated page", heading, x: 50, y: 750);
// Make the heading itself clickable.
page.AddLink(x: 50, y: 745, width: 280, height: 28,
uri: "https://jetsonpdf.net/");
// JPEG / PNG auto-detected from magic bytes.
var photo = PdfImage.FromFile("photo.jpg");
page.DrawImage(photo, x: 50, y: 580, width: 300, height: 150);
doc.Save("page.pdf");
PdfImage is created via the FromFile / FromJpeg / FromPng factories — the same instance can be drawn on many pages and is deduped to a single XObject in the output.
3Embedded TrueType + Unicode
Embed a font file once and use it like any standard font. Subsetting happens automatically on save.
using JetsonPDF;
var doc = new PdfDocument
{
Title = "Unicode demo",
PdfVersion = "2.0",
};
var calibri = EmbeddedFontFace.FromFile(@"C:\Windows\Fonts\calibri.ttf");
var font = new PdfFont(calibri, 14);
var page = doc.AddPage(PageSize.Letter);
page.DrawText("English — Français — Español",
font, x: 72, y: 720);
page.DrawText("中文 — 日本語 — 한국어",
font, x: 72, y: 690);
doc.Save("unicode.pdf");
For CJK content, JetsonPDF emits a Type 0 / CIDFontType2 font with a /ToUnicode CMap so text is still selectable and copyable.
4Vector graphics + transparency
Paths, fills, gradients, and a translucent overlay.
using JetsonPDF;
var doc = new PdfDocument();
var page = doc.AddPage(PageSize.Letter);
// Solid fill + stroke.
page.DrawRectangle(72, 600, 200, 120,
fill: PdfColor.Rgb(0.13, 0.31, 0.52),
stroke: PdfColor.Black,
strokeWidth: 1.5);
// A 2-stop axial gradient.
var shading = new PdfAxialShading(
x0: 72, y0: 480, x1: 272, y1: 480,
PdfColor.Rgb(1.0, 0.85, 0.20),
PdfColor.Rgb(0.90, 0.30, 0.30));
page.DrawShading(shading, clipX: 72, clipY: 460, clipW: 200, clipH: 80);
// Transparent overlay.
using (page.BeginTransparencyGroup(opacity: 0.4))
{
page.DrawRectangle(140, 540, 200, 100,
fill: PdfColor.Rgb(0.0, 0.6, 0.3));
}
doc.Save("vectors.pdf");
5AcroForm widgets
Add an interactive form — text fields, a checkbox, and a Print button — from C#.
using JetsonPDF;
using JetsonPDF.Forms;
var doc = new PdfDocument { Title = "Signup" };
var page = doc.AddPage(PageSize.Letter);
var label = new PdfFont(FontFamily.Helvetica, 11);
page.DrawText("Name:", label, x: 72, y: 720);
page.AddTextField("name", x: 130, y: 712, width: 280, height: 20, label);
page.DrawText("Email:", label, x: 72, y: 690);
var email = page.AddTextField("email", x: 130, y: 682, width: 280, height: 20, label);
email.MaxLength = 120;
var subscribe = page.AddCheckBox("subscribe", x: 130, y: 650, width: 14, height: 14);
subscribe.IsChecked = true;
page.DrawText("Send me occasional updates", label, x: 152, y: 651);
var print = page.AddPushButton("printBtn",
x: 130, y: 600, width: 90, height: 24, label, caption: "Print");
print.Action = PdfWidgetAction.NamedAction("Print");
doc.Save("signup.pdf");
6AES-256 encryption with permissions
Encrypt the document and limit what viewers can do without the owner password.
using JetsonPDF;
var doc = new PdfDocument
{
Title = "Confidential",
PdfVersion = "2.0",
Encryption = new PdfEncryptionOptions
{
Algorithm = PdfEncryptionAlgorithm.Aes256,
UserPassword = "open-me",
OwnerPassword = "owner-only",
Permissions = PdfPermissions.Print | PdfPermissions.CopyContent,
},
};
doc.AddPage(PageSize.Letter)
.DrawText("Confidential.",
new PdfFont(FontFamily.Helvetica, 24),
x: 72, y: 700);
doc.Save("encrypted.pdf");
AES-256 uses Standard Security Handler V5 (algorithms 2.B / 8 / 9 / 10 in PDF 2.0). Acrobat X+ and every modern viewer read it; for older viewers, drop down to Aes128.
7Detached PKCS#7 signature
Sign the rendered bytes with a certificate that has a private key. Multi-signing works via incremental update.
using System.Security.Cryptography.X509Certificates;
using JetsonPDF;
// 1. Build the document as usual.
var doc = new PdfDocument { Title = "Signed report" };
doc.AddPage(PageSize.Letter)
.DrawText("Signed.", new PdfFont(FontFamily.Helvetica, 24), 72, 700);
byte[] unsigned = doc.SaveToBytes();
// 2. Sign in-memory with a PFX certificate.
var cert = new X509Certificate2("signer.pfx", "pfx-password",
X509KeyStorageFlags.Exportable);
var signed = PdfSigner.Sign(unsigned, new PdfSignatureOptions(cert)
{
Reason = "Approved",
Location = "HQ",
FieldName = "Signature1",
});
File.WriteAllBytes("signed.pdf", signed);
For PAdES B-LT/B-LTA, follow up with PdfTimestamper.AddDocumentTimestamp and PdfDss.AddSecurityStore to embed the validation material.
8Fluent invoice with header, footer, and table
The JetsonPDF.Fluent namespace exposes a QuestPDF-style layout DSL. Header, footer, and content slots; rows; tables; per-run rich text; live page numbers.
using JetsonPDF;
using JetsonPDF.Fluent;
var doc = Document.Create(d =>
{
d.Page(p =>
{
p.Size(PageSize.Letter);
p.Margin(50);
p.DefaultTextStyle(s => s.FontSize = 10);
// Header — repeats on every page.
p.Header().Row(row =>
{
row.RelativeItem().Text("INVOICE", s =>
{
s.FontSize = 24;
s.FontStyle = FontStyle.Bold;
s.FontColor = Colors.IndigoDark;
});
row.ConstantItem(120).AlignRight().Text(t =>
{
t.AlignRight();
t.Span("Page ");
t.CurrentPageNumber().Bold();
t.Span(" / ");
t.TotalPages().Bold();
});
});
// Footer — repeats on every page.
p.Footer().AlignCenter().Text("https://jetsonpdf.net");
// Content — auto-paginates on overflow.
p.Content().PaddingVertical(12).Column(col =>
{
col.Spacing(14);
col.Item().Text("Bill to: Acme Corporation",
s => s.FontStyle = FontStyle.Bold);
col.Item().Table(t =>
{
t.ColumnsDefinition(c =>
{
c.RelativeColumn(3); // Description
c.ConstantColumn(50); // Qty
c.ConstantColumn(70); // Price
c.ConstantColumn(80); // Amount
});
// Header row — repeats per page.
t.Header(h =>
{
h.Cell().Padding(6).Background(Colors.IndigoDark)
.Text("Description", s => s.FontColor = Colors.White);
h.Cell().Padding(6).Background(Colors.IndigoDark).AlignRight()
.Text("Qty", s => s.FontColor = Colors.White);
h.Cell().Padding(6).Background(Colors.IndigoDark).AlignRight()
.Text("Price", s => s.FontColor = Colors.White);
h.Cell().Padding(6).Background(Colors.IndigoDark).AlignRight()
.Text("Amount", s => s.FontColor = Colors.White);
});
foreach (var (name, qty, price) in lineItems)
{
t.Cell().Padding(6).Text(name);
t.Cell().Padding(6).AlignRight().Text(qty.ToString());
t.Cell().Padding(6).AlignRight().Text($"${price:F2}");
t.Cell().Padding(6).AlignRight().Text($"${qty * price:F2}");
}
// Footer row — only on the last page; spans columns.
t.Footer(f =>
{
f.Cell(columnSpan: 3).Padding(6).AlignRight()
.Text("Total", s => s.FontStyle = FontStyle.Bold);
f.Cell().Padding(6).Background(Colors.IndigoDark).AlignRight()
.Text($"${total:F2}", s =>
{ s.FontStyle = FontStyle.Bold; s.FontColor = Colors.White; });
});
});
});
});
}).WithMetadata(title: "Invoice #INV-2026-001", author: "Acme");
doc.GeneratePdf("invoice.pdf");
The full source for this sample (with stripes, callout box, and form widgets) lives at samples/FluentInvoice/Program.cs — download the rendered PDF. See the Fluent API reference for every container method.
9Form widgets and internal nav with Fluent
Any leaf slot becomes an AcroForm widget. .Section(anchor) registers a named destination; .SectionLink(anchor) links to it.
using JetsonPDF;
using JetsonPDF.Fluent;
var doc = Document.Create(d => d.Page(p =>
{
p.Size(PageSize.Letter);
p.Margin(50);
p.Content().Column(col =>
{
col.Spacing(12);
// A named destination.
col.Item().Section("top").Text("Customer acknowledgement",
s => s.FontStyle = FontStyle.Bold);
// Text field — Acrobat draws the chrome at the arranged bounds.
col.Item().Row(row =>
{
row.ConstantItem(100).AlignMiddle().Text("Signed name:");
row.RelativeItem().Height(18).AsTextField("ack.name");
});
// Check box.
col.Item().Row(row =>
{
row.ConstantItem(15).Height(15).AsCheckBox("ack.agreed");
row.RelativeItem().PaddingLeft(6).AlignMiddle()
.Text("I agree to the payment terms above.");
});
// Combo box with options.
col.Item().Row(row =>
{
row.ConstantItem(100).AlignMiddle().Text("Region:");
row.ConstantItem(160).Height(18).AsComboBox("ack.region",
new[] { "North America", "EMEA", "APAC", "LATAM" });
});
// Push button — combine .Link(uri) with .AsPushButton.
col.Item().Width(120).Height(22)
.Link("https://jetsonpdf.net/pay")
.AsPushButton("ack.pay", "Pay now");
// Internal-link button to the named destination above.
col.Item().SectionLink("top").Width(60).Height(14)
.Background(Colors.Blue).AlignCenter().AlignMiddle()
.Text("Top", s => s.FontColor = Colors.White);
});
}));
doc.GeneratePdf("acknowledgement.pdf");
10Word-style report with Flow
JetsonPDF.Flow is a Word-like retained-mode DOM. Build a tree of Section/Paragraph/Table, mutate it freely, then Save. Auto-paginates over the Fluent renderer.
using JetsonPDF;
using JetsonPDF.Flow;
var doc = new FlowDocument
{
Title = "Q3 Quarterly Report",
Author = "Acme Corp",
DefaultRunProperties = new RunProperties
{
FontFamily = "Helvetica",
FontSize = 11,
Color = PdfColor.Black,
},
Hyphenator = JetsonPDF.Flow.Hyphenation.Hyphenator.EnglishDefault(),
};
// Named style with a basedOn graph.
doc.AddStyle("Callout", configure: s =>
{
s.RunProperties.Italic = true;
s.RunProperties.FontSize = 10;
s.ParagraphProperties.LeftIndent = 24;
s.ParagraphProperties.RightIndent = 24;
});
var section = doc.AddSection();
section.PageSize = PageSize.Letter;
section.PageMargins = new Margins(72);
// Footer with live page numbers.
section.Footer.Body.Add(new Paragraph(p =>
{
p.AddText("Page ");
p.AddPageNumber();
p.AddText(" of ");
p.AddPageCount();
}) { Alignment = TextAlign.Center });
// Headings auto-bookmark — TableOfContents picks them up.
section.Body.Add(new Paragraph("Q3 Quarterly Report")
{ Style = ParagraphStyle.Heading1, Alignment = TextAlign.Center });
section.Body.Add(new Paragraph("Contents") { Style = ParagraphStyle.Heading2 });
section.Body.Add(new TableOfContents());
section.Body.Add(new Paragraph("Introduction")
{ Style = ParagraphStyle.Heading1, PageBreakBefore = true });
// Rich inline runs: bold, italic, link, footnote, drop cap, justified.
section.Body.Add(new Paragraph(p =>
{
p.AddText("This report describes ");
p.AddText("quarterly performance").Bold();
p.AddText(" across our three primary business lines. ");
p.AddText("All comparisons reference the prior fiscal quarter");
p.AddFootnote("Constant-currency rates are locked at the start of FY25.");
p.AddText(". For methodology see ");
p.AddText("our public docs").Link("https://www.example.com/methodology");
p.AddText(".");
})
{
DropCap = new DropCap { LineSpan = 3 },
Alignment = TextAlign.Justify,
});
// Numbered list — auto-counter, hanging indent.
section.Body.Add(new Paragraph("Total revenue grew 12% year over year.")
{ ListMarker = new ListMarker(ListKind.Number) });
section.Body.Add(new Paragraph("Hardware margin expanded by 180 bps.")
{ ListMarker = new ListMarker(ListKind.Number) });
// Named-style callout.
section.Body.Add(new Paragraph("Forward-looking statements may change.")
{ StyleName = "Callout" });
// Table with repeating header.
var table = new Table
{
Columns = { 2.0, 1.0, 1.0 },
RepeatHeader = true,
CellBorderWidth = 0.5,
CellPadding = 5,
};
var hdr = new TableRow();
hdr.Cells.Add(new TableCell(new Paragraph("Region")
{ Style = ParagraphStyle.Heading3 }));
hdr.Cells.Add(new TableCell(new Paragraph("Q3")
{ Style = ParagraphStyle.Heading3, Alignment = TextAlign.Right }));
hdr.Cells.Add(new TableCell(new Paragraph("Δ")
{ Style = ParagraphStyle.Heading3, Alignment = TextAlign.Right }));
table.Header.Add(hdr);
foreach (var (region, q3, delta) in new[]
{
("North America", "$53.6M", "+11.2%"),
("EMEA", "$34.0M", "+9.3%"),
("APAC", "$25.9M", "+14.1%"),
})
{
var row = new TableRow();
row.Cells.Add(new TableCell(region));
row.Cells.Add(new TableCell(new Paragraph(q3)
{ Alignment = TextAlign.Right }));
row.Cells.Add(new TableCell(new Paragraph(delta)
{ Alignment = TextAlign.Right }));
table.Body.Add(row);
}
section.Body.Add(table);
// Endnote list — auto-collected from the body.
section.Body.Add(new Paragraph("Notes")
{ Style = ParagraphStyle.Heading2, PageBreakBefore = true });
section.Body.Add(new EndnoteList());
doc.Save("FlowReport.pdf");
The full FlowReport sample (cover, footnotes, two-column section, track changes, anchored images, comments, segment table) lives at samples/FlowReport/Program.cs — download the rendered PDF. See the Flow API reference for the complete tree.
11Author a PDF in XAML
Compose with the WPF layout engine you already know — Grid, StackPanel, Border, data-bound text. JetsonPDF runs Measure/Arrange and walks the resulting visual tree.
Add jetsonpdf:Form.FieldName="email" to a TextBox and that XAML control becomes a real AcroForm widget at the arranged bounds.
12Read a PDF and walk its content
Open a file, list its outlines, and dump the text of every page.
using JetsonPDF.Reading;
var doc = PdfReader.Load("input.pdf");
Console.WriteLine($"Title: {doc.Info.Title}");
Console.WriteLine($"Pages: {doc.Pages.Count}");
Console.WriteLine($"Encrypted: {doc.IsEncrypted}");
foreach (var item in doc.Outlines)
Console.WriteLine($" * {item.Title}");
foreach (var page in doc.Pages)
{
Console.WriteLine($"--- page {page.PageNumber} ---");
foreach (var content in page.Items.OfType<PageTextItem>())
Console.WriteLine(content.Text);
}
Need to read an encrypted file? Pass the password as the second argument to Load: PdfReader.Load("input.pdf", "open-me").
13Render any PDF as a WPF view
Convert ReadPdfDocument straight into XAML and host it in a WPF window. No filesystem access; images come back as base64 inline.
using JetsonPDF.Reading;
using JetsonPDF.Wpf;
using System.Windows.Markup;
var read = PdfReader.Load("input.pdf");
string xaml = PdfToXamlConverter.Convert(read);
// Parse the XAML and bind it into the host control.
var visual = (FrameworkElement)XamlReader.Parse(xaml);
PdfHost.Content = visual;
The output is a StackPanel — one Canvas per page — with absolute-positioned TextBlock, Image, Path, and Glyphs elements. Wrap it in a ScrollViewer for paging.