Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / MFC

Retrieving Font Name from TTF File

Rate me:
Please Sign up or sign in to vote.
4.86/5 (25 votes)
12 Dec 2002CPOL3 min read 283.4K   6.4K   35   43
Explains how to retrieve a font name from TrueType or OpenType files (.ttf)

Introduction

Everyone can get a font name of one of installed fonts. But what if the font is still not installed in the system and you want to know what is that, programmatically? Of course, you can temporary add it to system fonts and get its properties then (hmm... but how you will find now what was the font installed?). Well, maybe you can think about other ways, but I decided to look for specification of the TrueType and OpenType fonts file. Fortunately, Microsoft has very good articles on these files. If you want to know more about them, look at the end of this article for links.

Writing the Code

Since all that interested me (and you in most cases) is only the font name and not other properties in the TTF file, our code is going to be simple (actually only one function). The function will retrieve font name from a given file and return it to the calling program.

Data Types Definition

Since there are no structures defined in Windows header files (or I didn't find them), we are going to make our own. We need 4 structures and 2 macros (I will explain later, about them).

A TTF file consists of several tables, each table represent some data, regarding of its type. Some tables are required, some are not. We actually need only one of them, called "name", e.g. names table. This is the place where the font information is stored, like font name, copyright, trademark and more.

C++
//This is TTF file header
typedef struct _tagTT_OFFSET_TABLE{
    USHORT uMajorVersion;
    USHORT uMinorVersion;
    USHORT uNumOfTables;
    USHORT uSearchRange;
    USHORT uEntrySelector;
    USHORT uRangeShift;
}TT_OFFSET_TABLE;

//Tables in TTF file and there placement and name (tag)
typedef struct _tagTT_TABLE_DIRECTORY{
    char szTag[4]; //table name
    ULONG uCheckSum; //Check sum
    ULONG uOffset; //Offset from beginning of file
    ULONG uLength; //length of the table in bytes
}TT_TABLE_DIRECTORY;

//Header of names table
typedef struct _tagTT_NAME_TABLE_HEADER{
    USHORT uFSelector; //format selector. Always 0
    USHORT uNRCount; //Name Records count
    USHORT uStorageOffset; //Offset for strings storage, 
                           //from start of the table
}TT_NAME_TABLE_HEADER;

//Record in names table
typedef struct _tagTT_NAME_RECORD{
    USHORT uPlatformID;
    USHORT uEncodingID;
    USHORT uLanguageID;
    USHORT uNameID;
    USHORT uStringLength;
    USHORT uStringOffset; //from start of storage area
}TT_NAME_RECORD;

Macros

Now, the only thing left is macros I was talking before. The macros definition looks like:

C++
#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))

Now what is that? The reason we need those macros is that TTF files are stored in Big-Endian format, unlike in Windows systems, where all files are in Little Endian. Yeah, I know it sounds silly with all those "endians" :). Big Endian is used by Motorolla processors for example, where the higher byte is stored first, while in Little Endian (for Intel processors) the higher byte is the last. For example, you have an integer variable 1 (which is 4 bytes long). Try to save it to file and open in any hexadecimal editor, you will see:

01 00 00 00    //Little Endian - Intel

This is Little Endian system (Intel). But for Big-Endian (Motorolla), the number will be stored vise versa:

00 00 00 01    //Big Endian - Motorolla

So these formats are incompatible. And TTF file as I said, is stored in Motorolla style (Big Endian). That's why we need those 2 macros to rearrange bytes in variables retrieved from TrueType font file.

Reading the File

Now we are prepared to read the TTF file. So let's get started.

First of all, we need to read the file header (TT_OFFSET_TABLE structure):

C++
CFile f;
CString csRetVal;

//lpszFilePath is the path to our font file
if(f.Open(lpszFilePath, CFile::modeRead|CFile::shareDenyWrite)){

    //define and read file header
    TT_OFFSET_TABLE ttOffsetTable;
    f.Read(&ttOffsetTable, sizeof(TT_OFFSET_TABLE));

    //remember to rearrange bytes in the field you gonna use
    ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
    ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
    ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);

    //check is this is a true type font and the version is 1.0
    if(ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
        return csRetVal;

Right after the file header goes Offsets Table. You can find here an offset interesting in your table, "name" in our case.

C++
TT_TABLE_DIRECTORY tblDir;
BOOL bFound = FALSE;
CString csTemp;

for(int i=0; i< ttOffsetTable.uNumOfTables; i++){
    f.Read(&tblDir, sizeof(TT_TABLE_DIRECTORY));
    csTemp.Empty();

    //table's tag cannot exceed 4 characters
    strncpy(csTemp.GetBuffer(4), tblDir.szTag, 4);
    csTemp.ReleaseBuffer();
    if(csTemp.CompareNoCase(_T("name")) == 0){
        //we found our table. Rearrange order and quit the loop
        bFound = TRUE;
        tblDir.uLength = SWAPLONG(tblDir.uLength);
        tblDir.uOffset = SWAPLONG(tblDir.uOffset);
        break;
    }
}

We finally found the names table, so let's read its header:

C++
if(bFound){
    //move to offset we got from Offsets Table
    f.Seek(tblDir.uOffset, CFile::begin);
    TT_NAME_TABLE_HEADER ttNTHeader;
    f.Read(&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER));

    //again, don't forget to swap bytes!
    ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
    ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
    TT_NAME_RECORD ttRecord;
    bFound = FALSE;

Right after the Names Table header, go records in it. So we need to run through all records to find information interesting to us - font name.

C++
    for(int i=0; i<ttNTHeader.uNRCount; i++){
    f.Read(&ttRecord, sizeof(TT_NAME_RECORD));
    ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);

    //1 says that this is font name. 0 for example determines copyright info
    if(ttRecord.uNameID == 1){
        ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
        ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);

        //save file position, so we can return to continue with search
        int nPos = f.GetPosition();
        f.Seek(tblDir.uOffset + ttRecord.uStringOffset + 
                 ttNTHeader.uStorageOffset, CFile::begin);

        //bug fix: see the post by SimonSays to read more about it
        TCHAR lpszNameBuf = csTemp.GetBuffer(ttRecord.uStringLength + 1);
        ZeroMemory(lpszNameBuf, ttRecord.uStringLength + 1);
        f.Read(lpszNameBuf, ttRecord.uStringLength);
        csTemp.ReleaseBuffer();

        //yes, still need to check if the font name is not empty
        //if it is, continue the search
        if(csTemp.GetLength() > 0){
            csRetVal = csTemp;
            break;
        }
        f.Seek(nPos, CFile::begin);
    }
}

That's all! Now we can return csRetVal containing our font name.

You can download the full working function and use in your code. I also included a demo project with the same function, but customized a bit, so it also returned copyright and trademark information.

If you want to continue with TTF files, you can look at Microsoft's specification on them. But remember that deeper you are going to TTF, more differences between TrueType and OpenType you may find. Anyway, below are the links to articles about TTF.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader Varonis
Israel Israel
I was born in small town Penza, Russia, in October 13th, 1975 yr. So my mother tongue is Russian. I finished the school there and learned in University, then I came to Israel and since then, I live there (or here *s*)
My profession is a C++ programmer under MS Windows platforms, but my hobby is Web development and ASP programming.

I started interesting in computers and programming somewere in 1990-1991 yrs., when my father brought home our first computer - Sinclair ZX Spectrum (he made it by himself). So I learned Basic and joined the Basic programmers club at my school (me and my friend were the only 2 guys from all school there, lol). After I finished the school (1992yr) I decided to continue my study at University and got specialization Operation Systems and Software Engineer. Although I still like my profession, but I always wanted something new, thus I learned HTML, Javascript and ASP which turned to be my hobby Smile | :)

Comments and Discussions

 
SuggestionCpp version with Qt Pin
Leon Shahi26-Apr-22 13:51
Leon Shahi26-Apr-22 13:51 
SuggestionBugfix suggestion: platformId, PlatformSpecificId considrations Pin
Leon Shahi26-Apr-22 13:41
Leon Shahi26-Apr-22 13:41 
Questionfor me few ttf files still showing name field as empty Pin
Member 107633785-May-21 22:20
Member 107633785-May-21 22:20 
QuestionReading table OS/2 Pin
valakh111-Dec-14 14:29
valakh111-Dec-14 14:29 
GeneralFull VB Translation Pin
Nathaniel Moschkin3-Dec-13 8:15
professionalNathaniel Moschkin3-Dec-13 8:15 
GeneralPragmatical VB-Version Pin
chriha19-Mar-08 7:18
chriha19-Mar-08 7:18 
QuestionSome Help ?? Pin
tp200017-Feb-08 20:30
tp200017-Feb-08 20:30 
GeneralRe: Some Help ?? Pin
Philip Patrick17-Feb-08 20:45
professionalPhilip Patrick17-Feb-08 20:45 
GeneralRe: Some Help ?? Pin
tp200017-Feb-08 23:50
tp200017-Feb-08 23:50 
AnswerRe: Some Help ?? Pin
Arun Rajan21-Sep-10 9:48
Arun Rajan21-Sep-10 9:48 
Generalfonts related query Pin
Ganesh Kundapur3-Jan-08 0:49
Ganesh Kundapur3-Jan-08 0:49 
QuestionWhere are the old comments gone? Pin
softcode10-Dec-07 18:44
softcode10-Dec-07 18:44 
QuestionHow to actually use? Pin
Simon Kittle22-Nov-07 23:31
Simon Kittle22-Nov-07 23:31 
AnswerRe: How to actually use? Pin
Philip Patrick23-Nov-07 0:44
professionalPhilip Patrick23-Nov-07 0:44 
GeneralGREAT!!! just a little problem Pin
Jihodg5-Mar-06 8:21
Jihodg5-Mar-06 8:21 
Question00 00 00 01? Pin
fractal.doozie6-Feb-06 20:48
fractal.doozie6-Feb-06 20:48 
QuestionCan we use the GetFontData method toRetrieving font name from TTF file Pin
Rangashan4-Oct-05 0:02
Rangashan4-Oct-05 0:02 
AnswerRe: Can we use the GetFontData method toRetrieving font name from TTF file Pin
Philip Patrick4-Oct-05 0:25
professionalPhilip Patrick4-Oct-05 0:25 
QuestionRe: Can we use the GetFontData method toRetrieving font name from TTF file Pin
Arif Saiyed15-Mar-07 1:51
Arif Saiyed15-Mar-07 1:51 
AnswerRe: Can we use the GetFontData method toRetrieving font name from TTF file Pin
Ganesh Kundapur16-Oct-07 19:45
Ganesh Kundapur16-Oct-07 19:45 
QuestionIs there any windows API to get the Font Face name from a TTF file Pin
Rangashan3-Oct-05 19:43
Rangashan3-Oct-05 19:43 
AnswerRe: Is there any windows API to get the Font Face name from a TTF file Pin
Philip Patrick3-Oct-05 19:56
professionalPhilip Patrick3-Oct-05 19:56 
AnswerRe: Is there any windows API to get the Font Face name from a TTF file Pin
JongBock, Seon8-Aug-11 22:42
JongBock, Seon8-Aug-11 22:42 
GeneralGetting OpenType information Pin
Reunion2-Apr-05 18:18
Reunion2-Apr-05 18:18 
Hello!
This article is very helpful! Thank you.
I've got a problem with OpenType (*.ttf). They have information at ttRecord.uNameID > 0 and <= 20 (First 7 or 8 are similar to TrueType and they can easyly be read). But I need to read information when ttRecord.uNameID is more than 7. How can I do that.
Thank you in advance.
GeneralOpenType font Pin
heebo7-Mar-05 9:18
heebo7-Mar-05 9:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.