2016-01-05 13:08:56 +03:00
|
|
|
/* -*- 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 "BigEndianInts.h"
|
|
|
|
#include "Logging.h"
|
2016-01-05 13:08:58 +03:00
|
|
|
#include "mozilla/HashFunctions.h"
|
2016-01-05 13:08:56 +03:00
|
|
|
#include "SFNTNameTable.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) {}
|
|
|
|
|
|
|
|
bool GetU16FullName(mozilla::u16string& aU16FullName) {
|
|
|
|
const TableDirEntry* dirEntry =
|
|
|
|
GetDirEntry(TRUETYPE_TAG('n', 'a', 'm', 'e'));
|
|
|
|
if (!dirEntry) {
|
|
|
|
gfxWarning() << "Name table entry not found.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
UniquePtr<SFNTNameTable> nameTable =
|
|
|
|
SFNTNameTable::Create((mFontData + dirEntry->offset), dirEntry->length);
|
|
|
|
if (!nameTable) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nameTable->GetU16FullName(aU16FullName);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const TableDirEntry* GetDirEntry(const uint32_t aTag) {
|
|
|
|
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)) {
|
2016-04-22 15:23:25 +03:00
|
|
|
gfxWarning() << "Font data too short.";
|
2016-01-05 13:08:58 +03:00
|
|
|
return nullptr;
|
2016-01-05 13:08:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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))) {
|
2016-04-22 15:23:25 +03:00
|
|
|
gfxWarning() << "Font data too short to contain full TTC Header.";
|
2016-01-05 13:08:58 +03:00
|
|
|
return nullptr;
|
2016-01-05 13:08:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-06-01 18:59:07 +03:00
|
|
|
return sfntData;
|
2016-01-05 13:08:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
UniquePtr<SFNTData> sfntData(new SFNTData);
|
|
|
|
if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-06-01 18:59:07 +03:00
|
|
|
return sfntData;
|
2016-01-05 13:08:56 +03:00
|
|
|
}
|
|
|
|
|
2016-01-05 13:08:58 +03:00
|
|
|
/* static */
|
2017-01-06 19:35:12 +03:00
|
|
|
uint64_t SFNTData::GetUniqueKey(const uint8_t* aFontData, uint32_t aDataLength,
|
|
|
|
uint32_t aVarDataSize, const void* aVarData) {
|
2016-01-05 13:08:58 +03:00
|
|
|
uint64_t hash;
|
|
|
|
UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aDataLength);
|
|
|
|
mozilla::u16string firstName;
|
|
|
|
if (sfntData && sfntData->GetU16FullName(0, firstName)) {
|
|
|
|
hash = HashString(firstName.c_str(), firstName.length());
|
|
|
|
} else {
|
|
|
|
gfxWarning() << "Failed to get name from font data hashing whole font.";
|
|
|
|
hash = HashString(aFontData, aDataLength);
|
|
|
|
}
|
|
|
|
|
2017-01-06 19:35:12 +03:00
|
|
|
if (aVarDataSize) {
|
|
|
|
hash = AddToHash(hash, HashBytes(aVarData, aVarDataSize));
|
|
|
|
}
|
|
|
|
|
2016-01-05 13:08:58 +03:00
|
|
|
return hash << 32 | aDataLength;
|
2018-11-30 13:46:48 +03:00
|
|
|
;
|
2016-01-05 13:08:58 +03:00
|
|
|
}
|
|
|
|
|
2016-01-05 13:08:56 +03:00
|
|
|
SFNTData::~SFNTData() {
|
|
|
|
for (size_t i = 0; i < mFonts.length(); ++i) {
|
|
|
|
delete mFonts[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SFNTData::GetU16FullName(uint32_t aIndex,
|
|
|
|
mozilla::u16string& aU16FullName) {
|
|
|
|
if (aIndex >= mFonts.length()) {
|
|
|
|
gfxWarning() << "aIndex to font data too high.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mFonts[aIndex]->GetU16FullName(aU16FullName);
|
|
|
|
}
|
|
|
|
|
2016-01-05 13:08:58 +03:00
|
|
|
bool SFNTData::GetU16FullNames(Vector<mozilla::u16string>& aU16FullNames) {
|
|
|
|
bool fontFound = false;
|
|
|
|
for (size_t i = 0; i < mFonts.length(); ++i) {
|
|
|
|
mozilla::u16string name;
|
|
|
|
if (mFonts[i]->GetU16FullName(name)) {
|
|
|
|
fontFound = true;
|
|
|
|
}
|
2018-05-30 22:15:35 +03:00
|
|
|
if (!aU16FullNames.append(std::move(name))) {
|
2016-01-14 17:19:16 +03:00
|
|
|
return false;
|
|
|
|
}
|
2016-01-05 13:08:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return fontFound;
|
|
|
|
}
|
|
|
|
|
2016-01-05 13:08:56 +03:00
|
|
|
bool SFNTData::GetIndexForU16Name(const mozilla::u16string& aU16FullName,
|
2016-04-22 15:23:25 +03:00
|
|
|
uint32_t* aIndex, size_t aTruncatedLen) {
|
2016-01-05 13:08:56 +03:00
|
|
|
for (size_t i = 0; i < mFonts.length(); ++i) {
|
|
|
|
mozilla::u16string name;
|
2016-04-22 15:23:25 +03:00
|
|
|
if (!mFonts[i]->GetU16FullName(name)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aTruncatedLen) {
|
|
|
|
MOZ_ASSERT(aU16FullName.length() <= aTruncatedLen);
|
|
|
|
name = name.substr(0, aTruncatedLen);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (name == aU16FullName) {
|
2016-01-05 13:08:56 +03:00
|
|
|
*aIndex = i;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SFNTData::AddFont(const uint8_t* aFontData, uint32_t aDataLength,
|
|
|
|
uint32_t aOffset) {
|
|
|
|
uint32_t remainingLength = aDataLength - aOffset;
|
|
|
|
if (remainingLength < sizeof(OffsetTable)) {
|
2016-04-22 15:23:25 +03:00
|
|
|
gfxWarning() << "Font data too short to contain OffsetTable " << aOffset;
|
2016-01-05 13:08:56 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const OffsetTable* offsetTable =
|
|
|
|
reinterpret_cast<const OffsetTable*>(aFontData + aOffset);
|
|
|
|
if (remainingLength <
|
|
|
|
sizeof(OffsetTable) + (offsetTable->numTables * sizeof(TableDirEntry))) {
|
2016-04-22 15:23:25 +03:00
|
|
|
gfxWarning() << "Font data too short to contain tables.";
|
2016-01-05 13:08:56 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mFonts.append(new Font(offsetTable, aFontData, aDataLength));
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace gfx
|
|
|
|
} // namespace mozilla
|