#include "stdafx.h"
#include "TTFFont.h"
TTFFont::TTFFont(std::vector<BYTE>& fontData) : mData(fontData), mFilePos(0)
{
ParseHeader();
ParseTables();
}
void TTFFont::ParseHeader()
{
UINT fixedVersion = 0;
Read(fixedVersion);
swap32(fixedVersion);
if(fixedVersion != 0x00010000)
throw std::exception("Version not supported by this wrapper!");
Read(mNumTables);
if(mNumTables == 0)
throw std::exception("Font file does not contain any tables!");
swap16(mNumTables);
USHORT searchRange, entrySelector, rangeShift;
Read(searchRange);
Read(entrySelector);
Read(rangeShift);
swap16(searchRange);
swap16(entrySelector);
swap16(rangeShift);
searchRange /= 16;
if(!IsPowerOfTwo(searchRange))
throw std::exception("searchRange in OTF header is not correct (no power of two)!");
if(searchRange > mNumTables || (searchRange * 2) <= mNumTables)
throw std::exception("searchRange in OTF header is not correct!");
if(entrySelector != (USHORT)log2(searchRange))
throw std::exception("entrySelector in OTF header is not correct!");
}
void TTFFont::ParseTables()
{
std::vector<TTFTableHeader> tableHeaders(mNumTables);
Read(tableHeaders);
bool hasNameTag = false;
std::for_each(tableHeaders.begin(), tableHeaders.end(),
[this, &hasNameTag] (TTFTableHeader& table)
{
swap32(table.tag);
swap32(table.checkSum);
swap32(table.offset);
swap32(table.length);
if(table.tag == 'name' && !hasNameTag)
{
unsigned int tmpOfs = mFilePos;
ParseNameTable(table);
mFilePos = tmpOfs;
hasNameTag = true;
return;
}
}
);
if(hasNameTag == false)
throw std::exception("Font file has no name table!");
}
void TTFFont::ParseNameTable(const TTFTableHeader& header)
{
if(header.offset + header.length >= mData.size())
throw std::exception("Table exceeds file boundaries!");
struct NameRecord
{
USHORT platformID;
USHORT encodingID;
USHORT languageID;
USHORT nameID;
USHORT length;
USHORT offset;
};
mFilePos = header.offset;
UINT tableStart = mFilePos;
USHORT format, count, stringOffset;
Read(format);
Read(count);
Read(stringOffset);
swap16(format);
swap16(count);
swap16(stringOffset);
if(count == 0)
throw std::exception("Name table has no name records!");
std::vector<NameRecord> nameRecords(count);
Read(nameRecords);
/* Optional (if format == 1):
langTagCount
langTagRecord[langTagCount]
*/
bool familyFound = false;
std::for_each(nameRecords.begin(), nameRecords.end(),
[this, &stringOffset, &familyFound, tableStart](NameRecord& record)
{
swap16(record.platformID);
swap16(record.encodingID);
swap16(record.languageID);
swap16(record.nameID);
swap16(record.length);
swap16(record.offset);
if(record.nameID == 1 && !familyFound)
{
if(stringOffset + (UINT)record.offset + record.length >= mData.size())
throw std::exception("Family name exceeds file boundaries!");
// 3 => Windows
if(record.platformID != 3)
throw std::exception("Only windows platform supported by this implementation!");
// maybe there are more ways to encode an unicode string, but as its only a test...
bool isUnicode = record.encodingID == 1;
std::vector<BYTE> stringData;
auto start = mData.begin() + stringOffset + tableStart;
stringData.assign(start + record.offset, start + record.offset + record.length);
stringData.push_back(0); // just to be sure
if(isUnicode)
{
stringData.push_back(0); // we need one more 0-terminator (USHORT)
USHORT* strPtr = (USHORT*)&stringData[0];
std::for_each(strPtr, strPtr + stringData.size() / sizeof(USHORT),
std::tr1::bind(&TTFFont::swap16, this, std::tr1::placeholders::_1));
);
}
#ifdef UNICODE
if(isUnicode)
mFamilyName = (TCHAR*)&stringData[0];
else
{
std::vector<wchar_t> wc(stringData.size());
MultiByteToWideChar(CP_ACP, 0, (char*)&stringData[0], wc.size(),
&wc[0], wc.size());
mFamilyName = &wc[0];
}
#else
if(isUnicode)
{
std::vector<char> ansi(stringData.size() / sizeof(USHORT));
WideCharToMultiByte(CP_ACP, 0, (wchar_t*)&stringData[0],
ansi.size(), &ansi[0], ansi.size(), NULL, NULL);
mFamilyName = (TCHAR*)&ansi[0];
}
else
mFamilyName = (TCHAR*)&stringData[0];
#endif
}
}
);
}
void TTFFont::ParseFile(const std::tstring& fileName, std::vector<TTFFont*>& fonts)
{
std::ifstream inFile(fileName, std::ios::in | std::ios::binary);
if(!inFile.is_open())
throw std::exception("The specified file could not be opened!");
UINT tag = 0;
if(!inFile.read((char*)&tag, sizeof(tag)))
throw std::exception("Unable to read tag!");
if(tag == 'fctt')
throw std::exception("Collections not yet implemented!");
inFile.seekg(0, std::ios::end);
std::streamoff len = inFile.tellg();
inFile.seekg(0, std::ios::beg);
std::vector<BYTE> data((UINT)len);
if(!inFile.read((char*)&data[0], len))
throw std::exception("Unable to read file content!");
TTFFont* font = new TTFFont(data);
fonts.push_back(font);
return;
}