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/. */
6
7 #include "SFNTData.h"
8
9 #include <algorithm>
10 #include <numeric>
11
12 #include "BigEndianInts.h"
13 #include "Logging.h"
14 #include "mozilla/HashFunctions.h"
15 #include "mozilla/Span.h"
16
17 namespace mozilla {
18 namespace gfx {
19
20 #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
21
22 #pragma pack(push, 1)
23
24 struct TTCHeader {
25 BigEndianUint32 ttcTag; // Always 'ttcf'
26 BigEndianUint32 version; // Fixed, 0x00010000
27 BigEndianUint32 numFonts;
28 };
29
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.
36 };
37
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.
43
operator <(const TableDirEntry & lhs,const uint32_t aTag)44 friend bool operator<(const TableDirEntry& lhs, const uint32_t aTag) {
45 return lhs.tag < aTag;
46 }
47 };
48
49 #pragma pack(pop)
50
51 class SFNTData::Font {
52 public:
Font(const OffsetTable * aOffsetTable,const uint8_t * aFontData,uint32_t aDataLength)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) {}
60
GetHeadTableBytes() const61 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 nullptr;
67 }
68
69 return MakeSpan(mFontData + dirEntry->offset, dirEntry->length);
70 }
71
72 private:
GetDirEntry(const uint32_t aTag) const73 const TableDirEntry* GetDirEntry(const uint32_t aTag) const {
74 const TableDirEntry* foundDirEntry =
75 std::lower_bound(mFirstDirEntry, mEndOfDirEntries, aTag);
76
77 if (foundDirEntry == mEndOfDirEntries || foundDirEntry->tag != aTag) {
78 gfxWarning() << "Font data does not contain tag.";
79 return nullptr;
80 }
81
82 if (mDataLength < (foundDirEntry->offset + foundDirEntry->length)) {
83 gfxWarning() << "Font data too short to contain table.";
84 return nullptr;
85 }
86
87 return foundDirEntry;
88 }
89
90 const uint8_t* mFontData;
91 const TableDirEntry* mFirstDirEntry;
92 const TableDirEntry* mEndOfDirEntries;
93 uint32_t mDataLength;
94 };
95
96 /* static */
Create(const uint8_t * aFontData,uint32_t aDataLength)97 UniquePtr<SFNTData> SFNTData::Create(const uint8_t* aFontData,
98 uint32_t aDataLength) {
99 MOZ_ASSERT(aFontData);
100
101 // Check to see if this is a font collection.
102 if (aDataLength < sizeof(TTCHeader)) {
103 gfxWarning() << "Font data too short.";
104 return nullptr;
105 }
106
107 const TTCHeader* ttcHeader = reinterpret_cast<const TTCHeader*>(aFontData);
108 if (ttcHeader->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
109 uint32_t numFonts = ttcHeader->numFonts;
110 if (aDataLength <
111 sizeof(TTCHeader) + (numFonts * sizeof(BigEndianUint32))) {
112 gfxWarning() << "Font data too short to contain full TTC Header.";
113 return nullptr;
114 }
115
116 UniquePtr<SFNTData> sfntData(new SFNTData);
117 const BigEndianUint32* offset =
118 reinterpret_cast<const BigEndianUint32*>(aFontData + sizeof(TTCHeader));
119 const BigEndianUint32* endOfOffsets = offset + numFonts;
120 while (offset != endOfOffsets) {
121 if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
122 return nullptr;
123 }
124 ++offset;
125 }
126
127 return sfntData;
128 }
129
130 UniquePtr<SFNTData> sfntData(new SFNTData);
131 if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
132 return nullptr;
133 }
134
135 return sfntData;
136 }
137
138 /* static */
GetUniqueKey(const uint8_t * aFontData,uint32_t aDataLength,uint32_t aVarDataSize,const void * aVarData)139 uint64_t SFNTData::GetUniqueKey(const uint8_t* aFontData, uint32_t aDataLength,
140 uint32_t aVarDataSize, const void* aVarData) {
141 uint64_t hash = 0;
142 UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aDataLength);
143 if (sfntData) {
144 hash = sfntData->HashHeadTables();
145 } else {
146 gfxWarning() << "Failed to create SFNTData from data, hashing whole font.";
147 hash = HashBytes(aFontData, aDataLength);
148 }
149
150 if (aVarDataSize) {
151 hash = AddToHash(hash, HashBytes(aVarData, aVarDataSize));
152 }
153
154 return hash << 32 | aDataLength;
155 }
156
~SFNTData()157 SFNTData::~SFNTData() {
158 for (size_t i = 0; i < mFonts.length(); ++i) {
159 delete mFonts[i];
160 }
161 }
162
AddFont(const uint8_t * aFontData,uint32_t aDataLength,uint32_t aOffset)163 bool SFNTData::AddFont(const uint8_t* aFontData, uint32_t aDataLength,
164 uint32_t aOffset) {
165 uint32_t remainingLength = aDataLength - aOffset;
166 if (remainingLength < sizeof(OffsetTable)) {
167 gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
168 return false;
169 }
170
171 const OffsetTable* offsetTable =
172 reinterpret_cast<const OffsetTable*>(aFontData + aOffset);
173 if (remainingLength <
174 sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
175 gfxWarning() << "Font data too short to contain tables.";
176 return false;
177 }
178
179 return mFonts.append(new Font(offsetTable, aFontData, aDataLength));
180 }
181
HashHeadTables()182 uint32_t SFNTData::HashHeadTables() {
183 uint32_t headTableHash = std::accumulate(
184 mFonts.begin(), mFonts.end(), 0U, [](uint32_t hash, Font* font) {
185 Span<const uint8_t> headBytes = font->GetHeadTableBytes();
186 return AddToHash(hash, HashBytes(headBytes.data(), headBytes.size()));
187 });
188
189 return headTableHash;
190 }
191
192 } // namespace gfx
193 } // namespace mozilla
194