Custom Fonts & Full Unicode
The three built-in families — Helvetica, Times, Courier — are rendered through WinAnsiEncoding, which covers Western European text but not brand typefaces or scripts outside Windows-1252 (Cyrillic, Greek, Devanagari, and beyond). FontFamily.Register embeds a real TrueType font under a name of your choosing, so .FontFamily(...) can select it exactly like a built-in family, with full Unicode text wherever the font has glyphs.
No external package is required — font parsing, embedding, and glyph-ID text encoding are implemented entirely in System-namespace code.
Quick Start
using TerraPDF.Helpers;
// Register once — e.g. at application startup. Parsing is cached, so this
// is safe from a long-lived server process and reused by every document
// rendered afterwards, including concurrently.
FontFamily.Register("Brand", "fonts/Brand-Regular.ttf");
FontFamily.Register("Brand", "fonts/Brand-Bold.ttf", bold: true);
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSize.A4);
page.DefaultTextStyle(s => s.FontFamily("Brand"));
page.Content().Column(col =>
{
col.Item().Text("Héllo, Привет, Γειά σου").FontSize(18);
col.Item().Text("Now in bold.").Bold();
});
});
})
.PublishPdf("branded.pdf");
.FontFamily("Brand") and .Bold()/.Italic() are the same API you already use for the built-in families — nothing else about the fluent API changes.
Register overloads
| Overload | Use when |
|---|---|
Register(string familyName, string fontFilePath, bool bold = false, bool italic = false) |
Loading directly from a .ttf/.otf file on disk. |
Register(string familyName, byte[] fontFileBytes, bool bold = false, bool italic = false) |
The font data is already in memory — an embedded resource, a download, etc. |
Register(string familyName, Stream fontFileStream, bool bold = false, bool italic = false) |
Reading from a stream (read to completion; the stream is not disposed). |
A family can register up to four variants — regular, bold, italic, bold-italic — independently under the same name. Requesting a style that wasn't registered falls back to the closest registered variant instead of throwing: calling .Bold() on a family that only registered a regular file just renders with the regular outlines.
What Gets Embedded
Each registered variant is embedded as a Type0/CIDFontType2 composite font with Identity-H encoding: text is addressed by glyph ID rather than a fixed 256-slot table, so any glyph the font contains can be shown, not just Windows-1252. A ToUnicode CMap is included so copy/paste and text extraction recover the correct Unicode text.
The font is embedded once per document, no matter how many pages or how many times it's used — a registered font used throughout a 200-page report still costs one embedded copy.
Characters the font has no glyph for render as the font's .notdef glyph (usually a blank box) instead of throwing — the same graceful-fallback behaviour the standard fonts use, substituting ? for characters they can't map.
Devanagari-Aware Rendering
Registering a font that covers Devanagari script gets automatic corrections for any text drawn through it — no opt-in, no public API, purely a font-data-driven substitution done in managed code:
| Correction | What it fixes |
|---|---|
| Matra reordering | ि (U+093F) is stored after its consonant in Unicode but must render before it; TerraPDF moves it to the correct visual position. |
| Conjunct ligatures | Reads the font's own GSUB table for half/akhn/cjct and substitutes the joined-form glyphs the font defines — स्व, स्थ, क्ष, ज्ञ render properly instead of showing a separate ् mark. |
| Reph | Cluster-initial र् (धर्म, वर्तमान, दुर्बलता) is reordered to the end of its cluster and substituted via the font's rphf feature. |
| Below/post-base 'ra' | प्र, क्र, त्र, ष्ट्र substituted via the font's rkrf feature — codepoints are already in order, so no reordering is needed here. |
FontFamily.Register("NotoDevanagari", "fonts/NotoSansDevanagari-Regular.ttf");
page.DefaultTextStyle(s => s.FontFamily("NotoDevanagari"));
page.Content().Text("धर्म, वर्तमान, दुर्बलता, स्वास्थ्य, क्षमता").FontSize(16);
Every correction reads its substitution rules straight from the registered font's own GSUB table, so results depend on the font — most well-designed Devanagari fonts define these rules for nearly every consonant.
Known Limitations
- TrueType outlines only.
.ttffiles, and.otffiles that still carry aglyf/locatable, are supported. CFF-flavoured OpenType (OTTO) and TrueType Collections (.ttc) throwNotSupportedException. - No glyph subsetting. The whole font file is embedded, so output size scales with the font — future versions may add subsetting.
- No synthetic bold/italic. An unregistered style falls back to the closest registered variant rather than skewing or thickening glyphs to fake it.
- Partial Devanagari shaping.
blwf— below-base forms for consonants other than र, used by some fonts for other subjoined forms — relies on contextual GSUB lookups that aren't parsed, so those specific cases may still draw as separate glyphs. This is intentional: TerraPDF stays pure managed C# with no native dependency and won't bundle a full shaping engine (no GPOS mark positioning, no general Indic reordering beyond matra and reph) — this is a scoped, font-data-driven substitution, not a shaping engine.