Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/graphite/src/Face.cpp
Line
Count
Source
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
0
: m_appFaceHandle(appFaceHandle),
32
0
  m_pFileFace(NULL),
33
0
  m_pGlyphFaceCache(NULL),
34
0
  m_cmap(NULL),
35
0
  m_pNames(NULL),
36
0
  m_logger(NULL),
37
0
  m_error(0), m_errcntxt(0),
38
0
  m_silfs(NULL),
39
0
  m_numSilf(0),
40
0
  m_ascent(0),
41
0
  m_descent(0)
42
0
{
43
0
    memset(&m_ops, 0, sizeof m_ops);
44
0
    memcpy(&m_ops, &ops, min(sizeof m_ops, ops.size));
45
0
}
46
47
48
Face::~Face()
49
0
{
50
0
    setLogger(0);
51
0
    delete m_pGlyphFaceCache;
52
0
    delete m_cmap;
53
0
    delete[] m_silfs;
54
0
#ifndef GRAPHITE2_NFILEFACE
55
0
    delete m_pFileFace;
56
0
#endif
57
0
    delete m_pNames;
58
0
}
59
60
float Face::default_glyph_advance(const void* font_ptr, gr_uint16 glyphid)
61
0
{
62
0
    const Font & font = *reinterpret_cast<const Font *>(font_ptr);
63
64
0
    return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale();
65
0
}
66
67
bool Face::readGlyphs(uint32 faceOptions)
68
0
{
69
0
    Error e;
70
#ifdef GRAPHITE2_TELEMETRY
71
    telemetry::category _glyph_cat(tele.glyph);
72
#endif
73
0
    error_context(EC_READGLYPHS);
74
0
    m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
75
76
0
    if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
77
0
        || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
78
0
        || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM))
79
0
    {
80
0
        return error(e);
81
0
    }
82
83
0
    if (faceOptions & gr_face_cacheCmap)
84
0
        m_cmap = new CachedCmap(*this);
85
0
    else
86
0
        m_cmap = new DirectCmap(*this);
87
0
    if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
88
0
        return error(e);
89
90
0
    if (faceOptions & gr_face_preloadGlyphs)
91
0
        nameTable();        // preload the name table along with the glyphs.
92
93
0
    return true;
94
0
}
95
96
bool Face::readGraphite(const Table & silf)
97
0
{
98
#ifdef GRAPHITE2_TELEMETRY
99
    telemetry::category _silf_cat(tele.silf);
100
#endif
101
0
    Error e;
102
0
    error_context(EC_READSILF);
103
0
    const byte * p = silf;
104
0
    if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20, E_BADSIZE)) return error(e);
105
106
0
    const uint32 version = be::read<uint32>(p);
107
0
    if (e.test(version < 0x00020000, E_TOOOLD)) return error(e);
108
0
    if (version >= 0x00030000)
109
0
        be::skip<uint32>(p);        // compilerVersion
110
0
    m_numSilf = be::read<uint16>(p);
111
112
0
    be::skip<uint16>(p);            // reserved
113
114
0
    bool havePasses = false;
115
0
    m_silfs = new Silf[m_numSilf];
116
0
    if (e.test(!m_silfs, E_OUTOFMEM)) return error(e);
117
0
    for (int i = 0; i < m_numSilf; i++)
118
0
    {
119
0
        error_context(EC_ASILF + (i << 8));
120
0
        const uint32 offset = be::read<uint32>(p),
121
0
                     next   = i == m_numSilf - 1 ? uint32(silf.size()) : be::peek<uint32>(p);
122
0
        if (e.test(next > silf.size() || offset >= next, E_BADSIZE))
123
0
            return error(e);
124
125
0
        if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version))
126
0
            return false;
127
128
0
        if (m_silfs[i].numPasses())
129
0
            havePasses = true;
130
0
    }
131
132
0
    return havePasses;
133
0
}
134
135
bool Face::readFeatures()
136
0
{
137
0
    return m_Sill.readFace(*this);
138
0
}
139
140
bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
141
0
{
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
0
    if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF)
155
0
        seg->doMirror(aSilf->aMirror());
156
0
    bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
157
0
    if (res)
158
0
    {
159
0
        seg->associateChars(0, seg->charInfoCount());
160
0
        if (aSilf->flags() & 0x20)
161
0
            res &= seg->initCollisions();
162
0
        if (res)
163
0
            res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
164
0
    }
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
0
    return res;
187
0
}
188
189
void Face::setLogger(FILE * log_file GR_MAYBE_UNUSED)
190
0
{
191
#if !defined GRAPHITE2_NTRACING
192
    delete m_logger;
193
    m_logger = log_file ? new json(log_file) : 0;
194
#endif
195
0
}
196
197
const Silf *Face::chooseSilf(uint32 script) const
198
0
{
199
0
    if (m_numSilf == 0)
200
0
        return NULL;
201
0
    else if (m_numSilf == 1 || script == 0)
202
0
        return m_silfs;
203
0
    else // do more work here
204
0
        return m_silfs;
205
0
}
206
207
uint16 Face::findPseudo(uint32 uid) const
208
0
{
209
0
    return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0;
210
0
}
211
212
int32 Face::getGlyphMetric(uint16 gid, uint8 metric) const
213
0
{
214
0
    switch (metrics(metric))
215
0
    {
216
0
        case kgmetAscent : return m_ascent;
217
0
        case kgmetDescent : return m_descent;
218
0
        default:
219
0
            if (gid >= glyphs().numGlyphs()) return 0;
220
0
            return glyphs().glyph(gid)->getMetric(metric);
221
0
    }
222
0
}
223
224
void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/)
225
0
{
226
0
#ifndef GRAPHITE2_NFILEFACE
227
0
    if (m_pFileFace==pFileFace)
228
0
      return;
229
230
0
    delete m_pFileFace;
231
0
    m_pFileFace = pFileFace;
232
0
#endif
233
0
}
234
235
NameTable * Face::nameTable() const
236
0
{
237
0
    if (m_pNames) return m_pNames;
238
0
    const Table name(*this, Tag::name);
239
0
    if (name)
240
0
        m_pNames = new NameTable(name, name.size());
241
0
    return m_pNames;
242
0
}
243
244
uint16 Face::languageForLocale(const char * locale) const
245
0
{
246
0
    nameTable();
247
0
    if (m_pNames)
248
0
        return m_pNames->getLanguageId(locale);
249
0
    return 0;
250
0
}
251
252
253
254
Face::Table::Table(const Face & face, const Tag n, uint32 version) throw()
255
0
: _f(&face), _sz(0), _compressed(false)
256
0
{
257
0
    _p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &_sz));
258
259
0
    if (!TtfUtil::CheckTable(n, _p, _sz))
260
0
    {
261
0
        release();     // Make sure we release the table buffer even if the table failed its checks
262
0
        return;
263
0
    }
264
265
0
    if (be::peek<uint32>(_p) >= version)
266
0
        decompress();
267
0
}
268
269
void Face::Table::release()
270
0
{
271
0
    if (_compressed)
272
0
        free(const_cast<byte *>(_p));
273
0
    else if (_p && _f->m_ops.release_table)
274
0
        (*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
275
0
    _p = 0; _sz = 0;
276
0
}
277
278
Face::Table & Face::Table::operator = (const Table && rhs) throw()
279
0
{
280
0
    if (this == &rhs)   return *this;
281
0
    release();
282
0
    new (this) Table(std::move(rhs));
283
0
    return *this;
284
0
}
285
286
Error Face::Table::decompress()
287
0
{
288
0
    Error e;
289
0
    if (e.test(_sz < 5 * sizeof(uint32), E_BADSIZE))
290
0
        return e;
291
0
    byte * uncompressed_table = 0;
292
0
    size_t uncompressed_size = 0;
293
294
0
    const byte * p = _p;
295
0
    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
0
    const uint32 hdr = be::read<uint32>(p);
299
0
    switch(compression(hdr >> 27))
300
0
    {
301
0
    case NONE: return e;
302
303
0
    case LZ4:
304
0
    {
305
0
        uncompressed_size  = hdr & 0x07ffffff;
306
0
        uncompressed_table = gralloc<byte>(uncompressed_size);
307
0
        if (!e.test(!uncompressed_table || uncompressed_size < 4, E_OUTOFMEM))
308
0
        {
309
0
            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
0
            e.test(lz4::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
313
0
        }
314
0
        break;
315
0
    }
316
317
0
    default:
318
0
        e.error(E_BADSCHEME);
319
0
    };
320
321
    // Check the uncompressed version number against the original.
322
0
    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
0
        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
0
    release();
330
331
0
    if (e)
332
0
    {
333
0
        free(uncompressed_table);
334
0
        uncompressed_table = 0;
335
0
        uncompressed_size  = 0;
336
0
    }
337
338
0
    _p = uncompressed_table;
339
0
    _sz = uncompressed_size;
340
0
    _compressed = true;
341
342
0
    return e;
343
0
}