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