Coverage Report

Created: 2026-05-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/avar.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 "avar.h"
6
7
#include "fvar.h"
8
9
#include "variations.h"
10
11
namespace ots {
12
13
// -----------------------------------------------------------------------------
14
// OpenTypeAVAR
15
// -----------------------------------------------------------------------------
16
17
1.78k
bool OpenTypeAVAR::Parse(const uint8_t* data, size_t length) {
18
1.78k
  Buffer table(data, length);
19
1.78k
  if (!table.ReadU16(&this->majorVersion) ||
20
1.71k
      !table.ReadU16(&this->minorVersion) ||
21
1.68k
      !table.ReadU16(&this->reserved) ||
22
1.65k
      !table.ReadU16(&this->axisCount)) {
23
224
    return Drop("Failed to read table header");
24
224
  }
25
1.55k
  if (this->majorVersion > 2) {
26
589
    return Drop("Unknown table version");
27
589
  }
28
967
  if (this->majorVersion == 1) {
29
    // We can fix table
30
372
    if (this->minorVersion > 0) {
31
      // we only know how to serialize version 1.0
32
226
      Warning("Downgrading minor version to 0");
33
226
      this->minorVersion = 0;
34
226
    }
35
372
    if (this->reserved != 0) {
36
62
      Warning("Expected reserved=0");
37
62
      this->reserved = 0;
38
62
    }
39
595
  } else {
40
    // We serialize data unchanged, so drop even for minor errors
41
595
    if (this->minorVersion > 0) {
42
166
      return Drop("Unknown minor table version");
43
166
    }
44
429
    if (this->reserved != 0) {
45
79
      return Drop("Expected reserved=0");
46
79
    }
47
429
  }
48
49
722
  OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
50
722
      GetFont()->GetTypedTable(OTS_TAG_FVAR));
51
722
  if (!fvar) {
52
258
    return DropVariations("Required fvar table is missing");
53
258
  }
54
464
  if (axisCount != fvar->AxisCount()) {
55
22
    return Drop("Axis count mismatch");
56
22
  }
57
58
1.00k
  for (size_t i = 0; i < this->axisCount; i++) {
59
791
    this->axisSegmentMaps.emplace_back();
60
791
    uint16_t positionMapCount;
61
791
    if (!table.ReadU16(&positionMapCount)) {
62
7
      return Drop("Failed to read position map count");
63
7
    }
64
784
    int foundRequiredMappings = 0;
65
1.73k
    for (size_t j = 0; j < positionMapCount; j++) {
66
1.14k
      AxisValueMap map;
67
1.14k
      if (!table.ReadS16(&map.fromCoordinate) ||
68
1.12k
          !table.ReadS16(&map.toCoordinate)) {
69
29
        return Drop("Failed to read axis value map");
70
29
      }
71
1.11k
      if (map.fromCoordinate < -0x4000 ||
72
1.10k
          map.fromCoordinate > 0x4000 ||
73
1.08k
          map.toCoordinate < -0x4000 ||
74
1.06k
          map.toCoordinate > 0x4000) {
75
68
        return Drop("Axis value map coordinate out of range");
76
68
      }
77
1.04k
      if (j > 0) {
78
790
        if (map.fromCoordinate <= this->axisSegmentMaps[i].back().fromCoordinate ||
79
726
            map.toCoordinate < this->axisSegmentMaps[i].back().toCoordinate) {
80
92
          return Drop("Axis value map out of order");
81
92
        }
82
790
      }
83
954
      if ((map.fromCoordinate == -0x4000 && map.toCoordinate == -0x4000) ||
84
852
          (map.fromCoordinate == 0 && map.toCoordinate == 0) ||
85
699
          (map.fromCoordinate == 0x4000 && map.toCoordinate == 0x4000)) {
86
343
        ++foundRequiredMappings;
87
343
      }
88
954
      this->axisSegmentMaps[i].push_back(map);
89
954
    }
90
595
    if (positionMapCount > 0 && foundRequiredMappings != 3) {
91
33
      return Drop("A required mapping (for -1, 0 or 1) is missing");
92
33
    }
93
595
  }
94
95
213
  if (this->majorVersion < 2)
96
121
    return true;
97
98
92
  uint32_t axisIndexMapOffset;
99
92
  uint32_t varStoreOffset;
100
101
92
  if (!table.ReadU32(&axisIndexMapOffset) ||
102
91
      !table.ReadU32(&varStoreOffset)) {
103
2
    return Drop("Failed to read version 2 offsets");
104
2
  }
105
106
90
  Font *font = GetFont();
107
90
  uint32_t headerSize = table.offset();
108
109
90
  if (axisIndexMapOffset) {
110
87
    if (axisIndexMapOffset < headerSize || axisIndexMapOffset >= length) {
111
15
      return Drop("Bad delta set index offset in table header");
112
15
    }
113
72
    if (!ParseDeltaSetIndexMap(font, data + axisIndexMapOffset, length - axisIndexMapOffset)) {
114
7
      return Drop("Failed to parse delta set index map");
115
7
    }
116
72
  }
117
118
68
  if (varStoreOffset) {
119
65
    if (varStoreOffset < headerSize || varStoreOffset >= length) {
120
23
      return Drop("Bad item variation store offset in table header");
121
23
    }
122
42
    if (!ParseItemVariationStore(font, data + varStoreOffset, length - varStoreOffset)) {
123
38
      return Drop("Failed to parse item variation store");
124
38
    }
125
42
  }
126
127
7
  this->m_data = data;
128
7
  this->m_length = length;
129
130
7
  return true;
131
68
}
132
133
43
bool OpenTypeAVAR::Serialize(OTSStream* out) {
134
43
  if (this->majorVersion >= 2) {
135
4
    if (!out->Write(this->m_data, this->m_length)) {
136
0
      return Error("Failed to write table");
137
0
    }
138
4
    return true;
139
4
  }
140
141
39
  if (!out->WriteU16(this->majorVersion) ||
142
39
      !out->WriteU16(this->minorVersion) ||
143
39
      !out->WriteU16(this->reserved) ||
144
39
      !out->WriteU16(this->axisCount)) {
145
0
    return Error("Failed to write table");
146
0
  }
147
148
106
  for (size_t i = 0; i < this->axisCount; i++) {
149
67
    const auto& axisValueMap = this->axisSegmentMaps[i];
150
67
    if (!out->WriteU16(axisValueMap.size())) {
151
0
      return Error("Failed to write table");
152
0
    }
153
181
    for (size_t j = 0; j < axisValueMap.size(); j++) {
154
114
      if (!out->WriteS16(axisValueMap[j].fromCoordinate) ||
155
114
          !out->WriteS16(axisValueMap[j].toCoordinate)) {
156
0
        return Error("Failed to write table");
157
0
      }
158
114
    }
159
67
  }
160
161
39
  return true;
162
39
}
163
164
}  // namespace ots