Read a font’s names, style, metrics and tables in C# from a TTF, OTF or TTC file, on any platform, and tell TrueType outlines from CFF.
FontFace.Load reads TrueType fonts, OpenType fonts with CFF outlines, and TrueType Collections from a byte array. It doesn’t need the font to be installed, and it doesn’t use System.Drawing or any operating system font API.
using OneFont; string[] files = ["NotoSans-Regular.ttf", "NotoSansCJK-Regular.ttc", "KaTeX_Main-Regular.woff2"]; string[] tables = ["glyf", "CFF ", "GSUB", "GPOS", "kern", "COLR", "fvar"]; foreach (string file in files) { byte[] data = File.ReadAllBytes(file); try { int faces = FontFace.GetFaceCount(data); FontFace face = FontFace.Load(data); // face 0; pass faceIndex for the others in a collection Console.WriteLine($"{file}: {faces} face(s)"); Console.WriteLine($" family \"{face.FamilyName}\", style \"{face.SubfamilyName}\", full \"{face.FullName}\""); string outlines = face.IsCff ? "CFF (cubic)" : "TrueType (quadratic)"; Console.WriteLine($" weight {face.Weight}, bold {face.IsBold}, italic {face.IsItalic}, outlines {outlines}"); FontMetrics m = face.Metrics; Console.WriteLine($" {m.UnitsPerEm} units/em, ascender {m.Ascender}, descender {m.Descender}"); Console.WriteLine($" x-height {m.XHeight}, cap height {m.CapHeight}, line gap {m.LineGap}"); Console.WriteLine($" tables: {string.Join(", ", tables.Where(face.HasTable).Select(t => t.Trim()))}"); Console.WriteLine($" name table: {face.GetTable("name").Length} bytes"); if (faces > 1) { var names = Enumerable.Range(0, faces).Select(i => FontFace.Load(data, i).FamilyName); Console.WriteLine($" all faces: {string.Join(", ", names)}"); } } catch (FontFormatException e) { Console.WriteLine($"{file}: not readable: {e.Message}"); } }
NotoSans-Regular.ttf: 1 face(s) family "Noto Sans", style "Regular", full "Noto Sans Regular" weight 400, bold False, italic False, outlines TrueType (quadratic) 1000 units/em, ascender 1069, descender -293 x-height 536, cap height 714, line gap 0 tables: glyf, GSUB, GPOS name table: 1660 bytes NotoSansCJK-Regular.ttc: 10 face(s) family "Noto Sans CJK JP", style "Regular", full "Noto Sans CJK JP" weight 400, bold False, italic False, outlines CFF (cubic) 1000 units/em, ascender 1160, descender -288 x-height 543, cap height 733, line gap 0 tables: CFF, GSUB, GPOS name table: 2146 bytes all faces: Noto Sans CJK JP, Noto Sans CJK KR, Noto Sans CJK SC, Noto Sans CJK TC, Noto Sans CJK HK, Noto Sans Mono CJK JP, Noto Sans Mono CJK KR, Noto Sans Mono CJK SC, Noto Sans Mono CJK TC, Noto Sans Mono CJK HK KaTeX_Main-Regular.woff2: not readable: Not an sfnt font: unknown version tag.
- Names.
FamilyNameis the typographic family when the font has one, else the legacy family.SubfamilyNameis the style, andFullNameandPostScriptNamearenullwhen the font has none. - Style.
Weightis the OS/2 weight class, 100 to 900.IsBoldcomes from the OS/2 selection flags, theheadstyle bits or a weight of 600 or more, andIsItaliccovers oblique faces too. - TrueType or CFF. The
.ttfand.otfextensions don’t decide it: the outlines do.IsCffis true for cubic CFF outlines and false for quadratic TrueType (glyf) outlines. Both kinds can carry the OpenType layout tablesGSUBandGPOS, and onefont shapes with them either way. - Metrics.
Metricsholds the ascender, descender, line gap, x-height, cap height, and the underline and strikeout positions, all in design units. Divide byUnitsPerEmand multiply by the font size. - Raw tables.
HasTableandGetTablereach any table by its four-character tag, as stored in the file and not interpreted. A tag shorter than four characters is padded with spaces, so"CFF"findsCFF. - Collections.
GetFaceCountreturns the number of faces (at most 256), andLoad(data, faceIndex)reads one of them. - Errors. A file that isn’t a font, or is damaged or truncated, throws
FontFormatException. WOFF and WOFF2 are compressed wrappers that onefont doesn’t unpack, so the KaTeX.woff2above is refused. CFF2 fonts aren’t read either.
A face keeps a reference to the byte array and reads tables from it on demand, so leave the array unchanged while the face is in use. Faces are immutable and safe to use from several threads.
The first steps with a font are in Getting started.