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