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 
Parse(const uint8_t * data,size_t length)13 bool OpenTypeFVAR::Parse(const uint8_t* data, size_t length) {
14   Buffer table(data, length);
15   if (!table.ReadU16(&this->majorVersion) ||
16       !table.ReadU16(&this->minorVersion) ||
17       !table.ReadU16(&this->axesArrayOffset) ||
18       !table.ReadU16(&this->reserved) ||
19       !table.ReadU16(&this->axisCount) ||
20       !table.ReadU16(&this->axisSize) ||
21       !table.ReadU16(&this->instanceCount) ||
22       !table.ReadU16(&this->instanceSize)) {
23     return DropVariations("Failed to read table header");
24   }
25   if (this->majorVersion != 1) {
26     return DropVariations("Unknown table version");
27   }
28   if (this->minorVersion > 0) {
29     Warning("Downgrading minor version to 0");
30     this->minorVersion = 0;
31   }
32   if (this->axesArrayOffset > length || this->axesArrayOffset < table.offset()) {
33     return DropVariations("Bad axesArrayOffset");
34   }
35   if (this->reserved != 2) {
36     Warning("Expected reserved=2");
37     this->reserved = 2;
38   }
39   if (this->axisCount == 0) {
40     return DropVariations("No variation axes");
41   }
42   if (this->axisSize != 20) {
43     return DropVariations("Invalid axisSize");
44   }
45   // instanceCount is not validated
46   if (this->instanceSize == this->axisCount * sizeof(Fixed) + 6) {
47     this->instancesHavePostScriptNameID = true;
48   } else if (this->instanceSize == this->axisCount * sizeof(Fixed) + 4) {
49     this->instancesHavePostScriptNameID = false;
50   } else {
51     return DropVariations("Invalid instanceSize");
52   }
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   uint32_t origAxesArrayOffset = this->axesArrayOffset;
58   this->axesArrayOffset = table.offset();
59 
60   table.set_offset(origAxesArrayOffset);
61   for (unsigned i = 0; i < this->axisCount; i++) {
62     this->axes.emplace_back();
63     auto& axis = this->axes[i];
64     if (!table.ReadU32(&axis.axisTag) ||
65         !table.ReadS32(&axis.minValue) ||
66         !table.ReadS32(&axis.defaultValue) ||
67         !table.ReadS32(&axis.maxValue) ||
68         !table.ReadU16(&axis.flags) ||
69         !table.ReadU16(&axis.axisNameID)) {
70       return DropVariations("Failed to read axis record");
71     }
72     if (!CheckTag(axis.axisTag)) {
73       return DropVariations("Bad axis tag");
74     }
75     if (!(axis.minValue <= axis.defaultValue && axis.defaultValue <= axis.maxValue)) {
76       return DropVariations("Bad axis value range");
77     }
78     if ((axis.flags & 0xFFFEu) != 0) {
79       Warning("Discarding unknown axis flags");
80       axis.flags &= ~0xFFFEu;
81     }
82     if (axis.axisNameID <= 255 || axis.axisNameID >= 32768) {
83       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     }
87   }
88 
89   for (unsigned i = 0; i < this->instanceCount; i++) {
90     this->instances.emplace_back();
91     auto& inst = this->instances[i];
92     if (!table.ReadU16(&inst.subfamilyNameID) ||
93         !table.ReadU16(&inst.flags)) {
94       return DropVariations("Failed to read instance record");
95     }
96     inst.coordinates.reserve(this->axisCount);
97     for (unsigned j = 0; j < this->axisCount; j++) {
98       inst.coordinates.emplace_back();
99       auto& coord = inst.coordinates[j];
100       if (!table.ReadS32(&coord)) {
101         return DropVariations("Failed to read instance coordinates");
102       }
103     }
104     if (this->instancesHavePostScriptNameID) {
105       if (!table.ReadU16(&inst.postScriptNameID)) {
106         return DropVariations("Failed to read instance psname ID");
107       }
108     }
109   }
110 
111   if (table.remaining()) {
112     return Warning("%zu bytes unparsed", table.remaining());
113   }
114 
115   return true;
116 }
117 
Serialize(OTSStream * out)118 bool OpenTypeFVAR::Serialize(OTSStream* out) {
119   if (!out->WriteU16(this->majorVersion) ||
120       !out->WriteU16(this->minorVersion) ||
121       !out->WriteU16(this->axesArrayOffset) ||
122       !out->WriteU16(this->reserved) ||
123       !out->WriteU16(this->axisCount) ||
124       !out->WriteU16(this->axisSize) ||
125       !out->WriteU16(this->instanceCount) ||
126       !out->WriteU16(this->instanceSize)) {
127     return Error("Failed to write table");
128   }
129 
130   for (unsigned i = 0; i < this->axisCount; i++) {
131     const auto& axis = this->axes[i];
132     if (!out->WriteU32(axis.axisTag) ||
133         !out->WriteS32(axis.minValue) ||
134         !out->WriteS32(axis.defaultValue) ||
135         !out->WriteS32(axis.maxValue) ||
136         !out->WriteU16(axis.flags) ||
137         !out->WriteU16(axis.axisNameID)) {
138       return Error("Failed to write table");
139     }
140   }
141 
142   for (unsigned i = 0; i < this->instanceCount; i++) {
143     const auto& inst = this->instances[i];
144     if (!out->WriteU16(inst.subfamilyNameID) ||
145         !out->WriteU16(inst.flags)) {
146       return Error("Failed to write table");
147     }
148     for (unsigned j = 0; j < this->axisCount; j++) {
149       const auto& coord = inst.coordinates[j];
150       if (!out->WriteS32(coord)) {
151         return Error("Failed to write table");
152       }
153     }
154     if (this->instancesHavePostScriptNameID) {
155       if (!out->WriteU16(inst.postScriptNameID)) {
156         return Error("Failed to write table");
157       }
158     }
159   }
160 
161   return true;
162 }
163 
164 }  // namespace ots
165