Coverage Report

Created: 2026-06-14 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/fvar.cc
Line
Count
Source
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 "fvar.h"
6
7
namespace ots {
8
9
// -----------------------------------------------------------------------------
10
// OpenTypeFVAR
11
// -----------------------------------------------------------------------------
12
13
4.15k
bool OpenTypeFVAR::Parse(const uint8_t* data, size_t length) {
14
4.15k
  Buffer table(data, length);
15
4.15k
  if (!table.ReadU16(&this->majorVersion) ||
16
4.10k
      !table.ReadU16(&this->minorVersion) ||
17
3.95k
      !table.ReadU16(&this->axesArrayOffset) ||
18
3.87k
      !table.ReadU16(&this->reserved) ||
19
3.85k
      !table.ReadU16(&this->axisCount) ||
20
3.00k
      !table.ReadU16(&this->axisSize) ||
21
2.95k
      !table.ReadU16(&this->instanceCount) ||
22
2.85k
      !table.ReadU16(&this->instanceSize)) {
23
1.42k
    return DropVariations("Failed to read table header");
24
1.42k
  }
25
2.73k
  if (this->majorVersion != 1) {
26
899
    return DropVariations("Unknown table version");
27
899
  }
28
1.83k
  if (this->minorVersion > 0) {
29
261
    Warning("Downgrading minor version to 0");
30
261
    this->minorVersion = 0;
31
261
  }
32
1.83k
  if (this->axesArrayOffset > length || this->axesArrayOffset < table.offset()) {
33
70
    return DropVariations("Bad axesArrayOffset");
34
70
  }
35
1.76k
  if (this->reserved != 2) {
36
990
    Warning("Expected reserved=2");
37
990
    this->reserved = 2;
38
990
  }
39
1.76k
  if (this->axisCount == 0) {
40
35
    return DropVariations("No variation axes");
41
35
  }
42
1.73k
  if (this->axisSize != 20) {
43
28
    return DropVariations("Invalid axisSize");
44
28
  }
45
  // instanceCount is not validated
46
1.70k
  if (this->instanceSize == this->axisCount * sizeof(Fixed) + 6) {
47
1.18k
    this->instancesHavePostScriptNameID = true;
48
1.18k
  } else if (this->instanceSize == this->axisCount * sizeof(Fixed) + 4) {
49
456
    this->instancesHavePostScriptNameID = false;
50
456
  } else {
51
69
    return DropVariations("Invalid instanceSize");
52
69
  }
53
54
  // When we serialize, the axes array will go here, even if it was
55
  // originally at a different offset. So we update the axesArrayOffset
56
  // field for the header.
57
1.63k
  uint32_t origAxesArrayOffset = this->axesArrayOffset;
58
1.63k
  this->axesArrayOffset = table.offset();
59
60
1.63k
  table.set_offset(origAxesArrayOffset);
61
5.44k
  for (unsigned i = 0; i < this->axisCount; i++) {
62
4.07k
    this->axes.emplace_back();
63
4.07k
    auto& axis = this->axes[i];
64
4.07k
    if (!table.ReadU32(&axis.axisTag) ||
65
4.04k
        !table.ReadS32(&axis.minValue) ||
66
4.03k
        !table.ReadS32(&axis.defaultValue) ||
67
4.03k
        !table.ReadS32(&axis.maxValue) ||
68
4.02k
        !table.ReadU16(&axis.flags) ||
69
3.98k
        !table.ReadU16(&axis.axisNameID)) {
70
108
      return DropVariations("Failed to read axis record");
71
108
    }
72
3.96k
    if (!CheckTag(axis.axisTag)) {
73
61
      return DropVariations("Bad axis tag");
74
61
    }
75
3.90k
    if (!(axis.minValue <= axis.defaultValue && axis.defaultValue <= axis.maxValue)) {
76
93
      return DropVariations("Bad axis value range");
77
93
    }
78
3.81k
    if ((axis.flags & 0xFFFEu) != 0) {
79
1.71k
      Warning("Discarding unknown axis flags");
80
1.71k
      axis.flags &= ~0xFFFEu;
81
1.71k
    }
82
3.81k
    if (axis.axisNameID <= 255 || axis.axisNameID >= 32768) {
83
672
      Warning("Axis nameID out of range");
84
      // We don't check that the name actually exists -- assume the client can handle
85
      // a missing name when it tries to read the table.
86
672
    }
87
3.81k
  }
88
89
5.04k
  for (unsigned i = 0; i < this->instanceCount; i++) {
90
3.72k
    this->instances.emplace_back();
91
3.72k
    auto& inst = this->instances[i];
92
3.72k
    if (!table.ReadU16(&inst.subfamilyNameID) ||
93
3.70k
        !table.ReadU16(&inst.flags)) {
94
24
      return DropVariations("Failed to read instance record");
95
24
    }
96
3.69k
    inst.coordinates.reserve(this->axisCount);
97
12.8k
    for (unsigned j = 0; j < this->axisCount; j++) {
98
9.11k
      inst.coordinates.emplace_back();
99
9.11k
      auto& coord = inst.coordinates[j];
100
9.11k
      if (!table.ReadS32(&coord)) {
101
16
        return DropVariations("Failed to read instance coordinates");
102
16
      }
103
9.11k
    }
104
3.68k
    if (this->instancesHavePostScriptNameID) {
105
1.10k
      if (!table.ReadU16(&inst.postScriptNameID)) {
106
13
        return DropVariations("Failed to read instance psname ID");
107
13
      }
108
1.10k
    }
109
3.68k
  }
110
111
1.32k
  if (table.remaining()) {
112
255
    return Warning("%zu bytes unparsed", table.remaining());
113
255
  }
114
115
1.06k
  return true;
116
1.32k
}
117
118
620
bool OpenTypeFVAR::Serialize(OTSStream* out) {
119
620
  if (!out->WriteU16(this->majorVersion) ||
120
620
      !out->WriteU16(this->minorVersion) ||
121
620
      !out->WriteU16(this->axesArrayOffset) ||
122
620
      !out->WriteU16(this->reserved) ||
123
620
      !out->WriteU16(this->axisCount) ||
124
620
      !out->WriteU16(this->axisSize) ||
125
620
      !out->WriteU16(this->instanceCount) ||
126
620
      !out->WriteU16(this->instanceSize)) {
127
0
    return Error("Failed to write table");
128
0
  }
129
130
2.02k
  for (unsigned i = 0; i < this->axisCount; i++) {
131
1.40k
    const auto& axis = this->axes[i];
132
1.40k
    if (!out->WriteU32(axis.axisTag) ||
133
1.40k
        !out->WriteS32(axis.minValue) ||
134
1.40k
        !out->WriteS32(axis.defaultValue) ||
135
1.40k
        !out->WriteS32(axis.maxValue) ||
136
1.40k
        !out->WriteU16(axis.flags) ||
137
1.40k
        !out->WriteU16(axis.axisNameID)) {
138
0
      return Error("Failed to write table");
139
0
    }
140
1.40k
  }
141
142
1.52k
  for (unsigned i = 0; i < this->instanceCount; i++) {
143
903
    const auto& inst = this->instances[i];
144
903
    if (!out->WriteU16(inst.subfamilyNameID) ||
145
903
        !out->WriteU16(inst.flags)) {
146
0
      return Error("Failed to write table");
147
0
    }
148
3.38k
    for (unsigned j = 0; j < this->axisCount; j++) {
149
2.48k
      const auto& coord = inst.coordinates[j];
150
2.48k
      if (!out->WriteS32(coord)) {
151
0
        return Error("Failed to write table");
152
0
      }
153
2.48k
    }
154
903
    if (this->instancesHavePostScriptNameID) {
155
194
      if (!out->WriteU16(inst.postScriptNameID)) {
156
0
        return Error("Failed to write table");
157
0
      }
158
194
    }
159
903
  }
160
161
620
  return true;
162
620
}
163
164
}  // namespace ots