forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			206 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "SFNTData.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <numeric>
 | |
| 
 | |
| #include "BigEndianInts.h"
 | |
| #include "Logging.h"
 | |
| #include "mozilla/HashFunctions.h"
 | |
| #include "mozilla/Span.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace gfx {
 | |
| 
 | |
| #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
 | |
| 
 | |
| #pragma pack(push, 1)
 | |
| 
 | |
| struct TTCHeader {
 | |
|   BigEndianUint32 ttcTag;   // Always 'ttcf'
 | |
|   BigEndianUint32 version;  // Fixed, 0x00010000
 | |
|   BigEndianUint32 numFonts;
 | |
| };
 | |
| 
 | |
| struct OffsetTable {
 | |
|   BigEndianUint32 sfntVersion;  // Fixed, 0x00010000 for version 1.0.
 | |
|   BigEndianUint16 numTables;
 | |
|   BigEndianUint16 searchRange;    // (Maximum power of 2 <= numTables) x 16.
 | |
|   BigEndianUint16 entrySelector;  // Log2(maximum power of 2 <= numTables).
 | |
|   BigEndianUint16 rangeShift;     // NumTables x 16-searchRange.
 | |
| };
 | |
| 
 | |
| struct TableDirEntry {
 | |
|   BigEndianUint32 tag;       // 4 -byte identifier.
 | |
|   BigEndianUint32 checkSum;  // CheckSum for this table.
 | |
|   BigEndianUint32 offset;    // Offset from beginning of TrueType font file.
 | |
|   BigEndianUint32 length;    // Length of this table.
 | |
| 
 | |
|   friend bool operator<(const TableDirEntry& lhs, const uint32_t aTag) {
 | |
|     return lhs.tag < aTag;
 | |
|   }
 | |
| };
 | |
| 
 | |
| #pragma pack(pop)
 | |
| 
 | |
| class SFNTData::Font {
 | |
|  public:
 | |
|   Font(const OffsetTable* aOffsetTable, const uint8_t* aFontData,
 | |
|        uint32_t aDataLength)
 | |
|       : mFontData(aFontData),
 | |
|         mFirstDirEntry(
 | |
|             reinterpret_cast<const TableDirEntry*>(aOffsetTable + 1)),
 | |
|         mEndOfDirEntries(mFirstDirEntry + aOffsetTable->numTables),
 | |
|         mDataLength(aDataLength) {}
 | |
| 
 | |
|   Span<const uint8_t> GetHeadTableBytes() const {
 | |
|     const TableDirEntry* dirEntry =
 | |
|         GetDirEntry(TRUETYPE_TAG('h', 'e', 'a', 'd'));
 | |
|     if (!dirEntry) {
 | |
|       gfxWarning() << "Head table entry not found.";
 | |
|       return {};
 | |
|     }
 | |
| 
 | |
|     return {mFontData + dirEntry->offset, dirEntry->length};
 | |
|   }
 | |
| 
 | |
|   Span<const uint8_t> GetCmapTableBytes() const {
 | |
|     const TableDirEntry* dirEntry =
 | |
|         GetDirEntry(TRUETYPE_TAG('c', 'm', 'a', 'p'));
 | |
|     if (!dirEntry) {
 | |
|       gfxWarning() << "Cmap table entry not found.";
 | |
|       return {};
 | |
|     }
 | |
| 
 | |
|     return {mFontData + dirEntry->offset, dirEntry->length};
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   const TableDirEntry* GetDirEntry(const uint32_t aTag) const {
 | |
|     const TableDirEntry* foundDirEntry =
 | |
|         std::lower_bound(mFirstDirEntry, mEndOfDirEntries, aTag);
 | |
| 
 | |
|     if (foundDirEntry == mEndOfDirEntries || foundDirEntry->tag != aTag) {
 | |
|       gfxWarning() << "Font data does not contain tag.";
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     if (mDataLength < (foundDirEntry->offset + foundDirEntry->length)) {
 | |
|       gfxWarning() << "Font data too short to contain table.";
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     return foundDirEntry;
 | |
|   }
 | |
| 
 | |
|   const uint8_t* mFontData;
 | |
|   const TableDirEntry* mFirstDirEntry;
 | |
|   const TableDirEntry* mEndOfDirEntries;
 | |
|   uint32_t mDataLength;
 | |
| };
 | |
| 
 | |
| /* static */
 | |
| UniquePtr<SFNTData> SFNTData::Create(const uint8_t* aFontData,
 | |
|                                      uint32_t aDataLength) {
 | |
|   MOZ_ASSERT(aFontData);
 | |
| 
 | |
|   // Check to see if this is a font collection.
 | |
|   if (aDataLength < sizeof(TTCHeader)) {
 | |
|     gfxWarning() << "Font data too short.";
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   const TTCHeader* ttcHeader = reinterpret_cast<const TTCHeader*>(aFontData);
 | |
|   if (ttcHeader->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
 | |
|     uint32_t numFonts = ttcHeader->numFonts;
 | |
|     if (aDataLength <
 | |
|         sizeof(TTCHeader) + (numFonts * sizeof(BigEndianUint32))) {
 | |
|       gfxWarning() << "Font data too short to contain full TTC Header.";
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     UniquePtr<SFNTData> sfntData(new SFNTData);
 | |
|     const BigEndianUint32* offset =
 | |
|         reinterpret_cast<const BigEndianUint32*>(aFontData + sizeof(TTCHeader));
 | |
|     const BigEndianUint32* endOfOffsets = offset + numFonts;
 | |
|     while (offset != endOfOffsets) {
 | |
|       if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
 | |
|         return nullptr;
 | |
|       }
 | |
|       ++offset;
 | |
|     }
 | |
| 
 | |
|     return sfntData;
 | |
|   }
 | |
| 
 | |
|   UniquePtr<SFNTData> sfntData(new SFNTData);
 | |
|   if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return sfntData;
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| uint64_t SFNTData::GetUniqueKey(const uint8_t* aFontData, uint32_t aDataLength,
 | |
|                                 uint32_t aVarDataSize, const void* aVarData) {
 | |
|   uint64_t hash = 0;
 | |
|   UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aDataLength);
 | |
|   if (sfntData) {
 | |
|     hash = sfntData->HashHeadAndCmapTables();
 | |
|   } else {
 | |
|     gfxWarning() << "Failed to create SFNTData from data, hashing whole font.";
 | |
|     hash = HashBytes(aFontData, aDataLength);
 | |
|   }
 | |
| 
 | |
|   if (aVarDataSize) {
 | |
|     hash = AddToHash(hash, HashBytes(aVarData, aVarDataSize));
 | |
|   }
 | |
| 
 | |
|   return hash << 32 | aDataLength;
 | |
| }
 | |
| 
 | |
| SFNTData::~SFNTData() {
 | |
|   for (size_t i = 0; i < mFonts.length(); ++i) {
 | |
|     delete mFonts[i];
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool SFNTData::AddFont(const uint8_t* aFontData, uint32_t aDataLength,
 | |
|                        uint32_t aOffset) {
 | |
|   uint32_t remainingLength = aDataLength - aOffset;
 | |
|   if (remainingLength < sizeof(OffsetTable)) {
 | |
|     gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   const OffsetTable* offsetTable =
 | |
|       reinterpret_cast<const OffsetTable*>(aFontData + aOffset);
 | |
|   if (remainingLength <
 | |
|       sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
 | |
|     gfxWarning() << "Font data too short to contain tables.";
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return mFonts.append(new Font(offsetTable, aFontData, aDataLength));
 | |
| }
 | |
| 
 | |
| uint32_t SFNTData::HashHeadAndCmapTables() {
 | |
|   uint32_t tablesHash = std::accumulate(
 | |
|       mFonts.begin(), mFonts.end(), 0U, [](uint32_t hash, Font* font) {
 | |
|         Span<const uint8_t> headBytes = font->GetHeadTableBytes();
 | |
|         hash = AddToHash(hash, HashBytes(headBytes.data(), headBytes.size()));
 | |
|         Span<const uint8_t> cmapBytes = font->GetCmapTableBytes();
 | |
|         return AddToHash(hash, HashBytes(cmapBytes.data(), cmapBytes.size()));
 | |
|       });
 | |
| 
 | |
|   return tablesHash;
 | |
| }
 | |
| 
 | |
| }  // namespace gfx
 | |
| }  // namespace mozilla
 | 
