Coverage Report

Created: 2026-06-14 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/cmap.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 "cmap.h"
6
7
#include <algorithm>
8
#include <set>
9
#include <utility>
10
#include <vector>
11
12
#include "maxp.h"
13
#include "os2.h"
14
15
// cmap - Character To Glyph Index Mapping Table
16
// http://www.microsoft.com/typography/otspec/cmap.htm
17
18
namespace {
19
20
struct CMAPSubtableHeader {
21
  uint16_t platform;
22
  uint16_t encoding;
23
  uint32_t offset;
24
  uint16_t format;
25
  uint32_t length;
26
  uint32_t language;
27
};
28
29
struct Subtable314Range {
30
  uint16_t start_range;
31
  uint16_t end_range;
32
  int16_t id_delta;
33
  uint16_t id_range_offset;
34
  uint32_t id_range_offset_offset;
35
};
36
37
// Glyph array size for the Mac Roman (format 0) table.
38
const size_t kFormat0ArraySize = 256;
39
40
// The upper limit of the Unicode code point.
41
const uint32_t kUnicodeUpperLimit = 0x10FFFF;
42
43
// The maximum number of UVS records (See below).
44
const uint32_t kMaxCMAPSelectorRecords = 259;
45
// The range of UVSes are:
46
//   0x180B-0x180D (3 code points)
47
//   0xFE00-0xFE0F (16 code points)
48
//   0xE0100-0xE01EF (240 code points)
49
const uint32_t kMongolianVSStart = 0x180B;
50
const uint32_t kMongolianVSEnd = 0x180D;
51
const uint32_t kVSStart = 0xFE00;
52
const uint32_t kVSEnd = 0xFE0F;
53
const uint32_t kIVSStart = 0xE0100;
54
const uint32_t kIVSEnd = 0xE01EF;
55
const uint32_t kUVSUpperLimit = 0xFFFFFF;
56
57
} // namespace
58
59
namespace ots {
60
61
// Parses Format 4 tables
62
bool OpenTypeCMAP::ParseFormat4(int platform, int encoding,
63
62.7k
              const uint8_t *data, size_t length, uint16_t num_glyphs) {
64
62.7k
  ots::Buffer subtable(data, length);
65
66
  // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
67
  // whole thing and recompacting it, we validate it and include it verbatim
68
  // in the output.
69
70
62.7k
  OpenTypeOS2 *os2 = static_cast<OpenTypeOS2*>(
71
62.7k
      GetFont()->GetTypedTable(OTS_TAG_OS2));
72
62.7k
  if (!os2) {
73
0
    return Error("Required OS/2 table missing");
74
0
  }
75
76
62.7k
  if (!subtable.Skip(4)) {
77
59
    return Error("Can't read 4 bytes at start of cmap format 4 subtable");
78
59
  }
79
62.6k
  uint16_t language = 0;
80
62.6k
  if (!subtable.ReadU16(&language)) {
81
28
    return Error("Can't read language");
82
28
  }
83
62.6k
  if (language) {
84
    // Platform ID 3 (windows) subtables should have language '0'.
85
57
    return Error("Languages should be 0 (%d)", language);
86
57
  }
87
88
62.6k
  uint16_t segcountx2, search_range, entry_selector, range_shift;
89
62.6k
  segcountx2 = search_range = entry_selector = range_shift = 0;
90
62.6k
  if (!subtable.ReadU16(&segcountx2) ||
91
62.5k
      !subtable.ReadU16(&search_range) ||
92
62.5k
      !subtable.ReadU16(&entry_selector) ||
93
62.5k
      !subtable.ReadU16(&range_shift)) {
94
141
    return Error("Failed to read subcmap structure");
95
141
  }
96
97
62.4k
  if (segcountx2 & 1 || search_range & 1) {
98
71
    return Error("Bad subcmap structure");
99
71
  }
100
62.3k
  const uint16_t segcount = segcountx2 >> 1;
101
  // There must be at least one segment according the spec.
102
62.3k
  if (segcount < 1) {
103
22
    return Error("Segcount < 1 (%d)", segcount);
104
22
  }
105
106
  // log2segcount is the maximal x s.t. 2^x < segcount
107
62.3k
  unsigned log2segcount = 0;
108
187k
  while (1u << (log2segcount + 1) <= segcount) {
109
124k
    log2segcount++;
110
124k
  }
111
112
62.3k
  const uint16_t expected_search_range = 2 * 1u << log2segcount;
113
62.3k
  if (expected_search_range != search_range) {
114
13
    return Error("expected search range != search range (%d != %d)", expected_search_range, search_range);
115
13
  }
116
117
62.3k
  if (entry_selector != log2segcount) {
118
37
    return Error("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
119
37
  }
120
121
62.3k
  const uint16_t expected_range_shift = segcountx2 - search_range;
122
62.3k
  if (range_shift != expected_range_shift) {
123
43
    return Error("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
124
43
  }
125
126
62.2k
  std::vector<Subtable314Range> ranges(segcount);
127
128
424k
  for (unsigned i = 0; i < segcount; ++i) {
129
362k
    if (!subtable.ReadU16(&ranges[i].end_range)) {
130
21
      return Error("Failed to read segment %d", i);
131
21
    }
132
362k
  }
133
134
62.2k
  uint16_t padding;
135
62.2k
  if (!subtable.ReadU16(&padding)) {
136
31
    return Error("Failed to read cmap subtable segment padding");
137
31
  }
138
62.2k
  if (padding) {
139
24
    return Error("Non zero cmap subtable segment padding (%d)", padding);
140
24
  }
141
142
424k
  for (unsigned i = 0; i < segcount; ++i) {
143
362k
    if (!subtable.ReadU16(&ranges[i].start_range)) {
144
25
      return Error("Failed to read segment start range %d", i);
145
25
    }
146
362k
  }
147
423k
  for (unsigned i = 0; i < segcount; ++i) {
148
361k
    if (!subtable.ReadS16(&ranges[i].id_delta)) {
149
27
      return Error("Failed to read segment delta %d", i);
150
27
    }
151
361k
  }
152
423k
  for (unsigned i = 0; i < segcount; ++i) {
153
361k
    ranges[i].id_range_offset_offset = subtable.offset();
154
361k
    if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
155
38
      return Error("Failed to read segment range offset %d", i);
156
38
    }
157
158
361k
    if (ranges[i].id_range_offset & 1) {
159
      // Some font generators seem to put 65535 on id_range_offset
160
      // for 0xFFFF-0xFFFF range.
161
      // (e.g., many fonts in http://www.princexml.com/fonts/)
162
25.1k
      if (i == segcount - 1u) {
163
25.0k
        Warning("bad id_range_offset");
164
25.0k
        ranges[i].id_range_offset = 0;
165
        // The id_range_offset value in the transcoded font will not change
166
        // since this table is not actually "transcoded" yet.
167
25.0k
      } else {
168
61
        return Error("Bad segment offset (%d)", ranges[i].id_range_offset);
169
61
      }
170
25.1k
    }
171
361k
  }
172
173
  // ranges must be ascending order, based on the end_code. Ranges may not
174
  // overlap.
175
361k
  for (unsigned i = 1; i < segcount; ++i) {
176
299k
    if ((i == segcount - 1u) &&
177
61.8k
        (ranges[i - 1].start_range == 0xffff) &&
178
1.71k
        (ranges[i - 1].end_range == 0xffff) &&
179
6
        (ranges[i].start_range == 0xffff) &&
180
4
        (ranges[i].end_range == 0xffff)) {
181
      // Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
182
      // We'll accept them as an exception.
183
3
      Warning("multiple 0xffff terminators found");
184
3
      continue;
185
3
    }
186
187
    // Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
188
    // unsorted table...
189
299k
    if (ranges[i].end_range <= ranges[i - 1].end_range) {
190
48
      return Error("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
191
48
    }
192
299k
    if (ranges[i].start_range <= ranges[i - 1].end_range) {
193
22
      return Error("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
194
22
    }
195
196
    // On many fonts, the value of {first, last}_char_index are incorrect.
197
    // Fix them.
198
299k
    if (os2->table.first_char_index != 0xFFFF &&
199
287k
        ranges[i].start_range != 0xFFFF &&
200
216k
        os2->table.first_char_index > ranges[i].start_range) {
201
28.8k
      os2->table.first_char_index = ranges[i].start_range;
202
28.8k
    }
203
299k
    if (os2->table.last_char_index != 0xFFFF &&
204
237k
        ranges[i].end_range != 0xFFFF &&
205
183k
        os2->table.last_char_index < ranges[i].end_range) {
206
67.0k
      os2->table.last_char_index = ranges[i].end_range;
207
67.0k
    }
208
299k
  }
209
210
  // The last range must end at 0xffff
211
61.9k
  if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
212
82
    return Error("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
213
82
                           ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
214
82
  }
215
216
  // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of
217
  // each code-point defined in the table and make sure that they are all valid
218
  // glyphs and that we don't access anything out-of-bounds.
219
422k
  for (unsigned i = 0; i < segcount; ++i) {
220
2.94M
    for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
221
2.58M
      const uint16_t code_point = static_cast<uint16_t>(cp);
222
2.58M
      if (ranges[i].id_range_offset == 0) {
223
        // this is explictly allowed to overflow in the spec
224
2.02M
        const uint16_t glyph = code_point + ranges[i].id_delta;
225
2.02M
        if (glyph >= num_glyphs) {
226
33
          return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
227
33
        }
228
2.02M
      } else {
229
555k
        const uint16_t range_delta = code_point - ranges[i].start_range;
230
        // this might seem odd, but it's true. The offset is relative to the
231
        // location of the offset value itself.
232
555k
        const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset +
233
555k
                                         ranges[i].id_range_offset +
234
555k
                                         range_delta * 2;
235
        // We need to be able to access a 16-bit value from this offset
236
555k
        if (glyph_id_offset + 1 >= length) {
237
38
          return Error("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
238
38
        }
239
555k
        uint16_t glyph;
240
555k
        std::memcpy(&glyph, data + glyph_id_offset, 2);
241
555k
        glyph = ots_ntohs(glyph);
242
555k
        if (glyph >= num_glyphs) {
243
42
          return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
244
42
        }
245
555k
      }
246
2.58M
    }
247
360k
  }
248
249
  // We accept the table.
250
  // TODO(yusukes): transcode the subtable.
251
61.7k
  if (platform == 3 && encoding == 0) {
252
1.36k
    this->subtable_3_0_4_data = data;
253
1.36k
    this->subtable_3_0_4_length = length;
254
60.4k
  } else if (platform == 3 && encoding == 1) {
255
52.1k
    this->subtable_3_1_4_data = data;
256
52.1k
    this->subtable_3_1_4_length = length;
257
52.1k
  } else if (platform == 0 && encoding == 3) {
258
8.23k
    this->subtable_0_3_4_data = data;
259
8.23k
    this->subtable_0_3_4_length = length;
260
8.23k
  } else {
261
0
    return Error("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
262
0
  }
263
264
61.7k
  return true;
265
61.7k
}
266
267
bool OpenTypeCMAP::Parse31012(const uint8_t *data, size_t length,
268
2.15k
                              uint16_t num_glyphs) {
269
2.15k
  ots::Buffer subtable(data, length);
270
271
  // Format 12 tables are simple. We parse these and fully serialise them
272
  // later.
273
274
2.15k
  if (!subtable.Skip(8)) {
275
48
    return Error("failed to skip the first 8 bytes of format 12 subtable");
276
48
  }
277
2.10k
  uint32_t language = 0;
278
2.10k
  if (!subtable.ReadU32(&language)) {
279
28
    return Error("can't read format 12 subtable language");
280
28
  }
281
2.07k
  if (language) {
282
34
    return Error("format 12 subtable language should be zero (%d)", language);
283
34
  }
284
285
2.04k
  uint32_t num_groups = 0;
286
2.04k
  if (!subtable.ReadU32(&num_groups)) {
287
28
    return Error("can't read number of format 12 subtable groups");
288
28
  }
289
2.01k
  if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
290
100
    return Error("Bad format 12 subtable group count %d", num_groups);
291
100
  }
292
293
1.91k
  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
294
1.91k
      = this->subtable_3_10_12;
295
1.91k
  groups.resize(num_groups);
296
297
172k
  for (unsigned i = 0; i < num_groups; ++i) {
298
170k
    if (!subtable.ReadU32(&groups[i].start_range) ||
299
170k
        !subtable.ReadU32(&groups[i].end_range) ||
300
170k
        !subtable.ReadU32(&groups[i].start_glyph_id)) {
301
0
      return Error("can't read format 12 subtable group");
302
0
    }
303
304
170k
    if (groups[i].start_range > kUnicodeUpperLimit ||
305
170k
        groups[i].end_range > kUnicodeUpperLimit ||
306
170k
        groups[i].start_glyph_id > 0xFFFF) {
307
161
      return Error("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
308
161
                             groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
309
161
    }
310
311
    // We assert that the glyph value is within range. Because of the range
312
    // limits, above, we don't need to worry about overflow.
313
170k
    if (groups[i].end_range < groups[i].start_range) {
314
17
      return Error("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
315
17
                             groups[i].end_range, groups[i].start_range);
316
17
    }
317
170k
    if ((groups[i].end_range - groups[i].start_range) +
318
170k
        groups[i].start_glyph_id >= num_glyphs) {
319
29
      return Error("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
320
29
    }
321
170k
  }
322
323
  // the groups must be sorted by start code and may not overlap
324
169k
  for (unsigned i = 1; i < num_groups; ++i) {
325
168k
    if (groups[i].start_range <= groups[i - 1].start_range) {
326
22
      return Error("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
327
22
                             groups[i].start_range, groups[i-1].start_range);
328
22
    }
329
168k
    if (groups[i].start_range <= groups[i - 1].end_range) {
330
1
      return Error("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
331
1
                             groups[i].start_range, groups[i-1].end_range);
332
1
    }
333
168k
  }
334
335
1.68k
  return true;
336
1.70k
}
337
338
bool OpenTypeCMAP::Parse31013(const uint8_t *data, size_t length,
339
555
                              uint16_t num_glyphs) {
340
555
  ots::Buffer subtable(data, length);
341
342
  // Format 13 tables are simple. We parse these and fully serialise them
343
  // later.
344
345
555
  if (!subtable.Skip(8)) {
346
39
    return Error("Bad cmap subtable length");
347
39
  }
348
516
  uint32_t language = 0;
349
516
  if (!subtable.ReadU32(&language)) {
350
33
    return Error("Can't read cmap subtable language");
351
33
  }
352
483
  if (language) {
353
40
    return Error("Cmap subtable language should be zero but is %d", language);
354
40
  }
355
356
443
  uint32_t num_groups = 0;
357
443
  if (!subtable.ReadU32(&num_groups)) {
358
21
    return Error("Can't read number of groups in a cmap subtable");
359
21
  }
360
361
  // We limit the number of groups in the same way as in 3.10.12 tables. See
362
  // the comment there in
363
422
  if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
364
86
    return Error("Bad format 13 subtable group count %d", num_groups);
365
86
  }
366
367
336
  std::vector<ots::OpenTypeCMAPSubtableRange> &groups = this->subtable_3_10_13;
368
336
  groups.resize(num_groups);
369
370
4.10k
  for (unsigned i = 0; i < num_groups; ++i) {
371
3.95k
    if (!subtable.ReadU32(&groups[i].start_range) ||
372
3.95k
        !subtable.ReadU32(&groups[i].end_range) ||
373
3.95k
        !subtable.ReadU32(&groups[i].start_glyph_id)) {
374
0
      return Error("Can't read subrange structure in a cmap subtable");
375
0
    }
376
377
    // We conservatively limit all of the values to protect some parsers from
378
    // overflows
379
3.95k
    if (groups[i].start_range > kUnicodeUpperLimit ||
380
3.90k
        groups[i].end_range > kUnicodeUpperLimit ||
381
3.85k
        groups[i].start_glyph_id > 0xFFFF) {
382
139
      return Error("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
383
139
    }
384
385
3.81k
    if (groups[i].start_glyph_id >= num_glyphs) {
386
45
      return Error("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
387
45
    }
388
3.81k
  }
389
390
  // the groups must be sorted by start code and may not overlap
391
857
  for (unsigned i = 1; i < num_groups; ++i) {
392
733
    if (groups[i].start_range <= groups[i - 1].start_range) {
393
26
      return Error("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
394
26
    }
395
707
    if (groups[i].start_range <= groups[i - 1].end_range) {
396
2
      return Error("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
397
2
    }
398
707
  }
399
400
124
  return true;
401
152
}
402
403
1.13k
bool OpenTypeCMAP::Parse0514(const uint8_t *data, size_t length) {
404
  // Unicode Variation Selector table
405
1.13k
  ots::Buffer subtable(data, length);
406
407
  // Format 14 tables are simple. We parse these and fully serialise them
408
  // later.
409
410
  // Skip format (USHORT) and length (ULONG)
411
1.13k
  if (!subtable.Skip(6)) {
412
37
    return Error("Can't read start of cmap subtable");
413
37
  }
414
415
1.09k
  uint32_t num_records = 0;
416
1.09k
  if (!subtable.ReadU32(&num_records)) {
417
18
    return Error("Can't read number of records in cmap subtable");
418
18
  }
419
1.07k
  if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
420
91
    return Error("Bad format 14 subtable records count %d", num_records);
421
91
  }
422
423
988
  std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
424
988
      = this->subtable_0_5_14;
425
988
  records.resize(num_records);
426
427
5.59k
  for (unsigned i = 0; i < num_records; ++i) {
428
4.93k
    if (!subtable.ReadU24(&records[i].var_selector) ||
429
4.92k
        !subtable.ReadU32(&records[i].default_offset) ||
430
4.88k
        !subtable.ReadU32(&records[i].non_default_offset)) {
431
57
      return Error("Can't read record structure of record %d in cmap subtale", i);
432
57
    }
433
    // Checks the value of variation selector
434
4.87k
    if (!((records[i].var_selector >= kMongolianVSStart &&
435
4.84k
           records[i].var_selector <= kMongolianVSEnd) ||
436
4.87k
          (records[i].var_selector >= kVSStart &&
437
4.84k
           records[i].var_selector <= kVSEnd) ||
438
4.71k
          (records[i].var_selector >= kIVSStart &&
439
4.66k
           records[i].var_selector <= kIVSEnd))) {
440
104
      return Error("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
441
104
    }
442
4.77k
    if (i > 0 &&
443
3.89k
        records[i-1].var_selector >= records[i].var_selector) {
444
39
      return Error("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
445
39
    }
446
447
    // Checks offsets
448
4.73k
    if (!records[i].default_offset && !records[i].non_default_offset) {
449
23
      return Error("No default aoffset in variation selector record %d", i);
450
23
    }
451
4.71k
    if (records[i].default_offset &&
452
1.75k
        records[i].default_offset >= length) {
453
43
      return Error("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
454
43
    }
455
4.67k
    if (records[i].non_default_offset &&
456
3.58k
        records[i].non_default_offset >= length) {
457
63
      return Error("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
458
63
    }
459
4.67k
  }
460
461
1.76k
  for (unsigned i = 0; i < num_records; ++i) {
462
    // Checks default UVS table
463
1.53k
    if (records[i].default_offset) {
464
596
      subtable.set_offset(records[i].default_offset);
465
596
      uint32_t num_ranges = 0;
466
596
      if (!subtable.ReadU32(&num_ranges)) {
467
19
        return Error("Can't read number of ranges in record %d", i);
468
19
      }
469
577
      if (num_ranges == 0 || subtable.remaining() / 4 < num_ranges) {
470
76
        return Error("Bad number of ranges (%d) in record %d", num_ranges, i);
471
76
      }
472
473
501
      uint32_t last_unicode_value = 0;
474
501
      std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges
475
501
          = records[i].ranges;
476
501
      ranges.resize(num_ranges);
477
478
1.06k
      for (unsigned j = 0; j < num_ranges; ++j) {
479
676
        if (!subtable.ReadU24(&ranges[j].unicode_value) ||
480
676
            !subtable.ReadU8(&ranges[j].additional_count)) {
481
0
          return Error("Can't read range info in variation selector record %d", i);
482
0
        }
483
676
        const uint32_t check_value =
484
676
            ranges[j].unicode_value + ranges[j].additional_count;
485
676
        if (ranges[j].unicode_value == 0 ||
486
635
            ranges[j].unicode_value > kUnicodeUpperLimit ||
487
594
            check_value > kUVSUpperLimit ||
488
594
            (last_unicode_value &&
489
147
             ranges[j].unicode_value <= last_unicode_value)) {
490
117
          return Error("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
491
117
        }
492
559
        last_unicode_value = check_value;
493
559
      }
494
501
    }
495
496
    // Checks non default UVS table
497
1.31k
    if (records[i].non_default_offset) {
498
1.01k
      subtable.set_offset(records[i].non_default_offset);
499
1.01k
      uint32_t num_mappings = 0;
500
1.01k
      if (!subtable.ReadU32(&num_mappings)) {
501
18
        return Error("Can't read number of mappings in variation selector record %d", i);
502
18
      }
503
992
      if (num_mappings == 0 || subtable.remaining() / 5 < num_mappings) {
504
70
        return Error("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
505
70
      }
506
507
922
      uint32_t last_unicode_value = 0;
508
922
      std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings
509
922
          = records[i].mappings;
510
922
      mappings.resize(num_mappings);
511
512
2.03k
      for (unsigned j = 0; j < num_mappings; ++j) {
513
1.24k
        if (!subtable.ReadU24(&mappings[j].unicode_value) ||
514
1.24k
            !subtable.ReadU16(&mappings[j].glyph_id)) {
515
0
          return Error("Can't read mapping %d in variation selector record %d", j, i);
516
0
        }
517
1.24k
        if (mappings[j].glyph_id == 0 || mappings[j].unicode_value == 0) {
518
55
          return Error("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
519
55
        }
520
1.18k
        if (mappings[j].unicode_value > kUnicodeUpperLimit) {
521
40
          return Error("Invalid Unicode value (%04X > %04X) in mapping %d of variation selector %d", mappings[j].unicode_value, kUnicodeUpperLimit, j, i);
522
40
        }
523
1.14k
        if (last_unicode_value &&
524
246
            mappings[j].unicode_value <= last_unicode_value) {
525
29
          return Error("Out of order Unicode value (%04X <= %04X) in mapping %d of variation selector %d", mappings[j].unicode_value, last_unicode_value, j, i);
526
29
        }
527
1.11k
        last_unicode_value = mappings[j].unicode_value;
528
1.11k
      }
529
922
    }
530
1.31k
  }
531
532
235
  if (subtable.offset() != length) {
533
35
    return Error("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
534
35
  }
535
200
  this->subtable_0_5_14_length = subtable.offset();
536
200
  return true;
537
235
}
538
539
516
bool OpenTypeCMAP::Parse100(const uint8_t *data, size_t length) {
540
  // Mac Roman table
541
516
  ots::Buffer subtable(data, length);
542
543
516
  if (!subtable.Skip(4)) {
544
122
    return Error("Bad cmap subtable");
545
122
  }
546
394
  uint16_t language = 0;
547
394
  if (!subtable.ReadU16(&language)) {
548
28
    return Error("Can't read language in cmap subtable");
549
28
  }
550
366
  if (language) {
551
    // simsun.ttf has non-zero language id.
552
155
    Warning("language id should be zero: %u", language);
553
155
  }
554
555
366
  this->subtable_1_0_0.reserve(kFormat0ArraySize);
556
69.5k
  for (size_t i = 0; i < kFormat0ArraySize; ++i) {
557
69.3k
    uint8_t glyph_id = 0;
558
69.3k
    if (!subtable.ReadU8(&glyph_id)) {
559
127
      return Error("Can't read glyph id at array[%ld] in cmap subtable", i);
560
127
    }
561
69.1k
    this->subtable_1_0_0.push_back(glyph_id);
562
69.1k
  }
563
564
239
  return true;
565
366
}
566
567
74.3k
bool OpenTypeCMAP::Parse(const uint8_t *data, size_t length) {
568
74.3k
  Buffer table(data, length);
569
570
74.3k
  uint16_t version = 0;
571
74.3k
  uint16_t num_tables = 0;
572
74.3k
  if (!table.ReadU16(&version) ||
573
74.2k
      !table.ReadU16(&num_tables)) {
574
96
    return Error("Can't read structure of cmap");
575
96
  }
576
577
74.2k
  if (version != 0) {
578
131
    return Error("Non zero cmap version (%d)", version);
579
131
  }
580
74.0k
  if (!num_tables) {
581
68
    return Error("No subtables in cmap!");
582
68
  }
583
584
74.0k
  std::vector<CMAPSubtableHeader> subtable_headers;
585
586
  // read the subtable headers
587
74.0k
  subtable_headers.reserve(num_tables);
588
221k
  for (unsigned i = 0; i < num_tables; ++i) {
589
147k
    CMAPSubtableHeader subt;
590
591
147k
    if (!table.ReadU16(&subt.platform) ||
592
147k
        !table.ReadU16(&subt.encoding) ||
593
147k
        !table.ReadU32(&subt.offset)) {
594
138
      return Error("Can't read subtable information cmap subtable %d", i);
595
138
    }
596
597
147k
    subtable_headers.push_back(subt);
598
147k
  }
599
600
73.8k
  const size_t data_offset = table.offset();
601
602
  // make sure that all the offsets are valid.
603
218k
  for (unsigned i = 0; i < num_tables; ++i) {
604
145k
    if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
605
57
      return Error("Bad subtable offset in cmap subtable %d", i);
606
57
    }
607
145k
    if (subtable_headers[i].offset < data_offset ||
608
145k
        subtable_headers[i].offset >= length) {
609
109
      return Error("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
610
109
    }
611
145k
  }
612
613
  // the format of the table is the first couple of bytes in the table. The
614
  // length of the table is stored in a format-specific way.
615
218k
  for (unsigned i = 0; i < num_tables; ++i) {
616
144k
    table.set_offset(subtable_headers[i].offset);
617
144k
    if (!table.ReadU16(&subtable_headers[i].format)) {
618
34
      return Error("Can't read cmap subtable header format %d", i);
619
34
    }
620
621
144k
    uint16_t len = 0;
622
144k
    uint16_t lang = 0;
623
144k
    switch (subtable_headers[i].format) {
624
11.9k
      case 0:
625
77.5k
      case 4:
626
77.5k
        if (!table.ReadU16(&len)) {
627
26
          return Error("Can't read cmap subtable %d length", i);
628
26
        }
629
77.5k
        if (!table.ReadU16(&lang)) {
630
27
          return Error("Can't read cmap subtable %d language", i);
631
27
        }
632
77.5k
        subtable_headers[i].length = len;
633
77.5k
        subtable_headers[i].language = lang;
634
77.5k
        break;
635
4.34k
      case 12:
636
5.98k
      case 13:
637
5.98k
        if (!table.Skip(2)) {
638
34
          return Error("Bad cmap subtable %d structure", i);
639
34
        }
640
5.94k
        if (!table.ReadU32(&subtable_headers[i].length)) {
641
24
          return Error("Can read cmap subtable %d length", i);
642
24
        }
643
5.92k
        if (!table.ReadU32(&subtable_headers[i].language)) {
644
30
          return Error("Can't read cmap subtable %d language", i);
645
30
        }
646
5.89k
        break;
647
5.89k
      case 14:
648
1.43k
        if (!table.ReadU32(&subtable_headers[i].length)) {
649
32
          return Error("Can't read cmap subtable %d length", i);
650
32
        }
651
1.40k
        subtable_headers[i].language = 0;
652
1.40k
        break;
653
59.8k
      default:
654
59.8k
        subtable_headers[i].length = 0;
655
59.8k
        subtable_headers[i].language = 0;
656
59.8k
        break;
657
144k
    }
658
144k
  }
659
660
  // check if the table is sorted first by platform ID, then by encoding ID.
661
142k
  for (unsigned i = 1; i < num_tables; ++i) {
662
68.7k
    if (subtable_headers[i - 1].platform > subtable_headers[i].platform ||
663
45.1k
        (subtable_headers[i - 1].platform == subtable_headers[i].platform &&
664
16.7k
         (subtable_headers[i - 1].encoding > subtable_headers[i].encoding ||
665
10.3k
          (subtable_headers[i - 1].encoding == subtable_headers[i].encoding &&
666
1.30k
           subtable_headers[i - 1].language > subtable_headers[i].language))))
667
30.1k
      Warning("subtable %d with platform ID %d, encoding ID %d, language ID %d "
668
30.1k
                  "following subtable with platform ID %d, encoding ID %d, language ID %d",
669
30.1k
                  i,
670
30.1k
                  subtable_headers[i].platform,
671
30.1k
                  subtable_headers[i].encoding,
672
30.1k
                  subtable_headers[i].language,
673
30.1k
                  subtable_headers[i - 1].platform,
674
30.1k
                  subtable_headers[i - 1].encoding,
675
30.1k
                  subtable_headers[i - 1].language);
676
68.7k
  }
677
678
  // Now, verify that all the lengths are sane
679
212k
  for (unsigned i = 0; i < num_tables; ++i) {
680
138k
    if (!subtable_headers[i].length) continue;
681
76.0k
    if (subtable_headers[i].length > 1024 * 1024 * 1024) {
682
47
      return Error("Bad cmap subtable %d length", i);
683
47
    }
684
    // We know that both the offset and length are < 1GB, so the following
685
    // addition doesn't overflow
686
76.0k
    const uint32_t end_byte
687
76.0k
        = subtable_headers[i].offset + subtable_headers[i].length;
688
76.0k
    if (end_byte > length) {
689
213
      return Error("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
690
213
    }
691
76.0k
  }
692
693
  // check that the cmap subtables are not overlapping.
694
73.2k
  std::set<std::pair<uint32_t, uint32_t> > uniq_checker;
695
73.2k
  std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
696
210k
  for (unsigned i = 0; i < num_tables; ++i) {
697
137k
    const uint32_t end_byte
698
137k
        = subtable_headers[i].offset + subtable_headers[i].length;
699
700
137k
    if (!uniq_checker.insert(std::make_pair(subtable_headers[i].offset,
701
137k
                                            end_byte)).second) {
702
      // Sometimes Unicode table and MS table share exactly the same data.
703
      // We'll allow this.
704
14.2k
      continue;
705
14.2k
    }
706
122k
    overlap_checker.push_back(
707
122k
        std::make_pair(subtable_headers[i].offset,
708
122k
                       static_cast<uint8_t>(1) /* start */));
709
122k
    overlap_checker.push_back(
710
122k
        std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */));
711
122k
  }
712
73.2k
  std::sort(overlap_checker.begin(), overlap_checker.end());
713
73.2k
  int overlap_count = 0;
714
286k
  for (unsigned i = 0; i < overlap_checker.size(); ++i) {
715
213k
    overlap_count += (overlap_checker[i].second ? 1 : -1);
716
213k
    if (overlap_count > 1) {
717
660
      return Error("Excessive overlap count %d", overlap_count);
718
660
    }
719
213k
  }
720
721
  // we grab the number of glyphs in the file from the maxp table to make sure
722
  // that the character map isn't referencing anything beyound this range.
723
72.5k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
724
72.5k
      GetFont()->GetTypedTable(OTS_TAG_MAXP));
725
72.5k
  if (!maxp) {
726
0
    return Error("No maxp table in font! Needed by cmap.");
727
0
  }
728
72.5k
  const uint16_t num_glyphs = maxp->num_glyphs;
729
730
  // We only support a subset of the possible character map tables. Microsoft
731
  // 'strongly recommends' that everyone supports the Unicode BMP table with
732
  // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables:
733
  //   Platform ID   Encoding ID  Format
734
  //   0             0            4       (Unicode Default)
735
  //   0             1            4       (Unicode 1.1)
736
  //   0             3            4       (Unicode BMP)
737
  //   0             3            12      (Unicode UCS-4)
738
  //   0             5            14      (Unicode Variation Sequences)
739
  //   1             0            0       (Mac Roman)
740
  //   3             0            4       (MS Symbol)
741
  //   3             1            4       (MS Unicode BMP)
742
  //   3             10           12      (MS Unicode UCS-4)
743
  //   3             10           13      (MS UCS-4 Fallback mapping)
744
  //
745
  // Note:
746
  //  * 0-0-4 and 0-1-4 tables are (usually) written as a 3-1-4 table. If 3-1-4 table
747
  //    also exists, the 0-0-4 or 0-1-4 tables are ignored.
748
  //  * Unlike 0-0-4 table, 0-3-4 table is written as a 0-3-4 table.
749
  //    Some fonts which include 0-5-14 table seems to be required 0-3-4
750
  //    table. The 0-3-4 table will be wriiten even if 3-1-4 table also exists.
751
  //  * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also
752
  //    exists, the 0-3-12 table is ignored.
753
  //
754
755
172k
  for (unsigned i = 0; i < num_tables; ++i) {
756
103k
    if (subtable_headers[i].platform == 0) {
757
      // Unicode platform
758
759
68.8k
      if ((subtable_headers[i].encoding == 0 || subtable_headers[i].encoding == 1) &&
760
49.7k
          (subtable_headers[i].format == 4)) {
761
        // parse and output the 0-0-4 and 0-1-4 tables as 3-1-4 table. Sometimes the 0-0-4
762
        // table actually points to MS symbol data and thus should be parsed as
763
        // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
764
        // recovered in ots_cmap_serialise().
765
49.2k
        if (!ParseFormat4(3, 1, data + subtable_headers[i].offset,
766
49.2k
                      subtable_headers[i].length, num_glyphs)) {
767
384
          return Error("Failed to parse format 4 cmap subtable %d", i);
768
384
        }
769
49.2k
      } else if ((subtable_headers[i].encoding == 3) &&
770
9.74k
                 (subtable_headers[i].format == 4)) {
771
        // parse and output the 0-3-4 table as 0-3-4 table.
772
8.64k
        if (!ParseFormat4(0, 3, data + subtable_headers[i].offset,
773
8.64k
                      subtable_headers[i].length, num_glyphs)) {
774
408
          return Error("Failed to parse format 4 cmap subtable %d", i);
775
408
        }
776
10.9k
      } else if ((subtable_headers[i].encoding == 3 ||
777
9.81k
                  subtable_headers[i].encoding == 4) &&
778
2.60k
                 (subtable_headers[i].format == 12)) {
779
        // parse and output the 0-3-12 or 0-4-12 tables as 3-10-12 table.
780
1.02k
        if (!Parse31012(data + subtable_headers[i].offset,
781
1.02k
                        subtable_headers[i].length, num_glyphs)) {
782
259
          return Error("Failed to parse format 12 cmap subtable %d", i);
783
259
        }
784
9.88k
      } else if ((subtable_headers[i].encoding == 5) &&
785
1.42k
                 (subtable_headers[i].format == 14)) {
786
1.13k
        if (!Parse0514(data + subtable_headers[i].offset,
787
1.13k
                       subtable_headers[i].length)) {
788
934
          return Error("Failed to parse format 14 cmap subtable %d", i);
789
934
        }
790
1.13k
      }
791
68.8k
    } else if (subtable_headers[i].platform == 1) {
792
      // Mac platform
793
794
2.47k
      if ((subtable_headers[i].encoding == 0) &&
795
1.71k
          (subtable_headers[i].format == 0)) {
796
        // parse and output the 1-0-0 table.
797
516
        if (!Parse100(data + subtable_headers[i].offset,
798
516
                      subtable_headers[i].length)) {
799
277
          return OTS_FAILURE();
800
277
        }
801
516
      }
802
31.8k
    } else if (subtable_headers[i].platform == 3) {
803
      // MS platform
804
805
9.86k
      switch (subtable_headers[i].encoding) {
806
1.66k
        case 0:
807
6.45k
        case 1:
808
6.45k
          if (subtable_headers[i].format == 4) {
809
            // parse 3-0-4 or 3-1-4 table.
810
4.82k
            if (!ParseFormat4(subtable_headers[i].platform,
811
4.82k
                          subtable_headers[i].encoding,
812
4.82k
                          data + subtable_headers[i].offset,
813
4.82k
                          subtable_headers[i].length, num_glyphs)) {
814
171
              return OTS_FAILURE();
815
171
            }
816
4.82k
          }
817
6.28k
          break;
818
6.28k
        case 10:
819
2.24k
          if (subtable_headers[i].format == 12) {
820
1.12k
            this->subtable_3_10_12.clear();
821
1.12k
            if (!Parse31012(data + subtable_headers[i].offset,
822
1.12k
                            subtable_headers[i].length, num_glyphs)) {
823
209
              return OTS_FAILURE();
824
209
            }
825
1.12k
          } else if (subtable_headers[i].format == 13) {
826
555
            this->subtable_3_10_13.clear();
827
555
            if (!Parse31013(data + subtable_headers[i].offset,
828
555
                            subtable_headers[i].length, num_glyphs)) {
829
431
              return OTS_FAILURE();
830
431
            }
831
555
          }
832
1.60k
          break;
833
9.86k
      }
834
9.86k
    }
835
103k
  }
836
837
69.5k
  return true;
838
72.5k
}
839
840
37.5k
bool OpenTypeCMAP::Serialize(OTSStream *out) {
841
37.5k
  const bool have_034 = this->subtable_0_3_4_data != NULL;
842
37.5k
  const bool have_0514 = this->subtable_0_5_14.size() != 0;
843
37.5k
  const bool have_100 = this->subtable_1_0_0.size() != 0;
844
37.5k
  const bool have_304 = this->subtable_3_0_4_data != NULL;
845
  // MS Symbol and MS Unicode tables should not co-exist.
846
  // See the comment above in 0-0-4 parser.
847
37.5k
  const bool have_314 = (!have_304) && this->subtable_3_1_4_data;
848
37.5k
  const bool have_31012 = this->subtable_3_10_12.size() != 0;
849
37.5k
  const bool have_31013 = this->subtable_3_10_13.size() != 0;
850
37.5k
  const uint16_t num_subtables = static_cast<uint16_t>(have_034) +
851
37.5k
                                 static_cast<uint16_t>(have_0514) +
852
37.5k
                                 static_cast<uint16_t>(have_100) +
853
37.5k
                                 static_cast<uint16_t>(have_304) +
854
37.5k
                                 static_cast<uint16_t>(have_314) +
855
37.5k
                                 static_cast<uint16_t>(have_31012) +
856
37.5k
                                 static_cast<uint16_t>(have_31013);
857
37.5k
  const off_t table_start = out->Tell();
858
859
  // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
860
  // (e.g., old fonts for Mac). We don't support them.
861
37.5k
  if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
862
1.24k
    return Error("no supported subtables were found");
863
1.24k
  }
864
865
36.3k
  if (!out->WriteU16(0) ||
866
36.3k
      !out->WriteU16(num_subtables)) {
867
0
    return OTS_FAILURE();
868
0
  }
869
870
36.3k
  const off_t record_offset = out->Tell();
871
36.3k
  if (!out->Pad(num_subtables * 8)) {
872
0
    return OTS_FAILURE();
873
0
  }
874
875
36.3k
  const off_t offset_034 = out->Tell();
876
36.3k
  if (have_034) {
877
4.79k
    if (!out->Write(this->subtable_0_3_4_data,
878
4.79k
                    this->subtable_0_3_4_length)) {
879
0
      return OTS_FAILURE();
880
0
    }
881
4.79k
  }
882
883
36.3k
  const off_t offset_0514 = out->Tell();
884
36.3k
  if (have_0514) {
885
31
    const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
886
31
        = this->subtable_0_5_14;
887
31
    const unsigned num_records = records.size();
888
31
    if (!out->WriteU16(14) ||
889
31
        !out->WriteU32(this->subtable_0_5_14_length) ||
890
31
        !out->WriteU32(num_records)) {
891
0
      return OTS_FAILURE();
892
0
    }
893
122
    for (unsigned i = 0; i < num_records; ++i) {
894
91
      if (!out->WriteU24(records[i].var_selector) ||
895
91
          !out->WriteU32(records[i].default_offset) ||
896
91
          !out->WriteU32(records[i].non_default_offset)) {
897
0
        return OTS_FAILURE();
898
0
      }
899
91
    }
900
122
    for (unsigned i = 0; i < num_records; ++i) {
901
91
      if (records[i].default_offset) {
902
20
        const std::vector<ots::OpenTypeCMAPSubtableVSRange> &ranges
903
20
            = records[i].ranges;
904
20
        const unsigned num_ranges = ranges.size();
905
20
        if (!out->Seek(records[i].default_offset + offset_0514) ||
906
20
            !out->WriteU32(num_ranges)) {
907
0
          return OTS_FAILURE();
908
0
        }
909
43
        for (unsigned j = 0; j < num_ranges; ++j) {
910
23
          if (!out->WriteU24(ranges[j].unicode_value) ||
911
23
              !out->WriteU8(ranges[j].additional_count)) {
912
0
            return OTS_FAILURE();
913
0
          }
914
23
        }
915
20
      }
916
91
      if (records[i].non_default_offset) {
917
78
        const std::vector<ots::OpenTypeCMAPSubtableVSMapping> &mappings
918
78
            = records[i].mappings;
919
78
        const unsigned num_mappings = mappings.size();
920
78
        if (!out->Seek(records[i].non_default_offset + offset_0514) ||
921
78
            !out->WriteU32(num_mappings)) {
922
0
          return OTS_FAILURE();
923
0
        }
924
267
        for (unsigned j = 0; j < num_mappings; ++j) {
925
189
          if (!out->WriteU24(mappings[j].unicode_value) ||
926
189
              !out->WriteU16(mappings[j].glyph_id)) {
927
0
            return OTS_FAILURE();
928
0
          }
929
189
        }
930
78
      }
931
91
    }
932
31
  }
933
934
36.3k
  const off_t offset_100 = out->Tell();
935
36.3k
  if (have_100) {
936
35
    if (!out->WriteU16(0) ||  // format
937
35
        !out->WriteU16(6 + kFormat0ArraySize) ||  // length
938
35
        !out->WriteU16(0)) {  // language
939
0
      return OTS_FAILURE();
940
0
    }
941
35
    if (!out->Write(&(this->subtable_1_0_0[0]), kFormat0ArraySize)) {
942
0
      return OTS_FAILURE();
943
0
    }
944
35
  }
945
946
36.3k
  const off_t offset_304 = out->Tell();
947
36.3k
  if (have_304) {
948
964
    if (!out->Write(this->subtable_3_0_4_data,
949
964
                    this->subtable_3_0_4_length)) {
950
0
      return OTS_FAILURE();
951
0
    }
952
964
  }
953
954
36.3k
  const off_t offset_314 = out->Tell();
955
36.3k
  if (have_314) {
956
30.7k
    if (!out->Write(this->subtable_3_1_4_data,
957
30.7k
                    this->subtable_3_1_4_length)) {
958
0
      return OTS_FAILURE();
959
0
    }
960
30.7k
  }
961
962
36.3k
  const off_t offset_31012 = out->Tell();
963
36.3k
  if (have_31012) {
964
106
    std::vector<OpenTypeCMAPSubtableRange> &groups
965
106
        = this->subtable_3_10_12;
966
106
    const unsigned num_groups = groups.size();
967
106
    if (!out->WriteU16(12) ||
968
106
        !out->WriteU16(0) ||
969
106
        !out->WriteU32(num_groups * 12 + 16) ||
970
106
        !out->WriteU32(0) ||
971
106
        !out->WriteU32(num_groups)) {
972
0
      return OTS_FAILURE();
973
0
    }
974
975
5.04k
    for (unsigned i = 0; i < num_groups; ++i) {
976
4.94k
      if (!out->WriteU32(groups[i].start_range) ||
977
4.94k
          !out->WriteU32(groups[i].end_range) ||
978
4.94k
          !out->WriteU32(groups[i].start_glyph_id)) {
979
0
        return OTS_FAILURE();
980
0
      }
981
4.94k
    }
982
106
  }
983
984
36.3k
  const off_t offset_31013 = out->Tell();
985
36.3k
  if (have_31013) {
986
43
    std::vector<OpenTypeCMAPSubtableRange> &groups
987
43
        = this->subtable_3_10_13;
988
43
    const unsigned num_groups = groups.size();
989
43
    if (!out->WriteU16(13) ||
990
43
        !out->WriteU16(0) ||
991
43
        !out->WriteU32(num_groups * 12 + 16) ||
992
43
        !out->WriteU32(0) ||
993
43
        !out->WriteU32(num_groups)) {
994
0
      return OTS_FAILURE();
995
0
    }
996
997
182
    for (unsigned i = 0; i < num_groups; ++i) {
998
139
      if (!out->WriteU32(groups[i].start_range) ||
999
139
          !out->WriteU32(groups[i].end_range) ||
1000
139
          !out->WriteU32(groups[i].start_glyph_id)) {
1001
0
        return OTS_FAILURE();
1002
0
      }
1003
139
    }
1004
43
  }
1005
1006
36.3k
  const off_t table_end = out->Tell();
1007
1008
  // Now seek back and write the table of offsets
1009
36.3k
  if (!out->Seek(record_offset)) {
1010
0
    return OTS_FAILURE();
1011
0
  }
1012
1013
36.3k
  if (have_034) {
1014
4.79k
    if (!out->WriteU16(0) ||
1015
4.79k
        !out->WriteU16(3) ||
1016
4.79k
        !out->WriteU32(offset_034 - table_start)) {
1017
0
      return OTS_FAILURE();
1018
0
    }
1019
4.79k
  }
1020
1021
36.3k
  if (have_0514) {
1022
31
    if (!out->WriteU16(0) ||
1023
31
        !out->WriteU16(5) ||
1024
31
        !out->WriteU32(offset_0514 - table_start)) {
1025
0
      return OTS_FAILURE();
1026
0
    }
1027
31
  }
1028
1029
36.3k
  if (have_100) {
1030
35
    if (!out->WriteU16(1) ||
1031
35
        !out->WriteU16(0) ||
1032
35
        !out->WriteU32(offset_100 - table_start)) {
1033
0
      return OTS_FAILURE();
1034
0
    }
1035
35
  }
1036
1037
36.3k
  if (have_304) {
1038
964
    if (!out->WriteU16(3) ||
1039
964
        !out->WriteU16(0) ||
1040
964
        !out->WriteU32(offset_304 - table_start)) {
1041
0
      return OTS_FAILURE();
1042
0
    }
1043
964
  }
1044
1045
36.3k
  if (have_314) {
1046
30.7k
    if (!out->WriteU16(3) ||
1047
30.7k
        !out->WriteU16(1) ||
1048
30.7k
        !out->WriteU32(offset_314 - table_start)) {
1049
0
      return OTS_FAILURE();
1050
0
    }
1051
30.7k
  }
1052
1053
36.3k
  if (have_31012) {
1054
106
    if (!out->WriteU16(3) ||
1055
106
        !out->WriteU16(10) ||
1056
106
        !out->WriteU32(offset_31012 - table_start)) {
1057
0
      return OTS_FAILURE();
1058
0
    }
1059
106
  }
1060
1061
36.3k
  if (have_31013) {
1062
43
    if (!out->WriteU16(3) ||
1063
43
        !out->WriteU16(10) ||
1064
43
        !out->WriteU32(offset_31013 - table_start)) {
1065
0
      return OTS_FAILURE();
1066
0
    }
1067
43
  }
1068
1069
36.3k
  if (!out->Seek(table_end)) {
1070
0
    return OTS_FAILURE();
1071
0
  }
1072
1073
36.3k
  return true;
1074
36.3k
}
1075
1076
}  // namespace ots