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 |