blob: 324a0fc838741ef7e3b290c6ba6a612672fa9cc1 [file] [log] [blame]
// 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 "gvar.h"
#include "fvar.h"
#include "maxp.h"
#include "variations.h"
#define TABLE_NAME "gvar"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeGVAR
// -----------------------------------------------------------------------------
static bool ParseSharedTuples(const Font* font, const uint8_t* data, size_t length,
size_t sharedTupleCount, size_t axisCount) {
Buffer subtable(data, length);
for (unsigned i = 0; i < sharedTupleCount; i++) {
for (unsigned j = 0; j < axisCount; j++) {
int16_t coordinate;
if (!subtable.ReadS16(&coordinate)) {
return OTS_FAILURE_MSG("Failed to read shared tuple coordinate");
}
}
}
return true;
}
static bool ParseGlyphVariationDataArray(const Font* font, const uint8_t* data, size_t length,
uint16_t flags, size_t glyphCount, size_t axisCount,
size_t sharedTupleCount,
const uint8_t* glyphVariationData,
size_t glyphVariationDataLength) {
Buffer subtable(data, length);
bool glyphVariationDataOffsetsAreLong = (flags & 0x0001u);
uint32_t prevOffset = 0;
for (size_t i = 0; i < glyphCount + 1; i++) {
uint32_t offset;
if (glyphVariationDataOffsetsAreLong) {
if (!subtable.ReadU32(&offset)) {
return OTS_FAILURE_MSG("Failed to read GlyphVariationData offset");
}
} else {
uint16_t halfOffset;
if (!subtable.ReadU16(&halfOffset)) {
return OTS_FAILURE_MSG("Failed to read GlyphVariationData offset");
}
offset = halfOffset * 2;
}
if (i > 0 && offset > prevOffset) {
if (prevOffset > glyphVariationDataLength) {
return OTS_FAILURE_MSG("Invalid GlyphVariationData offset");
}
if (!ParseVariationData(font, glyphVariationData + prevOffset,
glyphVariationDataLength - prevOffset,
axisCount, sharedTupleCount)) {
return OTS_FAILURE_MSG("Failed to parse GlyphVariationData");
}
}
prevOffset = offset;
}
return true;
}
bool OpenTypeGVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t axisCount;
uint16_t sharedTupleCount;
uint32_t sharedTuplesOffset;
uint16_t glyphCount;
uint16_t flags;
uint32_t glyphVariationDataArrayOffset;
if (!table.ReadU16(&majorVersion) ||
!table.ReadU16(&minorVersion) ||
!table.ReadU16(&axisCount) ||
!table.ReadU16(&sharedTupleCount) ||
!table.ReadU32(&sharedTuplesOffset) ||
!table.ReadU16(&glyphCount) ||
!table.ReadU16(&flags) ||
!table.ReadU32(&glyphVariationDataArrayOffset)) {
return DropVariations("Failed to read table header");
}
if (majorVersion != 1) {
return DropVariations("Unknown table version");
}
// check axisCount == fvar->axisCount
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
GetFont()->GetTypedTable(OTS_TAG_FVAR));
if (!fvar) {
return DropVariations("Required fvar table is missing");
}
if (axisCount != fvar->AxisCount()) {
return DropVariations("Axis count mismatch");
}
// check glyphCount == maxp->num_glyphs
OpenTypeMAXP* maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return DropVariations("Required maxp table is missing");
}
if (glyphCount != maxp->num_glyphs) {
return DropVariations("Glyph count mismatch");
}
if (sharedTupleCount > 0) {
if (sharedTuplesOffset < table.offset() || sharedTuplesOffset > length) {
return DropVariations("Invalid sharedTuplesOffset");
}
if (!ParseSharedTuples(GetFont(),
data + sharedTuplesOffset, length - sharedTuplesOffset,
sharedTupleCount, axisCount)) {
return DropVariations("Failed to parse shared tuples");
}
}
if (glyphVariationDataArrayOffset) {
if (glyphVariationDataArrayOffset > length) {
return DropVariations("Invalid glyphVariationDataArrayOffset");
}
if (!ParseGlyphVariationDataArray(GetFont(),
data + table.offset(), length - table.offset(),
flags, glyphCount, axisCount, sharedTupleCount,
data + glyphVariationDataArrayOffset,
length - glyphVariationDataArrayOffset)) {
return DropVariations("Failed to read glyph variation data array");
}
}
this->m_data = data;
this->m_length = length;
return true;
}
bool OpenTypeGVAR::Serialize(OTSStream* out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write gvar table");
}
return true;
}
} // namespace ots
#undef TABLE_NAME