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