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)13bool 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)118bool 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