Data
types definition
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.
Collapse
Copy Code
typedef struct _tagTT_OFFSET_TABLE{
USHORT uMajorVersion;
USHORT uMinorVersion;
USHORT uNumOfTables;
USHORT uSearchRange;
USHORT uEntrySelector;
USHORT uRangeShift;
}TT_OFFSET_TABLE;
typedef struct _tagTT_TABLE_DIRECTORY{
char szTag[4]; ULONG uCheckSum; ULONG uOffset; ULONG uLength; }TT_TABLE_DIRECTORY;
typedef struct _tagTT_NAME_TABLE_HEADER{
USHORT uFSelector; USHORT uNRCount; USHORT uStorageOffset; }TT_NAME_TABLE_HEADER;
typedef struct _tagTT_NAME_RECORD{
USHORT uPlatformID;
USHORT uEncodingID;
USHORT uLanguageID;
USHORT uNameID;
USHORT uStringLength;
USHORT uStringOffset; }TT_NAME_RECORD;
Macros
Now only thing left is
macros I was talking before.
The macros definition looks
like:
Collapse
Copy Code
#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:
Collapse
Copy Code
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:
Collapse
Copy Code
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):
Collapse
Copy Code
CFile f;
CString csRetVal;
if(f.Open(lpszFilePath, CFile::modeRead|CFile::shareDenyWrite)){
TT_OFFSET_TABLE ttOffsetTable;
f.Read(&ttOffsetTable, sizeof(TT_OFFSET_TABLE));
ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
if(ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
return csRetVal;
Right after the
file header
goes Offsets Table. You can
find here an offset to
interesting you table,
"name" in our case.
Collapse
Copy Code
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();
strncpy(csTemp.GetBuffer(4), tblDir.szTag, 4);
csTemp.ReleaseBuffer();
if(csTemp.CompareNoCase(_T("name")) == 0){
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:
Collapse
Copy Code
if(bFound){
f.Seek(tblDir.uOffset, CFile::begin);
TT_NAME_TABLE_HEADER ttNTHeader;
f.Read(&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER));
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.
Collapse
Copy Code
for(int i=0; i<ttNTHeader.uNRCount; i++){
f.Read(&ttRecord, sizeof(TT_NAME_RECORD));
ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
if(ttRecord.uNameID == 1){
ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
int nPos = f.GetPosition();
f.Seek(tblDir.uOffset + ttRecord.uStringOffset +
ttNTHeader.uStorageOffset, CFile::begin);
TCHAR lpszNameBuf = csTemp.GetBuffer(ttRecord.uStringLength + 1);
ZeroMemory(lpszNameBuf, ttRecord.uStringLength + 1);
f.Read(lpszNameBuf, ttRecord.uStringLength);
csTemp.ReleaseBuffer();
if(csTemp.GetLength() > 0){
csRetVal = csTemp;
break;
}
f.Seek(nPos, CFile::begin);
}
}