gfx/skia/trunk/src/core/SkFontStream.cpp

changeset 0
6474c204b198
     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 +}

mercurial