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