1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/core/SkFontStream.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,210 @@ 1.4 +/* 1.5 + * Copyright 2011 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "SkEndian.h" 1.12 +#include "SkFontStream.h" 1.13 +#include "SkStream.h" 1.14 + 1.15 +struct SkSFNTHeader { 1.16 + uint32_t fVersion; 1.17 + uint16_t fNumTables; 1.18 + uint16_t fSearchRange; 1.19 + uint16_t fEntrySelector; 1.20 + uint16_t fRangeShift; 1.21 +}; 1.22 + 1.23 +struct SkTTCFHeader { 1.24 + uint32_t fTag; 1.25 + uint32_t fVersion; 1.26 + uint32_t fNumOffsets; 1.27 + uint32_t fOffset0; // the first of N (fNumOffsets) 1.28 +}; 1.29 + 1.30 +union SkSharedTTHeader { 1.31 + SkSFNTHeader fSingle; 1.32 + SkTTCFHeader fCollection; 1.33 +}; 1.34 + 1.35 +struct SkSFNTDirEntry { 1.36 + uint32_t fTag; 1.37 + uint32_t fChecksum; 1.38 + uint32_t fOffset; 1.39 + uint32_t fLength; 1.40 +}; 1.41 + 1.42 +static bool read(SkStream* stream, void* buffer, size_t amount) { 1.43 + return stream->read(buffer, amount) == amount; 1.44 +} 1.45 + 1.46 +static bool skip(SkStream* stream, size_t amount) { 1.47 + return stream->skip(amount) == amount; 1.48 +} 1.49 + 1.50 +/** Return the number of tables, or if this is a TTC (collection), return the 1.51 + number of tables in the first element of the collection. In either case, 1.52 + if offsetToDir is not-null, set it to the offset to the beginning of the 1.53 + table headers (SkSFNTDirEntry), relative to the start of the stream. 1.54 + 1.55 + On an error, return 0 for number of tables, and ignore offsetToDir 1.56 + */ 1.57 +static int count_tables(SkStream* stream, int ttcIndex, size_t* offsetToDir) { 1.58 + SkASSERT(ttcIndex >= 0); 1.59 + 1.60 + SkAutoSMalloc<1024> storage(sizeof(SkSharedTTHeader)); 1.61 + SkSharedTTHeader* header = (SkSharedTTHeader*)storage.get(); 1.62 + 1.63 + if (!read(stream, header, sizeof(SkSharedTTHeader))) { 1.64 + return 0; 1.65 + } 1.66 + 1.67 + // by default, SkSFNTHeader is at the start of the stream 1.68 + size_t offset = 0; 1.69 + 1.70 + // if we're really a collection, the first 4-bytes will be 'ttcf' 1.71 + uint32_t tag = SkEndian_SwapBE32(header->fCollection.fTag); 1.72 + if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { 1.73 + unsigned count = SkEndian_SwapBE32(header->fCollection.fNumOffsets); 1.74 + if ((unsigned)ttcIndex >= count) { 1.75 + return 0; 1.76 + } 1.77 + 1.78 + if (ttcIndex > 0) { // need to read more of the shared header 1.79 + stream->rewind(); 1.80 + size_t amount = sizeof(SkSharedTTHeader) + ttcIndex * sizeof(uint32_t); 1.81 + header = (SkSharedTTHeader*)storage.reset(amount); 1.82 + if (!read(stream, header, amount)) { 1.83 + return 0; 1.84 + } 1.85 + } 1.86 + // this is the offset to the local SkSFNTHeader 1.87 + offset = SkEndian_SwapBE32((&header->fCollection.fOffset0)[ttcIndex]); 1.88 + stream->rewind(); 1.89 + if (!skip(stream, offset)) { 1.90 + return 0; 1.91 + } 1.92 + if (!read(stream, header, sizeof(SkSFNTHeader))) { 1.93 + return 0; 1.94 + } 1.95 + } 1.96 + 1.97 + if (offsetToDir) { 1.98 + // add the size of the header, so we will point to the DirEntries 1.99 + *offsetToDir = offset + sizeof(SkSFNTHeader); 1.100 + } 1.101 + return SkEndian_SwapBE16(header->fSingle.fNumTables); 1.102 +} 1.103 + 1.104 +/////////////////////////////////////////////////////////////////////////////// 1.105 + 1.106 +struct SfntHeader { 1.107 + SfntHeader() : fCount(0), fDir(NULL) {} 1.108 + ~SfntHeader() { sk_free(fDir); } 1.109 + 1.110 + /** If it returns true, then fCount and fDir are properly initialized. 1.111 + Note: fDir will point to the raw array of SkSFNTDirEntry values, 1.112 + meaning they will still be in the file's native endianness (BE). 1.113 + 1.114 + fDir will be automatically freed when this object is destroyed 1.115 + */ 1.116 + bool init(SkStream* stream, int ttcIndex) { 1.117 + stream->rewind(); 1.118 + 1.119 + size_t offsetToDir; 1.120 + fCount = count_tables(stream, ttcIndex, &offsetToDir); 1.121 + if (0 == fCount) { 1.122 + return false; 1.123 + } 1.124 + 1.125 + stream->rewind(); 1.126 + if (!skip(stream, offsetToDir)) { 1.127 + return false; 1.128 + } 1.129 + 1.130 + size_t size = fCount * sizeof(SkSFNTDirEntry); 1.131 + fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size)); 1.132 + return read(stream, fDir, size); 1.133 + } 1.134 + 1.135 + int fCount; 1.136 + SkSFNTDirEntry* fDir; 1.137 +}; 1.138 + 1.139 +/////////////////////////////////////////////////////////////////////////////// 1.140 + 1.141 +int SkFontStream::CountTTCEntries(SkStream* stream) { 1.142 + stream->rewind(); 1.143 + 1.144 + SkSharedTTHeader shared; 1.145 + if (!read(stream, &shared, sizeof(shared))) { 1.146 + return 0; 1.147 + } 1.148 + 1.149 + // if we're really a collection, the first 4-bytes will be 'ttcf' 1.150 + uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag); 1.151 + if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { 1.152 + return SkEndian_SwapBE32(shared.fCollection.fNumOffsets); 1.153 + } else { 1.154 + return 1; // normal 'sfnt' has 1 dir entry 1.155 + } 1.156 +} 1.157 + 1.158 +int SkFontStream::GetTableTags(SkStream* stream, int ttcIndex, 1.159 + SkFontTableTag tags[]) { 1.160 + SfntHeader header; 1.161 + if (!header.init(stream, ttcIndex)) { 1.162 + return 0; 1.163 + } 1.164 + 1.165 + if (tags) { 1.166 + for (int i = 0; i < header.fCount; i++) { 1.167 + tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag); 1.168 + } 1.169 + } 1.170 + return header.fCount; 1.171 +} 1.172 + 1.173 +size_t SkFontStream::GetTableData(SkStream* stream, int ttcIndex, 1.174 + SkFontTableTag tag, 1.175 + size_t offset, size_t length, void* data) { 1.176 + SfntHeader header; 1.177 + if (!header.init(stream, ttcIndex)) { 1.178 + return 0; 1.179 + } 1.180 + 1.181 + for (int i = 0; i < header.fCount; i++) { 1.182 + if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) { 1.183 + size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset); 1.184 + size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength); 1.185 + // now sanity check the caller's offset/length 1.186 + if (offset >= realLength) { 1.187 + return 0; 1.188 + } 1.189 + // if the caller is trusting the length from the file, then a 1.190 + // hostile file might choose a value which would overflow offset + 1.191 + // length. 1.192 + if (offset + length < offset) { 1.193 + return 0; 1.194 + } 1.195 + if (length > realLength - offset) { 1.196 + length = realLength - offset; 1.197 + } 1.198 + if (data) { 1.199 + // skip the stream to the part of the table we want to copy from 1.200 + stream->rewind(); 1.201 + size_t bytesToSkip = realOffset + offset; 1.202 + if (!skip(stream, bytesToSkip)) { 1.203 + return 0; 1.204 + } 1.205 + if (!read(stream, data, length)) { 1.206 + return 0; 1.207 + } 1.208 + } 1.209 + return length; 1.210 + } 1.211 + } 1.212 + return 0; 1.213 +}