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