Recursive AI

OpenType vs TrueType

OpenType vs TrueType from the specification: the same file structure and features, quadratic vs cubic outlines, .otf vs .ttf, and how to check any font in C#.

OpenType vs TrueType is mostly a difference in how the letter shapes are drawn, not in what the font can do. OpenType is the font format; a “TrueType font” today is an OpenType font whose outlines are TrueType curves (quadratic Béziers in a glyf table), while a font usually called “OpenType”, or OpenType-CFF, stores PostScript-style cubic Béziers in a CFF table. Both use the same file structure, the same metric and naming tables, the same OpenType layout features (ligatures, kerning, small caps, Arabic and Indic shaping) and the same limit of 65,535 glyphs. Neither is better in general. Use the one your foundry ships, or the one your tools need. This page explains the differences from the OpenType specification and shows how to check what a font file really contains with a few lines of C#.

OpenType vs TrueType at a glance

TrueType outlines (.ttf, .otf)CFF outlines (.otf)
File structureThe same: the sfnt table directory followed by tables
First four bytes (sfntVersion)0x00010000OTTO
Outline tableglyf with locaCFF (or CFF2 in variable fonts)
CurvesQuadratic Bézier: one control point per curveCubic Bézier: two control points per curve
Layout features (GSUB, GPOS, GDEF)The same tables, used the same way
Maximum number of glyphs65,535 for both: numGlyphs in the maxp table is a 16-bit number
File extension.ttf or .otf.otf
Collections (several fonts in one file).ttc or .otc

Sources, all from Microsoft’s OpenType specification: the font file chapter (“OpenType fonts that contain TrueType outlines should use the value of 0x00010000 for sfntVersion. OpenType fonts containing CFF data (version 1 or 2) should use 0x4F54544F (‘OTTO’…)”), the maxp table, the CFF2 table (“cubic (3rd-order) Bézier curves”) and the recommendations on filenames. The quadratic curves of TrueType are described in Apple’s TrueType Reference Manual.

What “TrueType” and “OpenType” mean

The names cause most of the confusion. OpenType is built on TrueType’s file layout, a directory of named tables: the specification calls the TrueType sfnt “wrapper” “a key characteristic of the OpenType format”. Inside that wrapper an OpenType font holds either TrueType outlines or Compact Font Format (CFF) outlines, which come from Adobe’s PostScript fonts, and either kind can carry the layout tables that handle ligatures, contextual forms and complex scripts.

So in practice:

  • “TrueType font” means an OpenType font with glyf outlines, whatever its extension.
  • “OpenType font” in a font menu or a store usually means one with CFF outlines, but every TrueType-flavored font is also OpenType.
  • The extension doesn’t decide anything. The specification allows .otf for TrueType outlines, and says a file with only CFF outlines should be .otf. Read the first four bytes to know (below).
  • Features such as ligatures, small caps, old-style figures and Arabic joining live in GSUB and GPOS, which both flavors carry. A font without them, of either flavor, doesn’t have those features.

Is OpenType or TrueType better?

For most uses, neither. In the check below, the two builds of the same family have the same glyphs and shape a word identically. The differences that can matter:

  • Curves. A cubic curve can describe a shape with fewer points. In Adobe’s Source Sans 3 Regular, the “S” is 12 cubic curves in the CFF build and 29 quadratic curves in the TrueType build (measured below). Converting cubic outlines to quadratic approximates each curve with one or more quadratic ones, so a conversion is close to the original but not exact.
  • File size. Source Sans 3 Regular 3.052 is 334,924 bytes as .otf and 431,196 bytes as .ttf.
  • Hinting. Hints, which adjust outlines to the pixel grid at small sizes, work differently in the two flavors: TrueType carries instructions for each glyph, CFF carries hints that the rasterizer interprets. How much this matters depends on the platform and its rasterizer.
  • Tools. If an app rejects a font, or a library reads only glyf outlines, try the other build.
  • On the web, WOFF2 compresses fonts of either flavor: it has its own processing for both the glyf and the CFF table.

A practical rule: use the build your tools or platform ask for, and when they accept both, expect the same text either way. When you need the other flavor, get the foundry’s own build rather than converting the file yourself.

Check what a font file contains in C#

This program reads the CFF and TrueType builds of Source Sans 3 (release 3.052R, SIL Open Font License) with OneFont, a font and text-shaping library for .NET written in C#. It prints each file’s signature, its tables, the curve types of one glyph, and how a word shapes:

TerminalNuGet
dotnet new console -o FontCheck
cd FontCheck
dotnet add package RecursiveAI.OneFont --version 1.3.0
Program.csOneFont 1.3.0 · .NET 10
using System.Buffers.Binary;
using OneFont;
 
foreach (string path in new[] { "SourceSans3-Regular.otf", "SourceSans3-Regular.ttf" })
{
    byte[] data = File.ReadAllBytes(path);
 
    // The first four bytes say which outlines are inside, whatever the extension says:
    // "OTTO" = CFF (PostScript) outlines, 0x00010000 = TrueType (glyf) outlines.
    uint tag = BinaryPrimitives.ReadUInt32BigEndian(data);
    string kind = tag == 0x4F54544F ? "OTTO (CFF outlines)" : tag == 0x00010000 ? "0x00010000 (TrueType outlines)" : $"0x{tag:X8}";
 
    FontFace face = FontFace.Load(data);
    Console.WriteLine($"{path}: {face.FamilyName} {face.SubfamilyName}, sfnt version {kind}");
    Console.WriteLine($"  IsCff {face.IsCff}, {face.GlyphCount} glyphs, {face.UnitsPerEm} units/em");
 
    string[] tables = ["glyf", "loca", "CFF ", "GSUB", "GPOS", "GDEF", "cmap", "hmtx", "name", "OS/2"];
    Console.WriteLine("  tables: " + string.Join(" ", tables.Select(t => $"{t.Trim()}={(face.HasTable(t) ? "yes" : "no")}")));
 
    // Outline of "S": quadratic curves in TrueType, cubic curves in CFF.
    var counter = new SegmentCounter();
    face.GetOutline(face.GetGlyphIndex('S')).Write(counter, System.Numerics.Matrix3x2.Identity);
    Console.WriteLine($"  'S' outline: {counter.Lines} lines, {counter.Quads} quadratic, {counter.Cubics} cubic");
 
    // Shaping uses the same GSUB/GPOS tables in both files.
    var clusters = TextShaping.ShapeRun("office", face);
    Console.WriteLine("  \"office\" shapes as " + string.Join("|", clusters.Select(c => "office".Substring(c.Start, c.Length))) +
                      " with glyphs " + string.Join(" ", clusters.SelectMany(c => c.Glyphs.ToArray()).Select(g => g.GlyphId)));
}
 
sealed class SegmentCounter : IOutlineSink
{
    public int Lines, Quads, Cubics;
    public void MoveTo(float x, float y) { }
    public void LineTo(float x, float y) => Lines++;
    public void QuadTo(float cx, float cy, float x, float y) => Quads++;
    public void CubicTo(float c1x, float c1y, float c2x, float c2y, float x, float y) => Cubics++;
    public void Close() { }
}
OutputConsole
SourceSans3-Regular.otf: Source Sans 3 Regular, sfnt version OTTO (CFF outlines)
  IsCff True, 2478 glyphs, 1000 units/em
  tables: glyf=no loca=no CFF=yes GSUB=yes GPOS=yes GDEF=yes cmap=yes hmtx=yes name=yes OS/2=yes
  'S' outline: 4 lines, 0 quadratic, 12 cubic
  "office" shapes as o|ff|i|c|e with glyphs 42 687 36 30 32
SourceSans3-Regular.ttf: Source Sans 3 Regular, sfnt version 0x00010000 (TrueType outlines)
  IsCff False, 2478 glyphs, 1000 units/em
  tables: glyf=yes loca=yes CFF=no GSUB=yes GPOS=yes GDEF=yes cmap=yes hmtx=yes name=yes OS/2=yes
  'S' outline: 4 lines, 29 quadratic, 0 cubic
  "office" shapes as o|ff|i|c|e with glyphs 42 687 36 30 32
  • The two files differ in their signature and their outline tables, and in nothing else that this program checks. Both have 2,478 glyphs and 1000 units per em, and both carry GSUB, GPOS and GDEF tables.
  • Shaping “office” gives the same glyphs in both, including the “ff” ligature from GSUB: OpenType features don’t depend on the outline flavor.
  • FontFace.IsCff tells the flavors apart without reading the bytes yourself, and HasTable and GetTable check for any other table, such as COLR for color glyphs.
  • OneFont reads .ttf, .otf and .ttc files. It doesn’t read WOFF or WOFF2, or CFF2 outlines, and it doesn’t convert between flavors.

The read a font file recipe shows names, metrics and the character map, and text to SVG path turns these outlines into SVG, with Q commands for TrueType and C for CFF.

Where next