Line data Source code
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 :
11 : #include "BigEndianInts.h"
12 : #include "Logging.h"
13 : #include "mozilla/HashFunctions.h"
14 : #include "SFNTNameTable.h"
15 :
16 : namespace mozilla {
17 : namespace gfx {
18 :
19 : #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
20 :
21 : #pragma pack(push, 1)
22 :
23 : struct TTCHeader
24 : {
25 : BigEndianUint32 ttcTag; // Always 'ttcf'
26 : BigEndianUint32 version; // Fixed, 0x00010000
27 : BigEndianUint32 numFonts;
28 : };
29 :
30 : struct OffsetTable
31 : {
32 : BigEndianUint32 sfntVersion; // Fixed, 0x00010000 for version 1.0.
33 : BigEndianUint16 numTables;
34 : BigEndianUint16 searchRange; // (Maximum power of 2 <= numTables) x 16.
35 : BigEndianUint16 entrySelector; // Log2(maximum power of 2 <= numTables).
36 : BigEndianUint16 rangeShift; // NumTables x 16-searchRange.
37 : };
38 :
39 : struct TableDirEntry
40 : {
41 : BigEndianUint32 tag; // 4 -byte identifier.
42 : BigEndianUint32 checkSum; // CheckSum for this table.
43 : BigEndianUint32 offset; // Offset from beginning of TrueType font file.
44 : BigEndianUint32 length; // Length of this table.
45 :
46 0 : friend bool operator<(const TableDirEntry& lhs, const uint32_t aTag)
47 : {
48 0 : return lhs.tag < aTag;
49 : }
50 : };
51 :
52 : #pragma pack(pop)
53 :
54 : class SFNTData::Font
55 : {
56 : public:
57 0 : Font(const OffsetTable *aOffsetTable, const uint8_t *aFontData,
58 : uint32_t aDataLength)
59 0 : : mFontData(aFontData)
60 0 : , mFirstDirEntry(reinterpret_cast<const TableDirEntry*>(aOffsetTable + 1))
61 0 : , mEndOfDirEntries(mFirstDirEntry + aOffsetTable->numTables)
62 0 : , mDataLength(aDataLength)
63 : {
64 0 : }
65 :
66 0 : bool GetU16FullName(mozilla::u16string& aU16FullName)
67 : {
68 : const TableDirEntry* dirEntry =
69 0 : GetDirEntry(TRUETYPE_TAG('n', 'a', 'm', 'e'));
70 0 : if (!dirEntry) {
71 0 : gfxWarning() << "Name table entry not found.";
72 0 : return false;
73 : }
74 :
75 : UniquePtr<SFNTNameTable> nameTable =
76 0 : SFNTNameTable::Create((mFontData + dirEntry->offset), dirEntry->length);
77 0 : if (!nameTable) {
78 0 : return false;
79 : }
80 :
81 0 : return nameTable->GetU16FullName(aU16FullName);
82 : }
83 :
84 : private:
85 :
86 : const TableDirEntry*
87 0 : GetDirEntry(const uint32_t aTag)
88 : {
89 : const TableDirEntry* foundDirEntry =
90 0 : std::lower_bound(mFirstDirEntry, mEndOfDirEntries, aTag);
91 :
92 0 : if (foundDirEntry == mEndOfDirEntries || foundDirEntry->tag != aTag) {
93 0 : gfxWarning() << "Font data does not contain tag.";
94 0 : return nullptr;
95 : }
96 :
97 0 : if (mDataLength < (foundDirEntry->offset + foundDirEntry->length)) {
98 0 : gfxWarning() << "Font data too short to contain table.";
99 0 : return nullptr;
100 : }
101 :
102 0 : return foundDirEntry;
103 : }
104 :
105 : const uint8_t *mFontData;
106 : const TableDirEntry *mFirstDirEntry;
107 : const TableDirEntry *mEndOfDirEntries;
108 : uint32_t mDataLength;
109 : };
110 :
111 : /* static */
112 : UniquePtr<SFNTData>
113 0 : SFNTData::Create(const uint8_t *aFontData, uint32_t aDataLength)
114 : {
115 0 : MOZ_ASSERT(aFontData);
116 :
117 : // Check to see if this is a font collection.
118 0 : if (aDataLength < sizeof(TTCHeader)) {
119 0 : gfxWarning() << "Font data too short.";
120 0 : return nullptr;
121 : }
122 :
123 0 : const TTCHeader *ttcHeader = reinterpret_cast<const TTCHeader*>(aFontData);
124 0 : if (ttcHeader->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
125 0 : uint32_t numFonts = ttcHeader->numFonts;
126 0 : if (aDataLength < sizeof(TTCHeader) + (numFonts * sizeof(BigEndianUint32))) {
127 0 : gfxWarning() << "Font data too short to contain full TTC Header.";
128 0 : return nullptr;
129 : }
130 :
131 0 : UniquePtr<SFNTData> sfntData(new SFNTData);
132 : const BigEndianUint32* offset =
133 0 : reinterpret_cast<const BigEndianUint32*>(aFontData + sizeof(TTCHeader));
134 0 : const BigEndianUint32* endOfOffsets = offset + numFonts;
135 0 : while (offset != endOfOffsets) {
136 0 : if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
137 0 : return nullptr;
138 : }
139 0 : ++offset;
140 : }
141 :
142 0 : return Move(sfntData);
143 : }
144 :
145 0 : UniquePtr<SFNTData> sfntData(new SFNTData);
146 0 : if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
147 0 : return nullptr;
148 : }
149 :
150 0 : return Move(sfntData);
151 : }
152 :
153 : /* static */
154 : uint64_t
155 0 : SFNTData::GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength,
156 : uint32_t aVarDataSize, const void* aVarData)
157 : {
158 : uint64_t hash;
159 0 : UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aDataLength);
160 0 : mozilla::u16string firstName;
161 0 : if (sfntData && sfntData->GetU16FullName(0, firstName)) {
162 0 : hash = HashString(firstName.c_str(), firstName.length());
163 : } else {
164 0 : gfxWarning() << "Failed to get name from font data hashing whole font.";
165 0 : hash = HashString(aFontData, aDataLength);
166 : }
167 :
168 0 : if (aVarDataSize) {
169 0 : hash = AddToHash(hash, HashBytes(aVarData, aVarDataSize));
170 : }
171 :
172 0 : return hash << 32 | aDataLength;;
173 : }
174 :
175 0 : SFNTData::~SFNTData()
176 : {
177 0 : for (size_t i = 0; i < mFonts.length(); ++i) {
178 0 : delete mFonts[i];
179 : }
180 0 : }
181 :
182 : bool
183 0 : SFNTData::GetU16FullName(uint32_t aIndex, mozilla::u16string& aU16FullName)
184 : {
185 0 : if (aIndex >= mFonts.length()) {
186 0 : gfxWarning() << "aIndex to font data too high.";
187 0 : return false;
188 : }
189 :
190 0 : return mFonts[aIndex]->GetU16FullName(aU16FullName);
191 : }
192 :
193 : bool
194 0 : SFNTData::GetU16FullNames(Vector<mozilla::u16string>& aU16FullNames)
195 : {
196 0 : bool fontFound = false;
197 0 : for (size_t i = 0; i < mFonts.length(); ++i) {
198 0 : mozilla::u16string name;
199 0 : if (mFonts[i]->GetU16FullName(name)) {
200 0 : fontFound = true;
201 : }
202 0 : if (!aU16FullNames.append(Move(name))) {
203 0 : return false;
204 : }
205 : }
206 :
207 0 : return fontFound;
208 : }
209 :
210 : bool
211 0 : SFNTData::GetIndexForU16Name(const mozilla::u16string& aU16FullName,
212 : uint32_t* aIndex, size_t aTruncatedLen)
213 : {
214 0 : for (size_t i = 0; i < mFonts.length(); ++i) {
215 0 : mozilla::u16string name;
216 0 : if (!mFonts[i]->GetU16FullName(name)) {
217 0 : continue;
218 : }
219 :
220 0 : if (aTruncatedLen) {
221 0 : MOZ_ASSERT(aU16FullName.length() <= aTruncatedLen);
222 0 : name = name.substr(0, aTruncatedLen);
223 : }
224 :
225 0 : if (name == aU16FullName) {
226 0 : *aIndex = i;
227 0 : return true;
228 : }
229 : }
230 :
231 0 : return false;
232 : }
233 :
234 : bool
235 0 : SFNTData::AddFont(const uint8_t *aFontData, uint32_t aDataLength,
236 : uint32_t aOffset)
237 : {
238 0 : uint32_t remainingLength = aDataLength - aOffset;
239 0 : if (remainingLength < sizeof(OffsetTable)) {
240 0 : gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
241 0 : return false;
242 : }
243 :
244 : const OffsetTable *offsetTable =
245 0 : reinterpret_cast<const OffsetTable*>(aFontData + aOffset);
246 0 : if (remainingLength <
247 0 : sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
248 0 : gfxWarning() << "Font data too short to contain tables.";
249 0 : return false;
250 : }
251 :
252 0 : return mFonts.append(new Font(offsetTable, aFontData, aDataLength));
253 : }
254 :
255 : } // gfx
256 : } // mozilla
|