Retrieving the Font Name from a TTF File

Environment: VC6, MFC, Win95/98/NT/2000/XP

Introduction

You can always get the font name of one of your installed fonts. But what if the font is still not installed in the system and you want to know its name programmatically? Of course, you can temporarily add it to your system fonts and get its properties then (hmm... but how you will find now what the installed font was?). Well, maybe you can think about other ways, but I decided to look for specifications 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

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

Data types definition

Because there are no structures defined in Windows header files (or I didn't find them), we'll make our own. We need four structures and two macros (I will explain later about them).

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

//This is the 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 the TTF file and their 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 the 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;

//Records in the 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 the macros I talked about before. The macros definition looks like this:

#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 Motorola 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 a file and open it in any hexadecimal editor. You will see:

01 00 00 00      //Little Endian - Intel

This is Little Endian system (Intel). But for Big-Endian (Motorola), the number will be stored vise versa, as in this example:

00 00 00 01      //Big Endian - Motorola

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

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):

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're going to use
  ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
  ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
  ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);

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

Right after the file header goes the Offsets Table. Here you can find an interesting offset table; in our case, "name."

  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();

    //the 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 name's table, so let's read its header:

  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 the records in it. So, we need to run through all records to find information interesting to us -- such as the font name.

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 the 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);
    f.Read(csTemp.GetBuffer(ttRecord.uStringLength + 1),
                            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 that contains our font name. You can download the full working function and use it 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 the deeper you go into TTF, the more differences between TrueType and OpenType you may find. Anyway, below are the links to articles about TTF.

References

Downloads

Download demo project - 20 Kb
Download source - 1 Kb


Comments

  • As retriving the font name I need to retrieve glyf information from the ttf file

    Posted by ssvg on 04/21/2009 07:54am

    As retriving the font name I need to retrieve glyf information from the ttf file. look at the code below this is not full code just a piece of code void TTFReader::ReadGlyf() { setPosition(m_glyfOffset);//get glyf offset from the ttf file for(int c=0 ;c < m_glyphs.size();c++) { int gl = m_glyphs[c]; //unsigned short k = readWORD(); unsigned short numberOfContours = readWORD(); unsigned short minx1 = readWORD(); unsigned short miny1 = readWORD(); unsigned short maxx1 = readWORD(); unsigned short maxy1 = readWORD(); setPosition(m_glyfOffset+m_ttf_pos); gl = m_glyphs[c]; } } my code is not working correctly mean not giving the exact value for eg the number of contours for one glyph.

    Reply
  • & What About GetFontResourceInfo?

    Posted by Legacy on 05/19/2003 12:00am

    Originally posted by: TaveL

    & What About GetFontResourceInfo?
    It's undocumented, but fontview, for example, uses it rather smart. Who have a declaration for that function?
    It's more handy, than a parsing of TTF or other files ...

    Reply
  • How to get the name of a FON file

    Posted by Legacy on 07/19/2002 12:00am

    Originally posted by: Ren� Chodziuk

    It seems that this is not as easy as I though, is the some
    
    way to retrieve the name of a FON file ???.
    I guess there must some structures to fill, but I haven't been able to find them.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds