Coverage Report

Created: 2025-07-11 06:13

/src/ots/src/stat.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2018 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 "stat.h"
6
#include "name.h"
7
8
namespace ots {
9
10
// -----------------------------------------------------------------------------
11
// OpenTypeSTAT
12
// -----------------------------------------------------------------------------
13
14
3.32k
bool OpenTypeSTAT::ValidateNameId(uint16_t nameid) {
15
3.32k
  OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
16
3.32k
      GetFont()->GetTypedTable(OTS_TAG_NAME));
17
18
3.32k
  if (!name || !name->IsValidNameId(nameid)) {
19
98
    Drop("Invalid nameID: %d", nameid);
20
98
    return false;
21
98
  }
22
23
3.22k
  if ((nameid >= 26 && nameid <= 255) || nameid >= 32768) {
24
2
    Warning("nameID out of range: %d", nameid);
25
2
    return true;
26
2
  }
27
28
3.22k
  return  true;
29
3.22k
}
30
31
1.25k
bool OpenTypeSTAT::Parse(const uint8_t* data, size_t length) {
32
1.25k
  Buffer table(data, length);
33
1.25k
  if (!table.ReadU16(&this->majorVersion) ||
34
1.25k
      !table.ReadU16(&this->minorVersion) ||
35
1.25k
      !table.ReadU16(&this->designAxisSize) ||
36
1.25k
      !table.ReadU16(&this->designAxisCount) ||
37
1.25k
      !table.ReadU32(&this->designAxesOffset) ||
38
1.25k
      !table.ReadU16(&this->axisValueCount) ||
39
1.25k
      !table.ReadU32(&this->offsetToAxisValueOffsets) ||
40
1.25k
      !(this->minorVersion < 1 || table.ReadU16(&this->elidedFallbackNameID))) {
41
123
    return Drop("Failed to read table header");
42
123
  }
43
1.12k
  if (this->majorVersion != 1) {
44
196
    return Drop("Unknown table version");
45
196
  }
46
932
  if (this->minorVersion > 2) {
47
271
    Warning("Unknown minor version, downgrading to 2");
48
271
    this->minorVersion = 2;
49
271
  }
50
51
932
  size_t headerEnd = table.offset();
52
53
932
  if (this->designAxisCount == 0) {
54
304
    if (this->designAxesOffset != 0) {
55
265
      Warning("Unexpected non-zero designAxesOffset");
56
265
      this->designAxesOffset = 0;
57
265
    }
58
628
  } else {
59
628
    if (this->designAxisSize < sizeof(AxisRecord)) {
60
6
      return Drop("Invalid designAxisSize");
61
6
    }
62
622
    if (this->designAxesOffset < headerEnd ||
63
622
        size_t(this->designAxesOffset) > length ||
64
622
        size_t(this->designAxisCount) * size_t(this->designAxisSize) >
65
565
          length - size_t(this->designAxesOffset)) {
66
94
      return Drop("Invalid designAxesOffset");
67
94
    }
68
622
  }
69
70
3.13k
  for (size_t i = 0; i < this->designAxisCount; i++) {
71
2.42k
    table.set_offset(this->designAxesOffset + i * this->designAxisSize);
72
2.42k
    this->designAxes.emplace_back();
73
2.42k
    auto& axis = this->designAxes[i];
74
2.42k
    if (!table.ReadU32(&axis.axisTag) ||
75
2.42k
        !table.ReadU16(&axis.axisNameID) ||
76
2.42k
        !table.ReadU16(&axis.axisOrdering)) {
77
0
      return Drop("Failed to read design axis");
78
0
    }
79
2.42k
    if (!CheckTag(axis.axisTag)) {
80
51
      return Drop("Bad design axis tag");
81
51
    }
82
2.37k
    if (!ValidateNameId(axis.axisNameID)) {
83
69
      return true;
84
69
    }
85
2.37k
  }
86
87
  // TODO
88
  // - check that all axes defined in fvar are covered by STAT
89
  // - check that axisOrdering values are not duplicated (warn only)
90
91
712
  if (this->axisValueCount == 0) {
92
322
    if (this->offsetToAxisValueOffsets != 0) {
93
27
      Warning("Unexpected non-zero offsetToAxisValueOffsets");
94
27
      this->offsetToAxisValueOffsets = 0;
95
27
    }
96
390
  } else {
97
390
    if (this->offsetToAxisValueOffsets < headerEnd ||
98
390
        size_t(this->offsetToAxisValueOffsets) > length ||
99
390
        size_t(this->axisValueCount) * sizeof(uint16_t) >
100
335
          length - size_t(this->offsetToAxisValueOffsets)) {
101
61
      return Drop("Invalid offsetToAxisValueOffsets");
102
61
    }
103
390
  }
104
105
1.56k
  for (size_t i = 0; i < this->axisValueCount; i++) {
106
1.15k
    table.set_offset(this->offsetToAxisValueOffsets + i * sizeof(uint16_t));
107
1.15k
    uint16_t axisValueOffset;
108
1.15k
    if (!table.ReadU16(&axisValueOffset)) {
109
0
      return Drop("Failed to read axis value offset");
110
0
    }
111
    // We already checked that offsetToAxisValueOffsets doesn't exceed length,
112
    // so this subtraction will not underflow.
113
1.15k
    if (axisValueOffset > length - this->offsetToAxisValueOffsets) {
114
12
      return Drop("Invalid axis value offset");
115
12
    }
116
1.14k
    table.set_offset(this->offsetToAxisValueOffsets + axisValueOffset);
117
1.14k
    uint16_t format;
118
1.14k
    if (!table.ReadU16(&format)) {
119
12
      return Drop("Failed to read axis value format");
120
12
    }
121
1.13k
    this->axisValues.emplace_back(format);
122
1.13k
    auto& axisValue = axisValues[i];
123
1.13k
    switch (format) {
124
247
    case 1:
125
247
      if (!table.ReadU16(&axisValue.format1.axisIndex) ||
126
247
          !table.ReadU16(&axisValue.format1.flags) ||
127
247
          !table.ReadU16(&axisValue.format1.valueNameID) ||
128
247
          !table.ReadS32(&axisValue.format1.value)) {
129
17
        return Drop("Failed to read axis value (format 1)");
130
17
      }
131
230
      if (axisValue.format1.axisIndex >= this->designAxisCount) {
132
23
        return Drop("Axis index out of range");
133
23
      }
134
207
      if ((axisValue.format1.flags & 0xFFFCu) != 0) {
135
5
        Warning("Unexpected axis value flags");
136
5
        axisValue.format1.flags &= ~0xFFFCu;
137
5
      }
138
207
      if (!ValidateNameId(axisValue.format1.valueNameID)) {
139
1
        return true;
140
1
      }
141
206
      break;
142
578
    case 2:
143
578
      if (!table.ReadU16(&axisValue.format2.axisIndex) ||
144
578
          !table.ReadU16(&axisValue.format2.flags) ||
145
578
          !table.ReadU16(&axisValue.format2.valueNameID) ||
146
578
          !table.ReadS32(&axisValue.format2.nominalValue) ||
147
578
          !table.ReadS32(&axisValue.format2.rangeMinValue) ||
148
578
          !table.ReadS32(&axisValue.format2.rangeMaxValue)) {
149
12
        return Drop("Failed to read axis value (format 2)");
150
12
      }
151
566
      if (axisValue.format2.axisIndex >= this->designAxisCount) {
152
5
        return Drop("Axis index out of range");
153
5
      }
154
561
      if ((axisValue.format2.flags & 0xFFFCu) != 0) {
155
19
        Warning("Unexpected axis value flags");
156
19
        axisValue.format1.flags &= ~0xFFFCu;
157
19
      }
158
561
      if (!ValidateNameId(axisValue.format2.valueNameID)) {
159
6
        return true;
160
6
      }
161
555
      if (!(axisValue.format2.rangeMinValue <= axisValue.format2.nominalValue &&
162
555
            axisValue.format2.nominalValue <= axisValue.format2.rangeMaxValue)) {
163
38
        Warning("Bad axis value range or nominal value");
164
38
      }
165
555
      break;
166
117
    case 3:
167
117
      if (!table.ReadU16(&axisValue.format3.axisIndex) ||
168
117
          !table.ReadU16(&axisValue.format3.flags) ||
169
117
          !table.ReadU16(&axisValue.format3.valueNameID) ||
170
117
          !table.ReadS32(&axisValue.format3.value) ||
171
117
          !table.ReadS32(&axisValue.format3.linkedValue)) {
172
22
        return Drop("Failed to read axis value (format 3)");
173
22
      }
174
95
      if (axisValue.format3.axisIndex >= this->designAxisCount) {
175
21
        return Drop("Axis index out of range");
176
21
      }
177
74
      if ((axisValue.format3.flags & 0xFFFCu) != 0) {
178
4
        Warning("Unexpected axis value flags");
179
4
        axisValue.format3.flags &= ~0xFFFCu;
180
4
      }
181
74
      if (!ValidateNameId(axisValue.format3.valueNameID)) {
182
2
        return true;
183
2
      }
184
72
      break;
185
165
    case 4:
186
165
      if (this->minorVersion < 2) {
187
5
        return Drop("Invalid table minorVersion for format 4 axis values: %d", this->minorVersion);
188
5
      }
189
160
      if (!table.ReadU16(&axisValue.format4.axisCount) ||
190
160
          !table.ReadU16(&axisValue.format4.flags) ||
191
160
          !table.ReadU16(&axisValue.format4.valueNameID)) {
192
38
        return Drop("Failed to read axis value (format 4)");
193
38
      }
194
122
      if (axisValue.format4.axisCount > this->designAxisCount) {
195
20
        return Drop("Axis count out of range");
196
20
      }
197
102
      if ((axisValue.format4.flags & 0xFFFCu) != 0) {
198
73
        Warning("Unexpected axis value flags");
199
73
        axisValue.format4.flags &= ~0xFFFCu;
200
73
      }
201
102
      if (!ValidateNameId(axisValue.format4.valueNameID)) {
202
20
        return true;
203
20
      }
204
299
      for (unsigned j = 0; j < axisValue.format4.axisCount; j++) {
205
221
        axisValue.format4.axisValues.emplace_back();
206
221
        auto& v = axisValue.format4.axisValues[j];
207
221
        if (!table.ReadU16(&v.axisIndex) ||
208
221
            !table.ReadS32(&v.value)) {
209
1
          return Drop("Failed to read axis value");
210
1
        }
211
220
        if (v.axisIndex >= this->designAxisCount) {
212
3
          return Drop("Axis index out of range");
213
3
        }
214
220
      }
215
78
      break;
216
78
    default:
217
26
      return Drop("Unknown axis value format");
218
1.13k
    }
219
1.13k
  }
220
221
405
  return true;
222
651
}
223
224
82
bool OpenTypeSTAT::Serialize(OTSStream* out) {
225
82
  off_t tableStart = out->Tell();
226
227
82
  size_t headerSize = 5 * sizeof(uint16_t) + 2 * sizeof(uint32_t);
228
82
  if (this->minorVersion >= 1) {
229
63
    headerSize += sizeof(uint16_t);
230
63
  }
231
232
82
  if (this->designAxisCount == 0) {
233
44
    this->designAxesOffset = 0;
234
44
  } else {
235
38
    this->designAxesOffset = headerSize;
236
38
  }
237
238
82
  this->designAxisSize = sizeof(AxisRecord);
239
240
82
  if (this->axisValueCount == 0) {
241
70
    this->offsetToAxisValueOffsets = 0;
242
70
  } else {
243
12
    if (this->designAxesOffset == 0) {
244
2
      this->offsetToAxisValueOffsets = headerSize;
245
10
    } else {
246
10
      this->offsetToAxisValueOffsets = this->designAxesOffset + this->designAxisCount * this->designAxisSize;
247
10
    }
248
12
  }
249
250
82
  if (!out->WriteU16(this->majorVersion) ||
251
82
      !out->WriteU16(this->minorVersion) ||
252
82
      !out->WriteU16(this->designAxisSize) ||
253
82
      !out->WriteU16(this->designAxisCount) ||
254
82
      !out->WriteU32(this->designAxesOffset) ||
255
82
      !out->WriteU16(this->axisValueCount) ||
256
82
      !out->WriteU32(this->offsetToAxisValueOffsets) ||
257
82
      !(this->minorVersion < 1 || out->WriteU16(this->elidedFallbackNameID))) {
258
0
    return Error("Failed to write table header");
259
0
  }
260
261
82
  if (this->designAxisCount > 0) {
262
38
    if (out->Tell() - tableStart != this->designAxesOffset) {
263
0
      return Error("Error computing designAxesOffset");
264
0
    }
265
38
  }
266
267
212
  for (unsigned i = 0; i < this->designAxisCount; i++) {
268
130
    const auto& axis = this->designAxes[i];
269
130
    if (!out->WriteU32(axis.axisTag) ||
270
130
        !out->WriteU16(axis.axisNameID) ||
271
130
        !out->WriteU16(axis.axisOrdering)) {
272
0
      return Error("Failed to write design axis");
273
0
    }
274
130
  }
275
276
82
  if (this->axisValueCount > 0) {
277
12
    if (out->Tell() - tableStart != this->offsetToAxisValueOffsets) {
278
0
      return Error("Error computing offsetToAxisValueOffsets");
279
0
    }
280
12
  }
281
282
82
  uint32_t axisValueOffset = this->axisValueCount * sizeof(uint16_t);
283
160
  for (unsigned i = 0; i < this->axisValueCount; i++) {
284
78
    const auto& value = this->axisValues[i];
285
78
    if (!out->WriteU16(axisValueOffset)) {
286
0
      return Error("Failed to write axis value offset");
287
0
    }
288
78
    axisValueOffset += value.Length();
289
78
  }
290
160
  for (unsigned i = 0; i < this->axisValueCount; i++) {
291
78
    const auto& value = this->axisValues[i];
292
78
    if (!out->WriteU16(value.format)) {
293
0
      return Error("Failed to write axis value");
294
0
    }
295
78
    switch (value.format) {
296
29
    case 1:
297
29
      if (!out->WriteU16(value.format1.axisIndex) ||
298
29
          !out->WriteU16(value.format1.flags) ||
299
29
          !out->WriteU16(value.format1.valueNameID) ||
300
29
          !out->WriteS32(value.format1.value)) {
301
0
        return Error("Failed to write axis value");
302
0
      }
303
29
      break;
304
35
    case 2:
305
35
      if (!out->WriteU16(value.format2.axisIndex) ||
306
35
          !out->WriteU16(value.format2.flags) ||
307
35
          !out->WriteU16(value.format2.valueNameID) ||
308
35
          !out->WriteS32(value.format2.nominalValue) ||
309
35
          !out->WriteS32(value.format2.rangeMinValue) ||
310
35
          !out->WriteS32(value.format2.rangeMaxValue)) {
311
0
        return Error("Failed to write axis value");
312
0
      }
313
35
      break;
314
35
    case 3:
315
9
      if (!out->WriteU16(value.format3.axisIndex) ||
316
9
          !out->WriteU16(value.format3.flags) ||
317
9
          !out->WriteU16(value.format3.valueNameID) ||
318
9
          !out->WriteS32(value.format3.value) ||
319
9
          !out->WriteS32(value.format3.linkedValue)) {
320
0
        return Error("Failed to write axis value");
321
0
      }
322
9
      break;
323
9
    case 4:
324
5
      if (!out->WriteU16(value.format4.axisCount) ||
325
5
          !out->WriteU16(value.format4.flags) ||
326
5
          !out->WriteU16(value.format4.valueNameID)) {
327
0
        return Error("Failed to write axis value");
328
0
      }
329
8
      for (unsigned j = 0; j < value.format4.axisValues.size(); j++) {
330
3
        if (!out->WriteU16(value.format4.axisValues[j].axisIndex) ||
331
3
            !out->WriteS32(value.format4.axisValues[j].value)) {
332
0
          return Error("Failed to write axis value");
333
0
        }
334
3
      }
335
5
      break;
336
5
    default:
337
0
      return Error("Bad value format");
338
78
    }
339
78
  }
340
341
82
  return true;
342
82
}
343
344
}  // namespace ots