Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/graphite/src/Face.cpp
9902 views
1
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
2
// Copyright 2010, SIL International, All rights reserved.
3
4
#include <cstring>
5
#include "graphite2/Segment.h"
6
#include "inc/CmapCache.h"
7
#include "inc/debug.h"
8
#include "inc/Decompressor.h"
9
#include "inc/Endian.h"
10
#include "inc/Face.h"
11
#include "inc/FileFace.h"
12
#include "inc/GlyphFace.h"
13
#include "inc/json.h"
14
#include "inc/Segment.h"
15
#include "inc/NameTable.h"
16
#include "inc/Error.h"
17
18
using namespace graphite2;
19
20
namespace
21
{
22
enum compression
23
{
24
NONE,
25
LZ4
26
};
27
28
}
29
30
Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
31
: m_appFaceHandle(appFaceHandle),
32
m_pFileFace(NULL),
33
m_pGlyphFaceCache(NULL),
34
m_cmap(NULL),
35
m_pNames(NULL),
36
m_logger(NULL),
37
m_error(0), m_errcntxt(0),
38
m_silfs(NULL),
39
m_numSilf(0),
40
m_ascent(0),
41
m_descent(0)
42
{
43
memset(&m_ops, 0, sizeof m_ops);
44
memcpy(&m_ops, &ops, min(sizeof m_ops, ops.size));
45
}
46
47
48
Face::~Face()
49
{
50
setLogger(0);
51
delete m_pGlyphFaceCache;
52
delete m_cmap;
53
delete[] m_silfs;
54
#ifndef GRAPHITE2_NFILEFACE
55
delete m_pFileFace;
56
#endif
57
delete m_pNames;
58
}
59
60
float Face::default_glyph_advance(const void* font_ptr, gr_uint16 glyphid)
61
{
62
const Font & font = *reinterpret_cast<const Font *>(font_ptr);
63
64
return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale();
65
}
66
67
bool Face::readGlyphs(uint32 faceOptions)
68
{
69
Error e;
70
#ifdef GRAPHITE2_TELEMETRY
71
telemetry::category _glyph_cat(tele.glyph);
72
#endif
73
error_context(EC_READGLYPHS);
74
m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
75
76
if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
77
|| e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
78
|| e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM))
79
{
80
return error(e);
81
}
82
83
if (faceOptions & gr_face_cacheCmap)
84
m_cmap = new CachedCmap(*this);
85
else
86
m_cmap = new DirectCmap(*this);
87
if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
88
return error(e);
89
90
if (faceOptions & gr_face_preloadGlyphs)
91
nameTable(); // preload the name table along with the glyphs.
92
93
return true;
94
}
95
96
bool Face::readGraphite(const Table & silf)
97
{
98
#ifdef GRAPHITE2_TELEMETRY
99
telemetry::category _silf_cat(tele.silf);
100
#endif
101
Error e;
102
error_context(EC_READSILF);
103
const byte * p = silf;
104
if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20, E_BADSIZE)) return error(e);
105
106
const uint32 version = be::read<uint32>(p);
107
if (e.test(version < 0x00020000, E_TOOOLD)) return error(e);
108
if (version >= 0x00030000)
109
be::skip<uint32>(p); // compilerVersion
110
m_numSilf = be::read<uint16>(p);
111
112
be::skip<uint16>(p); // reserved
113
114
bool havePasses = false;
115
m_silfs = new Silf[m_numSilf];
116
if (e.test(!m_silfs, E_OUTOFMEM)) return error(e);
117
for (int i = 0; i < m_numSilf; i++)
118
{
119
error_context(EC_ASILF + (i << 8));
120
const uint32 offset = be::read<uint32>(p),
121
next = i == m_numSilf - 1 ? uint32(silf.size()) : be::peek<uint32>(p);
122
if (e.test(next > silf.size() || offset >= next, E_BADSIZE))
123
return error(e);
124
125
if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version))
126
return false;
127
128
if (m_silfs[i].numPasses())
129
havePasses = true;
130
}
131
132
return havePasses;
133
}
134
135
bool Face::readFeatures()
136
{
137
return m_Sill.readFace(*this);
138
}
139
140
bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
141
{
142
#if !defined GRAPHITE2_NTRACING
143
json * dbgout = logger();
144
if (dbgout)
145
{
146
*dbgout << json::object
147
<< "id" << objectid(seg)
148
<< "passes" << json::array;
149
}
150
#endif
151
152
// if ((seg->dir() & 1) != aSilf->dir())
153
// seg->reverseSlots();
154
if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF)
155
seg->doMirror(aSilf->aMirror());
156
bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
157
if (res)
158
{
159
seg->associateChars(0, seg->charInfoCount());
160
if (aSilf->flags() & 0x20)
161
res &= seg->initCollisions();
162
if (res)
163
res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
164
}
165
166
#if !defined GRAPHITE2_NTRACING
167
if (dbgout)
168
{
169
seg->positionSlots(0, 0, 0, seg->currdir());
170
*dbgout << json::item
171
<< json::close // Close up the passes array
172
<< "outputdir" << (seg->currdir() ? "rtl" : "ltr")
173
<< "output" << json::array;
174
for(Slot * s = seg->first(); s; s = s->next())
175
*dbgout << dslot(seg, s);
176
*dbgout << json::close
177
<< "advance" << seg->advance()
178
<< "chars" << json::array;
179
for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i)
180
*dbgout << json::flat << *seg->charinfo(int(i));
181
*dbgout << json::close // Close up the chars array
182
<< json::close; // Close up the segment object
183
}
184
#endif
185
186
return res;
187
}
188
189
void Face::setLogger(FILE * log_file GR_MAYBE_UNUSED)
190
{
191
#if !defined GRAPHITE2_NTRACING
192
delete m_logger;
193
m_logger = log_file ? new json(log_file) : 0;
194
#endif
195
}
196
197
const Silf *Face::chooseSilf(uint32 script) const
198
{
199
if (m_numSilf == 0)
200
return NULL;
201
else if (m_numSilf == 1 || script == 0)
202
return m_silfs;
203
else // do more work here
204
return m_silfs;
205
}
206
207
uint16 Face::findPseudo(uint32 uid) const
208
{
209
return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0;
210
}
211
212
int32 Face::getGlyphMetric(uint16 gid, uint8 metric) const
213
{
214
switch (metrics(metric))
215
{
216
case kgmetAscent : return m_ascent;
217
case kgmetDescent : return m_descent;
218
default:
219
if (gid >= glyphs().numGlyphs()) return 0;
220
return glyphs().glyph(gid)->getMetric(metric);
221
}
222
}
223
224
void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/)
225
{
226
#ifndef GRAPHITE2_NFILEFACE
227
if (m_pFileFace==pFileFace)
228
return;
229
230
delete m_pFileFace;
231
m_pFileFace = pFileFace;
232
#endif
233
}
234
235
NameTable * Face::nameTable() const
236
{
237
if (m_pNames) return m_pNames;
238
const Table name(*this, Tag::name);
239
if (name)
240
m_pNames = new NameTable(name, name.size());
241
return m_pNames;
242
}
243
244
uint16 Face::languageForLocale(const char * locale) const
245
{
246
nameTable();
247
if (m_pNames)
248
return m_pNames->getLanguageId(locale);
249
return 0;
250
}
251
252
253
254
Face::Table::Table(const Face & face, const Tag n, uint32 version) throw()
255
: _f(&face), _sz(0), _compressed(false)
256
{
257
_p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &_sz));
258
259
if (!TtfUtil::CheckTable(n, _p, _sz))
260
{
261
release(); // Make sure we release the table buffer even if the table failed its checks
262
return;
263
}
264
265
if (be::peek<uint32>(_p) >= version)
266
decompress();
267
}
268
269
void Face::Table::release()
270
{
271
if (_compressed)
272
free(const_cast<byte *>(_p));
273
else if (_p && _f->m_ops.release_table)
274
(*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
275
_p = 0; _sz = 0;
276
}
277
278
Face::Table & Face::Table::operator = (const Table && rhs) throw()
279
{
280
if (this == &rhs) return *this;
281
release();
282
new (this) Table(std::move(rhs));
283
return *this;
284
}
285
286
Error Face::Table::decompress()
287
{
288
Error e;
289
if (e.test(_sz < 5 * sizeof(uint32), E_BADSIZE))
290
return e;
291
byte * uncompressed_table = 0;
292
size_t uncompressed_size = 0;
293
294
const byte * p = _p;
295
const uint32 version = be::read<uint32>(p); // Table version number.
296
297
// The scheme is in the top 5 bits of the 1st uint32.
298
const uint32 hdr = be::read<uint32>(p);
299
switch(compression(hdr >> 27))
300
{
301
case NONE: return e;
302
303
case LZ4:
304
{
305
uncompressed_size = hdr & 0x07ffffff;
306
uncompressed_table = gralloc<byte>(uncompressed_size);
307
if (!e.test(!uncompressed_table || uncompressed_size < 4, E_OUTOFMEM))
308
{
309
memset(uncompressed_table, 0, 4); // make sure version number is initialised
310
// coverity[forward_null : FALSE] - uncompressed_table has been checked so can't be null
311
// coverity[checked_return : FALSE] - we test e later
312
e.test(lz4::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
313
}
314
break;
315
}
316
317
default:
318
e.error(E_BADSCHEME);
319
};
320
321
// Check the uncompressed version number against the original.
322
if (!e)
323
// coverity[forward_null : FALSE] - uncompressed_table has already been tested so can't be null
324
// coverity[checked_return : FALSE] - we test e later
325
e.test(be::peek<uint32>(uncompressed_table) != version, E_SHRINKERFAILED);
326
327
// Tell the provider to release the compressed form since were replacing
328
// it anyway.
329
release();
330
331
if (e)
332
{
333
free(uncompressed_table);
334
uncompressed_table = 0;
335
uncompressed_size = 0;
336
}
337
338
_p = uncompressed_table;
339
_sz = uncompressed_size;
340
_compressed = true;
341
342
return e;
343
}
344
345