Coverage Report

Created: 2026-07-16 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/glat.cc
Line
Count
Source
1
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#include "glat.h"
6
7
#include "gloc.h"
8
#include "lz4.h"
9
#include <list>
10
#include <memory>
11
12
namespace ots {
13
14
// -----------------------------------------------------------------------------
15
// OpenTypeGLAT_v1
16
// -----------------------------------------------------------------------------
17
18
282
bool OpenTypeGLAT_v1::Parse(const uint8_t* data, size_t length) {
19
282
  Buffer table(data, length);
20
282
  OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
21
282
      GetFont()->GetTypedTable(OTS_TAG_GLOC));
22
282
  if (!gloc) {
23
74
    return DropGraphite("Required Gloc table is missing");
24
74
  }
25
26
208
  if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
27
0
    return DropGraphite("Failed to read version");
28
0
  }
29
30
208
  const std::vector<uint32_t>& locations = gloc->GetLocations();
31
208
  if (locations.empty()) {
32
0
    return DropGraphite("No locations from Gloc table");
33
0
  }
34
208
  std::list<uint32_t> unverified(locations.begin(), locations.end());
35
47.4k
  while (table.remaining()) {
36
47.3k
    GlatEntry entry(this);
37
47.3k
    if (table.offset() > unverified.front()) {
38
39
      return DropGraphite("Offset check failed for a GlatEntry");
39
39
    }
40
47.3k
    if (table.offset() == unverified.front()) {
41
15.8k
      unverified.pop_front();
42
15.8k
    }
43
47.3k
    if (unverified.empty()) {
44
8
      return DropGraphite("Expected more locations");
45
8
    }
46
47.3k
    if (!entry.ParsePart(table)) {
47
59
      return DropGraphite("Failed to read a GlatEntry");
48
59
    }
49
47.2k
    this->entries.push_back(entry);
50
47.2k
  }
51
52
102
  if (unverified.size() != 1 || unverified.front() != table.offset()) {
53
61
    return DropGraphite("%zu location(s) could not be verified", unverified.size());
54
61
  }
55
41
  if (table.remaining()) {
56
0
    return Warning("%zu bytes unparsed", table.remaining());
57
0
  }
58
41
  return true;
59
41
}
60
61
40
bool OpenTypeGLAT_v1::Serialize(OTSStream* out) {
62
40
  assert(ShouldSerialize());
63
40
  if (!out->WriteU32(this->version) ||
64
40
      !SerializeParts(this->entries, out)) {
65
0
    return Error("Failed to write table");
66
0
  }
67
40
  return true;
68
40
}
69
70
47.3k
bool OpenTypeGLAT_v1::GlatEntry::ParsePart(Buffer& table) {
71
47.3k
  if (!table.ReadU8(&this->attNum)) {
72
0
    return parent->Error("GlatEntry: Failed to read attNum");
73
0
  }
74
47.3k
  if (!table.ReadU8(&this->num)) {
75
7
    return parent->Error("GlatEntry: Failed to read num");
76
7
  }
77
78
  //this->attributes.resize(this->num);
79
135k
  for (int i = 0; i < this->num; ++i) {
80
88.5k
    this->attributes.emplace_back();
81
88.5k
    if (!table.ReadS16(&this->attributes[i])) {
82
52
      return parent->Error("GlatEntry: Failed to read attribute %u", i);
83
52
    }
84
88.5k
  }
85
47.2k
  return true;
86
47.2k
}
87
88
36.2k
bool OpenTypeGLAT_v1::GlatEntry::SerializePart(OTSStream* out) const {
89
36.2k
  if (!out->WriteU8(this->attNum) ||
90
36.2k
      !out->WriteU8(this->num) ||
91
36.2k
      !SerializeParts(this->attributes, out)) {
92
0
    return parent->Error("GlatEntry: Failed to write");
93
0
  }
94
36.2k
  return true;
95
36.2k
}
96
97
// -----------------------------------------------------------------------------
98
// OpenTypeGLAT_v2
99
// -----------------------------------------------------------------------------
100
101
87
bool OpenTypeGLAT_v2::Parse(const uint8_t* data, size_t length) {
102
87
  Buffer table(data, length);
103
87
  OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
104
87
      GetFont()->GetTypedTable(OTS_TAG_GLOC));
105
87
  if (!gloc) {
106
54
    return DropGraphite("Required Gloc table is missing");
107
54
  }
108
109
33
  if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
110
33
    return DropGraphite("Failed to read version");
111
33
  }
112
113
0
  const std::vector<uint32_t>& locations = gloc->GetLocations();
114
0
  if (locations.empty()) {
115
0
    return DropGraphite("No locations from Gloc table");
116
0
  }
117
0
  std::list<uint32_t> unverified(locations.begin(), locations.end());
118
0
  while (table.remaining()) {
119
0
    GlatEntry entry(this);
120
0
    if (table.offset() > unverified.front()) {
121
0
      return DropGraphite("Offset check failed for a GlatEntry");
122
0
    }
123
0
    if (table.offset() == unverified.front()) {
124
0
      unverified.pop_front();
125
0
    }
126
0
    if (unverified.empty()) {
127
0
      return DropGraphite("Expected more locations");
128
0
    }
129
0
    if (!entry.ParsePart(table)) {
130
0
      return DropGraphite("Failed to read a GlatEntry");
131
0
    }
132
0
    this->entries.push_back(entry);
133
0
  }
134
135
0
  if (unverified.size() != 1 || unverified.front() != table.offset()) {
136
0
    return DropGraphite("%zu location(s) could not be verified", unverified.size());
137
0
  }
138
0
  if (table.remaining()) {
139
0
    return Warning("%zu bytes unparsed", table.remaining());
140
0
  }
141
0
  return true;
142
0
}
143
144
0
bool OpenTypeGLAT_v2::Serialize(OTSStream* out) {
145
0
  assert(ShouldSerialize());
146
0
  if (!out->WriteU32(this->version) ||
147
0
      !SerializeParts(this->entries, out)) {
148
0
    return Error("Failed to write table");
149
0
  }
150
0
  return true;
151
0
}
152
153
0
bool OpenTypeGLAT_v2::GlatEntry::ParsePart(Buffer& table) {
154
0
  if (!table.ReadS16(&this->attNum)) {
155
0
    return parent->Error("GlatEntry: Failed to read attNum");
156
0
  }
157
0
  if (!table.ReadS16(&this->num) || this->num < 0) {
158
0
    return parent->Error("GlatEntry: Failed to read valid num");
159
0
  }
160
161
  //this->attributes.resize(this->num);
162
0
  for (int i = 0; i < this->num; ++i) {
163
0
    this->attributes.emplace_back();
164
0
    if (!table.ReadS16(&this->attributes[i])) {
165
0
      return parent->Error("GlatEntry: Failed to read attribute %u", i);
166
0
    }
167
0
  }
168
0
  return true;
169
0
}
170
171
0
bool OpenTypeGLAT_v2::GlatEntry::SerializePart(OTSStream* out) const {
172
0
  if (!out->WriteS16(this->attNum) ||
173
0
      !out->WriteS16(this->num) ||
174
0
      !SerializeParts(this->attributes, out)) {
175
0
    return parent->Error("GlatEntry: Failed to write");
176
0
  }
177
0
  return true;
178
0
}
179
180
// -----------------------------------------------------------------------------
181
// OpenTypeGLAT_v3
182
// -----------------------------------------------------------------------------
183
184
bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
185
893
                            bool prevent_decompression) {
186
893
  Buffer table(data, length);
187
893
  OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
188
893
      GetFont()->GetTypedTable(OTS_TAG_GLOC));
189
893
  if (!gloc) {
190
103
    return DropGraphite("Required Gloc table is missing");
191
103
  }
192
193
790
  if (!table.ReadU32(&this->version) || this->version >> 16 != 3) {
194
21
    return DropGraphite("Failed to read version");
195
21
  }
196
769
  if (!table.ReadU32(&this->compHead)) {
197
10
    return DropGraphite("Failed to read compression header");
198
10
  }
199
759
  switch ((this->compHead & SCHEME) >> 27) {
200
537
    case 0:  // uncompressed
201
537
      break;
202
204
    case 1: {  // lz4
203
204
      if (prevent_decompression) {
204
1
        return DropGraphite("Illegal nested compression");
205
1
      }
206
203
      size_t decompressed_size = this->compHead & FULL_SIZE;
207
203
      if (decompressed_size < length) {
208
22
        return DropGraphite("Decompressed size is less than compressed size");
209
22
      }
210
181
      if (decompressed_size == 0) {
211
0
        return DropGraphite("Decompressed size is set to 0");
212
0
      }
213
      // decompressed table must be <= OTS_MAX_DECOMPRESSED_TABLE_SIZE
214
181
      if (decompressed_size > OTS_MAX_DECOMPRESSED_TABLE_SIZE) {
215
0
        return DropGraphite("Decompressed size exceeds %gMB: %gMB",
216
0
                            OTS_MAX_DECOMPRESSED_TABLE_SIZE / (1024.0 * 1024.0),
217
0
                            decompressed_size / (1024.0 * 1024.0));
218
0
      }
219
181
      std::unique_ptr<uint8_t> decompressed(new uint8_t[decompressed_size]());
220
181
      int ret = LZ4_decompress_safe_partial(
221
181
          reinterpret_cast<const char*>(data + table.offset()),
222
181
          reinterpret_cast<char*>(decompressed.get()),
223
181
          table.remaining(),  // input buffer size (input size + padding)
224
181
          decompressed_size,  // target output size
225
181
          decompressed_size);  // output buffer size
226
181
      if (ret < 0 || unsigned(ret) != decompressed_size) {
227
135
        return DropGraphite("Decompression failed with error code %d", ret);
228
135
      }
229
46
      return this->Parse(decompressed.get(), decompressed_size, true);
230
181
    }
231
18
    default:
232
18
      return DropGraphite("Unknown compression scheme");
233
759
  }
234
537
  if (this->compHead & RESERVED) {
235
468
    Warning("Nonzero reserved");
236
468
  }
237
238
537
  const std::vector<uint32_t>& locations = gloc->GetLocations();
239
537
  if (locations.empty()) {
240
0
    return DropGraphite("No locations from Gloc table");
241
0
  }
242
537
  std::list<uint32_t> unverified(locations.begin(), locations.end());
243
  //this->entries.resize(locations.size() - 1, this);
244
8.51k
  for (size_t i = 0; i < locations.size() - 1; ++i) {
245
8.43k
    this->entries.emplace_back(this);
246
8.43k
    if (table.offset() != unverified.front()) {
247
49
      return DropGraphite("Offset check failed for a GlyphAttrs");
248
49
    }
249
8.38k
    unverified.pop_front();
250
8.38k
    if (!this->entries[i].ParsePart(table,
251
8.38k
                                    unverified.front() - table.offset())) {
252
        // unverified.front() is guaranteed to exist because of the number of
253
        // iterations of this loop
254
409
      return DropGraphite("Failed to read a GlyphAttrs");
255
409
    }
256
8.38k
  }
257
258
79
  if (unverified.size() != 1 || unverified.front() != table.offset()) {
259
23
    return DropGraphite("%zu location(s) could not be verified", unverified.size());
260
23
  }
261
56
  if (table.remaining()) {
262
40
    return Warning("%zu bytes unparsed", table.remaining());
263
40
  }
264
16
  return true;
265
56
}
266
267
55
bool OpenTypeGLAT_v3::Serialize(OTSStream* out) {
268
55
  assert(ShouldSerialize());
269
55
  if (!out->WriteU32(this->version) ||
270
55
      !out->WriteU32(this->compHead) ||
271
55
      !SerializeParts(this->entries, out)) {
272
0
    return Error("Failed to write table");
273
0
  }
274
55
  return true;
275
55
}
276
277
8.38k
bool OpenTypeGLAT_v3::GlyphAttrs::ParsePart(Buffer& table, const size_t size) {
278
8.38k
  size_t init_offset = table.offset();
279
8.38k
  if (parent->compHead & OCTABOXES && !octabox.ParsePart(table)) {
280
    // parent->flags & 0b1: octaboxes are present flag
281
319
    return parent->Error("GlyphAttrs: Failed to read octabox");
282
319
  }
283
284
39.7k
  while (table.offset() < init_offset + size) {
285
31.7k
    GlatEntry entry(parent);
286
31.7k
    if (!entry.ParsePart(table)) {
287
90
      return parent->Error("GlyphAttrs: Failed to read a GlatEntry");
288
90
    }
289
31.6k
    this->entries.push_back(entry);
290
31.6k
  }
291
7.97k
  return true;
292
8.06k
}
293
294
3.22k
bool OpenTypeGLAT_v3::GlyphAttrs::SerializePart(OTSStream* out) const {
295
3.22k
  if ((parent->compHead & OCTABOXES && !octabox.SerializePart(out)) ||
296
3.22k
      !SerializeParts(this->entries, out)) {
297
0
    return parent->Error("GlyphAttrs: Failed to write");
298
0
  }
299
3.22k
  return true;
300
3.22k
}
301
302
bool OpenTypeGLAT_v3::GlyphAttrs::
303
8.27k
OctaboxMetrics::ParsePart(Buffer& table) {
304
8.27k
  if (!table.ReadU16(&this->subbox_bitmap)) {
305
14
    return parent->Error("OctaboxMetrics: Failed to read subbox_bitmap");
306
14
  }
307
8.25k
  if (!table.ReadU8(&this->diag_neg_min)) {
308
18
    return parent->Error("OctaboxMetrics: Failed to read diag_neg_min");
309
18
  }
310
8.24k
  if (!table.ReadU8(&this->diag_neg_max) ||
311
8.22k
      this->diag_neg_max < this->diag_neg_min) {
312
34
    return parent->Error("OctaboxMetrics: Failed to read valid diag_neg_max");
313
34
  }
314
8.20k
  if (!table.ReadU8(&this->diag_pos_min)) {
315
13
    return parent->Error("OctaboxMetrics: Failed to read diag_pos_min");
316
13
  }
317
8.19k
  if (!table.ReadU8(&this->diag_pos_max) ||
318
8.17k
      this->diag_pos_max < this->diag_pos_min) {
319
35
    return parent->Error("OctaboxMetrics: Failed to read valid diag_pos_max");
320
35
  }
321
322
8.15k
  unsigned subboxes_len = 0;  // count of 1's in this->subbox_bitmap
323
40.1k
  for (uint16_t i = this->subbox_bitmap; i; i >>= 1) {
324
31.9k
    if (i & 0b1) {
325
21.2k
      ++subboxes_len;
326
21.2k
    }
327
31.9k
  }
328
  //this->subboxes.resize(subboxes_len, parent);
329
27.7k
  for (unsigned i = 0; i < subboxes_len; i++) {
330
19.7k
    this->subboxes.emplace_back(parent);
331
19.7k
    if (!this->subboxes[i].ParsePart(table)) {
332
205
      return parent->Error("OctaboxMetrics: Failed to read subbox[%u]", i);
333
205
    }
334
19.7k
  }
335
7.95k
  return true;
336
8.15k
}
337
338
bool OpenTypeGLAT_v3::GlyphAttrs::
339
3.20k
OctaboxMetrics::SerializePart(OTSStream* out) const {
340
3.20k
  if (!out->WriteU16(this->subbox_bitmap) ||
341
3.20k
      !out->WriteU8(this->diag_neg_min) ||
342
3.20k
      !out->WriteU8(this->diag_neg_max) ||
343
3.20k
      !out->WriteU8(this->diag_pos_min) ||
344
3.20k
      !out->WriteU8(this->diag_pos_max) ||
345
3.20k
      !SerializeParts(this->subboxes, out)) {
346
0
    return parent->Error("OctaboxMetrics: Failed to write");
347
0
  }
348
3.20k
  return true;
349
3.20k
}
350
351
bool OpenTypeGLAT_v3::GlyphAttrs::OctaboxMetrics::
352
19.7k
SubboxEntry::ParsePart(Buffer& table) {
353
19.7k
  if (!table.ReadU8(&this->left)) {
354
15
    return parent->Error("SubboxEntry: Failed to read left");
355
15
  }
356
19.7k
  if (!table.ReadU8(&this->right) || this->right < this->left) {
357
50
    return parent->Error("SubboxEntry: Failed to read valid right");
358
50
  }
359
19.7k
  if (!table.ReadU8(&this->bottom)) {
360
10
    return parent->Error("SubboxEntry: Failed to read bottom");
361
10
  }
362
19.6k
  if (!table.ReadU8(&this->top) || this->top < this->bottom) {
363
28
    return parent->Error("SubboxEntry: Failed to read valid top");
364
28
  }
365
19.6k
  if (!table.ReadU8(&this->diag_pos_min)) {
366
19
    return parent->Error("SubboxEntry: Failed to read diag_pos_min");
367
19
  }
368
19.6k
  if (!table.ReadU8(&this->diag_pos_max) ||
369
19.6k
      this->diag_pos_max < this->diag_pos_min) {
370
36
    return parent->Error("SubboxEntry: Failed to read valid diag_pos_max");
371
36
  }
372
19.6k
  if (!table.ReadU8(&this->diag_neg_min)) {
373
21
    return parent->Error("SubboxEntry: Failed to read diag_neg_min");
374
21
  }
375
19.5k
  if (!table.ReadU8(&this->diag_neg_max) ||
376
19.5k
      this->diag_neg_max < this->diag_neg_min) {
377
26
    return parent->Error("SubboxEntry: Failed to read valid diag_neg_max");
378
26
  }
379
19.5k
  return true;
380
19.5k
}
381
382
bool OpenTypeGLAT_v3::GlyphAttrs::OctaboxMetrics::
383
10.7k
SubboxEntry::SerializePart(OTSStream* out) const {
384
10.7k
  if (!out->WriteU8(this->left) ||
385
10.7k
      !out->WriteU8(this->right) ||
386
10.7k
      !out->WriteU8(this->bottom) ||
387
10.7k
      !out->WriteU8(this->top) ||
388
10.7k
      !out->WriteU8(this->diag_pos_min) ||
389
10.7k
      !out->WriteU8(this->diag_pos_max) ||
390
10.7k
      !out->WriteU8(this->diag_neg_min) ||
391
10.7k
      !out->WriteU8(this->diag_neg_max)) {
392
0
    return parent->Error("SubboxEntry: Failed to write");
393
0
  }
394
10.7k
  return true;
395
10.7k
}
396
397
bool OpenTypeGLAT_v3::GlyphAttrs::
398
31.7k
GlatEntry::ParsePart(Buffer& table) {
399
31.7k
  if (!table.ReadS16(&this->attNum)) {
400
17
    return parent->Error("GlatEntry: Failed to read attNum");
401
17
  }
402
31.7k
  if (!table.ReadS16(&this->num) || this->num < 0) {
403
43
    return parent->Error("GlatEntry: Failed to read valid num");
404
43
  }
405
406
  //this->attributes.resize(this->num);
407
180k
  for (int i = 0; i < this->num; ++i) {
408
148k
    this->attributes.emplace_back();
409
148k
    if (!table.ReadS16(&this->attributes[i])) {
410
30
      return parent->Error("GlatEntry: Failed to read attribute %u", i);
411
30
    }
412
148k
  }
413
31.6k
  return true;
414
31.6k
}
415
416
bool OpenTypeGLAT_v3::GlyphAttrs::
417
14.6k
GlatEntry::SerializePart(OTSStream* out) const {
418
14.6k
  if (!out->WriteS16(this->attNum) ||
419
14.6k
      !out->WriteS16(this->num) ||
420
14.6k
      !SerializeParts(this->attributes, out)) {
421
0
    return parent->Error("GlatEntry: Failed to write");
422
0
  }
423
14.6k
  return true;
424
14.6k
}
425
426
// -----------------------------------------------------------------------------
427
// OpenTypeGLAT
428
// -----------------------------------------------------------------------------
429
430
1.76k
bool OpenTypeGLAT::Parse(const uint8_t* data, size_t length) {
431
1.76k
  Buffer table(data, length);
432
1.76k
  uint32_t version;
433
1.76k
  if (!table.ReadU32(&version)) {
434
238
    return DropGraphite("Failed to read version");
435
238
  }
436
1.52k
  switch (version >> 16) {
437
282
    case 1:
438
282
      this->handler = new OpenTypeGLAT_v1(this->font, this->tag);
439
282
      break;
440
87
    case 2:
441
87
      this->handler = new OpenTypeGLAT_v2(this->font, this->tag);
442
87
      break;
443
847
    case 3: {
444
847
      this->handler = new OpenTypeGLAT_v3(this->font, this->tag);
445
847
      break;
446
0
    }
447
308
    default:
448
308
      return DropGraphite("Unsupported table version: %u", version >> 16);
449
1.52k
  }
450
1.21k
  return this->handler->Parse(data, length);
451
1.52k
}
452
453
95
bool OpenTypeGLAT::Serialize(OTSStream* out) {
454
95
  if (!this->handler) {
455
0
    return Error("No Glat table parsed");
456
0
  }
457
95
  return this->handler->Serialize(out);
458
95
}
459
460
}  // namespace ots