• 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 "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  
read(SkStream * stream,void * buffer,size_t amount)39  static bool read(SkStream* stream, void* buffer, size_t amount) {
40      return stream->read(buffer, amount) == amount;
41  }
42  
skip(SkStream * stream,size_t amount)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   */
count_tables(SkStream * stream,int ttcIndex,size_t * offsetToDir)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 {
SfntHeaderSfntHeader104      SfntHeader() : fCount(0), fDir(nullptr) {}
~SfntHeaderSfntHeader105      ~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       */
initSfntHeader113      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  
CountTTCEntries(SkStream * stream)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  
GetTableTags(SkStream * stream,int ttcIndex,SkFontTableTag tags[])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  
GetTableData(SkStream * stream,int ttcIndex,SkFontTableTag tag,size_t offset,size_t length,void * data)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  }
211