CosmoFonts.Core 1.3.0

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

CosmoFonts

Pure-C# OpenType font loader and shaping engine. TrueType / CFF / WOFF / WOFF2 in, positioned glyphs out.

License: BSD-3-Clause.

What's here

src/
  CosmoFonts.Core/           — pure C#, no native deps
  CosmoFonts.Core.HarfBuzz/  — optional HarfBuzz wrapper for complex scripts
tools/
  CosmoFonts.SmokeTest/      — diagnostic + HarfBuzz parity comparator
  CosmoFonts.CffSmokeTest/   — CFF-specific diagnostic

Core is the pure-managed engine that almost every consumer should pull. Core.HarfBuzz is a thin P/Invoke wrapper around the native HarfBuzzSharp package, useful when you need HarfBuzz-grade shaping for Indic / Khmer / Myanmar / Tibetan and don't mind the native binaries.

Quick start

using CosmoFonts.Loader;
using CosmoFonts.Shaping;

// Load a font (TTF, OTF, TTC, WOFF, WOFF2 — auto-detected from magic).
var face = Face.Load(File.ReadAllBytes("/Library/Fonts/Arial.ttf"));

// Render text into a glyph outline via IGlyphRenderer.
var options = new TextOptions(face, sizePt: 12f) { Origin = new Vector2(50, 100) };
TextRenderer.RenderTextTo(myRenderer, "Hello, world!", options);

// Or measure without rendering.
var size = TextMeasurer.MeasureSize("Hello, world!", options);

The TextRenderer.RenderTextTo pipeline runs:

  1. NFC normalization (composes "a + combining acute" → "á" when the font has the precomposed glyph)
  2. BiDi segmentation per UAX #9 (X-rules, weak/neutral resolution, paired brackets, level reordering)
  3. Per BiDi run:
    • Script auto-detection (Latin/Cyrillic/Greek/Arabic/Hebrew/Devanagari/Thai/Han/Hangul/Hiragana/Katakana)
    • Arabic positional shaping (base codepoints → Presentation Forms-B based on joining context, with Lam-Alef ligation)
    • Per-face fallback segmentation (rune missing in primary face → look it up in fallbacks)
    • GSUB substitution (liga/smcp/calt/init/medi/fina/...)
    • GPOS positioning (kerning, mark attachment, cursive)
    • RTL reversal with mark-offset fix-up
  4. Concatenate runs in visual order
  5. Drive the IGlyphRenderer with positioned outline path commands

TTF / TTC / WOFF / WOFF2

// Single-font file
var face = Face.Load(ttfBytes);

// TTC (TrueType Collection — Helvetica.ttc, Avenir.ttc, Apple Color Emoji.ttc, …)
var helvetica = Face.Load(ttcBytes, fontIndex: 0);
int n = Face.CollectionFontCount(ttcBytes);

// WOFF1 (zlib-compressed) — auto-unwrapped
var face1 = Face.Load(woff1Bytes);

// WOFF2 (Brotli-compressed, with transformed glyf reconstruction) — auto-unwrapped
var face2 = Face.Load(woff2Bytes);

Font fallback

var primary  = Face.Load(File.ReadAllBytes("JetBrainsMono-Regular.ttf"));
var fallback = Face.Load(File.ReadAllBytes("Arial.ttf")); // covers Arabic, Cyrillic, …

var options = new TextOptions(primary, 12f)
{
    FallbackFaces = new[] { fallback },
};
TextRenderer.RenderTextTo(myRenderer, "Hi مرحبا", options);
// → "Hi " from JetBrains, "مرحبا" from Arial

Fallback faces should ideally have the same UnitsPerEm as the primary; mismatched units make fallback glyphs render at the wrong size.

System fonts

var sys = SystemFontCollection.LoadDefault();   // scans platform default paths

if (sys.TryFind("Helvetica", FontStyle.BoldItalic, out var entry))
    using var face = entry.Load();

// Or by exact subfamily name from the font's name table:
sys.TryFind("Avenir", "Heavy Oblique", out _);

// Iterate every variant:
foreach (var e in sys.Entries)
    Console.WriteLine($"{e.FamilyName} / {e.SubfamilyName}  ← {Path.GetFileName(e.FilePath)} #{e.FontIndex}");

The SystemFontCollection walks each platform's default font directories (macOS / Linux / Windows), indexes every TTF / OTF / TTC, prefers English subfamily names from the font's name table, and key by (family, subfamily) plus a Regular-preferred shortcut by family alone.

Variable fonts

if (face.IsVariable)
{
    foreach (var axis in face.Fvar!.Axes)
        Console.WriteLine($"{axis.Tag}: {axis.MinValue}..{axis.MaxValue} default={axis.DefaultValue}");

    foreach (var inst in face.Fvar.Instances)
        Console.WriteLine($"named instance — {string.Join(", ", inst.Coordinates)}");
}

Reading axis metadata + named instances works. Actual glyph interpolation at non-default instances (gvar / HVAR / VVAR / MVAR) is not implemented — you get the default-instance outline.

What's implemented

OpenType layout

Table Coverage
GSUB Type 1 (single, fmt 1+2), Type 2 (multiple), Type 3 (alternate), Type 4 (ligature), Type 5 (context, fmts 1+2+3), Type 6 (chained context, fmts 1+2+3), Type 7 (extension), Type 8 (reverse chaining)
GPOS Type 1 (single, fmt 1+2), Type 2 (pair, fmt 1+2), Type 3 (cursive), Type 4 (mark-to-base), Type 5 (mark-to-ligature), Type 6 (mark-to-mark), Type 7 (context, fmts 1+2+3), Type 8 (chained context, fmts 1+2+3), Type 9 (extension)
GDEF glyph class definition + mark attachment class
kern legacy kern table (fallback for fonts without GPOS PairPos)

Other tables

Table Status
cmap formats 4 + 12
head, hhea, hmtx, vhea, vmtx, maxp, name, OS/2, post, loca yes
glyf TrueType outlines + WOFF2 transformed-glyf reconstruction
CFF/CFF2 CFF Type 2 CharString interpreter (PostScript outlines)
COLR / CPAL parse only — v0 layered color glyphs (renderer integration is downstream)
fvar axes + named instances

Unicode / shaping

Subsystem Status
BiDi UAX #9 — X1–X10 explicit embedding, W1–W7 weak resolution, N0 paired brackets, N1–N2 neutrals, I1–I2 implicit levels, L1 trailing whitespace reset, L2 visual run reordering
NFC normalization via .NET's string.Normalize with font-coverage guard
Arabic positional shaping base codepoints → Presentation Forms-B, including Lam-Alef ligatures
Script auto-detection Latin / Cyrillic / Greek / Armenian / Hebrew / Arabic / Syriac / Devanagari / Bengali / Gurmukhi / Gujarati / Oriya / Tamil / Telugu / Kannada / Malayalam / Sinhala / Thai / Lao / Tibetan / Myanmar / Khmer / Hiragana / Katakana / Han / Hangul
Bidi class table block-range based (~99% of real-world input correctly classified)

File formats

Format Status
TTF (0x00010000) full
OTF ('OTTO') full
TTC every contained font addressable via fontIndex
WOFF1 full (zlib decompression + SFNT reassembly)
WOFF2 full including transformed glyf reconstruction (7-stream + 128-entry triplet decoding)

What's not implemented

The honest list of gaps relative to a full HarfBuzz:

  • Indic complex shapers (Devanagari / Bengali / Gujarati / Gurmukhi / Kannada / Malayalam / Tamil / Telugu / Oriya / Sinhala). We pass them through GSUB, but the multi-pass pre-base / post-base reordering and conjunct cluster shaping HarfBuzz does are not implemented. Output is degraded for these scripts.
  • South-East Asian shapers — Khmer / Myanmar / Tibetan / Thai / Lao similarly need stateful cluster transformations.
  • Variable font interpolationgvar, HVAR, VVAR, MVAR, CFF2 deltas. We read fvar metadata only.
  • Color emoji rendering — COLR v1 paint graph, sbix bitmap, CBDT/CBLC, SVG-in-OT.
  • Apple AAT tablesmorx, kerx (some macOS system fonts use these instead of OT layout).
  • Full UCD bidi data — we use block-range classification; HarfBuzz uses the full Unicode database.
  • UAX #14 line breakingTextOptions.LineBreakOracle is a hook the caller can wire; no built-in implementation.

For workloads that hit those gaps, install CosmoFonts.Core.HarfBuzz and use the HarfBuzzShaper directly — it's a thin P/Invoke wrapper around HarfBuzzSharp's native binaries.

Migrating from SixLabors.Fonts

CosmoFonts grew out of replacing SixLabors.Fonts in CosmoImage and tracks SixLabors's feature surface where CosmoImage actually exercised it. The shaping pipeline mirrors SixLabors's TextRenderer / TextOptions / IGlyphRenderer model so the migration is mostly a namespace swap.

At parity (drop-in equivalents)

Concern SixLabors.Fonts CosmoFonts
Font loading Font.Family.CreateFont(size) from a FontCollection Face.Load(bytes) plus SystemFontCollection.LoadDefault()
Text outline emission TextRenderer.RenderTextTo(IGlyphRenderer, text, RichTextOptions) TextRenderer.RenderTextTo(IGlyphRenderer, text, TextOptions) — same shape, slightly different option names
Glyph metrics TextMeasurer.MeasureSize / MeasureBounds / CountLines same names, same semantics
IGlyphRenderer callbacks BeginText, BeginGlyph, BeginFigure, MoveTo, LineTo, QuadraticBezierTo, CubicBezierTo, EndFigure, EndGlyph, EndText identical method set
Wrapping + alignment WrappingLength, HorizontalAlignment, WordBreaking, TextJustification, LayoutMode same property names + semantics
OpenType feature tags FeatureTags on RichTextOptions FeatureTags on TextOptions, type-safe Tag records
Font fallback fallback collections TextOptions.FallbackFaces
Variable fonts FontFamily with axis values face.Fvar (axis + named-instance metadata only — see gap below)

Where CosmoFonts goes further than SixLabors.Fonts

Feature Note
Full GSUB lookup-type coverage Including Type 2 (multiple), Type 3 (alternate), Type 5 / 6 / 8 (context, chained context — all three formats), Type 8 (reverse chaining). SixLabors's GSUB historically skips most of these.
Full GPOS lookup-type coverage Including Type 5 (mark-to-ligature), Type 7 (context positioning), Type 8 (chained context positioning) — all three formats each.
WOFF2 transformed glyf Full reconstruction including the 7-substream + 128-entry triplet decoder. SixLabors handled WOFF2 but not always the transformed glyf.
UAX #9 N0 paired-bracket BiDi Brackets inside opposite-direction contexts resolve correctly (Say (مرحبا) to me).
TTC with per-font addressing Helvetica.ttc exposes all 6 contained variants by family + style.
English-preferred name selection Fonts that ship localized name table entries (Catalan, Italian, …) still surface the English subfamily for (family, style) lookup.
Per-feature legacy-kern fallback Fonts with GPOS for marks but no GPOS kern feature still kern via the legacy kern table — matching HarfBuzz, not SixLabors.
NFC normalization with coverage guard "a + combining acute" composes to precomposed "á" only when the font has the precomposed glyph; otherwise the decomposed form is preserved so mark-to-base GPOS can position it.

Where SixLabors.Fonts goes further than CosmoFonts

Feature Status in CosmoFonts
Decoration callbacks (SetDecoration + EnabledDecorations on IGlyphRenderer) Not in IGlyphRenderer. Underline / strikeout / overline are synthesised post-shape from face metrics by the consumer — see CosmoImage's VipsText.cs for one approach.
Variable font interpolation fvar metadata is read; gvar / HVAR / VVAR / MVAR / CFF2 deltas are not. You get the default-instance outline, not interpolated weights/widths.
Indic shaping We pass Devanagari / Bengali / Tamil / etc. through GSUB but don't run the multi-pass pre-base/post-base reordering and conjunct cluster shaping HarfBuzz / SixLabors do. Output is degraded for these scripts.
Color emoji rendering COLR v0 + CPAL parsers exist; the renderer integration to fill paths with per-layer colors does not.
TrueType hinting The prep / fpgm / cvt instruction interpreter is not implemented. Glyph outlines are emitted unhinted.
Vertical mixed orientation (CJK tate-chu-yoko) We have horizontal vertical-layout support but rotated-Latin-in-vertical-CJK isn't implemented; needs renderer-side rotation.

Migration checklist

  1. Replace using SixLabors.Fonts; with using CosmoFonts.Loader; + using CosmoFonts.Shaping;.
  2. Replace Font with Face. Size moves from the Font constructor to the TextOptions(face, sizePt) constructor.
  3. Replace RichTextOptions(font) { … } with new TextOptions(face, sizePt) { … }. Property names mostly carry over (Origin, LineSpacing, HorizontalAlignment, WordBreaking, TextJustification, LayoutMode, WrappingLength, FeatureTags).
  4. Replace SystemFonts.CreateFont(family, size) with SystemFontCollection.LoadDefault().CreateFont(family) (caches the collection — LoadDefault does file-system I/O, so reuse the result).
  5. Update IGlyphRenderer implementations: BeginText / BeginGlyph take RectF instead of FontRectangle, BeginGlyph returns void instead of bool, no in modifier on parameters.
  6. Replace Tag.Parse("smcp") — same syntax, lives at CosmoFonts.Font.Opentype.Tag.Parse.
  7. If you used decorations: emit them yourself in your renderer or post-process the output path (CosmoImage's VipsText.cs has a worked example).

Verifying against HarfBuzz

CosmoFonts.SmokeTest is the diagnostic tool used to verify pure-managed output against native HarfBuzz. Throughout the engine's build-out, byte-for-byte parity was confirmed on:

  • 8 classical Latin kerning pairs on Arial / OpenSans / Times New Roman / JetBrains Mono / Amiri (Δ = 0.000pt)
  • JetBrains Mono code ligatures (==, =>, ->, !=)
  • Latin combining marks via NFC composition (á, ê, ö, ñ)
  • Arabic mark-to-base after BiDi reversal (بَ → fatha at offset (307, −106) matching HB exactly)
dotnet run --project tools/CosmoFonts.SmokeTest -- /System/Library/Fonts/Supplemental/Arial.ttf

Build

dotnet build src/CosmoFonts.Core/CosmoFonts.Core.csproj
dotnet build src/CosmoFonts.Core.HarfBuzz/CosmoFonts.Core.HarfBuzz.csproj  # optional
dotnet run   --project tools/CosmoFonts.SmokeTest -- <path-to-font>

Core targets net10.0. The only external dependency is a sibling Cosmo.Transport project (lightweight transport primitives).

Acknowledgments

Bidi table ranges and Arabic Presentation Forms-B mapping were derived from the Unicode UCD. Font format parsers follow the Microsoft OpenType spec and W3C WOFF2. Algorithm shapes and fixtures cross-checked against HarfBuzz via HarfBuzzSharp.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on CosmoFonts.Core:

Package Downloads
CosmoImage

Pure-managed image processing library — loaders, savers, geometric/colour/convolution/effects operations. No native dependencies.

CosmoPdf.FastReport

CosmoReport.Core PDF/Image export bridge backed by CosmoPdf — zero native deps, supports PDF/A-3b, embedded XML attachments, Arabic shaping, Roslyn-based ScriptText. ImageExport / EscPos exports use CosmoFonts (pure-C# OpenType + GSUB/GPOS/BiDi) + CosmoImagePdf.Shared raster surface + CosmoImage decode — no SixLabors.ImageSharp.

CosmoFonts.Core.HarfBuzz

HarfBuzz-backed shaper for CosmoFonts.Core. First-class Indic / Arabic / Tibetan / Khmer / Myanmar / complex-script shaping via P/Invoke to native HarfBuzz. Pulls in HarfBuzzSharp's native binaries.

CosmoPdf

Pure C# PDF generation library — a .NET port of gpdf with a layered architecture (pdf primitives, document model, template builder API).

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 188 5/10/2026
1.2.0 110 5/10/2026
1.1.0 112 5/9/2026
1.0.0 326 5/6/2026