FreeType offers two different ways to inspect TrueType and OpenType (SFNT) tables: FT_Get_Sfnt_Table returns a few tables as parsed, typed structures, while FT_Load_Sfnt_Table returns raw bytes from any table or from the font file itself. Use FT_Sfnt_Table_Info to enumerate what a face actually contains, and use the cmap helpers to identify a character map’s language ID and subtable format.
Which FreeType API should you use?
| Need | Use | What you receive | Important constraint |
|---|---|---|---|
| Read a standard parsed SFNT structure | FT_Get_Sfnt_Table |
A pointer to a FreeType structure such as TT_Header or TT_OS2 |
The pointer belongs to the FT_Face and is valid only while that face remains alive. |
| Read any table, a byte range, or the complete font | FT_Load_Sfnt_Table |
Caller-provided raw bytes | You must size and allocate the buffer, handle errors, and parse the SFNT data format yourself. |
| Discover which tables exist | FT_Sfnt_Table_Info |
Each table’s four-byte tag and byte length | Invalid indices report a missing-table error; zero-length tables are treated as missing during parsing. |
| Identify cmap metadata | FT_Get_CMap_Language_ID and FT_Get_CMap_Format |
Language identifier and cmap subtable format | Both helpers define special results for non-SFNT or synthetic charmaps. |
Parsed structures with FT_Get_Sfnt_Table
FT_Get_Sfnt_Table(face, tag) is the convenient choice when FreeType already exposes the table you need as a parsed structure. The API is declared in freetype/tttables.h. The returned pointer is type-less, so cast it to the structure associated with the requested FT_Sfnt_Tag, and always test for NULL.
TT_Header *header = (TT_Header *)FT_Get_Sfnt_Table(face, FT_SFNT_HEAD);
if (header == NULL) {
/* The table is unavailable for this face. */
}
The official ownership rule is simple: “The table is owned by the face object and disappears with it.” Do not retain the pointer after destroying the FT_Face, and do not treat it as memory that your code can free.
Tags and structures FreeType parses
FT_Sfnt_Tag |
Structure | Typical contents |
|---|---|---|
FT_SFNT_HEAD |
TT_Header |
Version and revision, checksum adjustment, magic number, units per em, timestamps, bounding box, style flags, pixels-per-em limits, direction, loca format, and glyph-data format. |
FT_SFNT_MAXP |
TT_MaxProfile |
Maximum-profile information used by the font. |
FT_SFNT_OS2 |
TT_OS2 |
OS/2 metrics and classification data. |
FT_SFNT_HHEA |
TT_HoriHeader |
Horizontal ascender, descender, line gap, advances, side bearings, extents, and caret metrics. |
FT_SFNT_VHEA |
TT_VertHeader |
Vertical metrics-header fields, when present. |
FT_SFNT_POST |
TT_Postscript |
PostScript-related font metadata. |
FT_SFNT_PCLT |
TT_PCLT |
PCLT metadata, when present. |
The older lowercase tag constants are deprecated aliases. Prefer the current uppercase names. Not every SFNT table has a corresponding FT_Sfnt_Tag structure; for those tables, use raw loading and parse the format defined by the TrueType or OpenType specification.
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
Timestamp detail in TT_Header
The creation and modification timestamps in TT_Header are 64-bit values represented as two 32-bit words, with the upper word followed by the lower word. Code that displays or compares these fields must combine the words deliberately rather than assuming a native 64-bit layout.
Raw bytes with FT_Load_Sfnt_Table
Use FT_Load_Sfnt_Table when you need a table that FreeType does not expose as a parsed structure, need exact on-disk bytes, need only a range at an offset, or want to retrieve the complete font data. The four-byte table tag identifies the target. Tag 0 addresses the complete font file; the current API also documents tag 1 for the table directory.
Rank #2
The two-call loading pattern
- Set the length argument to zero and call
FT_Load_Sfnt_Tableto ask FreeType for the required byte count. - Allocate a buffer of the reported size.
- Call the function again with that buffer and its length to copy the bytes.
- Check the returned
FT_Error; zero means success.
FT_ULong length = 0;
FT_Error error = FT_Load_Sfnt_Table(face, tag, 0, NULL, &length);
if (error != 0) {
/* The table cannot be queried or is unavailable. */
}
FT_Byte *bytes = malloc(length);
if (bytes == NULL) {
/* Handle allocation failure. */
}
error = FT_Load_Sfnt_Table(face, tag, 0, bytes, &length);
if (error != 0) {
free(bytes);
/* Handle the load failure. */
}
The buffer in this example is owned by your application, so your code must release it. A successful size query does not remove the need to check the second call.
Do not cast raw table data to FreeType structures
Do not cast bytes returned by FT_Load_Sfnt_Table directly to TT_Header, TT_OS2, or another FreeType structure. The reference restricts those structures to FT_Get_Sfnt_Table because their in-memory representation depends on processor architecture, including structure size and byte order. For raw data, decode fields according to the SFNT/TrueType/OpenType format instead.
Rank #3
Enumerating every table in a face
FT_Sfnt_Table_Info provides a safe inventory before you decide how to read a table. First call it with a NULL tag pointer; the function ignores the index and writes the number of SFNT tables to the length output. Then iterate from index zero through the count minus one.
FT_ULong table_count = 0;
FT_Error error = FT_Sfnt_Table_Info(face, 0, NULL, &table_count);
if (error != 0) {
/* The face does not provide an SFNT table directory. */
}
for (FT_ULong i = 0; i < table_count; ++i) {
FT_ULong tag = 0;
FT_ULong length = 0;
error = FT_Sfnt_Table_Info(face, i, &tag, &length);
if (error != 0) {
/* Handle an invalid index or missing table. */
continue;
}
/* Record tag and length, then choose parsed or raw access. */
}
An invalid table index returns FT_Err_Table_Missing. FreeType also treats zero-length tables as missing while parsing, so an inspector should not assume that every directory entry represents usable data.
Turning a tag into a readable name
Tags are four-byte identifiers. Keep the numeric value for API calls, and when displaying it, decode its four bytes in SFNT order into a four-character label. Do not assume that a table is optional or mandatory solely from its name; inspect the actual directory and handle absence.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Reading cmap language IDs and formats
Once you have an FT_CharMap, two helpers expose metadata about the underlying SFNT cmap subtable.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Best Value
FT_Get_CMap_Language_ID
FT_Get_CMap_Language_ID(charmap) returns the OpenType cmap language identifier. For a charmap that does not belong to an SFNT face, the result is 0. A format-14 cmap used for Unicode variation sequences returns 0xFFFFFFFF.
FT_Get_CMap_Format
FT_Get_CMap_Format(charmap) returns the SFNT cmap subtable format. If the charmap is not from an SFNT face—including a synthetic Unicode charmap that FreeType may create—the result is -1.
These return values are diagnostic signals, not ordinary format numbers. Check for the special cases before displaying a language ID or treating the format as a standard cmap subtable.
Quick Recap
A practical inspection workflow
- Open the font and create an
FT_Face. - Call
FT_Sfnt_Table_Infoto obtain the table count. - Enumerate tags and lengths, recording missing or zero-length entries as conditions to handle.
- For
head,maxp,OS/2,hhea,vhea,post, orPCLT, use the matchingFT_SFNT_*tag and cast the result ofFT_Get_Sfnt_Tableonly after checking forNULL. - For any other table, call
FT_Load_Sfnt_Table, size the buffer with the zero-length query, load the bytes, and decode them according to the SFNT format. - Inspect selected charmaps with
FT_Get_CMap_Language_IDandFT_Get_CMap_Format, preserving their documented special values. - Destroy the face only after all parsed-table pointers have been used; free every raw buffer allocated by the application.
Common mistakes to avoid
- Assuming every SFNT table has a typed
FT_Get_Sfnt_Tablerepresentation. - Keeping a parsed-table pointer after the owning
FT_Faceis destroyed. - Casting raw bytes from
FT_Load_Sfnt_Tableto a C structure without accounting for SFNT byte order and architecture-dependent layout. - Allocating a raw-table buffer without first querying its required length.
- Ignoring
FT_Error,NULLpointers, invalid indices, or zero-length tables. - Treating
0xFFFFFFFFor-1from the cmap helpers as ordinary language or format values.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

