SFNTData.cpp (6322B)
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 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: 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 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 {}; 67 } 68 69 return {mFontData + dirEntry->offset, dirEntry->length}; 70 } 71 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 {}; 78 } 79 80 return {mFontData + dirEntry->offset, dirEntry->length}; 81 } 82 83 private: 84 const TableDirEntry* GetDirEntry(const uint32_t aTag) const { 85 const TableDirEntry* foundDirEntry = 86 std::lower_bound(mFirstDirEntry, mEndOfDirEntries, aTag); 87 88 if (foundDirEntry == mEndOfDirEntries || foundDirEntry->tag != aTag) { 89 gfxWarning() << "Font data does not contain tag."; 90 return nullptr; 91 } 92 93 if (mDataLength < (foundDirEntry->offset + foundDirEntry->length)) { 94 gfxWarning() << "Font data too short to contain table."; 95 return nullptr; 96 } 97 98 return foundDirEntry; 99 } 100 101 const uint8_t* mFontData; 102 const TableDirEntry* mFirstDirEntry; 103 const TableDirEntry* mEndOfDirEntries; 104 uint32_t mDataLength; 105 }; 106 107 /* static */ 108 UniquePtr<SFNTData> SFNTData::Create(const uint8_t* aFontData, 109 uint32_t aDataLength) { 110 MOZ_ASSERT(aFontData); 111 112 // Check to see if this is a font collection. 113 if (aDataLength < sizeof(TTCHeader)) { 114 gfxWarning() << "Font data too short."; 115 return nullptr; 116 } 117 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; 125 } 126 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; 134 } 135 ++offset; 136 } 137 138 return sfntData; 139 } 140 141 UniquePtr<SFNTData> sfntData(new SFNTData); 142 if (!sfntData->AddFont(aFontData, aDataLength, 0)) { 143 return nullptr; 144 } 145 146 return sfntData; 147 } 148 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); 159 } 160 161 if (aVarDataSize) { 162 hash = AddToHash(hash, HashBytes(aVarData, aVarDataSize)); 163 } 164 165 return hash << 32 | aDataLength; 166 } 167 168 SFNTData::~SFNTData() { 169 for (size_t i = 0; i < mFonts.length(); ++i) { 170 delete mFonts[i]; 171 } 172 } 173 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; 180 } 181 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; 188 } 189 190 return mFonts.append(new Font(offsetTable, aFontData, aDataLength)); 191 } 192 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())); 200 }); 201 202 return tablesHash; 203 } 204 205 } // namespace gfx 206 } // namespace mozilla