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/. */
12 #include "BigEndianInts.h"
14 #include "mozilla/HashFunctions.h"
15 #include "mozilla/Span.h"
20 #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
25 BigEndianUint32 ttcTag
; // Always 'ttcf'
26 BigEndianUint32 version
; // Fixed, 0x00010000
27 BigEndianUint32 numFonts
;
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.
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.
44 friend bool operator<(const TableDirEntry
& lhs
, const uint32_t aTag
) {
45 return lhs
.tag
< aTag
;
51 class SFNTData::Font
{
53 Font(const OffsetTable
* aOffsetTable
, const uint8_t* aFontData
,
55 : mFontData(aFontData
),
57 reinterpret_cast<const TableDirEntry
*>(aOffsetTable
+ 1)),
58 mEndOfDirEntries(mFirstDirEntry
+ aOffsetTable
->numTables
),
59 mDataLength(aDataLength
) {}
61 Span
<const uint8_t> GetHeadTableBytes() const {
62 const TableDirEntry
* dirEntry
=
63 GetDirEntry(TRUETYPE_TAG('h', 'e', 'a', 'd'));
65 gfxWarning() << "Head table entry not found.";
69 return {mFontData
+ dirEntry
->offset
, dirEntry
->length
};
72 Span
<const uint8_t> GetCmapTableBytes() const {
73 const TableDirEntry
* dirEntry
=
74 GetDirEntry(TRUETYPE_TAG('c', 'm', 'a', 'p'));
76 gfxWarning() << "Cmap table entry not found.";
80 return {mFontData
+ dirEntry
->offset
, dirEntry
->length
};
84 const TableDirEntry
* GetDirEntry(const uint32_t aTag
) const {
85 const TableDirEntry
* foundDirEntry
=
86 std::lower_bound(mFirstDirEntry
, mEndOfDirEntries
, aTag
);
88 if (foundDirEntry
== mEndOfDirEntries
|| foundDirEntry
->tag
!= aTag
) {
89 gfxWarning() << "Font data does not contain tag.";
93 if (mDataLength
< (foundDirEntry
->offset
+ foundDirEntry
->length
)) {
94 gfxWarning() << "Font data too short to contain table.";
101 const uint8_t* mFontData
;
102 const TableDirEntry
* mFirstDirEntry
;
103 const TableDirEntry
* mEndOfDirEntries
;
104 uint32_t mDataLength
;
108 UniquePtr
<SFNTData
> SFNTData::Create(const uint8_t* aFontData
,
109 uint32_t aDataLength
) {
110 MOZ_ASSERT(aFontData
);
112 // Check to see if this is a font collection.
113 if (aDataLength
< sizeof(TTCHeader
)) {
114 gfxWarning() << "Font data too short.";
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
;
122 sizeof(TTCHeader
) + (numFonts
* sizeof(BigEndianUint32
))) {
123 gfxWarning() << "Font data too short to contain full TTC Header.";
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
)) {
141 UniquePtr
<SFNTData
> sfntData(new SFNTData
);
142 if (!sfntData
->AddFont(aFontData
, aDataLength
, 0)) {
150 uint64_t SFNTData::GetUniqueKey(const uint8_t* aFontData
, uint32_t aDataLength
,
151 uint32_t aVarDataSize
, const void* aVarData
) {
153 UniquePtr
<SFNTData
> sfntData
= SFNTData::Create(aFontData
, aDataLength
);
155 hash
= sfntData
->HashHeadAndCmapTables();
157 gfxWarning() << "Failed to create SFNTData from data, hashing whole font.";
158 hash
= HashBytes(aFontData
, aDataLength
);
162 hash
= AddToHash(hash
, HashBytes(aVarData
, aVarDataSize
));
165 return hash
<< 32 | aDataLength
;
168 SFNTData::~SFNTData() {
169 for (size_t i
= 0; i
< mFonts
.length(); ++i
) {
174 bool SFNTData::AddFont(const uint8_t* aFontData
, uint32_t aDataLength
,
176 uint32_t remainingLength
= aDataLength
- aOffset
;
177 if (remainingLength
< sizeof(OffsetTable
)) {
178 gfxWarning() << "Font data too short to contain OffsetTable " << aOffset
;
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.";
190 return mFonts
.append(new Font(offsetTable
, aFontData
, aDataLength
));
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()));
206 } // namespace mozilla