TerraPDF for AI Agents
This page is the context an AI coding agent needs to write correct, compiling TerraPDF code on the first try. It is written to be read by a machine, and it is also the page to read yourself if you want the whole API on one screen.
New to AI tools? Follow Create PDFs with AI: Step-by-Step to set up TerraPDF.Mcp in Claude Desktop, VS Code, or Cursor, or to build a .NET agent with TerraPDF.Agents. This page is the technical reference.
Everything below is verified against TerraPDF 2.4.0. Members marked [2.3+] were added in 2.3.0 and do not compile against older versions.
Give your agent one URL
| URL | What it contains | Use when |
|---|---|---|
https://terrapdf.com/llms-full.txt |
This contract plus every documentation guide, concatenated into one plain-text file | Default. One fetch, no follow-up requests |
https://terrapdf.com/llms.txt |
A short index linking to each guide | Your agent prefers to fetch pages on demand |
https://terrapdf.com/docs/ai-agents.md |
This page only, as raw Markdown | You only need the API contract, not the guides |
Both .txt files are plain text with no navigation, no HTML, and no
JavaScript, so they cost an agent nothing to parse.
Paste-ready prompt
Read https://terrapdf.com/llms-full.txt first. It is the complete API contract
for TerraPDF, a zero-dependency C# PDF library.
Then, using TerraPDF only (no other PDF library), write: <describe the document>
Follow the "Rules that prevent almost every failure" section exactly. Do not
invent API members — if something you need is not in the reference, say so
instead of guessing.
Project rules file
Drop this into CLAUDE.md, AGENTS.md, .github/copilot-instructions.md,
.cursor/rules/, or your agent's equivalent, so the contract is loaded on every
task in the repo. If your assistant supports skills, install the
TerraPDF skill instead: it loads only when PDF code is involved.
## PDF generation
This project generates PDFs with TerraPDF (NuGet: `TerraPDF`).
Before writing or editing any PDF-generating code, read
https://terrapdf.com/llms-full.txt and follow its API contract.
Key invariants: every container holds exactly one child; `PublishPdf(...)` is
the only output call; colours are hex strings from `TerraPDF.Helpers.Color`;
the standard-14 fonts cover WinAnsiEncoding only.
Tooling for AI agents
Beyond this page, TerraPDF ships three pieces for AI-assisted development. Pick by what the AI is doing:
| The AI is… | Use | What it gets |
|---|---|---|
| Writing C# that uses TerraPDF (GitHub Copilot, Cursor, Claude Code, Codex) | The TerraPDF skill | This contract as an Agent Skill: rules, the full API reference, compiling recipes, and troubleshooting, loaded only when PDF code is involved |
Creating PDFs at runtime inside a .NET agent (Microsoft Agent Framework, Semantic Kernel, IChatClient) |
The TerraPDF.Agents package |
create_pdf and get_pdf_document_format tools that take a validated JSON document, not generated C# |
| Either, through MCP (Copilot, Cursor, Claude, LangChain/LangGraph) | The TerraPDF.Mcp server |
The same PDF tools, plus get_terrapdf_csharp_guide, which serves the skill |
The skill, for coding assistants
Copy skills/terrapdf from the
repository into your project:
cp -r skills/terrapdf .github/skills/terrapdf # GitHub Copilot
cp -r skills/terrapdf .claude/skills/terrapdf # Claude Code
For assistants without skill support, use the project rules file above, or connect the MCP server below.
TerraPDF.Agents, for .NET agents
Do not have a runtime agent write and execute C#: that needs a compiler at
runtime, cannot be validated, and runs model-written code. Instead, the model
describes the document as JSON and calls create_pdf:
{
"title": "Q3 Sales Report",
"content": [
{ "type": "heading", "level": 1, "text": "Q3 Sales Report" },
{ "type": "paragraph", "text": "Revenue grew **18%** quarter over quarter." },
{ "type": "chart", "chartType": "bar", "labels": ["NA", "EMEA", "APAC"], "values": [430, 455, 240] },
{ "type": "table",
"columns": [ { "header": "Region", "width": 3 }, { "header": "Revenue", "align": "right" } ],
"rows": [ ["NA", "$430k"], ["EMEA", "$455k"], ["APAC", "$240k"] ] }
]
}
Blocks cover headings, paragraphs, lists, tables with totals, key/value pairs,
callouts, nested columns, images, bar/line/pie charts, Code128 barcodes, QR
codes, dividers, spacers, and page breaks. The document can also set headers,
footers, page numbers, a table of contents, custom fonts, a theme, and AES-256
encryption. An invalid document writes nothing; the tool returns every error
with a JSON path ($.content[2].rows[1]: Row has 5 cells but the table defines 4 columns.) so the model can correct itself.
dotnet add package TerraPDF.Agents
using TerraPDF.Agents.Tools;
var pdfTools = new TerraPdfTools(new TerraPdfToolOptions { OutputDirectory = "out" });
// Microsoft Agent Framework
AIAgent agent = chatClient.AsAIAgent(
instructions: "You produce polished PDF reports. Use create_pdf.",
tools: [.. pdfTools.AsAIFunctions()]);
// Semantic Kernel (the model sees terrapdf_create_pdf, ...)
kernel.Plugins.AddFromFunctions("terrapdf", pdfTools.AsAIFunctions().Select(f => f.AsKernelFunction()));
// Any Microsoft.Extensions.AI IChatClient
var options = new ChatOptions { Tools = [.. pdfTools.AsAIFunctions()] };
Tool arguments are model output, so the tools treat them as untrusted. The model picks a file name, never a directory. Image and font paths cannot escape the configured asset directory; with none configured, only base64 data is accepted. URLs are never fetched, and links allow only http, https, and mailto.
TerraPDF.Mcp, for MCP clients
dnx TerraPDF.Mcp --yes # .NET 10 SDK, no install
dotnet tool install -g TerraPDF.Mcp # or install terrapdf-mcp globally
| Client | Configuration |
|---|---|
VS Code / GitHub Copilot (.vscode/mcp.json) |
{ "servers": { "terrapdf": { "type": "stdio", "command": "dnx", "args": ["TerraPDF.Mcp", "--yes"] } } } |
Cursor (.cursor/mcp.json), Claude Desktop |
{ "mcpServers": { "terrapdf": { "command": "dnx", "args": ["TerraPDF.Mcp", "--yes"] } } } |
| Claude Code | claude mcp add terrapdf -- dnx TerraPDF.Mcp --yes |
| LangChain / LangGraph | MultiServerMCPClient({"terrapdf": {"transport": "stdio", "command": "dnx", "args": ["TerraPDF.Mcp", "--yes"]}}) |
PDFs are written to ./pdf-output (--output-dir or TERRAPDF_OUTPUT_DIR).
Image and font paths resolve against the working directory (--asset-dir or
TERRAPDF_ASSET_DIR).
Package and namespaces
dotnet add package TerraPDF --version 2.4.0
Targets net8.0, net9.0, and net10.0. Zero dependencies, no native
binaries, MIT licensed.
using TerraPDF.Core; // Document, descriptors, VectorCanvas, PathDescriptor,
// EncryptionOptions, PdfPermissions, ImageFit
using TerraPDF.Helpers; // Color, FontFamily, PageSize, TextStyle, Unit
using TerraPDF.Infra; // IContainer, IComponent, IDocument, IDocumentContainer
using TerraPDF.Barcodes; // QrErrorCorrectionLevel (only if you draw QR codes)
The shape of every document
Every TerraPDF document has the same skeleton. Start from this, then fill it in:
using TerraPDF.Core;
using TerraPDF.Helpers;
Document.Create(doc =>
{
doc.MetadataTitle("Quarterly Report");
doc.MetadataAuthor("Acme Analytics");
doc.Page(page =>
{
page.Size(PageSize.A4);
page.Margin(2, Unit.Centimetre);
page.DefaultTextStyle(style => style.FontSize(10));
page.Header()
.Text("Quarterly Report")
.Bold().FontSize(18).FontColor(Color.Blue.Darken2);
page.Content().PaddingVertical(12).Column(col =>
{
col.Spacing(8);
col.Item().Text("Revenue grew 18% quarter over quarter.");
col.Item().Table(table =>
{
table.ColumnsDefinition(c =>
{
c.RelativeColumn(3);
c.RelativeColumn(1);
});
table.HeaderRow(row =>
{
row.Cell().Text("Product").Bold();
row.Cell().Text("Revenue").Bold();
});
table.Row(row =>
{
row.Cell().Text("Widgets");
row.Cell().Text("$482,000");
});
});
});
page.Footer().AlignCenter().Text(t =>
{
t.Span("Page ").FontSize(8);
t.CurrentPageNumber().FontSize(8);
t.Span(" of ").FontSize(8);
t.TotalPages().FontSize(8);
});
});
}).PublishPdf("report.pdf");
The model in one line: Document → Page → three slots (Header,
Content, Footer) → a container → decorators → one element.
Rules that prevent almost every failure
1. A container holds exactly one child
This is the single most common mistake. IContainer is a single-child
container: a second terminal call on the same container replaces the first,
silently. Only the last one is drawn.
// WRONG — "Title" never appears; it is replaced by "Body"
page.Content().Text("Title");
page.Content().Text("Body");
// RIGHT — a Column holds many items
page.Content().Column(col =>
{
col.Item().Text("Title").Bold();
col.Item().Text("Body");
});
Use Column for vertical stacks, Row for horizontal, Table for grids.
2. PublishPdf is the only output call
There is no Save, Generate, Render, Build, or Export. Three overloads:
Document.Create(...).PublishPdf("file.pdf"); // to a path
byte[] bytes = Document.Create(...).PublishPdf(); // to memory
Document.Create(...).PublishPdf(stream); // to a stream
3. Decorators chain; elements terminate
Decorators (Padding, Margin, Background, Border, AlignCenter,
ShowIf, Hyperlink, …) return IContainer, so they chain. Elements (Text,
Image, Canvas, Table, Column, Row, Barcode, QrCode) end the chain.
col.Item()
.Background(Color.Grey.Lighten4) // decorator
.Border(1, Color.Grey.Lighten1) // decorator
.Padding(8) // decorator
.AlignCenter() // decorator
.Text("Boxed and centred") // element — chain ends
.Bold().FontSize(12); // TextDescriptor styling, not decorators
ShowIf(condition) hides everything chained after it when the condition is
false. Before 2.3.0 it did not: the next element replaced the placeholder and
rendered anyway, so on older versions use a C# if around the item.
4. Colours are hex strings — and the palette is uneven
Every colour parameter is a string hex value. Color constants are just
named hex strings, so "#1A3C5E" works anywhere a Color.* does.
Only Red, Blue, Green, and Grey have all ten shades. Most families
have only Medium and Darken2. Using a shade that does not exist is a
compile error, and it is the second most common generated-code failure.
| Family | Available shades |
|---|---|
Red, Blue, Green, Grey |
Lighten5 Lighten4 Lighten3 Lighten2 Lighten1 Medium Darken1 Darken2 Darken3 Darken4 |
BlueGrey |
Lighten5 Lighten4 Medium Darken2 Darken4 |
Indigo, Brown |
Lighten5 Medium Darken2 |
Pink, Purple, DeepPurple, LightBlue, Cyan, Teal, LightGreen, Lime, Yellow, Amber, Orange, DeepOrange |
Medium Darken2 only |
| (top level) | Color.Black, Color.White, Color.Transparent |
When in doubt, use a literal hex string.
5. The canvas has its own coordinate rules
- Origin is top-left, y increases downward — the same as page layout.
Textis the one primitive anchored at its baseline, not its top-left.PathDescriptor.Arc/Sectortake centre + radii (cx, cy, rx, ry).FillPie/StrokePie/DrawPietake a bounding box (x, y, w, h).- Angles start at 3 o'clock and increase clockwise. A pie chart that
should start at twelve o'clock starts at
-90. - The draw callback runs before layout, so the canvas cannot tell you its width. Compute it yourself: page width minus margins, minus any padding.
Gridis sized when the canvas is drawn, so it fills the laid-out width. Before 2.3.0 it drew nothing; on older versions draw grid lines withLine.- [2.3+]
LinkandInternalLinkare invisible: paint the button first, then place the link over it.Dashon a path affects only its stroke.
6. Standard fonts are Latin-only
The built-in standard-14 fonts (Helvetica, Times, Courier) cover
WinAnsiEncoding only. Cyrillic, Greek, Devanagari, CJK, →, −, and most
typographic symbols render as ? unless you register a TrueType font:
FontFamily.Register("Noto", "NotoSans-Regular.ttf");
FontFamily.Register("Noto", "NotoSans-Bold.ttf", bold: true);
col.Item().Text("Привет, धर्म").FontFamily("Noto");
Registered fonts are subsetted automatically — only the glyphs the document draws are embedded.
7. Page sizes are tuples, not enums
page.Size(PageSize.A4); // (595.28, 841.89)
page.Size(PageSize.Landscape(PageSize.A4)); // swapped
page.Size(210, 297, Unit.Millimetre); // explicit
8. Encrypt once, before adding pages
doc.Encrypt(new EncryptionOptions
{
UserPassword = "open-me",
OwnerPassword = "full-access",
Permissions = PdfPermissions.Print | PdfPermissions.CopyText,
// Algorithm defaults to EncryptionAlgorithm.Aes256
});
9. A table of contents needs headings
doc.TableOfContents() collects H1()–H6() headings only. Plain Text()
calls are never collected, and the TOC page is inserted where you call it.
10. Row and column items have distinct methods
col.Item() // Column: one stacked item
row.RelativeItem(2) // Row: proportional width (weight)
row.ConstantItem(120) // Row: fixed width in points
row.AutoItem() // Row: width of its content
API reference
Verified against 2.4.0. Optional parameters show their defaults.
Entry points
| Member | Signature |
|---|---|
Document.Create |
static DocumentComposer Create(Action<IDocumentContainer> compose) |
Document.Create |
static DocumentComposer Create(IDocument document) |
| Output | void PublishPdf(string path) · byte[] PublishPdf() · void PublishPdf(Stream stream) |
Document level (IDocumentContainer / DocumentComposer)
| Member | Signature |
|---|---|
| Page | void Page(Action<PageDescriptor> configure) |
| TOC | void TableOfContents(Action<PageDescriptor>? configure = null) |
| Bookmark | void Bookmark(string title, int pageNumber) |
| Bookmark | void Bookmark(string title, int pageNumber, double top) |
| Bookmark | void Bookmark(string title, int pageNumber, string parentTitle) |
| Bookmark | void Bookmark(string title, int pageNumber, string parentTitle, double top) |
| Metadata | MetadataTitle · MetadataAuthor · MetadataSubject · MetadataKeywords · MetadataCreator, each (string?) |
| Encryption | void Encrypt(EncryptionOptions options) |
Bookmark page numbers are 1-based.
PageDescriptor
| Member | Signature |
|---|---|
| Size | Size((double Width, double Height) size) · Size(double widthPt, double heightPt) · Size(double width, double height, Unit unit) |
| Margin | Margin(double value) · Margin(double value, Unit unit) · Margin(double top, double right, double bottom, double left) |
| Margin | MarginVertical(double) · MarginHorizontal(double) |
| Background | PageColor(string hexColor) |
| Text default | DefaultTextStyle(Func<TextStyle, TextStyle> configure) |
| Slots | IContainer Header() · IContainer Content() · IContainer Footer() |
| Header scope | HeaderOnFirstPageOnly() |
Container decorators (IContainer extensions)
| Group | Members |
|---|---|
| Padding | Padding(v) · Padding(v, Unit) · PaddingVertical · PaddingHorizontal · PaddingTop · PaddingBottom · PaddingLeft · PaddingRight |
| Margin | Margin(v) · Margin(v, Unit) · MarginVertical · MarginHorizontal · MarginTop · MarginBottom · MarginLeft · MarginRight |
| Background | Background(string hexColor) |
| Border | Border(double lineWidth, string hexColor) · Border(double lineWidth = 1) · BorderTop/Bottom/Left/Right(double lineWidth, string hexColor = "#000000") |
| Rounded | RoundedBorder(double radius = 8, double lineWidth = 1, string hexColor = "#000000") · RoundedBox(double radius = 8, string fillHexColor = "#FFFFFF", string borderHexColor = "#000000", double lineWidth = 1) |
| Align | AlignLeft() · AlignCenter() · AlignRight() · AlignMiddle() · AlignBottom() |
| Rules | LineHorizontal(double lineWidth = 1, string hexColor = "#000000") · LineVertical(...) |
| Flow | ShowIf(bool condition) (see rule 3) · PageBreak() |
| Links | Hyperlink(string url) · InternalLink(int pageNumber, double? top = null) · Bookmark(string title, string? parentTitle = null) |
| Compose | Component(IComponent component) |
Container elements (chain terminators)
| Element | Signature |
|---|---|
| Text | TextDescriptor Text(string text) · TextDescriptor Text(Action<TextDescriptor> compose) |
| Headings | H1(string) … H6(string), each returning TextDescriptor |
| Layout | Column(Action<ColumnDescriptor>) · Row(Action<RowDescriptor>) · Table(Action<TableDescriptor>) |
| Image | Image(string filePath) · Image(string filePath, double width) · Image(byte[]) · Image(byte[], double width) · Image(Stream) · Image(Stream, double width) |
| Canvas | Canvas(double height, Action<VectorCanvas> draw) |
| Barcode | Barcode(string data, double? width = null, double height = 40, string hexColor = "#000000", string backgroundHexColor = "#FFFFFF", bool showCaption = false, double quietZoneModules = 10) |
| QR | QrCode(string data, double? size = null, QrErrorCorrectionLevel level = QrErrorCorrectionLevel.M, string hexColor = "#000000", string backgroundHexColor = "#FFFFFF", int quietZoneModules = 4) |
ColumnDescriptor / RowDescriptor / TableDescriptor
| Type | Members |
|---|---|
ColumnDescriptor |
Spacing(double) · Item() · PageBreak() · AlignItemsLeft/Center/Right() |
RowDescriptor |
Spacing(double) · AutoItem() · RelativeItem(double weight = 1) · ConstantItem(double widthPt) |
TableDescriptor |
ColumnsDefinition(Action<ColumnsDefinitionDescriptor>) · HeaderRow(Action<TableRowDescriptor>) · Row(Action<TableRowDescriptor>) |
ColumnsDefinitionDescriptor |
RelativeColumn(double weight = 1) · ConstantColumn(double widthPt) |
TableRowDescriptor |
IContainer Cell(int columnSpan = 1, int rowSpan = 1) |
TextDescriptor and SpanDescriptor
| Type | Members |
|---|---|
TextDescriptor |
FontSize(double) · FontColor(string) · FontFamily(string) · Bold() · SemiBold() · Italic() · Underline() · Strikethrough() · LineHeight(double) · AlignLeft/Center/Right() · Justify() |
TextDescriptor (composite) |
Span(string text, Func<TextStyle, TextStyle>? styleAction = null) · CurrentPageNumber() · TotalPages() |
SpanDescriptor |
Bold() · SemiBold() · Italic() · Underline() · Strikethrough() · FontSize(double) · FontColor(string) · FontFamily(string) |
TextStyle (immutable) |
same setters, each returning a new instance; plus NormalWeight() · NormalStyle() · NoUnderline() · NoStrikethrough() |
VectorCanvas
Every fill and stroke primitive takes a trailing opacity (default 1).
Stroke methods that take dashPattern (alternating dash and gap lengths in
points, copied; null = solid) also take dashPhase (offset into the pattern).
| Method | Signature |
|---|---|
| Line | Line(double x1, double y1, double x2, double y2, string hexColor = "#000000", double lineWidth = 1, double opacity = 1, double[]? dashPattern = null, double dashPhase = 0) |
| Rect | FillRect(x, y, width, height, hexColor = "#000000", opacity = 1) |
| Rect | StrokeRect(x, y, width, height, hexColor = "#000000", lineWidth = 1, opacity = 1, double[]? dashPattern = null, dashPhase = 0) |
| Rect | DrawRect(x, y, width, height, fillHex = "#FFFFFF", strokeHex = "#000000", lineWidth = 1, opacity = 1, double[]? dashPattern = null, dashPhase = 0) |
| Rounded | FillRoundedRect(x, y, width, height, radius, hexColor = "#000000", opacity = 1) |
| Rounded | StrokeRoundedRect(x, y, width, height, radius, hexColor = "#000000", lineWidth = 1, opacity = 1, dashPattern = null, dashPhase = 0): dash [2.3+] |
| Rounded | DrawRoundedRect(x, y, width, height, radius, fillHex = "#FFFFFF", strokeHex = "#000000", lineWidth = 1, opacity = 1, dashPattern = null, dashPhase = 0): dash [2.3+] |
| Circle | FillCircle(cx, cy, radius, hexColor = "#000000", opacity = 1) · StrokeCircle(cx, cy, radius, hexColor = "#000000", lineWidth = 1, opacity = 1) · DrawCircle(cx, cy, radius, fillHex = "#FFFFFF", strokeHex = "#000000", lineWidth = 1, opacity = 1). No dash parameters: for a dashed circle use StrokeEllipse(cx, cy, r, r, ...) |
| Ellipse | FillEllipse(cx, cy, rx, ry, hexColor = "#000000", opacity = 1) |
| Ellipse | StrokeEllipse(cx, cy, rx, ry, hexColor = "#000000", lineWidth = 1, opacity = 1, dashPattern = null, dashPhase = 0): dash [2.3+] |
| Ellipse | DrawEllipse(cx, cy, rx, ry, fillHex = "#FFFFFF", strokeHex = "#000000", lineWidth = 1, opacity = 1, dashPattern = null, dashPhase = 0): dash [2.3+] |
| Pie | FillPie(x, y, width, height, startAngle, sweepAngle, fillHex = "#000000", opacity = 1) |
| Pie | StrokePie(x, y, width, height, startAngle, sweepAngle, strokeHex = "#000000", lineWidth = 1, opacity = 1, dashPattern = null, dashPhase = 0): dash [2.3+] |
| Pie | DrawPie(x, y, width, height, startAngle, sweepAngle, fillHex = "#FFFFFF", strokeHex = "#000000", lineWidth = 1, opacity = 1, dashPattern = null, dashPhase = 0): dash [2.3+] |
| Text | Text(string text, double x, double y, string hexColor = "#000000", double fontSize = 12, string? fontFamily = null, bool bold = false, bool italic = false, double opacity = 1, double angle = 0) |
| Measure | static double MeasureTextWidth(string text, double fontSize, string? fontFamily = null, bool bold = false, bool italic = false) |
| Image | Image(byte[] imageData, double x, double y, double width, double height, ImageFit fit = ImageFit.Stretch), plus string filePath and Stream overloads |
| Image size | static (double Width, double Height) GetImageSizeInPoints(byte[] imageData) |
| Path | Path(Action<PathDescriptor> configure) |
| Grid | Grid(double cellWidth, double? cellHeight = null, string hexColor = "#CCCCCC", double lineWidth = 0.5) |
| Link [2.3+] | Link(double x, double y, double width, double height, string url) |
| Internal link [2.3+] | InternalLink(double x, double y, double width, double height, int pageNumber, double? top = null) |
| Bookmark [2.3+] | Bookmark(string title, double y = 0, string? parentTitle = null) |
| QR code [2.3+] | QrCode(string data, double x, double y, double size, QrErrorCorrectionLevel level = QrErrorCorrectionLevel.M, string hexColor = "#000000", string? backgroundHex = null, int quietZoneModules = 4) |
Gridis sized at draw time to the canvas's laid-out width. Before 2.3.0 it drew nothing. On older versions, draw grid lines withLine.
Links, bookmarks, and QR codes [2.3+]:
LinkandInternalLinkdraw nothing. Paint the button or label first, then place the link over the same rectangle.InternalLink'spageNumberis the 1-based physical page. Rendering throwsInvalidOperationExceptionif the document has fewer pages.topis the distance from the top of the target page;nullfits the whole page.Bookmarktargets the page the canvas is drawn on, at canvas-relativey(negative values clamp to 0).parentTitlenests it under an earlier bookmark with that title. A repeated (title, parent) pair is recorded once, so a canvas repeated on every page adds one entry.QrCode'ssizeincludes the quiet zone.backgroundHex = nullleaves the square transparent. Data too large for the level throwsNotSupportedExceptionat theQrCodecall, not at render time. For a QR code in the layout flow, usecontainer.QrCode(...)instead.
PathDescriptor
MoveTo(x, y) · LineTo(x, y) · CurveTo(cx1, cy1, cx2, cy2, x, y) ·
Close() · Rect(x, y, width, height) · Ellipse(cx, cy, rx, ry) ·
Circle(cx, cy, radius) · Arc(cx, cy, rx, ry, startAngle, sweepAngle) ·
Sector(cx, cy, rx, ry, startAngle, sweepAngle) ·
Polyline(params (double X, double Y)[]) · Polygon(params (double X, double Y)[]) ·
Fill(string hexColor) · Stroke(string hexColor, double lineWidth = 1) ·
UseEvenOddFill() · Opacity(double opacity)
[2.3+]: RoundedRect(x, y, width, height, radius) ·
FillLinearGradient(string fromHex, string toHex, double angle = 0) ·
FillRadialGradient(string centerHex, string edgeHex) ·
Dash(double[] pattern, double phase = 0)
RoundedRectclamps the radius to half the shorter side.- Gradients are two-stop and span the path's bounding box. The linear
angleis in degrees clockwise from left-to-right: 0 runs left to right, 90 runs top to bottom. The radial gradient runs fromcenterHexat the centre toedgeHexat half the larger side. End colours extend past the axis. Filland the gradient methods replace each other; the last call wins.Stroke,UseEvenOddFill,Opacity, andDashstill apply to a gradient.Dashaffects only the stroke, so it needs.Stroke(...).
Enums and helpers
| Type | Values |
|---|---|
Unit |
Point Millimetre Centimetre Inch |
ImageFit |
Stretch Contain Cover CoverTopLeft CropTopLeft |
EncryptionAlgorithm |
Aes256 (default) Aes128 |
PdfPermissions |
None Print PrintLowResolution ModifyContents CopyText ModifyAnnotations FillForms ExtractForAccessibility AssembleDocument All — combine with | |
QrErrorCorrectionLevel |
L M (default) Q H |
PageSize |
A0–A6 Letter Legal Tabloid Executive · Landscape(size) |
FontFamily |
static void Register(string familyName, string fontFilePath, bool bold = false, bool italic = false) — also byte[] and Stream overloads |
Reusable pieces
class InvoiceHeader : IComponent
{
public void Compose(IContainer container) =>
container.Background(Color.Blue.Darken2).Padding(10).Text("INVOICE");
}
class MyReport : IDocument
{
public void Compose(IDocumentContainer container) =>
container.Page(page => page.Content().Text("Hello"));
}
Document.Create(new MyReport()).PublishPdf("report.pdf");
Mistakes that break generated code
| Wrong | Right | Why |
|---|---|---|
Two .Text(...) on one container |
Wrap in Column, use col.Item() |
A container holds one child; the second replaces the first |
.Save(path) / .Generate() |
.PublishPdf(path) |
No other output method exists |
Color.Orange.Lighten3 |
Color.Orange.Medium |
Most families only have Medium and Darken2 |
Color.FromHex("#FF0000") |
"#FF0000" |
Colour parameters are plain strings |
page.Size(PageSize.A4.Landscape) |
page.Size(PageSize.Landscape(PageSize.A4)) |
PageSize members are tuples, not objects |
canvas.Text(...) positioned by top-left |
Pass the baseline y | Text is the one baseline-anchored primitive |
FillPie(cx, cy, rx, ry, ...) |
FillPie(x, y, width, height, ...) |
Pies take a bounding box; only Arc/Sector take centre + radii |
| Cyrillic/Devanagari in a standard font | FontFamily.Register(...) then .FontFamily("Name") |
Standard-14 fonts are WinAnsiEncoding only |
doc.Encrypt(...) after doc.Page(...) |
Call Encrypt first |
Encryption must be configured before pages |
Text("Chapter 1") expecting a TOC entry |
H1("Chapter 1") |
Only H1–H6 are collected |
row.Item() |
row.RelativeItem() / ConstantItem() / AutoItem() |
Item() exists on ColumnDescriptor only |
ShowIf(cond) on TerraPDF 2.2.0 or earlier |
if (cond) col.Item().Text(...); |
Before 2.3.0, ShowIf(false) was overwritten by the chained element |
canvas.Grid(20) on TerraPDF 2.2.0 or earlier |
Draw lines with Line(...) |
Before 2.3.0, Grid saw a zero-sized canvas and drew nothing |
canvas.StrokeCircle(..., dashPattern: ...) |
canvas.StrokeEllipse(cx, cy, r, r, ..., dashPattern: ...) |
Circle methods have no dash parameters |
p.FillLinearGradient(...).Fill("#FFF") expecting both |
Keep one | Fill and gradients replace each other; the last call wins |
Before you hand back the code
- It compiles.
dotnet build— most failures are a missingusingor a colour shade that does not exist. - It runs and writes a file. Check the PDF is non-empty and opens.
- Nothing is silently dropped. Every container has exactly one child; anything that should stack is inside a
Column,Row, orTable. - Non-Latin text has a registered font. Search the content for anything outside Latin-1 and confirm a
FontFamily.Registercovers it. - Nothing overflows. Canvas drawings stay inside the declared height; a canvas does not paginate.
- Only real API members are used. If it is not in the reference above, it does not exist — say so rather than guessing.
Worked examples
Every sample below is a complete, runnable program, shown with its full source and the PDF it produces. The full set is at terrapdf.com/samples/; the most useful starting points for an agent:
- Invoice — tables, totals, and a structured business document
- Simple Report — text, rules, tables, page numbers
- Report with Table of Contents — headings and TOC generation
- Canvas Media Showcase — images, arcs, pies, dashes, rotated text
- Custom Font Embedding — full Unicode via registered fonts
- Encryption & Security — passwords and permission flags