Source code

Revision control

Copy as Markdown

Other Tools

// 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