Line data Source code
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 "SkAutoMalloc.h"
9 : #include "SkEndian.h"
10 : #include "SkFontStream.h"
11 : #include "SkStream.h"
12 :
13 : struct SkSFNTHeader {
14 : uint32_t fVersion;
15 : uint16_t fNumTables;
16 : uint16_t fSearchRange;
17 : uint16_t fEntrySelector;
18 : uint16_t fRangeShift;
19 : };
20 :
21 : struct SkTTCFHeader {
22 : uint32_t fTag;
23 : uint32_t fVersion;
24 : uint32_t fNumOffsets;
25 : uint32_t fOffset0; // the first of N (fNumOffsets)
26 : };
27 :
28 : union SkSharedTTHeader {
29 : SkSFNTHeader fSingle;
30 : SkTTCFHeader fCollection;
31 : };
32 :
33 : struct SkSFNTDirEntry {
34 : uint32_t fTag;
35 : uint32_t fChecksum;
36 : uint32_t fOffset;
37 : uint32_t fLength;
38 : };
39 :
40 0 : static bool read(SkStream* stream, void* buffer, size_t amount) {
41 0 : return stream->read(buffer, amount) == amount;
42 : }
43 :
44 0 : static bool skip(SkStream* stream, size_t amount) {
45 0 : 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 0 : static int count_tables(SkStream* stream, int ttcIndex, size_t* offsetToDir) {
56 0 : SkASSERT(ttcIndex >= 0);
57 :
58 0 : SkAutoSMalloc<1024> storage(sizeof(SkSharedTTHeader));
59 0 : SkSharedTTHeader* header = (SkSharedTTHeader*)storage.get();
60 :
61 0 : if (!read(stream, header, sizeof(SkSharedTTHeader))) {
62 0 : return 0;
63 : }
64 :
65 : // by default, SkSFNTHeader is at the start of the stream
66 0 : size_t offset = 0;
67 :
68 : // if we're really a collection, the first 4-bytes will be 'ttcf'
69 0 : uint32_t tag = SkEndian_SwapBE32(header->fCollection.fTag);
70 0 : if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
71 0 : unsigned count = SkEndian_SwapBE32(header->fCollection.fNumOffsets);
72 0 : if ((unsigned)ttcIndex >= count) {
73 0 : return 0;
74 : }
75 :
76 0 : if (ttcIndex > 0) { // need to read more of the shared header
77 0 : stream->rewind();
78 0 : size_t amount = sizeof(SkSharedTTHeader) + ttcIndex * sizeof(uint32_t);
79 0 : header = (SkSharedTTHeader*)storage.reset(amount);
80 0 : if (!read(stream, header, amount)) {
81 0 : return 0;
82 : }
83 : }
84 : // this is the offset to the local SkSFNTHeader
85 0 : offset = SkEndian_SwapBE32((&header->fCollection.fOffset0)[ttcIndex]);
86 0 : stream->rewind();
87 0 : if (!skip(stream, offset)) {
88 0 : return 0;
89 : }
90 0 : if (!read(stream, header, sizeof(SkSFNTHeader))) {
91 0 : return 0;
92 : }
93 : }
94 :
95 0 : if (offsetToDir) {
96 : // add the size of the header, so we will point to the DirEntries
97 0 : *offsetToDir = offset + sizeof(SkSFNTHeader);
98 : }
99 0 : return SkEndian_SwapBE16(header->fSingle.fNumTables);
100 : }
101 :
102 : ///////////////////////////////////////////////////////////////////////////////
103 :
104 : struct SfntHeader {
105 0 : SfntHeader() : fCount(0), fDir(nullptr) {}
106 0 : ~SfntHeader() { 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 0 : bool init(SkStream* stream, int ttcIndex) {
115 0 : stream->rewind();
116 :
117 : size_t offsetToDir;
118 0 : fCount = count_tables(stream, ttcIndex, &offsetToDir);
119 0 : if (0 == fCount) {
120 0 : return false;
121 : }
122 :
123 0 : stream->rewind();
124 0 : if (!skip(stream, offsetToDir)) {
125 0 : return false;
126 : }
127 :
128 0 : size_t size = fCount * sizeof(SkSFNTDirEntry);
129 0 : fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size));
130 0 : return read(stream, fDir, size);
131 : }
132 :
133 : int fCount;
134 : SkSFNTDirEntry* fDir;
135 : };
136 :
137 : ///////////////////////////////////////////////////////////////////////////////
138 :
139 0 : int SkFontStream::CountTTCEntries(SkStream* stream) {
140 0 : stream->rewind();
141 :
142 : SkSharedTTHeader shared;
143 0 : if (!read(stream, &shared, sizeof(shared))) {
144 0 : return 0;
145 : }
146 :
147 : // if we're really a collection, the first 4-bytes will be 'ttcf'
148 0 : uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag);
149 0 : if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
150 0 : return SkEndian_SwapBE32(shared.fCollection.fNumOffsets);
151 : } else {
152 0 : return 1; // normal 'sfnt' has 1 dir entry
153 : }
154 : }
155 :
156 0 : int SkFontStream::GetTableTags(SkStream* stream, int ttcIndex,
157 : SkFontTableTag tags[]) {
158 0 : SfntHeader header;
159 0 : if (!header.init(stream, ttcIndex)) {
160 0 : return 0;
161 : }
162 :
163 0 : if (tags) {
164 0 : for (int i = 0; i < header.fCount; i++) {
165 0 : tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag);
166 : }
167 : }
168 0 : return header.fCount;
169 : }
170 :
171 0 : size_t SkFontStream::GetTableData(SkStream* stream, int ttcIndex,
172 : SkFontTableTag tag,
173 : size_t offset, size_t length, void* data) {
174 0 : SfntHeader header;
175 0 : if (!header.init(stream, ttcIndex)) {
176 0 : return 0;
177 : }
178 :
179 0 : for (int i = 0; i < header.fCount; i++) {
180 0 : if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
181 0 : size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset);
182 0 : size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength);
183 : // now sanity check the caller's offset/length
184 0 : if (offset >= realLength) {
185 0 : 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 0 : if (offset + length < offset) {
191 0 : return 0;
192 : }
193 0 : if (length > realLength - offset) {
194 0 : length = realLength - offset;
195 : }
196 0 : if (data) {
197 : // skip the stream to the part of the table we want to copy from
198 0 : stream->rewind();
199 0 : size_t bytesToSkip = realOffset + offset;
200 0 : if (!skip(stream, bytesToSkip)) {
201 0 : return 0;
202 : }
203 0 : if (!read(stream, data, length)) {
204 0 : return 0;
205 : }
206 : }
207 0 : return length;
208 : }
209 : }
210 0 : return 0;
211 : }
|