// Copyright (c) 2018 The OTS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "fvar.h" namespace ots { // ----------------------------------------------------------------------------- // OpenTypeFVAR // ----------------------------------------------------------------------------- bool OpenTypeFVAR::Parse(const uint8_t* data, size_t length) { Buffer table(data, length); if (!table.ReadU16(&this->majorVersion) || !table.ReadU16(&this->minorVersion) || !table.ReadU16(&this->axesArrayOffset) || !table.ReadU16(&this->reserved) || !table.ReadU16(&this->axisCount) || !table.ReadU16(&this->axisSize) || !table.ReadU16(&this->instanceCount) || !table.ReadU16(&this->instanceSize)) { return DropVariations("Failed to read table header"); } if (this->majorVersion != 1) { return DropVariations("Unknown table version"); } if (this->minorVersion > 0) { Warning("Downgrading minor version to 0"); this->minorVersion = 0; } if (this->axesArrayOffset > length || this->axesArrayOffset < table.offset()) { return DropVariations("Bad axesArrayOffset"); } if (this->reserved != 2) { Warning("Expected reserved=2"); this->reserved = 2; } if (this->axisCount == 0) { return DropVariations("No variation axes"); } if (this->axisSize != 20) { return DropVariations("Invalid axisSize"); } // instanceCount is not validated if (this->instanceSize == this->axisCount * sizeof(Fixed) + 6) { this->instancesHavePostScriptNameID = true; } else if (this->instanceSize == this->axisCount * sizeof(Fixed) + 4) { this->instancesHavePostScriptNameID = false; } else { return DropVariations("Invalid instanceSize"); } // When we serialize, the axes array will go here, even if it was // originally at a different offset. So we update the axesArrayOffset // field for the header. uint32_t origAxesArrayOffset = this->axesArrayOffset; this->axesArrayOffset = table.offset(); table.set_offset(origAxesArrayOffset); for (unsigned i = 0; i < this->axisCount; i++) { this->axes.emplace_back(); auto& axis = this->axes[i]; if (!table.ReadU32(&axis.axisTag) || !table.ReadS32(&axis.minValue) || !table.ReadS32(&axis.defaultValue) || !table.ReadS32(&axis.maxValue) || !table.ReadU16(&axis.flags) || !table.ReadU16(&axis.axisNameID)) { return DropVariations("Failed to read axis record"); } if (!CheckTag(axis.axisTag)) { return DropVariations("Bad axis tag"); } if (!(axis.minValue <= axis.defaultValue && axis.defaultValue <= axis.maxValue)) { return DropVariations("Bad axis value range"); } if ((axis.flags & 0xFFFEu) != 0) { Warning("Discarding unknown axis flags"); axis.flags &= ~0xFFFEu; } if (axis.axisNameID <= 255 || axis.axisNameID >= 32768) { Warning("Axis nameID out of range"); // We don't check that the name actually exists -- assume the client can handle // a missing name when it tries to read the table. } } for (unsigned i = 0; i < this->instanceCount; i++) { this->instances.emplace_back(); auto& inst = this->instances[i]; if (!table.ReadU16(&inst.subfamilyNameID) || !table.ReadU16(&inst.flags)) { return DropVariations("Failed to read instance record"); } inst.coordinates.reserve(this->axisCount); for (unsigned j = 0; j < this->axisCount; j++) { inst.coordinates.emplace_back(); auto& coord = inst.coordinates[j]; if (!table.ReadS32(&coord)) { return DropVariations("Failed to read instance coordinates"); } } if (this->instancesHavePostScriptNameID) { if (!table.ReadU16(&inst.postScriptNameID)) { return DropVariations("Failed to read instance psname ID"); } } } if (table.remaining()) { return Warning("%zu bytes unparsed", table.remaining()); } return true; } bool OpenTypeFVAR::Serialize(OTSStream* out) { if (!out->WriteU16(this->majorVersion) || !out->WriteU16(this->minorVersion) || !out->WriteU16(this->axesArrayOffset) || !out->WriteU16(this->reserved) || !out->WriteU16(this->axisCount) || !out->WriteU16(this->axisSize) || !out->WriteU16(this->instanceCount) || !out->WriteU16(this->instanceSize)) { return Error("Failed to write table"); } for (unsigned i = 0; i < this->axisCount; i++) { const auto& axis = this->axes[i]; if (!out->WriteU32(axis.axisTag) || !out->WriteS32(axis.minValue) || !out->WriteS32(axis.defaultValue) || !out->WriteS32(axis.maxValue) || !out->WriteU16(axis.flags) || !out->WriteU16(axis.axisNameID)) { return Error("Failed to write table"); } } for (unsigned i = 0; i < this->instanceCount; i++) { const auto& inst = this->instances[i]; if (!out->WriteU16(inst.subfamilyNameID) || !out->WriteU16(inst.flags)) { return Error("Failed to write table"); } for (unsigned j = 0; j < this->axisCount; j++) { const auto& coord = inst.coordinates[j]; if (!out->WriteS32(coord)) { return Error("Failed to write table"); } } if (this->instancesHavePostScriptNameID) { if (!out->WriteU16(inst.postScriptNameID)) { return Error("Failed to write table"); } } } return true; } } // namespace ots