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