Add support for getting TTC font names and sharing the font file data between multiple TrueTypeFont instances

This commit is contained in:
Magnus Norddahl 2024-01-08 19:25:44 +01:00 committed by Christoph Oelckers
parent 96b812e880
commit 10ce3abb77
2 changed files with 204 additions and 18 deletions

View File

@ -11,13 +11,14 @@
#include <fstream>
#endif
TrueTypeFont::TrueTypeFont(std::vector<uint8_t> initdata, int ttcFontIndex) : data(std::move(initdata))
TrueTypeFont::TrueTypeFont(std::shared_ptr<TrueTypeFontFileData> initdata, int ttcFontIndex) : data(std::move(initdata))
{
if (data.size() > 0x7fffffff)
if (data->size() > 0x7fffffff)
throw std::runtime_error("TTF file is larger than 2 gigabytes!");
TrueTypeFileReader reader(data.data(), data.size());
TrueTypeFileReader reader(data->data(), data->size());
ttf_Tag versionTag = reader.ReadTag();
reader.Seek(0);
if (memcmp(versionTag.data(), "ttcf", 4) == 0) // TTC header
{
@ -26,10 +27,6 @@ TrueTypeFont::TrueTypeFont(std::vector<uint8_t> initdata, int ttcFontIndex) : da
throw std::runtime_error("TTC font index out of bounds");
reader.Seek(ttcHeader.tableDirectoryOffsets[ttcFontIndex]);
}
else
{
reader.Seek(0);
}
directory.Load(reader);
@ -38,32 +35,32 @@ TrueTypeFont::TrueTypeFont(std::vector<uint8_t> initdata, int ttcFontIndex) : da
// Load required tables:
reader = directory.GetReader(data.data(), data.size(), "head");
reader = directory.GetReader(data->data(), data->size(), "head");
head.Load(reader);
reader = directory.GetReader(data.data(), data.size(), "hhea");
reader = directory.GetReader(data->data(), data->size(), "hhea");
hhea.Load(reader);
reader = directory.GetReader(data.data(), data.size(), "maxp");
reader = directory.GetReader(data->data(), data->size(), "maxp");
maxp.Load(reader);
reader = directory.GetReader(data.data(), data.size(), "hmtx");
reader = directory.GetReader(data->data(), data->size(), "hmtx");
hmtx.Load(hhea, maxp, reader);
reader = directory.GetReader(data.data(), data.size(), "name");
reader = directory.GetReader(data->data(), data->size(), "name");
name.Load(reader);
reader = directory.GetReader(data.data(), data.size(), "OS/2");
reader = directory.GetReader(data->data(), data->size(), "OS/2");
os2.Load(reader);
reader = directory.GetReader(data.data(), data.size(), "cmap");
reader = directory.GetReader(data->data(), data->size(), "cmap");
cmap.Load(reader);
LoadCharacterMapEncoding(reader);
// Load TTF Outlines:
reader = directory.GetReader(data.data(), data.size(), "loca");
reader = directory.GetReader(data->data(), data->size(), "loca");
loca.Load(head, maxp, reader);
glyf = directory.GetRecord("glyf");
@ -302,7 +299,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
if (glyphIndex >= loca.offsets.size())
throw std::runtime_error("Glyph index out of bounds");
TrueTypeFileReader reader = glyf.GetReader(data.data(), data.size());
TrueTypeFileReader reader = glyf.GetReader(data->data(), data->size());
reader.Seek(loca.offsets[glyphIndex]);
ttf_int16 numberOfContours = reader.ReadInt16();
@ -631,6 +628,49 @@ void TrueTypeFont::LoadCharacterMapEncoding(TrueTypeFileReader& reader)
}
}
std::vector<TTCFontName> TrueTypeFont::GetFontNames(const std::shared_ptr<TrueTypeFontFileData>& data)
{
if (data->size() > 0x7fffffff)
throw std::runtime_error("TTF file is larger than 2 gigabytes!");
TrueTypeFileReader reader(data->data(), data->size());
ttf_Tag versionTag = reader.ReadTag();
reader.Seek(0);
std::vector<TTCFontName> names;
if (memcmp(versionTag.data(), "ttcf", 4) == 0) // TTC header
{
TTC_Header ttcHeader;
ttcHeader.Load(reader);
for (size_t i = 0; i < ttcHeader.tableDirectoryOffsets.size(); i++)
{
reader.Seek(ttcHeader.tableDirectoryOffsets[i]);
TTF_TableDirectory directory;
directory.Load(reader);
TTF_NamingTable name;
auto name_reader = directory.GetReader(data->data(), data->size(), "name");
name.Load(name_reader);
names.push_back(name.GetFontName());
}
}
else
{
TTF_TableDirectory directory;
directory.Load(reader);
TTF_NamingTable name;
auto name_reader = directory.GetReader(data->data(), data->size(), "name");
name.Load(name_reader);
names.push_back(name.GetFontName());
}
return names;
}
/////////////////////////////////////////////////////////////////////////////
void TTF_CMapSubtable0::Load(TrueTypeFileReader& reader)
@ -857,6 +897,62 @@ void TTF_MaximumProfile::Load(TrueTypeFileReader& reader)
/////////////////////////////////////////////////////////////////////////////
static std::string unicode_to_utf8(unsigned int value)
{
char text[8];
if ((value < 0x80) && (value > 0))
{
text[0] = (char)value;
text[1] = 0;
}
else if (value < 0x800)
{
text[0] = (char)(0xc0 | (value >> 6));
text[1] = (char)(0x80 | (value & 0x3f));
text[2] = 0;
}
else if (value < 0x10000)
{
text[0] = (char)(0xe0 | (value >> 12));
text[1] = (char)(0x80 | ((value >> 6) & 0x3f));
text[2] = (char)(0x80 | (value & 0x3f));
text[3] = 0;
}
else if (value < 0x200000)
{
text[0] = (char)(0xf0 | (value >> 18));
text[1] = (char)(0x80 | ((value >> 12) & 0x3f));
text[2] = (char)(0x80 | ((value >> 6) & 0x3f));
text[3] = (char)(0x80 | (value & 0x3f));
text[4] = 0;
}
else if (value < 0x4000000)
{
text[0] = (char)(0xf8 | (value >> 24));
text[1] = (char)(0x80 | ((value >> 18) & 0x3f));
text[2] = (char)(0x80 | ((value >> 12) & 0x3f));
text[3] = (char)(0x80 | ((value >> 6) & 0x3f));
text[4] = (char)(0x80 | (value & 0x3f));
text[5] = 0;
}
else if (value < 0x80000000)
{
text[0] = (char)(0xfc | (value >> 30));
text[1] = (char)(0x80 | ((value >> 24) & 0x3f));
text[2] = (char)(0x80 | ((value >> 18) & 0x3f));
text[3] = (char)(0x80 | ((value >> 12) & 0x3f));
text[4] = (char)(0x80 | ((value >> 6) & 0x3f));
text[5] = (char)(0x80 | (value & 0x3f));
text[6] = 0;
}
else
{
text[0] = 0; // Invalid wchar value
}
return text;
}
void TTF_NamingTable::Load(TrueTypeFileReader& reader)
{
version = reader.ReadUInt16();
@ -885,6 +981,34 @@ void TTF_NamingTable::Load(TrueTypeFileReader& reader)
langTagRecord.push_back(record);
}
}
for (NameRecord& record : nameRecord)
{
if (record.length > 0 && record.platformID == 3 && record.encodingID == 1)
{
reader.Seek(storageOffset + record.stringOffset);
ttf_uint16 ucs2len = record.length / 2;
for (ttf_uint16 i = 0; i < ucs2len; i++)
{
record.text += unicode_to_utf8(reader.ReadUInt16());
}
}
}
}
TTCFontName TTF_NamingTable::GetFontName() const
{
TTCFontName fname;
for (const auto& record : nameRecord)
{
if (record.nameID == 1) fname.FamilyName = record.text;
if (record.nameID == 2) fname.SubfamilyName = record.text;
if (record.nameID == 3) fname.UniqueID = record.text;
if (record.nameID == 4) fname.FullName = record.text;
if (record.nameID == 5) fname.VersionString = record.text;
if (record.nameID == 6) fname.PostscriptName = record.text;
}
return fname;
}
/////////////////////////////////////////////////////////////////////////////

View File

@ -289,6 +289,17 @@ struct TTF_MaximumProfile // 'maxp' Maximum profile
void Load(TrueTypeFileReader& reader);
};
class TTCFontName
{
public:
std::string FamilyName; // Arial
std::string SubfamilyName; // Regular
std::string FullName; // Arial Regular
std::string UniqueID;
std::string VersionString;
std::string PostscriptName;
};
struct TTF_NamingTable // 'name' Naming table
{
struct NameRecord
@ -299,6 +310,7 @@ struct TTF_NamingTable // 'name' Naming table
ttf_uint16 nameID = {};
ttf_uint16 length = {};
ttf_Offset16 stringOffset = {};
std::string text;
};
struct LangTagRecord
@ -318,6 +330,8 @@ struct TTF_NamingTable // 'name' Naming table
std::vector<LangTagRecord> langTagRecord; // [langTagCount]
void Load(TrueTypeFileReader& reader);
TTCFontName GetFontName() const;
};
struct TTF_OS2Windows // 'OS/2' Windows specific metrics
@ -428,10 +442,58 @@ public:
double lineGap = 0.0;
};
class TrueTypeFontFileData
{
public:
TrueTypeFontFileData(std::vector<uint8_t> data) : dataVector(std::move(data))
{
dataPtr = dataVector.data();
dataSize = dataVector.size();
}
TrueTypeFontFileData(const void* data, size_t size, bool copyData = true)
{
dataSize = size;
if (copyData)
{
dataPtr = new uint8_t[size];
deleteDataPtr = true;
memcpy(const_cast<void*>(dataPtr), data, size);
}
else
{
dataPtr = data;
}
}
~TrueTypeFontFileData()
{
if (deleteDataPtr)
{
delete[](uint8_t*)dataPtr;
}
dataPtr = nullptr;
}
const void* data() const { return dataPtr; }
size_t size() const { return dataSize; }
private:
std::vector<uint8_t> dataVector;
const void* dataPtr = nullptr;
size_t dataSize = 0;
bool deleteDataPtr = false;
TrueTypeFontFileData(const TrueTypeFontFileData&) = delete;
TrueTypeFontFileData& operator=(const TrueTypeFontFileData&) = delete;
};
class TrueTypeFont
{
public:
TrueTypeFont(std::vector<uint8_t> data, int ttcFontIndex = 0);
TrueTypeFont(std::shared_ptr<TrueTypeFontFileData> data, int ttcFontIndex = 0);
static std::vector<TTCFontName> GetFontNames(const std::shared_ptr<TrueTypeFontFileData>& data);
TrueTypeTextMetrics GetTextMetrics(double height) const;
uint32_t GetGlyphIndex(uint32_t codepoint) const;
@ -442,7 +504,7 @@ private:
void LoadGlyph(TTF_SimpleGlyph& glyph, uint32_t glyphIndex, int compositeDepth = 0) const;
static float F2DOT14_ToFloat(ttf_F2DOT14 v);
std::vector<uint8_t> data;
std::shared_ptr<TrueTypeFontFileData> data;
TTC_Header ttcHeader;
TTF_TableDirectory directory;