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
18using namespace graphite2;
19
20namespace
21{
22enum compression
23{
24 NONE,
25 LZ4
26};
27
28}
29
30Face::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
48Face::~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
60float 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
67bool 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
96bool 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
135bool Face::readFeatures()
136{
137 return m_Sill.readFace(*this);
138}
139
140bool 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
189void 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
197const 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
207uint16 Face::findPseudo(uint32 uid) const
208{
209 return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0;
210}
211
212int32 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
224void 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
235NameTable * 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
244uint16 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
254Face::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
269void 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
278Face::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
286Error 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