Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

The two-call loading pattern

  1. Set the length argument to zero and call FT_Load_Sfnt_Table to ask FreeType for the required byte count.
  2. Allocate a buffer of the reported size.
  3. Call the function again with that buffer and its length to copy the bytes.
  4. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

A practical inspection workflow

  1. Open the font and create an FT_Face.
  2. Call FT_Sfnt_Table_Info to obtain the table count.
  3. Enumerate tags and lengths, recording missing or zero-length entries as conditions to handle.
  4. For head, maxp, OS/2, hhea, vhea, post, or PCLT, use the matching FT_SFNT_* tag and cast the result of FT_Get_Sfnt_Table only after checking for NULL.
  5. 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.
  6. Inspect selected charmaps with FT_Get_CMap_Language_ID and FT_Get_CMap_Format, preserving their documented special values.
  7. 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_Table representation.
  • Keeping a parsed-table pointer after the owning FT_Face is destroyed.
  • Casting raw bytes from FT_Load_Sfnt_Table to 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, NULL pointers, invalid indices, or zero-length tables.
  • Treating 0xFFFFFFFF or -1 from 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.