165 lines
5.4 KiB
C++
165 lines
5.4 KiB
C++
// 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
|