16ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com/*
26ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com * Copyright 2011 Google Inc.
36ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com *
46ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com * Use of this source code is governed by a BSD-style license that can be
56ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com * found in the LICENSE file.
66ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com */
76ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
86ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com#include "SkEndian.h"
96ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com#include "SkFontStream.h"
106ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com#include "SkStream.h"
116ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
126ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.comstruct SkSFNTHeader {
136ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fVersion;
146ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint16_t    fNumTables;
156ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint16_t    fSearchRange;
166ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint16_t    fEntrySelector;
176ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint16_t    fRangeShift;
186ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com};
196ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
206ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.comstruct SkTTCFHeader {
216ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fTag;
226ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fVersion;
236ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fNumOffsets;
246ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fOffset0;   // the first of N (fNumOffsets)
256ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com};
266ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
276ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.comunion SkSharedTTHeader {
286ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    SkSFNTHeader    fSingle;
296ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    SkTTCFHeader    fCollection;
306ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com};
316ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
326ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.comstruct SkSFNTDirEntry {
336ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fTag;
346ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fChecksum;
356ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fOffset;
366ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    uint32_t    fLength;
376ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com};
386ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
3961f70b4478fea5d7d805480227536d8a8e633426reed@google.comstatic bool read(SkStream* stream, void* buffer, size_t amount) {
4061f70b4478fea5d7d805480227536d8a8e633426reed@google.com    return stream->read(buffer, amount) == amount;
4161f70b4478fea5d7d805480227536d8a8e633426reed@google.com}
4261f70b4478fea5d7d805480227536d8a8e633426reed@google.com
4361f70b4478fea5d7d805480227536d8a8e633426reed@google.comstatic bool skip(SkStream* stream, size_t amount) {
4461f70b4478fea5d7d805480227536d8a8e633426reed@google.com    return stream->skip(amount) == amount;
4561f70b4478fea5d7d805480227536d8a8e633426reed@google.com}
4661f70b4478fea5d7d805480227536d8a8e633426reed@google.com
476ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com/** Return the number of tables, or if this is a TTC (collection), return the
486ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    number of tables in the first element of the collection. In either case,
496ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    if offsetToDir is not-null, set it to the offset to the beginning of the
506ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    table headers (SkSFNTDirEntry), relative to the start of the stream.
516ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
526ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    On an error, return 0 for number of tables, and ignore offsetToDir
536ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com */
545bfc8396228b7a9e5be679aeb10f30860adf938freed@google.comstatic int count_tables(SkStream* stream, int ttcIndex, size_t* offsetToDir) {
555bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    SkASSERT(ttcIndex >= 0);
565bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com
5761f70b4478fea5d7d805480227536d8a8e633426reed@google.com    SkAutoSMalloc<1024> storage(sizeof(SkSharedTTHeader));
5861f70b4478fea5d7d805480227536d8a8e633426reed@google.com    SkSharedTTHeader* header = (SkSharedTTHeader*)storage.get();
5961f70b4478fea5d7d805480227536d8a8e633426reed@google.com
6061f70b4478fea5d7d805480227536d8a8e633426reed@google.com    if (!read(stream, header, sizeof(SkSharedTTHeader))) {
616ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        return 0;
626ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    }
636ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
646ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    // by default, SkSFNTHeader is at the start of the stream
656ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    size_t offset = 0;
666ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
676ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    // if we're really a collection, the first 4-bytes will be 'ttcf'
6861f70b4478fea5d7d805480227536d8a8e633426reed@google.com    uint32_t tag = SkEndian_SwapBE32(header->fCollection.fTag);
696ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
7061f70b4478fea5d7d805480227536d8a8e633426reed@google.com        unsigned count = SkEndian_SwapBE32(header->fCollection.fNumOffsets);
715bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com        if ((unsigned)ttcIndex >= count) {
726ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            return 0;
736ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        }
7464334352cc3f29f52dfa07225d65eb218d2fd830skia.committer@gmail.com
755bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com        if (ttcIndex > 0) { // need to read more of the shared header
765bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com            stream->rewind();
7761f70b4478fea5d7d805480227536d8a8e633426reed@google.com            size_t amount = sizeof(SkSharedTTHeader) + ttcIndex * sizeof(uint32_t);
7861f70b4478fea5d7d805480227536d8a8e633426reed@google.com            header = (SkSharedTTHeader*)storage.reset(amount);
7961f70b4478fea5d7d805480227536d8a8e633426reed@google.com            if (!read(stream, header, amount)) {
805bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com                return 0;
815bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com            }
825bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com        }
835bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com        // this is the offset to the local SkSFNTHeader
8461f70b4478fea5d7d805480227536d8a8e633426reed@google.com        offset = SkEndian_SwapBE32((&header->fCollection.fOffset0)[ttcIndex]);
856ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        stream->rewind();
8661f70b4478fea5d7d805480227536d8a8e633426reed@google.com        if (!skip(stream, offset)) {
876ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            return 0;
886ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        }
8961f70b4478fea5d7d805480227536d8a8e633426reed@google.com        if (!read(stream, header, sizeof(SkSFNTHeader))) {
906ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            return 0;
916ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        }
926ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    }
936ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
946ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    if (offsetToDir) {
956ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        // add the size of the header, so we will point to the DirEntries
966ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        *offsetToDir = offset + sizeof(SkSFNTHeader);
976ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    }
9861f70b4478fea5d7d805480227536d8a8e633426reed@google.com    return SkEndian_SwapBE16(header->fSingle.fNumTables);
996ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com}
1006ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1016ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com///////////////////////////////////////////////////////////////////////////////
1026ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1036ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.comstruct SfntHeader {
1046ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    SfntHeader() : fCount(0), fDir(NULL) {}
1056ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    ~SfntHeader() { sk_free(fDir); }
1066ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1076ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    /** If it returns true, then fCount and fDir are properly initialized.
1086ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        Note: fDir will point to the raw array of SkSFNTDirEntry values,
1096ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        meaning they will still be in the file's native endianness (BE).
1106ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1116ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        fDir will be automatically freed when this object is destroyed
1126ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com     */
1135bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    bool init(SkStream* stream, int ttcIndex) {
11490ee4488e9c6b8ec4cb1137250fed43b5919ce2creed@google.com        stream->rewind();
11590ee4488e9c6b8ec4cb1137250fed43b5919ce2creed@google.com
1166ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        size_t offsetToDir;
1175bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com        fCount = count_tables(stream, ttcIndex, &offsetToDir);
1186ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        if (0 == fCount) {
1196ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            return false;
1206ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        }
1216ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1226ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        stream->rewind();
123ed268bfed3205347a90557c5029f37e90cc01956reed@google.com        if (!skip(stream, offsetToDir)) {
1246ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            return false;
1256ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        }
1266ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1276ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        size_t size = fCount * sizeof(SkSFNTDirEntry);
1286ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size));
129ed268bfed3205347a90557c5029f37e90cc01956reed@google.com        return read(stream, fDir, size);
1306ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    }
1316ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1326ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    int             fCount;
1336ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    SkSFNTDirEntry* fDir;
1346ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com};
1356ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1366ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com///////////////////////////////////////////////////////////////////////////////
1376ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1385bfc8396228b7a9e5be679aeb10f30860adf938freed@google.comint SkFontStream::CountTTCEntries(SkStream* stream) {
1395bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    stream->rewind();
1405bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com
1415bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    SkSharedTTHeader shared;
142ed268bfed3205347a90557c5029f37e90cc01956reed@google.com    if (!read(stream, &shared, sizeof(shared))) {
1435bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com        return 0;
1445bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    }
1455bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com
1465bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    // if we're really a collection, the first 4-bytes will be 'ttcf'
1475bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag);
1485bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
1495bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com        return SkEndian_SwapBE32(shared.fCollection.fNumOffsets);
1505bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    } else {
151ed268bfed3205347a90557c5029f37e90cc01956reed@google.com        return 1;   // normal 'sfnt' has 1 dir entry
15264334352cc3f29f52dfa07225d65eb218d2fd830skia.committer@gmail.com    }
1535bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com}
1545bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com
1555bfc8396228b7a9e5be679aeb10f30860adf938freed@google.comint SkFontStream::GetTableTags(SkStream* stream, int ttcIndex,
1565bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com                               SkFontTableTag tags[]) {
1576ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    SfntHeader  header;
1585bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    if (!header.init(stream, ttcIndex)) {
1596ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        return 0;
1606ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    }
1616ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1626ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    if (tags) {
1636ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        for (int i = 0; i < header.fCount; i++) {
1646ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag);
1656ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        }
1666ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    }
1676ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    return header.fCount;
1686ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com}
1696ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1705bfc8396228b7a9e5be679aeb10f30860adf938freed@google.comsize_t SkFontStream::GetTableData(SkStream* stream, int ttcIndex,
1715bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com                                  SkFontTableTag tag,
1726ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                                  size_t offset, size_t length, void* data) {
1736ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    SfntHeader  header;
1745bfc8396228b7a9e5be679aeb10f30860adf938freed@google.com    if (!header.init(stream, ttcIndex)) {
1756ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        return 0;
1766ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    }
1776ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com
1786ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    for (int i = 0; i < header.fCount; i++) {
1796ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
1806ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset);
1816ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength);
1826ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            // now sanity check the caller's offset/length
1836ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            if (offset >= realLength) {
1846ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                return 0;
1856ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            }
1866ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            // if the caller is trusting the length from the file, then a
1876ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            // hostile file might choose a value which would overflow offset +
1886ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            // length.
1896ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            if (offset + length < offset) {
1906ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                return 0;
1916ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            }
1926ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            if (length > realLength - offset) {
1936ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                length = realLength - offset;
1946ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            }
1956ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            if (data) {
1966ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                // skip the stream to the part of the table we want to copy from
1976ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                stream->rewind();
1986ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                size_t bytesToSkip = realOffset + offset;
1998fff39804398cbf33b77ff7061391d47d6090e4freed@google.com                if (!skip(stream, bytesToSkip)) {
2006ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                    return 0;
2016ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                }
202ed268bfed3205347a90557c5029f37e90cc01956reed@google.com                if (!read(stream, data, length)) {
2036ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                    return 0;
2046ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com                }
2056ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            }
2066ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com            return length;
2076ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com        }
2086ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    }
2096ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com    return 0;
2106ec97b6e4bb19b2f1aab4b21a41f482d46234089reed@google.com}
211