1 #include "SkEndian.h"
2 #include "SkFontHost.h"
3 #include "SkStream.h"
4
5 struct SkSFNTHeader {
6 uint32_t fVersion;
7 uint16_t fNumTables;
8 uint16_t fSearchRange;
9 uint16_t fEntrySelector;
10 uint16_t fRangeShift;
11 };
12
13 struct SkTTCFHeader {
14 uint32_t fTag;
15 uint32_t fVersion;
16 uint32_t fNumOffsets;
17 uint32_t fOffset0; // the first of N (fNumOffsets)
18 };
19
20 union SkSharedTTHeader {
21 SkSFNTHeader fSingle;
22 SkTTCFHeader fCollection;
23 };
24
25 struct SkSFNTDirEntry {
26 uint32_t fTag;
27 uint32_t fChecksum;
28 uint32_t fOffset;
29 uint32_t fLength;
30 };
31
32 /** Return the number of tables, or if this is a TTC (collection), return the
33 number of tables in the first element of the collection. In either case,
34 if offsetToDir is not-null, set it to the offset to the beginning of the
35 table headers (SkSFNTDirEntry), relative to the start of the stream.
36
37 On an error, return 0 for number of tables, and ignore offsetToDir
38 */
count_tables(SkStream * stream,size_t * offsetToDir=NULL)39 static int count_tables(SkStream* stream, size_t* offsetToDir = NULL) {
40 SkSharedTTHeader shared;
41 if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) {
42 return 0;
43 }
44
45 // by default, SkSFNTHeader is at the start of the stream
46 size_t offset = 0;
47
48 // if we're really a collection, the first 4-bytes will be 'ttcf'
49 uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag);
50 if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
51 if (shared.fCollection.fNumOffsets == 0) {
52 return 0;
53 }
54 // this is the offset to the first local SkSFNTHeader
55 offset = SkEndian_SwapBE32(shared.fCollection.fOffset0);
56 stream->rewind();
57 if (stream->skip(offset) != offset) {
58 return 0;
59 }
60 if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) {
61 return 0;
62 }
63 }
64
65 if (offsetToDir) {
66 // add the size of the header, so we will point to the DirEntries
67 *offsetToDir = offset + sizeof(SkSFNTHeader);
68 }
69 return SkEndian_SwapBE16(shared.fSingle.fNumTables);
70 }
71
72 ///////////////////////////////////////////////////////////////////////////////
73
74 struct SfntHeader {
SfntHeaderSfntHeader75 SfntHeader() : fCount(0), fDir(NULL) {}
~SfntHeaderSfntHeader76 ~SfntHeader() { sk_free(fDir); }
77
78 /** If it returns true, then fCount and fDir are properly initialized.
79 Note: fDir will point to the raw array of SkSFNTDirEntry values,
80 meaning they will still be in the file's native endianness (BE).
81
82 fDir will be automatically freed when this object is destroyed
83 */
initSfntHeader84 bool init(SkStream* stream) {
85 size_t offsetToDir;
86 fCount = count_tables(stream, &offsetToDir);
87 if (0 == fCount) {
88 return false;
89 }
90
91 stream->rewind();
92 if (stream->skip(offsetToDir) != offsetToDir) {
93 return false;
94 }
95
96 size_t size = fCount * sizeof(SkSFNTDirEntry);
97 fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size));
98 return stream->read(fDir, size) == size;
99 }
100
101 int fCount;
102 SkSFNTDirEntry* fDir;
103 };
104
105 ///////////////////////////////////////////////////////////////////////////////
106
CountTables(SkFontID fontID)107 int SkFontHost::CountTables(SkFontID fontID) {
108 SkStream* stream = SkFontHost::OpenStream(fontID);
109 if (NULL == stream) {
110 return 0;
111 }
112
113 SkAutoUnref au(stream);
114 return count_tables(stream);
115 }
116
GetTableTags(SkFontID fontID,SkFontTableTag tags[])117 int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
118 SkStream* stream = SkFontHost::OpenStream(fontID);
119 if (NULL == stream) {
120 return 0;
121 }
122
123 SkAutoUnref au(stream);
124 SfntHeader header;
125 if (!header.init(stream)) {
126 return 0;
127 }
128
129 for (int i = 0; i < header.fCount; i++) {
130 tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag);
131 }
132 return header.fCount;
133 }
134
GetTableSize(SkFontID fontID,SkFontTableTag tag)135 size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
136 SkStream* stream = SkFontHost::OpenStream(fontID);
137 if (NULL == stream) {
138 return 0;
139 }
140
141 SkAutoUnref au(stream);
142 SfntHeader header;
143 if (!header.init(stream)) {
144 return 0;
145 }
146
147 for (int i = 0; i < header.fCount; i++) {
148 if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
149 return SkEndian_SwapBE32(header.fDir[i].fLength);
150 }
151 }
152 return 0;
153 }
154
GetTableData(SkFontID fontID,SkFontTableTag tag,size_t offset,size_t length,void * data)155 size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
156 size_t offset, size_t length, void* data) {
157 SkStream* stream = SkFontHost::OpenStream(fontID);
158 if (NULL == stream) {
159 return 0;
160 }
161
162 SkAutoUnref au(stream);
163 SfntHeader header;
164 if (!header.init(stream)) {
165 return 0;
166 }
167
168 for (int i = 0; i < header.fCount; i++) {
169 if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
170 size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset);
171 size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength);
172 // now sanity check the caller's offset/length
173 if (offset >= realLength) {
174 return 0;
175 }
176 // if the caller is trusting the length from the file, then a
177 // hostile file might choose a value which would overflow offset +
178 // length.
179 if (offset + length < offset) {
180 return 0;
181 }
182 if (offset + length > realLength) {
183 length = realLength - offset;
184 }
185 // skip the stream to the part of the table we want to copy from
186 stream->rewind();
187 size_t bytesToSkip = realOffset + offset;
188 if (stream->skip(bytesToSkip) != bytesToSkip) {
189 return 0;
190 }
191 if (stream->read(data, length) != length) {
192 return 0;
193 }
194 return length;
195 }
196 }
197 return 0;
198 }
199
200