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