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