1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2011 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkStream.h"
9cb93a386Sopenharmony_ci#include "src/core/SkAutoMalloc.h"
10cb93a386Sopenharmony_ci#include "src/core/SkEndian.h"
11cb93a386Sopenharmony_ci#include "src/core/SkFontStream.h"
12cb93a386Sopenharmony_ci
13cb93a386Sopenharmony_cistruct SkSFNTHeader {
14cb93a386Sopenharmony_ci    uint32_t    fVersion;
15cb93a386Sopenharmony_ci    uint16_t    fNumTables;
16cb93a386Sopenharmony_ci    uint16_t    fSearchRange;
17cb93a386Sopenharmony_ci    uint16_t    fEntrySelector;
18cb93a386Sopenharmony_ci    uint16_t    fRangeShift;
19cb93a386Sopenharmony_ci};
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_cistruct SkTTCFHeader {
22cb93a386Sopenharmony_ci    uint32_t    fTag;
23cb93a386Sopenharmony_ci    uint32_t    fVersion;
24cb93a386Sopenharmony_ci    uint32_t    fNumOffsets;
25cb93a386Sopenharmony_ci    uint32_t    fOffset0;   // the first of N (fNumOffsets)
26cb93a386Sopenharmony_ci};
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ciunion SkSharedTTHeader {
29cb93a386Sopenharmony_ci    SkSFNTHeader    fSingle;
30cb93a386Sopenharmony_ci    SkTTCFHeader    fCollection;
31cb93a386Sopenharmony_ci};
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_cistruct SkSFNTDirEntry {
34cb93a386Sopenharmony_ci    uint32_t    fTag;
35cb93a386Sopenharmony_ci    uint32_t    fChecksum;
36cb93a386Sopenharmony_ci    uint32_t    fOffset;
37cb93a386Sopenharmony_ci    uint32_t    fLength;
38cb93a386Sopenharmony_ci};
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_cistatic bool read(SkStream* stream, void* buffer, size_t amount) {
41cb93a386Sopenharmony_ci    return stream->read(buffer, amount) == amount;
42cb93a386Sopenharmony_ci}
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_cistatic bool skip(SkStream* stream, size_t amount) {
45cb93a386Sopenharmony_ci    return stream->skip(amount) == amount;
46cb93a386Sopenharmony_ci}
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci/** Return the number of tables, or if this is a TTC (collection), return the
49cb93a386Sopenharmony_ci    number of tables in the first element of the collection. In either case,
50cb93a386Sopenharmony_ci    if offsetToDir is not-null, set it to the offset to the beginning of the
51cb93a386Sopenharmony_ci    table headers (SkSFNTDirEntry), relative to the start of the stream.
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ci    On an error, return 0 for number of tables, and ignore offsetToDir
54cb93a386Sopenharmony_ci */
55cb93a386Sopenharmony_cistatic int count_tables(SkStream* stream, int ttcIndex, size_t* offsetToDir) {
56cb93a386Sopenharmony_ci    SkASSERT(ttcIndex >= 0);
57cb93a386Sopenharmony_ci
58cb93a386Sopenharmony_ci    SkAutoSMalloc<1024> storage(sizeof(SkSharedTTHeader));
59cb93a386Sopenharmony_ci    SkSharedTTHeader* header = (SkSharedTTHeader*)storage.get();
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    if (!read(stream, header, sizeof(SkSharedTTHeader))) {
62cb93a386Sopenharmony_ci        return 0;
63cb93a386Sopenharmony_ci    }
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci    // by default, SkSFNTHeader is at the start of the stream
66cb93a386Sopenharmony_ci    size_t offset = 0;
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_ci    // if we're really a collection, the first 4-bytes will be 'ttcf'
69cb93a386Sopenharmony_ci    uint32_t tag = SkEndian_SwapBE32(header->fCollection.fTag);
70cb93a386Sopenharmony_ci    if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
71cb93a386Sopenharmony_ci        unsigned count = SkEndian_SwapBE32(header->fCollection.fNumOffsets);
72cb93a386Sopenharmony_ci        if ((unsigned)ttcIndex >= count) {
73cb93a386Sopenharmony_ci            return 0;
74cb93a386Sopenharmony_ci        }
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci        if (ttcIndex > 0) { // need to read more of the shared header
77cb93a386Sopenharmony_ci            stream->rewind();
78cb93a386Sopenharmony_ci            size_t amount = sizeof(SkSharedTTHeader) + ttcIndex * sizeof(uint32_t);
79cb93a386Sopenharmony_ci            header = (SkSharedTTHeader*)storage.reset(amount);
80cb93a386Sopenharmony_ci            if (!read(stream, header, amount)) {
81cb93a386Sopenharmony_ci                return 0;
82cb93a386Sopenharmony_ci            }
83cb93a386Sopenharmony_ci        }
84cb93a386Sopenharmony_ci        // this is the offset to the local SkSFNTHeader
85cb93a386Sopenharmony_ci        offset = SkEndian_SwapBE32((&header->fCollection.fOffset0)[ttcIndex]);
86cb93a386Sopenharmony_ci        stream->rewind();
87cb93a386Sopenharmony_ci        if (!skip(stream, offset)) {
88cb93a386Sopenharmony_ci            return 0;
89cb93a386Sopenharmony_ci        }
90cb93a386Sopenharmony_ci        if (!read(stream, header, sizeof(SkSFNTHeader))) {
91cb93a386Sopenharmony_ci            return 0;
92cb93a386Sopenharmony_ci        }
93cb93a386Sopenharmony_ci    }
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_ci    if (offsetToDir) {
96cb93a386Sopenharmony_ci        // add the size of the header, so we will point to the DirEntries
97cb93a386Sopenharmony_ci        *offsetToDir = offset + sizeof(SkSFNTHeader);
98cb93a386Sopenharmony_ci    }
99cb93a386Sopenharmony_ci    return SkEndian_SwapBE16(header->fSingle.fNumTables);
100cb93a386Sopenharmony_ci}
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_cistruct SfntHeader {
105cb93a386Sopenharmony_ci    SfntHeader() : fCount(0), fDir(nullptr) {}
106cb93a386Sopenharmony_ci    ~SfntHeader() { sk_free(fDir); }
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ci    /** If it returns true, then fCount and fDir are properly initialized.
109cb93a386Sopenharmony_ci        Note: fDir will point to the raw array of SkSFNTDirEntry values,
110cb93a386Sopenharmony_ci        meaning they will still be in the file's native endianness (BE).
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_ci        fDir will be automatically freed when this object is destroyed
113cb93a386Sopenharmony_ci     */
114cb93a386Sopenharmony_ci    bool init(SkStream* stream, int ttcIndex) {
115cb93a386Sopenharmony_ci        stream->rewind();
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_ci        size_t offsetToDir;
118cb93a386Sopenharmony_ci        fCount = count_tables(stream, ttcIndex, &offsetToDir);
119cb93a386Sopenharmony_ci        if (0 == fCount) {
120cb93a386Sopenharmony_ci            return false;
121cb93a386Sopenharmony_ci        }
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci        stream->rewind();
124cb93a386Sopenharmony_ci        if (!skip(stream, offsetToDir)) {
125cb93a386Sopenharmony_ci            return false;
126cb93a386Sopenharmony_ci        }
127cb93a386Sopenharmony_ci
128cb93a386Sopenharmony_ci        size_t size = fCount * sizeof(SkSFNTDirEntry);
129cb93a386Sopenharmony_ci        fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size));
130cb93a386Sopenharmony_ci        return read(stream, fDir, size);
131cb93a386Sopenharmony_ci    }
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ci    int             fCount;
134cb93a386Sopenharmony_ci    SkSFNTDirEntry* fDir;
135cb93a386Sopenharmony_ci};
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ciint SkFontStream::CountTTCEntries(SkStream* stream) {
140cb93a386Sopenharmony_ci    stream->rewind();
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ci    SkSharedTTHeader shared;
143cb93a386Sopenharmony_ci    if (!read(stream, &shared, sizeof(shared))) {
144cb93a386Sopenharmony_ci        return 0;
145cb93a386Sopenharmony_ci    }
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_ci    // if we're really a collection, the first 4-bytes will be 'ttcf'
148cb93a386Sopenharmony_ci    uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag);
149cb93a386Sopenharmony_ci    if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
150cb93a386Sopenharmony_ci        return SkEndian_SwapBE32(shared.fCollection.fNumOffsets);
151cb93a386Sopenharmony_ci    } else {
152cb93a386Sopenharmony_ci        return 1;   // normal 'sfnt' has 1 dir entry
153cb93a386Sopenharmony_ci    }
154cb93a386Sopenharmony_ci}
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ciint SkFontStream::GetTableTags(SkStream* stream, int ttcIndex,
157cb93a386Sopenharmony_ci                               SkFontTableTag tags[]) {
158cb93a386Sopenharmony_ci    SfntHeader  header;
159cb93a386Sopenharmony_ci    if (!header.init(stream, ttcIndex)) {
160cb93a386Sopenharmony_ci        return 0;
161cb93a386Sopenharmony_ci    }
162cb93a386Sopenharmony_ci
163cb93a386Sopenharmony_ci    if (tags) {
164cb93a386Sopenharmony_ci        for (int i = 0; i < header.fCount; i++) {
165cb93a386Sopenharmony_ci            tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag);
166cb93a386Sopenharmony_ci        }
167cb93a386Sopenharmony_ci    }
168cb93a386Sopenharmony_ci    return header.fCount;
169cb93a386Sopenharmony_ci}
170cb93a386Sopenharmony_ci
171cb93a386Sopenharmony_cisize_t SkFontStream::GetTableData(SkStream* stream, int ttcIndex,
172cb93a386Sopenharmony_ci                                  SkFontTableTag tag,
173cb93a386Sopenharmony_ci                                  size_t offset, size_t length, void* data) {
174cb93a386Sopenharmony_ci    SfntHeader  header;
175cb93a386Sopenharmony_ci    if (!header.init(stream, ttcIndex)) {
176cb93a386Sopenharmony_ci        return 0;
177cb93a386Sopenharmony_ci    }
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ci    for (int i = 0; i < header.fCount; i++) {
180cb93a386Sopenharmony_ci        if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
181cb93a386Sopenharmony_ci            size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset);
182cb93a386Sopenharmony_ci            size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength);
183cb93a386Sopenharmony_ci            if (offset >= realLength) {
184cb93a386Sopenharmony_ci                // invalid
185cb93a386Sopenharmony_ci                return 0;
186cb93a386Sopenharmony_ci            }
187cb93a386Sopenharmony_ci            // if the caller is trusting the length from the file, then a
188cb93a386Sopenharmony_ci            // hostile file might choose a value which would overflow offset +
189cb93a386Sopenharmony_ci            // length.
190cb93a386Sopenharmony_ci            if (offset + length < offset) {
191cb93a386Sopenharmony_ci                return 0;
192cb93a386Sopenharmony_ci            }
193cb93a386Sopenharmony_ci            if (length > realLength - offset) {
194cb93a386Sopenharmony_ci                length = realLength - offset;
195cb93a386Sopenharmony_ci            }
196cb93a386Sopenharmony_ci            if (data) {
197cb93a386Sopenharmony_ci                // skip the stream to the part of the table we want to copy from
198cb93a386Sopenharmony_ci                stream->rewind();
199cb93a386Sopenharmony_ci                size_t bytesToSkip = realOffset + offset;
200cb93a386Sopenharmony_ci                if (!skip(stream, bytesToSkip)) {
201cb93a386Sopenharmony_ci                    return 0;
202cb93a386Sopenharmony_ci                }
203cb93a386Sopenharmony_ci                if (!read(stream, data, length)) {
204cb93a386Sopenharmony_ci                    return 0;
205cb93a386Sopenharmony_ci                }
206cb93a386Sopenharmony_ci            }
207cb93a386Sopenharmony_ci            return length;
208cb93a386Sopenharmony_ci        }
209cb93a386Sopenharmony_ci    }
210cb93a386Sopenharmony_ci    return 0;
211cb93a386Sopenharmony_ci}
212