Bug 1885602 - Part 4: Implement navigating to the settings from the menu header for...
[gecko.git] / gfx / 2d / SFNTData.cpp
blob42cf95c33cb6d116cb62aec504bb2c9660cd4a65
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SFNTData.h"
9 #include <algorithm>
10 #include <numeric>
12 #include "BigEndianInts.h"
13 #include "Logging.h"
14 #include "mozilla/HashFunctions.h"
15 #include "mozilla/Span.h"
17 namespace mozilla {
18 namespace gfx {
20 #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
22 #pragma pack(push, 1)
24 struct TTCHeader {
25 BigEndianUint32 ttcTag; // Always 'ttcf'
26 BigEndianUint32 version; // Fixed, 0x00010000
27 BigEndianUint32 numFonts;
30 struct OffsetTable {
31 BigEndianUint32 sfntVersion; // Fixed, 0x00010000 for version 1.0.
32 BigEndianUint16 numTables;
33 BigEndianUint16 searchRange; // (Maximum power of 2 <= numTables) x 16.
34 BigEndianUint16 entrySelector; // Log2(maximum power of 2 <= numTables).
35 BigEndianUint16 rangeShift; // NumTables x 16-searchRange.
38 struct TableDirEntry {
39 BigEndianUint32 tag; // 4 -byte identifier.
40 BigEndianUint32 checkSum; // CheckSum for this table.
41 BigEndianUint32 offset; // Offset from beginning of TrueType font file.
42 BigEndianUint32 length; // Length of this table.
44 friend bool operator<(const TableDirEntry& lhs, const uint32_t aTag) {
45 return lhs.tag < aTag;
49 #pragma pack(pop)
51 class SFNTData::Font {
52 public:
53 Font(const OffsetTable* aOffsetTable, const uint8_t* aFontData,
54 uint32_t aDataLength)
55 : mFontData(aFontData),
56 mFirstDirEntry(
57 reinterpret_cast<const TableDirEntry*>(aOffsetTable + 1)),
58 mEndOfDirEntries(mFirstDirEntry + aOffsetTable->numTables),
59 mDataLength(aDataLength) {}
61 Span<const uint8_t> GetHeadTableBytes() const {
62 const TableDirEntry* dirEntry =
63 GetDirEntry(TRUETYPE_TAG('h', 'e', 'a', 'd'));
64 if (!dirEntry) {
65 gfxWarning() << "Head table entry not found.";
66 return {};
69 return {mFontData + dirEntry->offset, dirEntry->length};
72 Span<const uint8_t> GetCmapTableBytes() const {
73 const TableDirEntry* dirEntry =
74 GetDirEntry(TRUETYPE_TAG('c', 'm', 'a', 'p'));
75 if (!dirEntry) {
76 gfxWarning() << "Cmap table entry not found.";
77 return {};
80 return {mFontData + dirEntry->offset, dirEntry->length};
83 private:
84 const TableDirEntry* GetDirEntry(const uint32_t aTag) const {
85 const TableDirEntry* foundDirEntry =
86 std::lower_bound(mFirstDirEntry, mEndOfDirEntries, aTag);
88 if (foundDirEntry == mEndOfDirEntries || foundDirEntry->tag != aTag) {
89 gfxWarning() << "Font data does not contain tag.";
90 return nullptr;
93 if (mDataLength < (foundDirEntry->offset + foundDirEntry->length)) {
94 gfxWarning() << "Font data too short to contain table.";
95 return nullptr;
98 return foundDirEntry;
101 const uint8_t* mFontData;
102 const TableDirEntry* mFirstDirEntry;
103 const TableDirEntry* mEndOfDirEntries;
104 uint32_t mDataLength;
107 /* static */
108 UniquePtr<SFNTData> SFNTData::Create(const uint8_t* aFontData,
109 uint32_t aDataLength) {
110 MOZ_ASSERT(aFontData);
112 // Check to see if this is a font collection.
113 if (aDataLength < sizeof(TTCHeader)) {
114 gfxWarning() << "Font data too short.";
115 return nullptr;
118 const TTCHeader* ttcHeader = reinterpret_cast<const TTCHeader*>(aFontData);
119 if (ttcHeader->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
120 uint32_t numFonts = ttcHeader->numFonts;
121 if (aDataLength <
122 sizeof(TTCHeader) + (numFonts * sizeof(BigEndianUint32))) {
123 gfxWarning() << "Font data too short to contain full TTC Header.";
124 return nullptr;
127 UniquePtr<SFNTData> sfntData(new SFNTData);
128 const BigEndianUint32* offset =
129 reinterpret_cast<const BigEndianUint32*>(aFontData + sizeof(TTCHeader));
130 const BigEndianUint32* endOfOffsets = offset + numFonts;
131 while (offset != endOfOffsets) {
132 if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
133 return nullptr;
135 ++offset;
138 return sfntData;
141 UniquePtr<SFNTData> sfntData(new SFNTData);
142 if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
143 return nullptr;
146 return sfntData;
149 /* static */
150 uint64_t SFNTData::GetUniqueKey(const uint8_t* aFontData, uint32_t aDataLength,
151 uint32_t aVarDataSize, const void* aVarData) {
152 uint64_t hash = 0;
153 UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aDataLength);
154 if (sfntData) {
155 hash = sfntData->HashHeadAndCmapTables();
156 } else {
157 gfxWarning() << "Failed to create SFNTData from data, hashing whole font.";
158 hash = HashBytes(aFontData, aDataLength);
161 if (aVarDataSize) {
162 hash = AddToHash(hash, HashBytes(aVarData, aVarDataSize));
165 return hash << 32 | aDataLength;
168 SFNTData::~SFNTData() {
169 for (size_t i = 0; i < mFonts.length(); ++i) {
170 delete mFonts[i];
174 bool SFNTData::AddFont(const uint8_t* aFontData, uint32_t aDataLength,
175 uint32_t aOffset) {
176 uint32_t remainingLength = aDataLength - aOffset;
177 if (remainingLength < sizeof(OffsetTable)) {
178 gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
179 return false;
182 const OffsetTable* offsetTable =
183 reinterpret_cast<const OffsetTable*>(aFontData + aOffset);
184 if (remainingLength <
185 sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
186 gfxWarning() << "Font data too short to contain tables.";
187 return false;
190 return mFonts.append(new Font(offsetTable, aFontData, aDataLength));
193 uint32_t SFNTData::HashHeadAndCmapTables() {
194 uint32_t tablesHash = std::accumulate(
195 mFonts.begin(), mFonts.end(), 0U, [](uint32_t hash, Font* font) {
196 Span<const uint8_t> headBytes = font->GetHeadTableBytes();
197 hash = AddToHash(hash, HashBytes(headBytes.data(), headBytes.size()));
198 Span<const uint8_t> cmapBytes = font->GetCmapTableBytes();
199 return AddToHash(hash, HashBytes(cmapBytes.data(), cmapBytes.size()));
202 return tablesHash;
205 } // namespace gfx
206 } // namespace mozilla