From fbb43718cdbfb69faa686077f848286685690bbe Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Mon, 8 Jan 2024 19:26:35 +0100 Subject: [PATCH] Add support for getting TTC font names and sharing the font file data between multiple TrueTypeFont instances --- src/core/canvas.cpp | 2 +- src/core/truetypefont.cpp | 163 +++++++++++++++++++++++++++++++++++--- src/core/truetypefont.h | 69 +++++++++++++++- 3 files changed, 218 insertions(+), 16 deletions(-) diff --git a/src/core/canvas.cpp b/src/core/canvas.cpp index 3c43371..9653235 100644 --- a/src/core/canvas.cpp +++ b/src/core/canvas.cpp @@ -48,7 +48,7 @@ class CanvasFont public: CanvasFont(const std::string& fontname, double height) : fontname(fontname), height(height) { - ttf = std::make_unique(LoadWidgetFontData(fontname)); + ttf = std::make_unique(std::make_shared(LoadWidgetFontData(fontname))); textmetrics = ttf->GetTextMetrics(height); } diff --git a/src/core/truetypefont.cpp b/src/core/truetypefont.cpp index 07b599e..ccc65a2 100644 --- a/src/core/truetypefont.cpp +++ b/src/core/truetypefont.cpp @@ -5,17 +5,29 @@ #include "core/pathfill.h" #include #include +#include #ifdef DUMP_GLYPH #include #endif -TrueTypeFont::TrueTypeFont(std::vector initdata) : data(std::move(initdata)) +TrueTypeFont::TrueTypeFont(std::shared_ptr 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()) @@ -23,32 +35,32 @@ TrueTypeFont::TrueTypeFont(std::vector initdata) : data(std::move(initd // 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"); @@ -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(); @@ -616,6 +628,49 @@ void TrueTypeFont::LoadCharacterMapEncoding(TrueTypeFileReader& reader) } } +std::vector TrueTypeFont::GetFontNames(const std::shared_ptr& 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 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) @@ -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(); @@ -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; } ///////////////////////////////////////////////////////////////////////////// diff --git a/src/core/truetypefont.h b/src/core/truetypefont.h index 40d62ad..53d8837 100644 --- a/src/core/truetypefont.h +++ b/src/core/truetypefont.h @@ -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; // [langTagCount] void Load(TrueTypeFileReader& reader); + + TTCFontName GetFontName() const; }; struct TTF_OS2Windows // 'OS/2' Windows specific metrics @@ -428,10 +442,58 @@ class TrueTypeTextMetrics double lineGap = 0.0; }; +class TrueTypeFontFileData +{ +public: + TrueTypeFontFileData(std::vector 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(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 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 data); + TrueTypeFont(std::shared_ptr data, int ttcFontIndex = 0); + + static std::vector GetFontNames(const std::shared_ptr& data); TrueTypeTextMetrics GetTextMetrics(double height) const; uint32_t GetGlyphIndex(uint32_t codepoint) const; @@ -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 data; - + std::shared_ptr data; + + TTC_Header ttcHeader; TTF_TableDirectory directory; // Required for all OpenType fonts: