diff options
Diffstat (limited to 'gfx/ots/src/variations.cc')
-rw-r--r-- | gfx/ots/src/variations.cc | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/gfx/ots/src/variations.cc b/gfx/ots/src/variations.cc new file mode 100644 index 0000000000..55afd976ca --- /dev/null +++ b/gfx/ots/src/variations.cc @@ -0,0 +1,271 @@ +// 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 "layout.h" + +#include "fvar.h" + +// OpenType Variations Common Table Formats + +#define TABLE_NAME "Variations" // XXX: use individual table names + +namespace { + +bool ParseVariationRegionList(const ots::Font* font, const uint8_t* data, const size_t length, + uint16_t* regionCount) { + ots::Buffer subtable(data, length); + + uint16_t axisCount; + + if (!subtable.ReadU16(&axisCount) || + !subtable.ReadU16(regionCount)) { + return OTS_FAILURE_MSG("Failed to read variation region list header"); + } + + if (*regionCount == 0) { + return true; + } + + const ots::OpenTypeFVAR* fvar = + static_cast<ots::OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR)); + if (!fvar) { + return OTS_FAILURE_MSG("Required fvar table is missing"); + } + if (axisCount != fvar->AxisCount()) { + return OTS_FAILURE_MSG("Axis count mismatch"); + } + + for (unsigned i = 0; i < *regionCount; i++) { + for (unsigned j = 0; j < axisCount; j++) { + int16_t startCoord, peakCoord, endCoord; + if (!subtable.ReadS16(&startCoord) || + !subtable.ReadS16(&peakCoord) || + !subtable.ReadS16(&endCoord)) { + return OTS_FAILURE_MSG("Failed to read region axis coordinates"); + } + if (startCoord > peakCoord || peakCoord > endCoord) { + return OTS_FAILURE_MSG("Region axis coordinates out of order"); + } + if (startCoord < -0x4000 || endCoord > 0x4000) { + return OTS_FAILURE_MSG("Region axis coordinate out of range"); + } + if ((peakCoord < 0 && endCoord > 0) || + (peakCoord > 0 && startCoord < 0)) { + return OTS_FAILURE_MSG("Invalid region axis coordinates"); + } + } + } + + return true; +} + +bool +ParseVariationDataSubtable(const ots::Font* font, const uint8_t* data, const size_t length, + const uint16_t regionCount, + uint16_t* regionIndexCount) { + ots::Buffer subtable(data, length); + + uint16_t itemCount; + uint16_t wordDeltaCount; + + const uint16_t LONG_WORDS = 0x8000u; + const uint16_t WORD_DELTA_COUNT_MASK = 0x7FFF; + + if (!subtable.ReadU16(&itemCount) || + !subtable.ReadU16(&wordDeltaCount) || + !subtable.ReadU16(regionIndexCount)) { + return OTS_FAILURE_MSG("Failed to read variation data subtable header"); + } + + size_t valueSize = (wordDeltaCount & LONG_WORDS) ? 2 : 1; + wordDeltaCount &= WORD_DELTA_COUNT_MASK; + + if (wordDeltaCount > *regionIndexCount) { + return OTS_FAILURE_MSG("Bad word delta count"); + } + + for (unsigned i = 0; i < *regionIndexCount; i++) { + uint16_t regionIndex; + if (!subtable.ReadU16(®ionIndex) || regionIndex >= regionCount) { + return OTS_FAILURE_MSG("Bad region index"); + } + } + + if (!subtable.Skip(valueSize * size_t(itemCount) * (size_t(wordDeltaCount) + size_t(*regionIndexCount)))) { + return OTS_FAILURE_MSG("Failed to read delta data"); + } + + return true; +} + +} // namespace + +namespace ots { + +bool +ParseItemVariationStore(const Font* font, + const uint8_t* data, const size_t length, + std::vector<uint16_t>* regionIndexCounts) { + Buffer subtable(data, length); + + uint16_t format; + uint32_t variationRegionListOffset; + uint16_t itemVariationDataCount; + + if (!subtable.ReadU16(&format) || + !subtable.ReadU32(&variationRegionListOffset) || + !subtable.ReadU16(&itemVariationDataCount)) { + return OTS_FAILURE_MSG("Failed to read item variation store header"); + } + + if (format != 1) { + return OTS_FAILURE_MSG("Unknown item variation store format"); + } + + if (variationRegionListOffset < subtable.offset() + 4 * itemVariationDataCount || + variationRegionListOffset > length) { + return OTS_FAILURE_MSG("Invalid variation region list offset"); + } + + uint16_t regionCount; + if (!ParseVariationRegionList(font, + data + variationRegionListOffset, + length - variationRegionListOffset, + ®ionCount)) { + return OTS_FAILURE_MSG("Failed to parse variation region list"); + } + + for (unsigned i = 0; i < itemVariationDataCount; i++) { + uint32_t offset; + if (!subtable.ReadU32(&offset)) { + return OTS_FAILURE_MSG("Failed to read variation data subtable offset"); + } + if (offset >= length) { + return OTS_FAILURE_MSG("Bad offset to variation data subtable"); + } + uint16_t regionIndexCount = 0; + if (!ParseVariationDataSubtable(font, data + offset, length - offset, + regionCount, + ®ionIndexCount)) { + return OTS_FAILURE_MSG("Failed to parse variation data subtable"); + } + if (regionIndexCounts) { + regionIndexCounts->push_back(regionIndexCount); + } + } + + return true; +} + +bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length) { + Buffer subtable(data, length); + + uint16_t entryFormat; + uint16_t mapCount; + + if (!subtable.ReadU16(&entryFormat) || + !subtable.ReadU16(&mapCount)) { + return OTS_FAILURE_MSG("Failed to read delta set index map header"); + } + + const uint16_t MAP_ENTRY_SIZE_MASK = 0x0030; + + const uint16_t entrySize = (((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1); + if (!subtable.Skip(entrySize * mapCount)) { + return OTS_FAILURE_MSG("Failed to read delta set index map data"); + } + + return true; +} + +bool ParseVariationData(const Font* font, const uint8_t* data, size_t length, + size_t axisCount, size_t sharedTupleCount) { + Buffer subtable(data, length); + + uint16_t tupleVariationCount; + uint16_t dataOffset; + if (!subtable.ReadU16(&tupleVariationCount) || + !subtable.ReadU16(&dataOffset)) { + return OTS_FAILURE_MSG("Failed to read variation data header"); + } + + if (dataOffset > length) { + return OTS_FAILURE_MSG("Invalid serialized data offset"); + } + + tupleVariationCount &= 0x0FFF; // mask off flags + + const uint16_t EMBEDDED_PEAK_TUPLE = 0x8000; + const uint16_t INTERMEDIATE_REGION = 0x4000; + const uint16_t TUPLE_INDEX_MASK = 0x0FFF; + + for (unsigned i = 0; i < tupleVariationCount; i++) { + uint16_t variationDataSize; + uint16_t tupleIndex; + + if (!subtable.ReadU16(&variationDataSize) || + !subtable.ReadU16(&tupleIndex)) { + return OTS_FAILURE_MSG("Failed to read tuple variation header"); + } + + if (tupleIndex & EMBEDDED_PEAK_TUPLE) { + for (unsigned axis = 0; axis < axisCount; axis++) { + int16_t coordinate; + if (!subtable.ReadS16(&coordinate)) { + return OTS_FAILURE_MSG("Failed to read tuple coordinate"); + } + if (coordinate < -0x4000 || coordinate > 0x4000) { + return OTS_FAILURE_MSG("Tuple coordinate not in the range [-1.0, 1.0]: %g", coordinate / 16384.); + } + } + } + + if (tupleIndex & INTERMEDIATE_REGION) { + std::vector<int16_t> startTuple(axisCount); + for (unsigned axis = 0; axis < axisCount; axis++) { + int16_t coordinate; + if (!subtable.ReadS16(&coordinate)) { + return OTS_FAILURE_MSG("Failed to read tuple coordinate"); + } + if (coordinate < -0x4000 || coordinate > 0x4000) { + return OTS_FAILURE_MSG("Tuple coordinate not in the range [-1.0, 1.0]: %g", coordinate / 16384.); + } + startTuple.push_back(coordinate); + } + + std::vector<int16_t> endTuple(axisCount); + for (unsigned axis = 0; axis < axisCount; axis++) { + int16_t coordinate; + if (!subtable.ReadS16(&coordinate)) { + return OTS_FAILURE_MSG("Failed to read tuple coordinate"); + } + if (coordinate < -0x4000 || coordinate > 0x4000) { + return OTS_FAILURE_MSG("Tuple coordinate not in the range [-1.0, 1.0]: %g", coordinate / 16384.); + } + endTuple.push_back(coordinate); + } + + for (unsigned axis = 0; axis < axisCount; axis++) { + if (startTuple[axis] > endTuple[axis]) { + return OTS_FAILURE_MSG("Invalid intermediate range"); + } + } + } + + if (!(tupleIndex & EMBEDDED_PEAK_TUPLE)) { + tupleIndex &= TUPLE_INDEX_MASK; + if (tupleIndex >= sharedTupleCount) { + return OTS_FAILURE_MSG("Tuple index out of range"); + } + } + } + + // TODO: we don't attempt to interpret the serialized data block + + return true; +} + +} // namespace ots + +#undef TABLE_NAME |