Skip to content

Instantly share code, notes, and snippets.

@pklaus
Forked from starrhorne/gist:1637310
Last active January 26, 2024 18:20
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save pklaus/dce37521579513c574d0 to your computer and use it in GitHub Desktop.
Save pklaus/dce37521579513c574d0 to your computer and use it in GitHub Desktop.
Extracting font names from TTF/OTF files using Python and fontTools
#!/usr/bin/env python
"""
From
https://github.com/gddc/ttfquery/blob/master/ttfquery/describe.py
and
http://www.starrhorne.com/2012/01/18/how-to-extract-font-names-from-ttf-files-using-python-and-our-old-friend-the-command-line.html
ported to Python 3
"""
import sys
from fontTools import ttLib
FONT_SPECIFIER_NAME_ID = 4
FONT_SPECIFIER_FAMILY_ID = 1
def shortName( font ):
"""Get the short name from the font's names table"""
name = ""
family = ""
for record in font['name'].names:
if b'\x00' in record.string:
name_str = record.string.decode('utf-16-be')
else:
name_str = record.string.decode('utf-8')
if record.nameID == FONT_SPECIFIER_NAME_ID and not name:
name = name_str
elif record.nameID == FONT_SPECIFIER_FAMILY_ID and not family:
family = name_str
if name and family: break
return name, family
tt = ttLib.TTFont(sys.argv[1])
print("Name: %s Family: %s" % shortName(tt))
@moi15moi
Copy link

@tatarize If you really want to decode it by yourself, you should use the right encoding.

Here is what GDI (windows) does:

    def get_name_encoding(name: NameRecord) -> Optional[str]:
        """
        Parameters:
            names (NameRecord): Name record from the naming record
        Returns:
            The cmap codepoint encoding.
            If GDI does not support the name, return None.
        """
        # From: https://github.com/MicrosoftDocs/typography-issues/issues/956#issuecomment-1205678068
        if name.platformID == 3:
            if name.platEncID == 3:
                return "cp936"
            elif name.platEncID == 4:
                if name.nameID == 2:
                    return "utf_16_be"
                else:
                    return "cp950"
            elif name.platEncID == 5:
                if name.nameID == 2:
                    return "utf_16_be"
                else:
                    return "cp949"
            else:
                return "utf_16_be"
        elif name.platformID == 1 and name.platEncID == 0:
            return "iso-8859-1"

        return None

    @staticmethod
    def get_decoded_name(name: NameRecord) -> str:
        """
        Parameters:
            names (NameRecord): Name record from the naming record
        Returns:
            The decoded name
        """

        encoding = FontParser.get_name_encoding(name)

        if name.platformID == 3 and encoding != "utf_16_be":
            # Compatibility for really old font
            name_to_decode = name.string.replace(b"\x00", b"")
        else:
            name_to_decode = name.string

        return name_to_decode.decode(encoding)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment