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.
9 // -----------------------------------------------------------------------------
11 // -----------------------------------------------------------------------------
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");
25 if (this->majorVersion
!= 1) {
26 return DropVariations("Unknown table version");
28 if (this->minorVersion
> 0) {
29 Warning("Downgrading minor version to 0");
30 this->minorVersion
= 0;
32 if (this->axesArrayOffset
> length
|| this->axesArrayOffset
< table
.offset()) {
33 return DropVariations("Bad axesArrayOffset");
35 if (this->reserved
!= 2) {
36 Warning("Expected reserved=2");
39 if (this->axisCount
== 0) {
40 return DropVariations("No variation axes");
42 if (this->axisSize
!= 20) {
43 return DropVariations("Invalid axisSize");
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;
51 return DropVariations("Invalid instanceSize");
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();
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");
72 if (!CheckTag(axis
.axisTag
)) {
73 return DropVariations("Bad axis tag");
75 if (!(axis
.minValue
<= axis
.defaultValue
&& axis
.defaultValue
<= axis
.maxValue
)) {
76 return DropVariations("Bad axis value range");
78 if ((axis
.flags
& 0xFFFEu
) != 0) {
79 Warning("Discarding unknown axis flags");
80 axis
.flags
&= ~0xFFFEu
;
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.
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");
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");
104 if (this->instancesHavePostScriptNameID
) {
105 if (!table
.ReadU16(&inst
.postScriptNameID
)) {
106 return DropVariations("Failed to read instance psname ID");
111 if (table
.remaining()) {
112 return Warning("%zu bytes unparsed", table
.remaining());
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");
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");
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");
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");
154 if (this->instancesHavePostScriptNameID
) {
155 if (!out
->WriteU16(inst
.postScriptNameID
)) {
156 return Error("Failed to write table");