| /* | 
 |  * Copyright 2011 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "include/core/SkStream.h" | 
 | #include "src/base/SkAutoMalloc.h" | 
 | #include "src/base/SkEndian.h" | 
 | #include "src/core/SkFontStream.h" | 
 |  | 
 | struct SkSFNTHeader { | 
 |     uint32_t    fVersion; | 
 |     uint16_t    fNumTables; | 
 |     uint16_t    fSearchRange; | 
 |     uint16_t    fEntrySelector; | 
 |     uint16_t    fRangeShift; | 
 | }; | 
 |  | 
 | struct SkTTCFHeader { | 
 |     uint32_t    fTag; | 
 |     uint32_t    fVersion; | 
 |     uint32_t    fNumOffsets; | 
 |     uint32_t    fOffset0;   // the first of N (fNumOffsets) | 
 | }; | 
 |  | 
 | union SkSharedTTHeader { | 
 |     SkSFNTHeader    fSingle; | 
 |     SkTTCFHeader    fCollection; | 
 | }; | 
 |  | 
 | struct SkSFNTDirEntry { | 
 |     uint32_t    fTag; | 
 |     uint32_t    fChecksum; | 
 |     uint32_t    fOffset; | 
 |     uint32_t    fLength; | 
 | }; | 
 |  | 
 | static bool read(SkStream* stream, void* buffer, size_t amount) { | 
 |     return stream->read(buffer, amount) == amount; | 
 | } | 
 |  | 
 | static bool skip(SkStream* stream, size_t amount) { | 
 |     return stream->skip(amount) == amount; | 
 | } | 
 |  | 
 | /** Return the number of tables, or if this is a TTC (collection), return the | 
 |     number of tables in the first element of the collection. In either case, | 
 |     if offsetToDir is not-null, set it to the offset to the beginning of the | 
 |     table headers (SkSFNTDirEntry), relative to the start of the stream. | 
 |  | 
 |     On an error, return 0 for number of tables, and ignore offsetToDir | 
 |  */ | 
 | static int count_tables(SkStream* stream, int ttcIndex, size_t* offsetToDir) { | 
 |     SkASSERT(ttcIndex >= 0); | 
 |  | 
 |     SkAutoSMalloc<1024> storage(sizeof(SkSharedTTHeader)); | 
 |     SkSharedTTHeader* header = (SkSharedTTHeader*)storage.get(); | 
 |  | 
 |     if (!read(stream, header, sizeof(SkSharedTTHeader))) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     // by default, SkSFNTHeader is at the start of the stream | 
 |     size_t offset = 0; | 
 |  | 
 |     // if we're really a collection, the first 4-bytes will be 'ttcf' | 
 |     uint32_t tag = SkEndian_SwapBE32(header->fCollection.fTag); | 
 |     if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { | 
 |         unsigned count = SkEndian_SwapBE32(header->fCollection.fNumOffsets); | 
 |         if ((unsigned)ttcIndex >= count) { | 
 |             return 0; | 
 |         } | 
 |  | 
 |         if (ttcIndex > 0) { // need to read more of the shared header | 
 |             stream->rewind(); | 
 |             size_t amount = sizeof(SkSharedTTHeader) + ttcIndex * sizeof(uint32_t); | 
 |             header = (SkSharedTTHeader*)storage.reset(amount); | 
 |             if (!read(stream, header, amount)) { | 
 |                 return 0; | 
 |             } | 
 |         } | 
 |         // this is the offset to the local SkSFNTHeader | 
 |         offset = SkEndian_SwapBE32((&header->fCollection.fOffset0)[ttcIndex]); | 
 |         stream->rewind(); | 
 |         if (!skip(stream, offset)) { | 
 |             return 0; | 
 |         } | 
 |         if (!read(stream, header, sizeof(SkSFNTHeader))) { | 
 |             return 0; | 
 |         } | 
 |     } | 
 |  | 
 |     if (offsetToDir) { | 
 |         // add the size of the header, so we will point to the DirEntries | 
 |         *offsetToDir = offset + sizeof(SkSFNTHeader); | 
 |     } | 
 |     return SkEndian_SwapBE16(header->fSingle.fNumTables); | 
 | } | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | struct SfntHeader { | 
 |     SfntHeader() : fCount(0), fDir(nullptr) {} | 
 |     ~SfntHeader() { sk_free(fDir); } | 
 |  | 
 |     /** If it returns true, then fCount and fDir are properly initialized. | 
 |         Note: fDir will point to the raw array of SkSFNTDirEntry values, | 
 |         meaning they will still be in the file's native endianness (BE). | 
 |  | 
 |         fDir will be automatically freed when this object is destroyed | 
 |      */ | 
 |     bool init(SkStream* stream, int ttcIndex) { | 
 |         stream->rewind(); | 
 |  | 
 |         size_t offsetToDir; | 
 |         fCount = count_tables(stream, ttcIndex, &offsetToDir); | 
 |         if (0 == fCount) { | 
 |             return false; | 
 |         } | 
 |  | 
 |         stream->rewind(); | 
 |         if (!skip(stream, offsetToDir)) { | 
 |             return false; | 
 |         } | 
 |  | 
 |         size_t size = fCount * sizeof(SkSFNTDirEntry); | 
 |         fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size)); | 
 |         return read(stream, fDir, size); | 
 |     } | 
 |  | 
 |     int             fCount; | 
 |     SkSFNTDirEntry* fDir; | 
 | }; | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | int SkFontStream::CountTTCEntries(SkStream* stream) { | 
 |     stream->rewind(); | 
 |  | 
 |     SkSharedTTHeader shared; | 
 |     if (!read(stream, &shared, sizeof(shared))) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     // if we're really a collection, the first 4-bytes will be 'ttcf' | 
 |     uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag); | 
 |     if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { | 
 |         return SkEndian_SwapBE32(shared.fCollection.fNumOffsets); | 
 |     } else { | 
 |         return 1;   // normal 'sfnt' has 1 dir entry | 
 |     } | 
 | } | 
 |  | 
 | int SkFontStream::GetTableTags(SkStream* stream, int ttcIndex, | 
 |                                SkFontTableTag tags[]) { | 
 |     SfntHeader  header; | 
 |     if (!header.init(stream, ttcIndex)) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     if (tags) { | 
 |         for (int i = 0; i < header.fCount; i++) { | 
 |             tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag); | 
 |         } | 
 |     } | 
 |     return header.fCount; | 
 | } | 
 |  | 
 | size_t SkFontStream::GetTableData(SkStream* stream, int ttcIndex, | 
 |                                   SkFontTableTag tag, | 
 |                                   size_t offset, size_t length, void* data) { | 
 |     SfntHeader  header; | 
 |     if (!header.init(stream, ttcIndex)) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     for (int i = 0; i < header.fCount; i++) { | 
 |         if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) { | 
 |             size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset); | 
 |             size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength); | 
 |             if (offset >= realLength) { | 
 |                 // invalid | 
 |                 return 0; | 
 |             } | 
 |             // if the caller is trusting the length from the file, then a | 
 |             // hostile file might choose a value which would overflow offset + | 
 |             // length. | 
 |             if (offset + length < offset) { | 
 |                 return 0; | 
 |             } | 
 |             if (length > realLength - offset) { | 
 |                 length = realLength - offset; | 
 |             } | 
 |             if (data) { | 
 |                 // skip the stream to the part of the table we want to copy from | 
 |                 stream->rewind(); | 
 |                 size_t bytesToSkip = realOffset + offset; | 
 |                 if (!skip(stream, bytesToSkip)) { | 
 |                     return 0; | 
 |                 } | 
 |                 if (!read(stream, data, length)) { | 
 |                     return 0; | 
 |                 } | 
 |             } | 
 |             return length; | 
 |         } | 
 |     } | 
 |     return 0; | 
 | } |