diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/ots/src/fvar.cc | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/gfx/ots/src/fvar.cc b/gfx/ots/src/fvar.cc new file mode 100644 index 0000000000..6f9b4d6ebf --- /dev/null +++ b/gfx/ots/src/fvar.cc @@ -0,0 +1,164 @@ +// 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 |