tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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