Skip to content

Commit

Permalink
Add support for getting TTC font names and sharing the font file data…
Browse files Browse the repository at this point in the history
… between multiple TrueTypeFont instances
  • Loading branch information
dpjudas committed Jan 8, 2024
1 parent b8874d2 commit fbb4371
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/core/canvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class CanvasFont
public:
CanvasFont(const std::string& fontname, double height) : fontname(fontname), height(height)
{
ttf = std::make_unique<TrueTypeFont>(LoadWidgetFontData(fontname));
ttf = std::make_unique<TrueTypeFont>(std::make_shared<TrueTypeFontFileData>(LoadWidgetFontData(fontname)));
textmetrics = ttf->GetTextMetrics(height);
}

Expand Down
163 changes: 151 additions & 12 deletions src/core/truetypefont.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,62 @@
#include "core/pathfill.h"
#include <algorithm>
#include <cmath>
#include <cstring>

#ifdef DUMP_GLYPH
#include <fstream>
#endif

TrueTypeFont::TrueTypeFont(std::vector<uint8_t> initdata) : 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
{
ttcHeader.Load(reader);
if (ttcFontIndex >= ttcHeader.numFonts)
throw std::runtime_error("TTC font index out of bounds");
reader.Seek(ttcHeader.tableDirectoryOffsets[ttcFontIndex]);
}

directory.Load(reader);

if (!directory.ContainsTTFOutlines())
throw std::runtime_error("Only truetype outline fonts are supported");

// 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");
Expand Down Expand Up @@ -287,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();
Expand Down Expand Up @@ -616,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)
Expand Down Expand Up @@ -842,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();
Expand Down Expand Up @@ -870,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;
}

/////////////////////////////////////////////////////////////////////////////
Expand Down
69 changes: 66 additions & 3 deletions src/core/truetypefont.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -299,6 +310,7 @@ struct TTF_NamingTable // 'name' Naming table
ttf_uint16 nameID = {};
ttf_uint16 length = {};
ttf_Offset16 stringOffset = {};
std::string text;
};

struct LangTagRecord
Expand All @@ -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
Expand Down Expand Up @@ -428,10 +442,58 @@ class TrueTypeTextMetrics
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);
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;
Expand All @@ -442,8 +504,9 @@ class TrueTypeFont
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;

// Required for all OpenType fonts:
Expand Down

0 comments on commit fbb4371

Please sign in to comment.