Merge remote-tracking branch 'origin/master' into custom

This commit is contained in:
Roy Tam 2019-11-15 15:03:49 +08:00
commit d2b5ffafc2
210 changed files with 10739 additions and 14561 deletions

109
gfx/ots/src/avar.cc Normal file
View file

@ -0,0 +1,109 @@
// 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 "avar.h"
#include "fvar.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeAVAR
// -----------------------------------------------------------------------------
bool OpenTypeAVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
if (!table.ReadU16(&this->majorVersion) ||
!table.ReadU16(&this->minorVersion) ||
!table.ReadU16(&this->reserved) ||
!table.ReadU16(&this->axisCount)) {
return Drop("Failed to read table header");
}
if (this->majorVersion != 1) {
return Drop("Unknown table version");
}
if (this->minorVersion > 0) {
// we only know how to serialize version 1.0
Warning("Downgrading minor version to 0");
this->minorVersion = 0;
}
if (this->reserved != 0) {
Warning("Expected reserved=0");
this->reserved = 0;
}
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
GetFont()->GetTypedTable(OTS_TAG_FVAR));
if (!fvar) {
return DropVariations("Required fvar table is missing");
}
if (axisCount != fvar->AxisCount()) {
return Drop("Axis count mismatch");
}
for (size_t i = 0; i < this->axisCount; i++) {
this->axisSegmentMaps.emplace_back();
uint16_t positionMapCount;
if (!table.ReadU16(&positionMapCount)) {
return Drop("Failed to read position map count");
}
int foundRequiredMappings = 0;
for (size_t j = 0; j < positionMapCount; j++) {
AxisValueMap map;
if (!table.ReadS16(&map.fromCoordinate) ||
!table.ReadS16(&map.toCoordinate)) {
return Drop("Failed to read axis value map");
}
if (map.fromCoordinate < -0x4000 ||
map.fromCoordinate > 0x4000 ||
map.toCoordinate < -0x4000 ||
map.toCoordinate > 0x4000) {
return Drop("Axis value map coordinate out of range");
}
if (j > 0) {
if (map.fromCoordinate <= this->axisSegmentMaps[i].back().fromCoordinate ||
map.toCoordinate < this->axisSegmentMaps[i].back().toCoordinate) {
return Drop("Axis value map out of order");
}
}
if ((map.fromCoordinate == -0x4000 && map.toCoordinate == -0x4000) ||
(map.fromCoordinate == 0 && map.toCoordinate == 0) ||
(map.fromCoordinate == 0x4000 && map.toCoordinate == 0x4000)) {
++foundRequiredMappings;
}
this->axisSegmentMaps[i].push_back(map);
}
if (positionMapCount > 0 && foundRequiredMappings != 3) {
return Drop("A required mapping (for -1, 0 or 1) is missing");
}
}
return true;
}
bool OpenTypeAVAR::Serialize(OTSStream* out) {
if (!out->WriteU16(this->majorVersion) ||
!out->WriteU16(this->minorVersion) ||
!out->WriteU16(this->reserved) ||
!out->WriteU16(this->axisCount)) {
return Error("Failed to write table");
}
for (size_t i = 0; i < this->axisCount; i++) {
const auto& axisValueMap = this->axisSegmentMaps[i];
if (!out->WriteU16(axisValueMap.size())) {
return Error("Failed to write table");
}
for (size_t j = 0; j < axisValueMap.size(); j++) {
if (!out->WriteS16(axisValueMap[j].fromCoordinate) ||
!out->WriteS16(axisValueMap[j].toCoordinate)) {
return Error("Failed to write table");
}
}
}
return true;
}
} // namespace ots

42
gfx/ots/src/avar.h Normal file
View file

@ -0,0 +1,42 @@
// 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.
#ifndef OTS_AVAR_H_
#define OTS_AVAR_H_
#include "ots.h"
#include <vector>
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeAVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeAVAR : public Table {
public:
explicit OpenTypeAVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t reserved;
uint16_t axisCount;
struct AxisValueMap {
int16_t fromCoordinate;
int16_t toCoordinate;
};
std::vector<std::vector<AxisValueMap>> axisSegmentMaps;
};
} // namespace ots
#endif // OTS_AVAR_H_

File diff suppressed because it is too large Load diff

View file

@ -11,22 +11,28 @@
#include <string>
#include <vector>
#undef major // glibc defines major!
namespace ots {
struct CFFIndex {
CFFIndex()
: count(0), off_size(0), offset_to_next(0) {}
uint16_t count;
uint32_t count;
uint8_t off_size;
std::vector<uint32_t> offsets;
uint32_t offset_to_next;
};
typedef std::map<uint32_t, uint16_t> CFFFDSelect;
class OpenTypeCFF : public Table {
public:
explicit OpenTypeCFF(Font *font, uint32_t tag)
: Table(font, tag, tag),
major(0),
font_dict_length(0),
charstrings_index(NULL),
local_subrs(NULL),
m_data(NULL),
m_length(0) {
@ -37,21 +43,46 @@ class OpenTypeCFF : public Table {
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
// Major version number.
uint8_t major;
// Name INDEX. This name is used in name.cc as a postscript font name.
std::string name;
// The number of fonts the file has.
size_t font_dict_length;
// A map from glyph # to font #.
std::map<uint16_t, uint8_t> fd_select;
CFFFDSelect fd_select;
// A list of char strings.
std::vector<CFFIndex *> char_strings_array;
CFFIndex* charstrings_index;
// A list of Local Subrs associated with FDArrays. Can be empty.
std::vector<CFFIndex *> local_subrs_per_font;
// A Local Subrs associated with Top DICT. Can be NULL.
CFFIndex *local_subrs;
// CFF2 VariationStore regionIndexCount.
std::vector<uint16_t> region_index_count;
protected:
bool ValidateFDSelect(uint16_t num_glyphs);
private:
const uint8_t *m_data;
size_t m_length;
};
class OpenTypeCFF2 : public OpenTypeCFF {
public:
explicit OpenTypeCFF2(Font *font, uint32_t tag)
: OpenTypeCFF(font, tag),
m_data(NULL),
m_length(0) {
}
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
private:
const uint8_t *m_data;
size_t m_length;

View file

@ -5,7 +5,7 @@
// A parser for the Type 2 Charstring Format.
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
#include "cff_type2_charstring.h"
#include "cff_charstring.h"
#include <climits>
#include <cstdio>
@ -22,7 +22,6 @@ namespace {
// Note #5177.
const int32_t kMaxSubrsCount = 65536;
const size_t kMaxCharStringLength = 65535;
const size_t kMaxArgumentStack = 48;
const size_t kMaxNumberOfStemHints = 96;
const size_t kMaxSubrNesting = 10;
@ -30,117 +29,130 @@ const size_t kMaxSubrNesting = 10;
// will fail with the dummy value.
const int32_t dummy_result = INT_MAX;
bool ExecuteType2CharString(ots::Font *font,
size_t call_depth,
const ots::CFFIndex& global_subrs_index,
const ots::CFFIndex& local_subrs_index,
ots::Buffer *cff_table,
ots::Buffer *char_string,
std::stack<int32_t> *argument_stack,
bool *out_found_endchar,
bool *out_found_width,
size_t *in_out_num_stems);
bool ExecuteCharString(ots::OpenTypeCFF& cff,
size_t call_depth,
const ots::CFFIndex& global_subrs_index,
const ots::CFFIndex& local_subrs_index,
ots::Buffer *cff_table,
ots::Buffer *char_string,
std::stack<int32_t> *argument_stack,
bool *out_found_endchar,
bool *out_found_width,
size_t *in_out_num_stems,
bool cff2);
bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) {
if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) ||
(!cff2 && argument_stack->size() > ots::kMaxCFF1ArgumentStack)) {
return true;
}
return false;
}
#ifdef DUMP_T2CHARSTRING
// Converts |op| to a string and returns it.
const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
const char *CharStringOperatorToString(ots::CharStringOperator op) {
switch (op) {
case ots::kHStem:
return "HStem";
return "hstem";
case ots::kVStem:
return "VStem";
return "vstem";
case ots::kVMoveTo:
return "VMoveTo";
return "vmoveto";
case ots::kRLineTo:
return "RLineTo";
return "rlineto";
case ots::kHLineTo:
return "HLineTo";
return "hlineto";
case ots::kVLineTo:
return "VLineTo";
return "vlineto";
case ots::kRRCurveTo:
return "RRCurveTo";
return "rrcurveto";
case ots::kCallSubr:
return "CallSubr";
return "callsubr";
case ots::kReturn:
return "Return";
return "return";
case ots::kEndChar:
return "EndChar";
return "endchar";
case ots::kVSIndex:
return "vsindex";
case ots::kBlend:
return "blend";
case ots::kHStemHm:
return "HStemHm";
return "hstemhm";
case ots::kHintMask:
return "HintMask";
return "hintmask";
case ots::kCntrMask:
return "CntrMask";
return "cntrmask";
case ots::kRMoveTo:
return "RMoveTo";
return "rmoveto";
case ots::kHMoveTo:
return "HMoveTo";
return "hmoveto";
case ots::kVStemHm:
return "VStemHm";
return "vstemhm";
case ots::kRCurveLine:
return "RCurveLine";
return "rcurveline";
case ots::kRLineCurve:
return "RLineCurve";
return "rlinecurve";
case ots::kVVCurveTo:
return "VVCurveTo";
case ots::kHHCurveTo:
return "HHCurveTo";
return "hhcurveto";
case ots::kCallGSubr:
return "CallGSubr";
return "callgsubr";
case ots::kVHCurveTo:
return "VHCurveTo";
return "vhcurveto";
case ots::kHVCurveTo:
return "HVCurveTo";
case ots::kDotSection:
return "DotSection";
return "dotsection";
case ots::kAnd:
return "And";
return "and";
case ots::kOr:
return "Or";
return "or";
case ots::kNot:
return "Not";
return "not";
case ots::kAbs:
return "Abs";
return "abs";
case ots::kAdd:
return "Add";
return "add";
case ots::kSub:
return "Sub";
return "sub";
case ots::kDiv:
return "Div";
return "div";
case ots::kNeg:
return "Neg";
return "neg";
case ots::kEq:
return "Eq";
return "eq";
case ots::kDrop:
return "Drop";
return "drop";
case ots::kPut:
return "Put";
return "put";
case ots::kGet:
return "Get";
return "get";
case ots::kIfElse:
return "IfElse";
return "ifelse";
case ots::kRandom:
return "Random";
return "random";
case ots::kMul:
return "Mul";
return "mul";
case ots::kSqrt:
return "Sqrt";
return "sqrt";
case ots::kDup:
return "Dup";
return "dup";
case ots::kExch:
return "Exch";
return "exch";
case ots::kIndex:
return "Index";
return "index";
case ots::kRoll:
return "Roll";
return "roll";
case ots::kHFlex:
return "HFlex";
return "hflex";
case ots::kFlex:
return "Flex";
return "flex";
case ots::kHFlex1:
return "HFlex1";
return "hflex1";
case ots::kFlex1:
return "Flex1";
return "flex1";
}
return "UNKNOWN";
@ -150,9 +162,9 @@ const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
// Read one or more bytes from the |char_string| buffer and stores the number
// read on |out_number|. If the number read is an operator (ex 'vstem'), sets
// true on |out_is_operator|. Returns true if the function read a number.
bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
int32_t *out_number,
bool *out_is_operator) {
bool ReadNextNumberFromCharString(ots::Buffer *char_string,
int32_t *out_number,
bool *out_is_operator) {
uint8_t v = 0;
if (!char_string->ReadU8(&v)) {
return OTS_FAILURE();
@ -174,7 +186,7 @@ bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
*out_is_operator = true;
} else if (v <= 27) {
// Special handling for v==19 and v==20 are implemented in
// ExecuteType2CharStringOperator().
// ExecuteCharStringOperator().
*out_number = v;
*out_is_operator = true;
} else if (v == 28) {
@ -221,23 +233,63 @@ bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
return true;
}
bool ValidCFF2Operator(int32_t op) {
switch (op) {
case ots::kReturn:
case ots::kEndChar:
case ots::kAbs:
case ots::kAdd:
case ots::kSub:
case ots::kDiv:
case ots::kNeg:
case ots::kRandom:
case ots::kMul:
case ots::kSqrt:
case ots::kDrop:
case ots::kExch:
case ots::kIndex:
case ots::kRoll:
case ots::kDup:
case ots::kPut:
case ots::kGet:
case ots::kDotSection:
case ots::kAnd:
case ots::kOr:
case ots::kNot:
case ots::kEq:
case ots::kIfElse:
return false;
}
return true;
}
// Executes |op| and updates |argument_stack|. Returns true if the execution
// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
// calls ExecuteType2CharString() function. The arguments other than |op| and
// calls ExecuteCharString() function. The arguments other than |op| and
// |argument_stack| are passed for that reason.
bool ExecuteType2CharStringOperator(ots::Font *font,
int32_t op,
size_t call_depth,
const ots::CFFIndex& global_subrs_index,
const ots::CFFIndex& local_subrs_index,
ots::Buffer *cff_table,
ots::Buffer *char_string,
std::stack<int32_t> *argument_stack,
bool *out_found_endchar,
bool *in_out_found_width,
size_t *in_out_num_stems) {
bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff,
int32_t op,
size_t call_depth,
const ots::CFFIndex& global_subrs_index,
const ots::CFFIndex& local_subrs_index,
ots::Buffer *cff_table,
ots::Buffer *char_string,
std::stack<int32_t> *argument_stack,
bool *out_found_endchar,
bool *in_out_found_width,
size_t *in_out_num_stems,
bool *in_out_have_blend,
bool *in_out_have_visindex,
int32_t *in_out_vsindex,
bool cff2) {
ots::Font* font = cff.GetFont();
const size_t stack_size = argument_stack->size();
if (cff2 && !ValidCFF2Operator(op)) {
return OTS_FAILURE();
}
switch (op) {
case ots::kCallSubr:
case ots::kCallGSubr: {
@ -290,16 +342,17 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
}
ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
return ExecuteType2CharString(font,
call_depth + 1,
global_subrs_index,
local_subrs_index,
cff_table,
&char_string_to_jump,
argument_stack,
out_found_endchar,
in_out_found_width,
in_out_num_stems);
return ExecuteCharString(cff,
call_depth + 1,
global_subrs_index,
local_subrs_index,
cff_table,
&char_string_to_jump,
argument_stack,
out_found_endchar,
in_out_found_width,
in_out_num_stems,
cff2);
}
case ots::kReturn:
@ -310,6 +363,51 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
*in_out_found_width = true; // just in case.
return true;
case ots::kVSIndex: {
if (!cff2) {
return OTS_FAILURE();
}
if (stack_size != 1) {
return OTS_FAILURE();
}
if (*in_out_have_blend || *in_out_have_visindex) {
return OTS_FAILURE();
}
if (argument_stack->top() >= cff.region_index_count.size()) {
return OTS_FAILURE();
}
*in_out_have_visindex = true;
*in_out_vsindex = argument_stack->top();
while (!argument_stack->empty())
argument_stack->pop();
return true;
}
case ots::kBlend: {
if (!cff2) {
return OTS_FAILURE();
}
if (stack_size < 1) {
return OTS_FAILURE();
}
if (*in_out_vsindex >= cff.region_index_count.size()) {
return OTS_FAILURE();
}
uint16_t k = cff.region_index_count.at(*in_out_vsindex);
uint16_t n = argument_stack->top();
if (stack_size < n * (k + 1) + 1) {
return OTS_FAILURE();
}
// Keep the 1st n operands on the stack for the next operator to use and
// pop the rest. There can be multiple consecutive blend operator, so this
// makes sure the operands of all of them are kept on the stack.
while (argument_stack->size() > stack_size - ((n * k) + 1))
argument_stack->pop();
*in_out_have_blend = true;
return true;
}
case ots::kHStem:
case ots::kVStem:
case ots::kHStemHm:
@ -649,7 +747,7 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
argument_stack->pop();
argument_stack->push(dummy_result);
argument_stack->push(dummy_result);
if (argument_stack->size() > kMaxArgumentStack) {
if (ArgumentStackOverflows(argument_stack, cff2)) {
return OTS_FAILURE();
}
// TODO(yusukes): Implement this. We should push a real value for all
@ -729,26 +827,29 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
// in_out_found_width: true is set if |char_string| contains 'width' byte (which
// is 0 or 1 byte.)
// in_out_num_stems: total number of hstems and vstems processed so far.
bool ExecuteType2CharString(ots::Font *font,
size_t call_depth,
const ots::CFFIndex& global_subrs_index,
const ots::CFFIndex& local_subrs_index,
ots::Buffer *cff_table,
ots::Buffer *char_string,
std::stack<int32_t> *argument_stack,
bool *out_found_endchar,
bool *in_out_found_width,
size_t *in_out_num_stems) {
bool ExecuteCharString(ots::OpenTypeCFF& cff,
size_t call_depth,
const ots::CFFIndex& global_subrs_index,
const ots::CFFIndex& local_subrs_index,
ots::Buffer *cff_table,
ots::Buffer *char_string,
std::stack<int32_t> *argument_stack,
bool *out_found_endchar,
bool *in_out_found_width,
size_t *in_out_num_stems,
bool cff2) {
if (call_depth > kMaxSubrNesting) {
return OTS_FAILURE();
}
*out_found_endchar = false;
bool in_out_have_blend = false, in_out_have_visindex = false;
int32_t in_out_vsindex = 0;
const size_t length = char_string->length();
while (char_string->offset() < length) {
int32_t operator_or_operand = 0;
bool is_operator = false;
if (!ReadNextNumberFromType2CharString(char_string,
if (!ReadNextNumberFromCharString(char_string,
&operator_or_operand,
&is_operator)) {
return OTS_FAILURE();
@ -761,35 +862,39 @@ bool ExecuteType2CharString(ots::Font *font,
*/
if (!is_operator) {
std::fprintf(stderr, "#%d# ", operator_or_operand);
std::fprintf(stderr, "%d ", operator_or_operand);
} else {
std::fprintf(stderr, "#%s#\n",
Type2CharStringOperatorToString(
ots::Type2CharStringOperator(operator_or_operand))
std::fprintf(stderr, "%s\n",
CharStringOperatorToString(
ots::CharStringOperator(operator_or_operand))
);
}
#endif
if (!is_operator) {
argument_stack->push(operator_or_operand);
if (argument_stack->size() > kMaxArgumentStack) {
if (ArgumentStackOverflows(argument_stack, cff2)) {
return OTS_FAILURE();
}
continue;
}
// An operator is found. Execute it.
if (!ExecuteType2CharStringOperator(font,
operator_or_operand,
call_depth,
global_subrs_index,
local_subrs_index,
cff_table,
char_string,
argument_stack,
out_found_endchar,
in_out_found_width,
in_out_num_stems)) {
if (!ExecuteCharStringOperator(cff,
operator_or_operand,
call_depth,
global_subrs_index,
local_subrs_index,
cff_table,
char_string,
argument_stack,
out_found_endchar,
in_out_found_width,
in_out_num_stems,
&in_out_have_blend,
&in_out_have_visindex,
&in_out_vsindex,
cff2)) {
return OTS_FAILURE();
}
if (*out_found_endchar) {
@ -801,37 +906,39 @@ bool ExecuteType2CharString(ots::Font *font,
}
// No endchar operator is found.
if (cff2)
return true;
return OTS_FAILURE();
}
// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
// |out_local_subrs_to_use|. Returns true on success.
bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
const std::vector<ots::CFFIndex *> &local_subrs_per_font,
const ots::CFFIndex *local_subrs,
bool SelectLocalSubr(const ots::OpenTypeCFF& cff,
uint16_t glyph_index, // 0-origin
const ots::CFFIndex **out_local_subrs_to_use) {
bool cff2 = (cff.major == 2);
*out_local_subrs_to_use = NULL;
// First, find local subrs from |local_subrs_per_font|.
if ((fd_select.size() > 0) &&
(!local_subrs_per_font.empty())) {
if ((cff.fd_select.size() > 0) &&
(!cff.local_subrs_per_font.empty())) {
// Look up FDArray index for the glyph.
std::map<uint16_t, uint8_t>::const_iterator iter =
fd_select.find(glyph_index);
if (iter == fd_select.end()) {
const auto& iter = cff.fd_select.find(glyph_index);
if (iter == cff.fd_select.end()) {
return OTS_FAILURE();
}
const uint8_t fd_index = iter->second;
if (fd_index >= local_subrs_per_font.size()) {
const auto fd_index = iter->second;
if (fd_index >= cff.local_subrs_per_font.size()) {
return OTS_FAILURE();
}
*out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
} else if (local_subrs) {
*out_local_subrs_to_use = cff.local_subrs_per_font.at(fd_index);
} else if (cff.local_subrs) {
// Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
// entries. If The font has a local subrs index associated with the Top
// DICT (not FDArrays), use it.
*out_local_subrs_to_use = local_subrs;
*out_local_subrs_to_use = cff.local_subrs;
} else if (cff2 && cff.local_subrs_per_font.size() == 1) {
*out_local_subrs_to_use = cff.local_subrs_per_font.at(0);
} else {
// Just return NULL.
*out_local_subrs_to_use = NULL;
@ -844,18 +951,16 @@ bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
namespace ots {
bool ValidateType2CharStringIndex(
ots::Font *font,
const CFFIndex& char_strings_index,
bool ValidateCFFCharStrings(
ots::OpenTypeCFF& cff,
const CFFIndex& global_subrs_index,
const std::map<uint16_t, uint8_t> &fd_select,
const std::vector<CFFIndex *> &local_subrs_per_font,
const CFFIndex *local_subrs,
Buffer* cff_table) {
const CFFIndex& char_strings_index = *(cff.charstrings_index);
if (char_strings_index.offsets.size() == 0) {
return OTS_FAILURE(); // no charstring.
}
bool cff2 = (cff.major == 2);
// For each glyph, validate the corresponding charstring.
for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) {
// Prepare a Buffer object, |char_string|, which contains the charstring
@ -875,9 +980,7 @@ bool ValidateType2CharStringIndex(
// Get a local subrs for the glyph.
const unsigned glyph_index = i - 1; // index in the map is 0-origin.
const CFFIndex *local_subrs_to_use = NULL;
if (!SelectLocalSubr(fd_select,
local_subrs_per_font,
local_subrs,
if (!SelectLocalSubr(cff,
glyph_index,
&local_subrs_to_use)) {
return OTS_FAILURE();
@ -891,16 +994,19 @@ bool ValidateType2CharStringIndex(
// Check a charstring for the |i|-th glyph.
std::stack<int32_t> argument_stack;
bool found_endchar = false;
bool found_width = false;
// CFF2 CharString has no value for width, so we start with true here to
// error out if width is found.
bool found_width = cff2;
size_t num_stems = 0;
if (!ExecuteType2CharString(font,
0 /* initial call_depth is zero */,
global_subrs_index, *local_subrs_to_use,
cff_table, &char_string, &argument_stack,
&found_endchar, &found_width, &num_stems)) {
if (!ExecuteCharString(cff,
0 /* initial call_depth is zero */,
global_subrs_index, *local_subrs_to_use,
cff_table, &char_string, &argument_stack,
&found_endchar, &found_width, &num_stems,
cff2)) {
return OTS_FAILURE();
}
if (!found_endchar) {
if (!cff2 && !found_endchar) {
return OTS_FAILURE();
}
}

View file

@ -13,6 +13,9 @@
namespace ots {
const size_t kMaxCFF1ArgumentStack = 48;
const size_t kMaxCFF2ArgumentStack = 513;
// Validates all charstrings in |char_strings_index|. Charstring is a small
// language for font hinting defined in Adobe Technical Note #5177.
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
@ -34,17 +37,14 @@ namespace ots {
// local_subrs: A Local Subrs associated with Top DICT. Can be NULL.
// cff_table: A buffer which contains actual byte code of charstring, global
// subroutines and local subroutines.
bool ValidateType2CharStringIndex(
Font *font,
const CFFIndex &char_strings_index,
bool ValidateCFFCharStrings(
OpenTypeCFF& cff,
const CFFIndex &global_subrs_index,
const std::map<uint16_t, uint8_t> &fd_select,
const std::vector<CFFIndex *> &local_subrs_per_font,
const CFFIndex *local_subrs,
Buffer *cff_table);
// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
enum Type2CharStringOperator {
// and https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr
enum CharStringOperator {
kHStem = 1,
kVStem = 3,
kVMoveTo = 4,
@ -55,6 +55,8 @@ enum Type2CharStringOperator {
kCallSubr = 10,
kReturn = 11,
kEndChar = 14,
kVSIndex = 15,
kBlend = 16,
kHStemHm = 18,
kHintMask = 19,
kCntrMask = 20,

View file

@ -238,7 +238,7 @@ bool OpenTypeCMAP::ParseFormat4(int platform, int encoding,
}
uint16_t glyph;
std::memcpy(&glyph, data + glyph_id_offset, 2);
glyph = ntohs(glyph);
glyph = ots_ntohs(glyph);
if (glyph >= num_glyphs) {
return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
}
@ -771,9 +771,10 @@ bool OpenTypeCMAP::Parse(const uint8_t *data, size_t length) {
subtable_headers[i].length, num_glyphs)) {
return Error("Failed to parse format 4 cmap subtable %d", i);
}
} else if ((subtable_headers[i].encoding == 3) &&
} else if ((subtable_headers[i].encoding == 3 ||
subtable_headers[i].encoding == 4) &&
(subtable_headers[i].format == 12)) {
// parse and output the 0-3-12 table as 3-10-12 table.
// parse and output the 0-3-12 or 0-4-12 tables as 3-10-12 table.
if (!Parse31012(data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
return Error("Failed to parse format 12 cmap subtable %d", i);

56
gfx/ots/src/cvar.cc Normal file
View file

@ -0,0 +1,56 @@
// 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 "cvar.h"
#include "fvar.h"
#include "variations.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeCVAR
// -----------------------------------------------------------------------------
bool OpenTypeCVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
uint16_t majorVersion;
uint16_t minorVersion;
if (!table.ReadU16(&majorVersion) ||
!table.ReadU16(&minorVersion)) {
return Drop("Failed to read table header");
}
if (majorVersion != 1) {
return Drop("Unknown table version");
}
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
GetFont()->GetTypedTable(OTS_TAG_FVAR));
if (!fvar) {
return DropVariations("Required fvar table is missing");
}
if (!ParseVariationData(GetFont(), data + table.offset(), length - table.offset(),
fvar->AxisCount(), 0)) {
return Drop("Failed to parse variation data");
}
this->m_data = data;
this->m_length = length;
return true;
}
bool OpenTypeCVAR::Serialize(OTSStream* out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write cvar table");
}
return true;
}
} // namespace ots

31
gfx/ots/src/cvar.h Normal file
View file

@ -0,0 +1,31 @@
// 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.
#ifndef OTS_CVAR_H_
#define OTS_CVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeCVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeCVAR : public Table {
public:
explicit OpenTypeCVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_CVAR_H_

164
gfx/ots/src/fvar.cc Normal file
View file

@ -0,0 +1,164 @@
// 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

63
gfx/ots/src/fvar.h Normal file
View file

@ -0,0 +1,63 @@
// 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.
#ifndef OTS_FVAR_H_
#define OTS_FVAR_H_
#include <vector>
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeFVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeFVAR : public Table {
public:
explicit OpenTypeFVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
uint16_t AxisCount() const { return axisCount; }
private:
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t axesArrayOffset;
uint16_t reserved;
uint16_t axisCount;
uint16_t axisSize;
uint16_t instanceCount;
uint16_t instanceSize;
typedef int32_t Fixed; /* 16.16 fixed-point value */
struct VariationAxisRecord {
uint32_t axisTag;
Fixed minValue;
Fixed defaultValue;
Fixed maxValue;
uint16_t flags;
uint16_t axisNameID;
};
std::vector<VariationAxisRecord> axes;
struct InstanceRecord {
uint16_t subfamilyNameID;
uint16_t flags;
std::vector<Fixed> coordinates;
uint16_t postScriptNameID; // optional
};
std::vector<InstanceRecord> instances;
bool instancesHavePostScriptNameID;
};
} // namespace ots
#endif // OTS_FVAR_H_

View file

@ -11,20 +11,17 @@
#include "gsub.h"
#include "layout.h"
#include "maxp.h"
#include "variations.h"
// GDEF - The Glyph Definition Table
// http://www.microsoft.com/typography/otspec/gdef.htm
namespace {
// The maximum class value in class definition tables.
const uint16_t kMaxClassDefValue = 0xFFFF;
// The maximum class value in the glyph class definision table.
const uint16_t kMaxGlyphClassDefValue = 4;
// The maximum format number of caret value tables.
// We don't support format 3 for now. See the comment in
// ParseLigCaretListTable() for the reason.
const uint16_t kMaxCaretValueFormat = 2;
const uint16_t kMaxCaretValueFormat = 3;
} // namespace
@ -165,9 +162,6 @@ bool OpenTypeGDEF::ParseLigCaretListTable(const uint8_t *data, size_t length) {
if (!subtable.ReadU16(&caret_format)) {
return Error("Can't read caret values table %d in glyph %d", j, i);
}
// TODO(bashi): We only support caret value format 1 and 2 for now
// because there are no fonts which contain caret value format 3
// as far as we investigated.
if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
return Error("bad caret value format: %u", caret_format);
}
@ -176,6 +170,24 @@ bool OpenTypeGDEF::ParseLigCaretListTable(const uint8_t *data, size_t length) {
if (!subtable.Skip(2)) {
return Error("Bad caret value table structure %d in glyph %d", j, i);
}
if (caret_format == 3) {
uint16_t offset_device = 0;
if (!subtable.ReadU16(&offset_device)) {
return Error("Can't read device offset for caret value %d "
"in glyph %d", j, i);
}
uint16_t absolute_offset = lig_glyphs[i] + caret_value_offsets[j]
+ offset_device;
if (offset_device == 0 || absolute_offset >= length) {
return Error("Bad device offset for caret value %d in glyph %d: %d",
j, i, offset_device);
}
if (!ots::ParseDeviceTable(GetFont(), data + absolute_offset,
length - absolute_offset)) {
return Error("Bad device table for caret value %d in glyph %d",
j, i, offset_device);
}
}
}
}
return true;
@ -228,18 +240,15 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
uint32_t version = 0;
if (!table.ReadU32(&version)) {
uint16_t version_major = 0, version_minor = 0;
if (!table.ReadU16(&version_major) ||
!table.ReadU16(&version_minor)) {
return Error("Incomplete table");
}
if (version < 0x00010000 || version == 0x00010001) {
if (version_major != 1 || version_minor == 1) { // there is no v1.1
return Error("Bad version");
}
if (version >= 0x00010002) {
this->version_2 = true;
}
uint16_t offset_glyph_class_def = 0;
uint16_t offset_attach_list = 0;
uint16_t offset_lig_caret_list = 0;
@ -251,15 +260,23 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
return Error("Incomplete table");
}
uint16_t offset_mark_glyph_sets_def = 0;
if (this->version_2) {
if (version_minor >= 2) {
if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
return Error("Incomplete table");
}
}
uint32_t item_var_store_offset = 0;
if (version_minor >= 3) {
if (!table.ReadU32(&item_var_store_offset)) {
return Error("Incomplete table");
}
}
unsigned gdef_header_end = 4 + 4 * 2;
if (this->version_2)
if (version_minor >= 2)
gdef_header_end += 2;
if (version_minor >= 3)
gdef_header_end += 4;
// Parse subtables
if (offset_glyph_class_def) {
@ -272,7 +289,6 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
this->m_num_glyphs, kMaxGlyphClassDefValue)) {
return Error("Invalid glyph classes");
}
this->has_glyph_class_def = true;
}
if (offset_attach_list) {
@ -308,7 +324,6 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
this->m_num_glyphs, kMaxClassDefValue)) {
return Error("Invalid mark attachment list");
}
this->has_mark_attachment_class_def = true;
}
if (offset_mark_glyph_sets_def) {
@ -320,8 +335,19 @@ bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
length - offset_mark_glyph_sets_def)) {
return Error("Invalid mark glyph sets");
}
this->has_mark_glyph_sets_def = true;
}
if (item_var_store_offset) {
if (item_var_store_offset >= length ||
item_var_store_offset < gdef_header_end) {
return Error("invalid offset to item variation store");
}
if (!ParseItemVariationStore(GetFont(), data + item_var_store_offset,
length - item_var_store_offset)) {
return Error("Invalid item variation store");
}
}
this->m_data = data;
this->m_length = length;
return true;

View file

@ -13,10 +13,6 @@ class OpenTypeGDEF : public Table {
public:
explicit OpenTypeGDEF(Font *font, uint32_t tag)
: Table(font, tag, tag),
version_2(false),
has_glyph_class_def(false),
has_mark_attachment_class_def(false),
has_mark_glyph_sets_def(false),
num_mark_glyph_sets(0),
m_data(NULL),
m_length(0),
@ -26,10 +22,6 @@ class OpenTypeGDEF : public Table {
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool version_2;
bool has_glyph_class_def;
bool has_mark_attachment_class_def;
bool has_mark_glyph_sets_def;
uint16_t num_mark_glyph_sets;
private:

View file

@ -200,7 +200,19 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
if (prevent_decompression) {
return DropGraphite("Illegal nested compression");
}
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
size_t decompressed_size = this->compHead & FULL_SIZE;
if (decompressed_size < length) {
return DropGraphite("Decompressed size is less than compressed size");
}
if (decompressed_size == 0) {
return DropGraphite("Decompressed size is set to 0");
}
// decompressed table must be <= 30MB
if (decompressed_size > 30 * 1024 * 1024) {
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
decompressed_size / (1024.0 * 1024.0));
}
std::vector<uint8_t> decompressed(decompressed_size);
size_t outputSize = 0;
bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()),
@ -305,7 +317,7 @@ OctaboxMetrics::ParsePart(Buffer& table) {
unsigned subboxes_len = 0; // count of 1's in this->subbox_bitmap
for (uint16_t i = this->subbox_bitmap; i; i >>= 1) {
if (i & 1) {
if (i & 0b1) {
++subboxes_len;
}
}

View file

@ -25,7 +25,7 @@ bool OpenTypeGLOC::Parse(const uint8_t* data, size_t length) {
if (this->version >> 16 != 1) {
return DropGraphite("Unsupported table version: %u", this->version >> 16);
}
if (!table.ReadU16(&this->flags) || this->flags > 3) {
if (!table.ReadU16(&this->flags) || this->flags > 0b11) {
return DropGraphite("Failed to read valid flags");
}
if (!table.ReadU16(&this->numAttribs)) {

View file

@ -38,6 +38,16 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
delta += 2;
}
/* MS and Apple specs say this bit is reserved and must be set to zero, but
* Apple spec then contradicts itself and says it should be set on the first
* contour flag for simple glyphs with overlapping contours:
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html
* (Overlapping contours section) */
if (flag & (1u << 6) && *flag_index != 0) {
return Error("Bad glyph flag (%d), "
"bit 6 must be set to zero for flag %d", flag, *flag_index);
}
if (flag & (1u << 3)) { // repeat
if (*flag_index + 1 >= num_flags) {
return Error("Count too high (%d + 1 >= %d)", *flag_index, num_flags);
@ -57,8 +67,8 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
}
}
if ((flag & (1u << 6)) || (flag & (1u << 7))) { // reserved flags
return Error("Bad glyph flag value (%d), reserved flags must be set to zero", flag);
if (flag & (1u << 7)) { // reserved flag
return Error("Bad glyph flag (%d), reserved bit 7 must be set to zero", flag);
}
*coordinates_length += delta;
@ -96,8 +106,9 @@ bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,
if (this->maxp->version_1 &&
this->maxp->max_size_glyf_instructions < bytecode_length) {
return Error("Bytecode length is bigger than maxp.maxSizeOfInstructions "
"%d: %d", this->maxp->max_size_glyf_instructions, bytecode_length);
this->maxp->max_size_glyf_instructions = bytecode_length;
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions %d: %d",
this->maxp->max_size_glyf_instructions, bytecode_length);
}
if (!glyph.Skip(bytecode_length)) {
@ -192,9 +203,10 @@ bool OpenTypeGLYF::ParseCompositeGlyph(Buffer &glyph) {
if (this->maxp->version_1 &&
this->maxp->max_size_glyf_instructions < bytecode_length) {
return Error("Bytecode length is bigger than maxp.maxSizeOfInstructions "
"%d: %d",
this->maxp->max_size_glyf_instructions, bytecode_length);
this->maxp->max_size_glyf_instructions = bytecode_length;
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions "
"%d: %d",
this->maxp->max_size_glyf_instructions, bytecode_length);
}
if (!glyph.Skip(bytecode_length)) {

View file

@ -30,12 +30,12 @@ enum GPOS_TYPE {
GPOS_TYPE_RESERVED = 10
};
// The size of gpos header.
const unsigned kGposHeaderSize = 10;
// The size of gpos header, version 1.0.
const unsigned kGposHeaderSize_1_0 = 10;
// The size of gpos header, version 1.1.
const unsigned kGposHeaderSize_1_1 = 14;
// The maximum format number for anchor tables.
const uint16_t kMaxAnchorFormat = 3;
// The maximum number of class value.
const uint16_t kMaxClassDefValue = 0xFFFF;
// Lookup type parsers.
bool ParseSingleAdjustment(const ots::Font *font,
@ -393,12 +393,12 @@ bool ParsePairPosFormat2(const ots::Font *font,
// Check class definition tables.
if (!ots::ParseClassDefTable(font, data + offset_class_def1,
length - offset_class_def1,
num_glyphs, kMaxClassDefValue)) {
num_glyphs, ots::kMaxClassDefValue)) {
return OTS_FAILURE_MSG("Failed to parse class definition table 1");
}
if (!ots::ParseClassDefTable(font, data + offset_class_def2,
length - offset_class_def2,
num_glyphs, kMaxClassDefValue)) {
num_glyphs, ots::kMaxClassDefValue)) {
return OTS_FAILURE_MSG("Failed to parse class definition table 2");
}
@ -749,23 +749,34 @@ bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
Font *font = GetFont();
Buffer table(data, length);
uint32_t version = 0;
uint16_t version_major = 0, version_minor = 0;
uint16_t offset_script_list = 0;
uint16_t offset_feature_list = 0;
uint16_t offset_lookup_list = 0;
if (!table.ReadU32(&version) ||
uint32_t offset_feature_variations = 0;
if (!table.ReadU16(&version_major) ||
!table.ReadU16(&version_minor) ||
!table.ReadU16(&offset_script_list) ||
!table.ReadU16(&offset_feature_list) ||
!table.ReadU16(&offset_lookup_list)) {
return Error("Incomplete table");
}
if (version != 0x00010000) {
if (version_major != 1 || version_minor > 1) {
return Error("Bad version");
}
if (version_minor > 0) {
if (!table.ReadU32(&offset_feature_variations)) {
return Error("Incomplete table");
}
}
const size_t header_size =
(version_minor == 0) ? kGposHeaderSize_1_0 : kGposHeaderSize_1_1;
if (offset_lookup_list) {
if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
if (offset_lookup_list < header_size || offset_lookup_list >= length) {
return Error("Bad lookup list offset in table header");
}
@ -779,7 +790,7 @@ bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
uint16_t num_features = 0;
if (offset_feature_list) {
if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
if (offset_feature_list < header_size || offset_feature_list >= length) {
return Error("Bad feature list offset in table header");
}
@ -791,7 +802,7 @@ bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
}
if (offset_script_list) {
if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
if (offset_script_list < header_size || offset_script_list >= length) {
return Error("Bad script list offset in table header");
}
@ -801,6 +812,18 @@ bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
}
}
if (offset_feature_variations) {
if (offset_feature_variations < header_size || offset_feature_variations >= length) {
return Error("Bad feature variations offset in table header");
}
if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
length - offset_feature_variations,
this->num_lookups)) {
return Error("Failed to parse feature variations table");
}
}
this->m_data = data;
this->m_length = length;
return true;

View file

@ -14,6 +14,7 @@ template<typename ParentType>
class TablePart {
public:
TablePart(ParentType* parent) : parent(parent) { }
virtual ~TablePart() { }
virtual bool ParsePart(Buffer& table) = 0;
virtual bool SerializePart(OTSStream* out) const = 0;
protected:

View file

@ -17,8 +17,10 @@
namespace {
// The GSUB header size
const size_t kGsubHeaderSize = 4 + 3 * 2;
// The GSUB header size for table version 1.0
const size_t kGsubHeaderSize_1_0 = 4 + 3 * 2;
// GSUB header size v1.1
const size_t kGsubHeaderSize_1_1 = 4 + 3 * 2 + 4;
enum GSUB_TYPE {
GSUB_TYPE_SINGLE = 1,
@ -580,23 +582,34 @@ bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
Font *font = GetFont();
Buffer table(data, length);
uint32_t version = 0;
uint16_t version_major = 0, version_minor = 0;
uint16_t offset_script_list = 0;
uint16_t offset_feature_list = 0;
uint16_t offset_lookup_list = 0;
if (!table.ReadU32(&version) ||
uint32_t offset_feature_variations = 0;
if (!table.ReadU16(&version_major) ||
!table.ReadU16(&version_minor) ||
!table.ReadU16(&offset_script_list) ||
!table.ReadU16(&offset_feature_list) ||
!table.ReadU16(&offset_lookup_list)) {
return Error("Incomplete table");
}
if (version != 0x00010000) {
if (version_major != 1 || version_minor > 1) {
return Error("Bad version");
}
if (version_minor > 0) {
if (!table.ReadU32(&offset_feature_variations)) {
return Error("Incomplete table");
}
}
const size_t header_size =
(version_minor == 0) ? kGsubHeaderSize_1_0 : kGsubHeaderSize_1_1;
if (offset_lookup_list) {
if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
if (offset_lookup_list < header_size || offset_lookup_list >= length) {
return Error("Bad lookup list offset in table header");
}
@ -610,7 +623,7 @@ bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
uint16_t num_features = 0;
if (offset_feature_list) {
if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
if (offset_feature_list < header_size || offset_feature_list >= length) {
return Error("Bad feature list offset in table header");
}
@ -622,7 +635,7 @@ bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
}
if (offset_script_list) {
if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
if (offset_script_list < header_size || offset_script_list >= length) {
return Error("Bad script list offset in table header");
}
@ -632,6 +645,18 @@ bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
}
}
if (offset_feature_variations) {
if (offset_feature_variations < header_size || offset_feature_variations >= length) {
return Error("Bad feature variations offset in table header");
}
if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
length - offset_feature_variations,
this->num_lookups)) {
return Error("Failed to parse feature variations table");
}
}
this->m_data = data;
this->m_length = length;
return true;

158
gfx/ots/src/gvar.cc Normal file
View file

@ -0,0 +1,158 @@
// 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

31
gfx/ots/src/gvar.h Normal file
View file

@ -0,0 +1,31 @@
// 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.
#ifndef OTS_GVAR_H_
#define OTS_GVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeGVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeGVAR : public Table {
public:
explicit OpenTypeGVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_GVAR_H_

85
gfx/ots/src/hvar.cc Normal file
View file

@ -0,0 +1,85 @@
// 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 "hvar.h"
#include "variations.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeHVAR
// -----------------------------------------------------------------------------
bool OpenTypeHVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
uint16_t majorVersion;
uint16_t minorVersion;
uint32_t itemVariationStoreOffset;
uint32_t advanceWidthMappingOffset;
uint32_t lsbMappingOffset;
uint32_t rsbMappingOffset;
if (!table.ReadU16(&majorVersion) ||
!table.ReadU16(&minorVersion) ||
!table.ReadU32(&itemVariationStoreOffset) ||
!table.ReadU32(&advanceWidthMappingOffset) ||
!table.ReadU32(&lsbMappingOffset) ||
!table.ReadU32(&rsbMappingOffset)) {
return DropVariations("Failed to read table header");
}
if (majorVersion != 1) {
return DropVariations("Unknown table version");
}
if (itemVariationStoreOffset > length ||
advanceWidthMappingOffset > length ||
lsbMappingOffset > length ||
rsbMappingOffset > length) {
return DropVariations("Invalid subtable offset");
}
if (!ParseItemVariationStore(GetFont(), data + itemVariationStoreOffset,
length - itemVariationStoreOffset)) {
return DropVariations("Failed to parse item variation store");
}
if (advanceWidthMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + advanceWidthMappingOffset,
length - advanceWidthMappingOffset)) {
return DropVariations("Failed to parse advance width mappings");
}
}
if (lsbMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + lsbMappingOffset,
length - lsbMappingOffset)) {
return DropVariations("Failed to parse LSB mappings");
}
}
if (rsbMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + rsbMappingOffset,
length - rsbMappingOffset)) {
return DropVariations("Failed to parse RSB mappings");
}
}
this->m_data = data;
this->m_length = length;
return true;
}
bool OpenTypeHVAR::Serialize(OTSStream* out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write HVAR table");
}
return true;
}
} // namespace ots

31
gfx/ots/src/hvar.h Normal file
View file

@ -0,0 +1,31 @@
// 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.
#ifndef OTS_HVAR_H_
#define OTS_HVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeHVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeHVAR : public Table {
public:
explicit OpenTypeHVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_HVAR_H_

View file

@ -7,6 +7,7 @@
#include <limits>
#include <vector>
#include "fvar.h"
#include "gdef.h"
// OpenType Layout Common Table Formats
@ -22,14 +23,11 @@ const uint32_t kScriptTableTagDflt = 0x44464c54;
const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF;
// The lookup flag bit which indicates existence of MarkFilteringSet.
const uint16_t kUseMarkFilteringSetBit = 0x0010;
// The lookup flags which require GDEF table.
const uint16_t kGdefRequiredFlags = 0x0002 | 0x0004 | 0x0008;
// The mask for MarkAttachmentType.
const uint16_t kMarkAttachmentTypeMask = 0xFF00;
// The maximum type number of format for device tables.
const uint16_t kMaxDeltaFormatType = 3;
// The maximum number of class value.
const uint16_t kMaxClassDefValue = 0xFFFF;
// In variation fonts, Device Tables are replaced by VariationIndex tables,
// indicated by this flag in the deltaFormat field.
const uint16_t kVariationIndex = 0x8000;
struct ScriptRecord {
uint32_t tag;
@ -194,30 +192,7 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data,
return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
}
ots::OpenTypeGDEF *gdef = static_cast<ots::OpenTypeGDEF*>(
font->GetTypedTable(OTS_TAG_GDEF));
// Check lookup flags.
if ((lookup_flag & kGdefRequiredFlags) &&
(!gdef || !gdef->has_glyph_class_def)) {
return OTS_FAILURE_MSG("Lookup flags require GDEF table, "
"but none was found: %d", lookup_flag);
}
if ((lookup_flag & kMarkAttachmentTypeMask) &&
(!gdef || !gdef->has_mark_attachment_class_def)) {
return OTS_FAILURE_MSG("Lookup flags ask for mark attachment, "
"but there is no GDEF table or it has no "
"mark attachment classes: %d", lookup_flag);
}
bool use_mark_filtering_set = false;
if (lookup_flag & kUseMarkFilteringSetBit) {
if (!gdef || !gdef->has_mark_glyph_sets_def) {
return OTS_FAILURE_MSG("Lookup flags ask for mark filtering, "
"but there is no GDEF table or it has no "
"mark filtering sets: %d", lookup_flag);
}
use_mark_filtering_set = true;
}
bool use_mark_filtering_set = lookup_flag & kUseMarkFilteringSetBit;
std::vector<uint16_t> subtables;
subtables.reserve(subtable_count);
@ -248,8 +223,12 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data,
if (!subtable.ReadU16(&mark_filtering_set)) {
return OTS_FAILURE_MSG("Failed to read mark filtering set");
}
if (gdef->num_mark_glyph_sets == 0 ||
mark_filtering_set >= gdef->num_mark_glyph_sets) {
ots::OpenTypeGDEF *gdef = static_cast<ots::OpenTypeGDEF*>(
font->GetTypedTable(OTS_TAG_GDEF));
if (gdef && (gdef->num_mark_glyph_sets == 0 ||
mark_filtering_set >= gdef->num_mark_glyph_sets)) {
return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
}
}
@ -664,7 +643,7 @@ bool ParseContextFormat2(const ots::Font *font,
}
if (!ots::ParseClassDefTable(font, data + offset_class_def,
length - offset_class_def,
num_glyphs, kMaxClassDefValue)) {
num_glyphs, ots::kMaxClassDefValue)) {
return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2");
}
@ -1017,7 +996,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
}
if (!ots::ParseClassDefTable(font, data + offset_backtrack_class_def,
length - offset_backtrack_class_def,
num_glyphs, kMaxClassDefValue)) {
num_glyphs, ots::kMaxClassDefValue)) {
return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2");
}
}
@ -1028,7 +1007,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
}
if (!ots::ParseClassDefTable(font, data + offset_input_class_def,
length - offset_input_class_def,
num_glyphs, kMaxClassDefValue)) {
num_glyphs, ots::kMaxClassDefValue)) {
return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2");
}
@ -1039,7 +1018,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
}
if (!ots::ParseClassDefTable(font, data + offset_lookahead_class_def,
length - offset_lookahead_class_def,
num_glyphs, kMaxClassDefValue)) {
num_glyphs, ots::kMaxClassDefValue)) {
return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2");
}
}
@ -1403,11 +1382,17 @@ bool ParseDeviceTable(const ots::Font *font,
!subtable.ReadU16(&delta_format)) {
return OTS_FAILURE_MSG("Failed to read device table header");
}
if (delta_format == kVariationIndex) {
// start_size and end_size are replaced by deltaSetOuterIndex
// and deltaSetInnerIndex respectively, but we don't attempt to
// check them here, so nothing more to do.
return true;
}
if (start_size > end_size) {
return OTS_FAILURE_MSG("bad size range: %u > %u", start_size, end_size);
return OTS_FAILURE_MSG("Bad device table size range: %u > %u", start_size, end_size);
}
if (delta_format == 0 || delta_format > kMaxDeltaFormatType) {
return OTS_FAILURE_MSG("bad delta format: %u", delta_format);
return OTS_FAILURE_MSG("Bad device table delta format: 0x%x", delta_format);
}
// The number of delta values per uint16. The device table should contain
// at least |num_units| * 2 bytes compressed data.
@ -1519,6 +1504,173 @@ bool ParseExtensionSubtable(const Font *font,
return true;
}
bool ParseConditionTable(const Font *font,
const uint8_t *data, const size_t length,
const uint16_t axis_count) {
Buffer subtable(data, length);
uint16_t format = 0;
if (!subtable.ReadU16(&format)) {
return OTS_FAILURE_MSG("Failed to read condition table format");
}
if (format != 1) {
// An unknown format is not an error, but should be ignored per spec.
return true;
}
uint16_t axis_index = 0;
int16_t filter_range_min_value = 0;
int16_t filter_range_max_value = 0;
if (!subtable.ReadU16(&axis_index) ||
!subtable.ReadS16(&filter_range_min_value) ||
!subtable.ReadS16(&filter_range_max_value)) {
return OTS_FAILURE_MSG("Failed to read condition table (format 1)");
}
if (axis_index >= axis_count) {
return OTS_FAILURE_MSG("Axis index out of range in condition");
}
// Check min/max values are within range -1.0 .. 1.0 and properly ordered
if (filter_range_min_value < -0x4000 || // -1.0 in F2DOT14 format
filter_range_max_value > 0x4000 || // +1.0 in F2DOT14 format
filter_range_min_value > filter_range_max_value) {
return OTS_FAILURE_MSG("Invalid filter range in condition");
}
return true;
}
bool ParseConditionSetTable(const Font *font,
const uint8_t *data, const size_t length,
const uint16_t axis_count) {
Buffer subtable(data, length);
uint16_t condition_count = 0;
if (!subtable.ReadU16(&condition_count)) {
return OTS_FAILURE_MSG("Failed to read condition count");
}
for (uint16_t i = 0; i < condition_count; i++) {
uint32_t condition_offset = 0;
if (!subtable.ReadU32(&condition_offset)) {
return OTS_FAILURE_MSG("Failed to read condition offset");
}
if (condition_offset < subtable.offset() || condition_offset >= length) {
return OTS_FAILURE_MSG("Offset out of range");
}
if (!ParseConditionTable(font, data + condition_offset, length - condition_offset,
axis_count)) {
return OTS_FAILURE_MSG("Failed to parse condition table");
}
}
return true;
}
bool ParseFeatureTableSubstitutionTable(const Font *font,
const uint8_t *data, const size_t length,
const uint16_t num_lookups) {
Buffer subtable(data, length);
uint16_t version_major = 0;
uint16_t version_minor = 0;
uint16_t substitution_count = 0;
const size_t kFeatureTableSubstitutionHeaderSize = 3 * sizeof(uint16_t);
if (!subtable.ReadU16(&version_major) ||
!subtable.ReadU16(&version_minor) ||
!subtable.ReadU16(&substitution_count)) {
return OTS_FAILURE_MSG("Failed to read feature table substitution table header");
}
for (uint16_t i = 0; i < substitution_count; i++) {
uint16_t feature_index = 0;
uint32_t alternate_feature_table_offset = 0;
const size_t kFeatureTableSubstitutionRecordSize = sizeof(uint16_t) + sizeof(uint32_t);
if (!subtable.ReadU16(&feature_index) ||
!subtable.ReadU32(&alternate_feature_table_offset)) {
return OTS_FAILURE_MSG("Failed to read feature table substitution record");
}
if (alternate_feature_table_offset < kFeatureTableSubstitutionHeaderSize +
kFeatureTableSubstitutionRecordSize * substitution_count ||
alternate_feature_table_offset >= length) {
return OTS_FAILURE_MSG("Invalid alternate feature table offset");
}
if (!ParseFeatureTable(font, data + alternate_feature_table_offset,
length - alternate_feature_table_offset, num_lookups)) {
return OTS_FAILURE_MSG("Failed to parse alternate feature table");
}
}
return true;
}
bool ParseFeatureVariationsTable(const Font *font,
const uint8_t *data, const size_t length,
const uint16_t num_lookups) {
Buffer subtable(data, length);
uint16_t version_major = 0;
uint16_t version_minor = 0;
uint32_t feature_variation_record_count = 0;
if (!subtable.ReadU16(&version_major) ||
!subtable.ReadU16(&version_minor) ||
!subtable.ReadU32(&feature_variation_record_count)) {
return OTS_FAILURE_MSG("Failed to read feature variations table header");
}
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
if (!fvar) {
return OTS_FAILURE_MSG("Not a variation font");
}
const uint16_t axis_count = fvar->AxisCount();
const size_t kEndOfFeatureVariationRecords =
2 * sizeof(uint16_t) + sizeof(uint32_t) +
feature_variation_record_count * 2 * sizeof(uint32_t);
for (uint32_t i = 0; i < feature_variation_record_count; i++) {
uint32_t condition_set_offset = 0;
uint32_t feature_table_substitution_offset = 0;
if (!subtable.ReadU32(&condition_set_offset) ||
!subtable.ReadU32(&feature_table_substitution_offset)) {
return OTS_FAILURE_MSG("Failed to read feature variation record");
}
if (condition_set_offset) {
if (condition_set_offset < kEndOfFeatureVariationRecords ||
condition_set_offset >= length) {
return OTS_FAILURE_MSG("Condition set offset out of range");
}
if (!ParseConditionSetTable(font, data + condition_set_offset,
length - condition_set_offset,
axis_count)) {
return OTS_FAILURE_MSG("Failed to parse condition set table");
}
}
if (feature_table_substitution_offset) {
if (feature_table_substitution_offset < kEndOfFeatureVariationRecords ||
feature_table_substitution_offset >= length) {
return OTS_FAILURE_MSG("Feature table substitution offset out of range");
}
if (!ParseFeatureTableSubstitutionTable(font, data + feature_table_substitution_offset,
length - feature_table_substitution_offset,
num_lookups)) {
return OTS_FAILURE_MSG("Failed to parse feature table substitution table");
}
}
}
return true;
}
} // namespace ots
#undef TABLE_NAME

View file

@ -12,6 +12,8 @@
namespace ots {
// The maximum number of class value.
const uint16_t kMaxClassDefValue = 0xFFFF;
struct LookupSubtableParser {
struct TypeParser {
@ -70,6 +72,23 @@ bool ParseExtensionSubtable(const Font *font,
const uint8_t *data, const size_t length,
const LookupSubtableParser* parser);
// For feature variations table (in GSUB/GPOS v1.1)
bool ParseConditionTable(const Font *font,
const uint8_t *data, const size_t length,
const uint16_t axis_count);
bool ParseConditionSetTable(const Font *font,
const uint8_t *data, const size_t length,
const uint16_t axis_count);
bool ParseFeatureTableSubstitutionTable(const Font *font,
const uint8_t *data, const size_t length,
const uint16_t num_lookups);
bool ParseFeatureVariationsTable(const Font *font,
const uint8_t *data, const size_t length,
const uint16_t num_lookups);
} // namespace ots
#endif // OTS_LAYOUT_H_

View file

@ -9,28 +9,29 @@ EXPORTS += [
'../include/ots-memory-stream.h',
]
SOURCES += [
# needs to be separate because gpos.cc also defines kMaxClassDefValue
'gdef.cc',
]
UNIFIED_SOURCES += [
'avar.cc',
'cff.cc',
'cff_type2_charstring.cc',
'cff_charstring.cc',
'cmap.cc',
'cvar.cc',
'cvt.cc',
'feat.cc',
'fpgm.cc',
'fvar.cc',
'gasp.cc',
'gdef.cc',
'glat.cc',
'gloc.cc',
'glyf.cc',
'gpos.cc',
'gsub.cc',
'gvar.cc',
'hdmx.cc',
'head.cc',
'hhea.cc',
'hmtx.cc',
'hvar.cc',
'kern.cc',
'layout.cc',
'loca.cc',
@ -38,6 +39,7 @@ UNIFIED_SOURCES += [
'math.cc',
'maxp.cc',
'metrics.cc',
'mvar.cc',
'name.cc',
'os2.cc',
'ots.cc',
@ -46,10 +48,13 @@ UNIFIED_SOURCES += [
'sile.cc',
'silf.cc',
'sill.cc',
'stat.cc',
'variations.cc',
'vdmx.cc',
'vhea.cc',
'vmtx.cc',
'vorg.cc',
'vvar.cc',
]
# We allow warnings for third-party code that can be updated from upstream.
@ -60,6 +65,7 @@ FINAL_LIBRARY = 'gkmedias'
DEFINES['PACKAGE_VERSION'] = '"moz"'
DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
DEFINES['OTS_GRAPHITE'] = 1
DEFINES['OTS_VARIATIONS'] = 1
USE_LIBS += [
'brotli',

105
gfx/ots/src/mvar.cc Normal file
View file

@ -0,0 +1,105 @@
// 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 "mvar.h"
#include "variations.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeMVAR
// -----------------------------------------------------------------------------
bool OpenTypeMVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t reserved;
uint16_t valueRecordSize;
uint16_t valueRecordCount;
uint16_t itemVariationStoreOffset;
if (!table.ReadU16(&majorVersion) ||
!table.ReadU16(&minorVersion) ||
!table.ReadU16(&reserved) ||
!table.ReadU16(&valueRecordSize) ||
!table.ReadU16(&valueRecordCount) ||
!table.ReadU16(&itemVariationStoreOffset)) {
return DropVariations("Failed to read table header");
}
if (majorVersion != 1) {
return DropVariations("Unknown table version");
}
if (reserved != 0) {
Warning("Expected reserved=0");
}
// The spec says that valueRecordSize "must be greater than zero",
// but we don't enforce this in the case where valueRecordCount
// is zero.
// The minimum size for a valueRecord to be valid is 8, for the
// three fields currently defined in the record (see below).
if (valueRecordSize < 8) {
if (valueRecordCount != 0) {
return DropVariations("Value record size too small");
}
}
if (valueRecordCount == 0) {
if (itemVariationStoreOffset != 0) {
// The spec says "if valueRecordCount is zero, set to zero",
// but having a variation store even when record count is zero
// should be harmless -- it just won't be useful for anything.
// But we don't need to reject altogether.
Warning("Unexpected item variation store");
}
} else {
if (itemVariationStoreOffset < table.offset() || itemVariationStoreOffset > length) {
return DropVariations("Invalid item variation store offset");
}
if (!ParseItemVariationStore(GetFont(), data + itemVariationStoreOffset,
length - itemVariationStoreOffset)) {
return DropVariations("Failed to parse item variation store");
}
}
uint32_t prevTag = 0;
size_t offset = table.offset();
for (unsigned i = 0; i < valueRecordCount; i++) {
uint32_t tag;
uint16_t deltaSetOuterIndex, deltaSetInnerIndex;
if (!table.ReadU32(&tag) ||
!table.ReadU16(&deltaSetOuterIndex) ||
!table.ReadU16(&deltaSetInnerIndex)) {
return DropVariations("Failed to read value record");
}
if (tag <= prevTag) {
return DropVariations("Invalid or out-of-order value tag");
}
prevTag = tag;
// Adjust offset in case additional fields have been added to the
// valueRecord by a new minor version (allowed by spec).
offset += valueRecordSize;
table.set_offset(offset);
}
this->m_data = data;
this->m_length = length;
return true;
}
bool OpenTypeMVAR::Serialize(OTSStream* out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write MVAR table");
}
return true;
}
} // namespace ots

31
gfx/ots/src/mvar.h Normal file
View file

@ -0,0 +1,31 @@
// 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.
#ifndef OTS_MVAR_H_
#define OTS_MVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeMVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeMVAR : public Table {
public:
explicit OpenTypeMVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_MVAR_H_

View file

@ -169,6 +169,14 @@ bool OpenTypeNAME::Parse(const uint8_t* data, size_t length) {
if (tag_end > length) {
return Error("bad end of tag %d > %ld for langTagRecord %d", tag_end, length, i);
}
// Lang tag is BCP 47 tag per the spec, the recommonded BCP 47 max tag
// length is 35:
// https://tools.ietf.org/html/bcp47#section-4.4.1
// We are being too generous and allowing for 100 (multiplied by 2 since
// this is UTF-16 string).
if (tag_length > 100 * 2) {
return Error("Too long language tag for LangTagRecord %d: %d", i, tag_length);
}
std::string tag(string_base + tag_offset, tag_length);
this->lang_tags.push_back(tag);
}
@ -203,17 +211,16 @@ bool OpenTypeNAME::Parse(const uint8_t* data, size_t length) {
// if not, we'll add our fixed versions here
bool mac_name[kStdNameCount] = { 0 };
bool win_name[kStdNameCount] = { 0 };
for (std::vector<NameRecord>::iterator name_iter = this->names.begin();
name_iter != this->names.end(); ++name_iter) {
const uint16_t id = name_iter->name_id;
for (const auto& name : this->names) {
const uint16_t id = name.name_id;
if (id >= kStdNameCount || kStdNames[id] == NULL) {
continue;
}
if (name_iter->platform_id == 1) {
if (name.platform_id == 1) {
mac_name[id] = true;
continue;
}
if (name_iter->platform_id == 3) {
if (name.platform_id == 3) {
win_name[id] = true;
continue;
}
@ -266,9 +273,7 @@ bool OpenTypeNAME::Serialize(OTSStream* out) {
}
std::string string_data;
for (std::vector<NameRecord>::const_iterator name_iter = this->names.begin();
name_iter != this->names.end(); ++name_iter) {
const NameRecord& rec = *name_iter;
for (const auto& rec : this->names) {
if (string_data.size() + rec.text.size() >
std::numeric_limits<uint16_t>::max() ||
!out->WriteU16(rec.platform_id) ||
@ -286,16 +291,14 @@ bool OpenTypeNAME::Serialize(OTSStream* out) {
if (!out->WriteU16(lang_tag_count)) {
return Error("Faile to write langTagCount");
}
for (std::vector<std::string>::const_iterator tag_iter =
this->lang_tags.begin();
tag_iter != this->lang_tags.end(); ++tag_iter) {
if (string_data.size() + tag_iter->size() >
for (const auto& tag : this->lang_tags) {
if (string_data.size() + tag.size() >
std::numeric_limits<uint16_t>::max() ||
!out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
!out->WriteU16(static_cast<uint16_t>(tag.size())) ||
!out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
return Error("Failed to write langTagRecord");
}
string_data.append(*tag_iter);
string_data.append(tag);
}
}

View file

@ -38,17 +38,14 @@ bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
return Error("Unsupported table version: %u", this->table.version);
}
// Follow WPF Font Selection Model's advice.
if (1 <= this->table.weight_class && this->table.weight_class <= 9) {
Warning("Bad usWeightClass: %u, changing it to %u",
this->table.weight_class, this->table.weight_class * 100);
this->table.weight_class *= 100;
}
// Ditto.
if (this->table.weight_class > 999) {
if (this->table.weight_class < 1) {
Warning("Bad usWeightClass: %u, changing it to %d",
this->table.weight_class, 999);
this->table.weight_class = 999;
this->table.weight_class, 1);
this->table.weight_class = 1;
} else if (this->table.weight_class > 1000) {
Warning("Bad usWeightClass: %u, changing it to %d",
this->table.weight_class, 1000);
this->table.weight_class = 1000;
}
if (this->table.width_class < 1) {
@ -89,7 +86,7 @@ bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
SET_TO_ZERO("yStrikeoutSize", strikeout_size);
#undef SET_TO_ZERO
static std::string panose_strings[10] = {
static const char* panose_strings[10] = {
"bFamilyType",
"bSerifStyle",
"bWeight",
@ -103,7 +100,7 @@ bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
};
for (unsigned i = 0; i < 10; ++i) {
if (!table.ReadU8(&this->table.panose[i])) {
return Error("Failed to read PANOSE %s", panose_strings[i].c_str());
return Error("Failed to read PANOSE %s", panose_strings[i]);
}
}
@ -155,16 +152,17 @@ bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
if ((this->table.version < 4) &&
(this->table.selection & 0x300)) {
// bit 8 and 9 must be unset in OS/2 table versions less than 4.
return Error("fSelection bits 8 and 9 must be unset for table version %d",
this->table.version);
Warning("fsSelection bits 8 and 9 must be unset for table version %d",
this->table.version);
}
// mask reserved bits. use only 0..9 bits.
this->table.selection &= 0x3ff;
if (this->table.first_char_index > this->table.last_char_index) {
return Error("usFirstCharIndex %d > usLastCharIndex %d",
this->table.first_char_index, this->table.last_char_index);
Warning("usFirstCharIndex %d > usLastCharIndex %d",
this->table.first_char_index, this->table.last_char_index);
this->table.first_char_index = this->table.last_char_index;
}
if (this->table.typo_linegap < 0) {
Warning("Bad sTypoLineGap, setting it to 0: %d", this->table.typo_linegap);

View file

@ -14,38 +14,46 @@
#include <map>
#include <vector>
#include "woff2_dec.h"
#include <woff2/decode.h>
// The OpenType Font File
// http://www.microsoft.com/typography/otspec/cmap.htm
// http://www.microsoft.com/typography/otspec/otff.htm
#include "avar.h"
#include "cff.h"
#include "cmap.h"
#include "cvar.h"
#include "cvt.h"
#include "fpgm.h"
#include "fvar.h"
#include "gasp.h"
#include "gdef.h"
#include "glyf.h"
#include "gpos.h"
#include "gsub.h"
#include "gvar.h"
#include "hdmx.h"
#include "head.h"
#include "hhea.h"
#include "hmtx.h"
#include "hvar.h"
#include "kern.h"
#include "loca.h"
#include "ltsh.h"
#include "math_.h"
#include "maxp.h"
#include "mvar.h"
#include "name.h"
#include "os2.h"
#include "ots.h"
#include "post.h"
#include "prep.h"
#include "stat.h"
#include "vdmx.h"
#include "vhea.h"
#include "vmtx.h"
#include "vorg.h"
#include "vvar.h"
// Graphite tables
#ifdef OTS_GRAPHITE
@ -62,9 +70,8 @@ namespace ots {
struct Arena {
public:
~Arena() {
for (std::vector<uint8_t*>::iterator
i = hunks_.begin(); i != hunks_.end(); ++i) {
delete[] *i;
for (auto& hunk : hunks_) {
delete[] hunk;
}
}
@ -78,6 +85,17 @@ struct Arena {
std::vector<uint8_t*> hunks_;
};
bool CheckTag(uint32_t tag_value) {
for (unsigned i = 0; i < 4; ++i) {
const uint32_t check = tag_value & 0xff;
if (check < 32 || check > 126) {
return false; // non-ASCII character found.
}
tag_value >>= 8;
}
return true;
}
}; // namespace ots
namespace {
@ -91,17 +109,6 @@ namespace {
#define OTS_WARNING_MSG_HDR(...) OTS_WARNING_MSG_(header, __VA_ARGS__)
bool CheckTag(uint32_t tag_value) {
for (unsigned i = 0; i < 4; ++i) {
const uint32_t check = tag_value & 0xff;
if (check < 32 || check > 126) {
return false; // non-ASCII character found.
}
tag_value >>= 8;
}
return true;
}
const struct {
uint32_t tag;
bool required;
@ -126,6 +133,17 @@ const struct {
{ OTS_TAG_LTSH, false },
{ OTS_TAG_VORG, false },
{ OTS_TAG_KERN, false },
// We need to parse fvar table before other tables that may need to know
// the number of variation axes (if any)
{ OTS_TAG_FVAR, false },
{ OTS_TAG_AVAR, false },
{ OTS_TAG_CVAR, false },
{ OTS_TAG_GVAR, false },
{ OTS_TAG_HVAR, false },
{ OTS_TAG_MVAR, false },
{ OTS_TAG_STAT, false },
{ OTS_TAG_VVAR, false },
{ OTS_TAG_CFF2, false },
// We need to parse GDEF table in advance of parsing GSUB/GPOS tables
// because they could refer GDEF table.
{ OTS_TAG_GDEF, false },
@ -580,7 +598,7 @@ bool ProcessGeneric(ots::FontFile *header,
}
// all tag names must be built from printable ASCII characters
if (!CheckTag(tables[i].tag)) {
if (!ots::CheckTag(tables[i].tag)) {
OTS_WARNING_MSG_HDR("Invalid table tag: 0x%X", tables[i].tag);
}
@ -686,7 +704,7 @@ bool ProcessGeneric(ots::FontFile *header,
}
}
if (font->GetTable(OTS_TAG_CFF)) {
if (font->GetTable(OTS_TAG_CFF) || font->GetTable(OTS_TAG_CFF2)) {
// font with PostScript glyph
if (font->version != OTS_TAG('O','T','T','O')) {
return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
@ -862,32 +880,41 @@ bool Font::ParseTable(const TableEntry& table_entry, const uint8_t* data,
table = new TablePassthru(this, tag);
} else {
switch (tag) {
case OTS_TAG_AVAR: table = new OpenTypeAVAR(this, tag); break;
case OTS_TAG_CFF: table = new OpenTypeCFF(this, tag); break;
case OTS_TAG_CFF2: table = new OpenTypeCFF2(this, tag); break;
case OTS_TAG_CMAP: table = new OpenTypeCMAP(this, tag); break;
case OTS_TAG_CVAR: table = new OpenTypeCVAR(this, tag); break;
case OTS_TAG_CVT: table = new OpenTypeCVT(this, tag); break;
case OTS_TAG_FPGM: table = new OpenTypeFPGM(this, tag); break;
case OTS_TAG_FVAR: table = new OpenTypeFVAR(this, tag); break;
case OTS_TAG_GASP: table = new OpenTypeGASP(this, tag); break;
case OTS_TAG_GDEF: table = new OpenTypeGDEF(this, tag); break;
case OTS_TAG_GLYF: table = new OpenTypeGLYF(this, tag); break;
case OTS_TAG_GPOS: table = new OpenTypeGPOS(this, tag); break;
case OTS_TAG_GSUB: table = new OpenTypeGSUB(this, tag); break;
case OTS_TAG_GVAR: table = new OpenTypeGVAR(this, tag); break;
case OTS_TAG_HDMX: table = new OpenTypeHDMX(this, tag); break;
case OTS_TAG_HEAD: table = new OpenTypeHEAD(this, tag); break;
case OTS_TAG_HHEA: table = new OpenTypeHHEA(this, tag); break;
case OTS_TAG_HMTX: table = new OpenTypeHMTX(this, tag); break;
case OTS_TAG_HVAR: table = new OpenTypeHVAR(this, tag); break;
case OTS_TAG_KERN: table = new OpenTypeKERN(this, tag); break;
case OTS_TAG_LOCA: table = new OpenTypeLOCA(this, tag); break;
case OTS_TAG_LTSH: table = new OpenTypeLTSH(this, tag); break;
case OTS_TAG_MATH: table = new OpenTypeMATH(this, tag); break;
case OTS_TAG_MAXP: table = new OpenTypeMAXP(this, tag); break;
case OTS_TAG_MVAR: table = new OpenTypeMVAR(this, tag); break;
case OTS_TAG_NAME: table = new OpenTypeNAME(this, tag); break;
case OTS_TAG_OS2: table = new OpenTypeOS2(this, tag); break;
case OTS_TAG_POST: table = new OpenTypePOST(this, tag); break;
case OTS_TAG_PREP: table = new OpenTypePREP(this, tag); break;
case OTS_TAG_STAT: table = new OpenTypeSTAT(this, tag); break;
case OTS_TAG_VDMX: table = new OpenTypeVDMX(this, tag); break;
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
case OTS_TAG_VHEA: table = new OpenTypeVHEA(this, tag); break;
case OTS_TAG_VMTX: table = new OpenTypeVMTX(this, tag); break;
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
case OTS_TAG_VVAR: table = new OpenTypeVVAR(this, tag); break;
// Graphite tables
#ifdef OTS_GRAPHITE
case OTS_TAG_FEAT: table = new OpenTypeFEAT(this, tag); break;
@ -953,6 +980,23 @@ void Font::DropGraphite() {
dropped_graphite = true;
}
void Font::DropVariations() {
file->context->Message(0, "Dropping all Variation tables");
for (const std::pair<uint32_t, Table*> entry : m_tables) {
if (entry.first == OTS_TAG_AVAR ||
entry.first == OTS_TAG_CVAR ||
entry.first == OTS_TAG_FVAR ||
entry.first == OTS_TAG_GVAR ||
entry.first == OTS_TAG_HVAR ||
entry.first == OTS_TAG_MVAR ||
entry.first == OTS_TAG_STAT ||
entry.first == OTS_TAG_VVAR) {
entry.second->Drop("Discarding Variations table");
}
}
dropped_variations = true;
}
bool Table::ShouldSerialize() {
return m_shouldSerialize;
}
@ -960,7 +1004,6 @@ bool Table::ShouldSerialize() {
void Table::Message(int level, const char *format, va_list va) {
char msg[206] = { OTS_UNTAG(m_tag), ':', ' ' };
std::vsnprintf(msg + 6, 200, format, va);
// fprintf(stderr, format, va); // font debugging, see TenFourFox issue 317
m_font->file->context->Message(level, msg);
}
@ -1004,6 +1047,16 @@ bool Table::DropGraphite(const char *format, ...) {
return true;
}
bool Table::DropVariations(const char *format, ...) {
va_list va;
va_start(va, format);
Message(0, format, va);
va_end(va);
m_font->DropVariations();
return true;
}
bool TablePassthru::Parse(const uint8_t *data, size_t length) {
m_data = data;
m_length = length;

View file

@ -112,7 +112,7 @@ class Buffer {
return OTS_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
*value = ntohs(*value);
*value = ots_ntohs(*value);
offset_ += 2;
return true;
}
@ -137,7 +137,7 @@ class Buffer {
return OTS_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
*value = ntohl(*value);
*value = ots_ntohl(*value);
offset_ += 4;
return true;
}
@ -184,9 +184,13 @@ template<typename T> T Round2(T value) {
return (value + 1) & ~1;
}
// Check that a tag consists entirely of printable ASCII characters
bool CheckTag(uint32_t tag_value);
bool IsValidVersionTag(uint32_t tag);
#define OTS_TAG_CFF OTS_TAG('C','F','F',' ')
#define OTS_TAG_CFF2 OTS_TAG('C','F','F','2')
#define OTS_TAG_CMAP OTS_TAG('c','m','a','p')
#define OTS_TAG_CVT OTS_TAG('c','v','t',' ')
#define OTS_TAG_FEAT OTS_TAG('F','e','a','t')
@ -219,6 +223,15 @@ bool IsValidVersionTag(uint32_t tag);
#define OTS_TAG_VMTX OTS_TAG('v','m','t','x')
#define OTS_TAG_VORG OTS_TAG('V','O','R','G')
#define OTS_TAG_AVAR OTS_TAG('a','v','a','r')
#define OTS_TAG_CVAR OTS_TAG('c','v','a','r')
#define OTS_TAG_FVAR OTS_TAG('f','v','a','r')
#define OTS_TAG_GVAR OTS_TAG('g','v','a','r')
#define OTS_TAG_HVAR OTS_TAG('H','V','A','R')
#define OTS_TAG_MVAR OTS_TAG('M','V','A','R')
#define OTS_TAG_VVAR OTS_TAG('V','V','A','R')
#define OTS_TAG_STAT OTS_TAG('S','T','A','T')
struct Font;
struct FontFile;
struct TableEntry;
@ -252,6 +265,7 @@ class Table {
bool Warning(const char *format, ...);
bool Drop(const char *format, ...);
bool DropGraphite(const char *format, ...);
bool DropVariations(const char *format, ...);
private:
void Message(int level, const char *format, va_list va);
@ -286,7 +300,8 @@ struct Font {
search_range(0),
entry_selector(0),
range_shift(0),
dropped_graphite(false) {
dropped_graphite(false),
dropped_variations(false) {
}
bool ParseTable(const TableEntry& tableinfo, const uint8_t* data,
@ -301,6 +316,9 @@ struct Font {
// Drop all Graphite tables and don't parse new ones.
void DropGraphite();
// Drop all Variations tables and don't parse new ones.
void DropVariations();
FontFile *file;
uint32_t version;
@ -309,6 +327,7 @@ struct Font {
uint16_t entry_selector;
uint16_t range_shift;
bool dropped_graphite;
bool dropped_variations;
private:
std::map<uint32_t, Table*> m_tables;

View file

@ -9,8 +9,6 @@
// post - PostScript
// http://www.microsoft.com/typography/otspec/post.htm
#define TABLE_NAME "post"
namespace ots {
bool OpenTypePOST::Parse(const uint8_t *data, size_t length) {

View file

@ -38,7 +38,19 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
if (prevent_decompression) {
return DropGraphite("Illegal nested compression");
}
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
size_t decompressed_size = this->compHead & FULL_SIZE;
if (decompressed_size < length) {
return DropGraphite("Decompressed size is less than compressed size");
}
if (decompressed_size == 0) {
return DropGraphite("Decompressed size is set to 0");
}
// decompressed table must be <= 30MB
if (decompressed_size > 30 * 1024 * 1024) {
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
decompressed_size / (1024.0 * 1024.0));
}
std::vector<uint8_t> decompressed(decompressed_size);
size_t outputSize = 0;
bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()),
@ -165,15 +177,9 @@ bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
if (!table.ReadU8(&this->attrMirroring)) {
return parent->Error("SILSub: Failed to read attrMirroring");
}
if (parent->version >> 16 < 4 && this->attrMirroring != 0) {
parent->Warning("SILSub: Nonzero attrMirroring (reserved before v4)");
}
if (!table.ReadU8(&this->attrSkipPasses)) {
return parent->Error("SILSub: Failed to read attrSkipPasses");
}
if (parent->version >> 16 < 4 && this->attrSkipPasses != 0) {
parent->Warning("SILSub: Nonzero attrSkipPasses (reserved2 before v4)");
}
if (!table.ReadU8(&this->numJLevels)) {
return parent->Error("SILSub: Failed to read numJLevels");
@ -642,9 +648,6 @@ SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
if (!table.ReadU16(&this->fsmOffset)) {
return parent->Error("SILPass: Failed to read fsmOffset");
}
if (parent->version >> 16 == 2 && this->fsmOffset != 0) {
parent->Warning("SILPass: Nonzero fsmOffset (reserved in SILSub v2)");
}
if (!table.ReadU32(&this->pcCode) ||
(parent->version >= 3 && this->pcCode < this->fsmOffset)) {
return parent->Error("SILPass: Failed to read pcCode");
@ -770,10 +773,6 @@ SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
if (!table.ReadU8(&this->collisionThreshold)) {
return parent->Error("SILPass: Failed to read collisionThreshold");
}
if (parent->version >> 16 < 5 && this->collisionThreshold != 0) {
parent->Warning("SILPass: Nonzero collisionThreshold"
" (reserved before v5)");
}
if (!table.ReadU16(&this->pConstraint)) {
return parent->Error("SILPass: Failed to read pConstraint");
}

347
gfx/ots/src/stat.cc Normal file
View file

@ -0,0 +1,347 @@
// 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 "stat.h"
#include "name.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeSTAT
// -----------------------------------------------------------------------------
bool OpenTypeSTAT::ValidateNameId(uint16_t nameid, bool allowPredefined) {
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
GetFont()->GetTypedTable(OTS_TAG_NAME));
if (!name || !name->IsValidNameId(nameid)) {
Drop("Invalid nameID: %d", nameid);
return false;
}
if (!allowPredefined && nameid < 26) {
Warning("nameID out of range: %d", nameid);
return true;
}
if ((nameid >= 26 && nameid <= 255) || nameid >= 32768) {
Warning("nameID out of range: %d", nameid);
return true;
}
return true;
}
bool OpenTypeSTAT::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
if (!table.ReadU16(&this->majorVersion) ||
!table.ReadU16(&this->minorVersion) ||
!table.ReadU16(&this->designAxisSize) ||
!table.ReadU16(&this->designAxisCount) ||
!table.ReadU32(&this->designAxesOffset) ||
!table.ReadU16(&this->axisValueCount) ||
!table.ReadU32(&this->offsetToAxisValueOffsets) ||
!(this->minorVersion < 1 || table.ReadU16(&this->elidedFallbackNameID))) {
return Drop("Failed to read table header");
}
if (this->majorVersion != 1) {
return Drop("Unknown table version");
}
if (this->minorVersion > 2) {
Warning("Unknown minor version, downgrading to 2");
this->minorVersion = 2;
}
if (this->designAxisSize < sizeof(AxisRecord)) {
return Drop("Invalid designAxisSize");
}
size_t headerEnd = table.offset();
if (this->designAxisCount == 0) {
if (this->designAxesOffset != 0) {
Warning("Unexpected non-zero designAxesOffset");
this->designAxesOffset = 0;
}
} else {
if (this->designAxesOffset < headerEnd ||
size_t(this->designAxesOffset) +
size_t(this->designAxisCount) * size_t(this->designAxisSize) > length) {
return Drop("Invalid designAxesOffset");
}
}
for (size_t i = 0; i < this->designAxisCount; i++) {
table.set_offset(this->designAxesOffset + i * this->designAxisSize);
this->designAxes.emplace_back();
auto& axis = this->designAxes[i];
if (!table.ReadU32(&axis.axisTag) ||
!table.ReadU16(&axis.axisNameID) ||
!table.ReadU16(&axis.axisOrdering)) {
return Drop("Failed to read design axis");
}
if (!CheckTag(axis.axisTag)) {
return Drop("Bad design axis tag");
}
if (!ValidateNameId(axis.axisNameID, false)) {
return true;
}
}
// TODO
// - check that all axes defined in fvar are covered by STAT
// - check that axisOrdering values are not duplicated (warn only)
if (this->axisValueCount == 0) {
if (this->offsetToAxisValueOffsets != 0) {
Warning("Unexpected non-zero offsetToAxisValueOffsets");
this->offsetToAxisValueOffsets = 0;
}
} else {
if (this->offsetToAxisValueOffsets < headerEnd ||
size_t(this->offsetToAxisValueOffsets) +
size_t(this->axisValueCount) * sizeof(uint16_t) > length) {
return Drop("Invalid offsetToAxisValueOffsets");
}
}
for (size_t i = 0; i < this->axisValueCount; i++) {
table.set_offset(this->offsetToAxisValueOffsets + i * sizeof(uint16_t));
uint16_t axisValueOffset;
if (!table.ReadU16(&axisValueOffset)) {
return Drop("Failed to read axis value offset");
}
if (this->offsetToAxisValueOffsets + axisValueOffset > length) {
return Drop("Invalid axis value offset");
}
table.set_offset(this->offsetToAxisValueOffsets + axisValueOffset);
uint16_t format;
if (!table.ReadU16(&format)) {
return Drop("Failed to read axis value format");
}
this->axisValues.emplace_back(format);
auto& axisValue = axisValues[i];
switch (format) {
case 1:
if (!table.ReadU16(&axisValue.format1.axisIndex) ||
!table.ReadU16(&axisValue.format1.flags) ||
!table.ReadU16(&axisValue.format1.valueNameID) ||
!table.ReadS32(&axisValue.format1.value)) {
return Drop("Failed to read axis value (format 1)");
}
if (axisValue.format1.axisIndex >= this->designAxisCount) {
return Drop("Axis index out of range");
}
if ((axisValue.format1.flags & 0xFFFCu) != 0) {
Warning("Unexpected axis value flags");
axisValue.format1.flags &= ~0xFFFCu;
}
if (!ValidateNameId(axisValue.format1.valueNameID)) {
return true;
}
break;
case 2:
if (!table.ReadU16(&axisValue.format2.axisIndex) ||
!table.ReadU16(&axisValue.format2.flags) ||
!table.ReadU16(&axisValue.format2.valueNameID) ||
!table.ReadS32(&axisValue.format2.nominalValue) ||
!table.ReadS32(&axisValue.format2.rangeMinValue) ||
!table.ReadS32(&axisValue.format2.rangeMaxValue)) {
return Drop("Failed to read axis value (format 2)");
}
if (axisValue.format2.axisIndex >= this->designAxisCount) {
return Drop("Axis index out of range");
}
if ((axisValue.format2.flags & 0xFFFCu) != 0) {
Warning("Unexpected axis value flags");
axisValue.format1.flags &= ~0xFFFCu;
}
if (!ValidateNameId(axisValue.format2.valueNameID)) {
return true;
}
if (!(axisValue.format2.rangeMinValue <= axisValue.format2.nominalValue &&
axisValue.format2.nominalValue <= axisValue.format2.rangeMaxValue)) {
Warning("Bad axis value range or nominal value");
}
break;
case 3:
if (!table.ReadU16(&axisValue.format3.axisIndex) ||
!table.ReadU16(&axisValue.format3.flags) ||
!table.ReadU16(&axisValue.format3.valueNameID) ||
!table.ReadS32(&axisValue.format3.value) ||
!table.ReadS32(&axisValue.format3.linkedValue)) {
return Drop("Failed to read axis value (format 3)");
}
if (axisValue.format3.axisIndex >= this->designAxisCount) {
return Drop("Axis index out of range");
}
if ((axisValue.format3.flags & 0xFFFCu) != 0) {
Warning("Unexpected axis value flags");
axisValue.format3.flags &= ~0xFFFCu;
}
if (!ValidateNameId(axisValue.format3.valueNameID)) {
return true;
}
break;
case 4:
if (this->minorVersion < 2) {
Warning("Invalid table version for format 4 axis values - updating");
this->minorVersion = 2;
}
if (!table.ReadU16(&axisValue.format4.axisCount) ||
!table.ReadU16(&axisValue.format4.flags) ||
!table.ReadU16(&axisValue.format4.valueNameID)) {
return Drop("Failed to read axis value (format 4)");
}
if (axisValue.format4.axisCount > this->designAxisCount) {
return Drop("Axis count out of range");
}
if ((axisValue.format4.flags & 0xFFFCu) != 0) {
Warning("Unexpected axis value flags");
axisValue.format4.flags &= ~0xFFFCu;
}
if (!ValidateNameId(axisValue.format4.valueNameID)) {
return true;
}
for (unsigned j = 0; j < axisValue.format4.axisCount; j++) {
axisValue.format4.axisValues.emplace_back();
auto& v = axisValue.format4.axisValues[j];
if (!table.ReadU16(&v.axisIndex) ||
!table.ReadS32(&v.value)) {
return Drop("Failed to read axis value");
}
if (v.axisIndex >= this->designAxisCount) {
return Drop("Axis index out of range");
}
}
break;
default:
return Drop("Unknown axis value format");
}
}
return true;
}
bool OpenTypeSTAT::Serialize(OTSStream* out) {
off_t tableStart = out->Tell();
size_t headerSize = 5 * sizeof(uint16_t) + 2 * sizeof(uint32_t);
if (this->minorVersion >= 1) {
headerSize += sizeof(uint16_t);
}
if (this->designAxisCount == 0) {
this->designAxesOffset = 0;
} else {
this->designAxesOffset = headerSize;
}
this->designAxisSize = sizeof(AxisRecord);
if (this->axisValueCount == 0) {
this->offsetToAxisValueOffsets = 0;
} else {
if (this->designAxesOffset == 0) {
this->offsetToAxisValueOffsets = headerSize;
} else {
this->offsetToAxisValueOffsets = this->designAxesOffset + this->designAxisCount * this->designAxisSize;
}
}
if (!out->WriteU16(this->majorVersion) ||
!out->WriteU16(this->minorVersion) ||
!out->WriteU16(this->designAxisSize) ||
!out->WriteU16(this->designAxisCount) ||
!out->WriteU32(this->designAxesOffset) ||
!out->WriteU16(this->axisValueCount) ||
!out->WriteU32(this->offsetToAxisValueOffsets) ||
!(this->minorVersion < 1 || out->WriteU16(this->elidedFallbackNameID))) {
return Error("Failed to write table header");
}
if (this->designAxisCount > 0) {
if (out->Tell() - tableStart != this->designAxesOffset) {
return Error("Error computing designAxesOffset");
}
}
for (unsigned i = 0; i < this->designAxisCount; i++) {
const auto& axis = this->designAxes[i];
if (!out->WriteU32(axis.axisTag) ||
!out->WriteU16(axis.axisNameID) ||
!out->WriteU16(axis.axisOrdering)) {
return Error("Failed to write design axis");
}
}
if (this->axisValueCount > 0) {
if (out->Tell() - tableStart != this->offsetToAxisValueOffsets) {
return Error("Error computing offsetToAxisValueOffsets");
}
}
uint32_t axisValueOffset = this->axisValueCount * sizeof(uint16_t);
for (unsigned i = 0; i < this->axisValueCount; i++) {
const auto& value = this->axisValues[i];
if (!out->WriteU16(axisValueOffset)) {
return Error("Failed to write axis value offset");
}
axisValueOffset += value.Length();
}
for (unsigned i = 0; i < this->axisValueCount; i++) {
const auto& value = this->axisValues[i];
if (!out->WriteU16(value.format)) {
return Error("Failed to write axis value");
}
switch (value.format) {
case 1:
if (!out->WriteU16(value.format1.axisIndex) ||
!out->WriteU16(value.format1.flags) ||
!out->WriteU16(value.format1.valueNameID) ||
!out->WriteS32(value.format1.value)) {
return Error("Failed to write axis value");
}
break;
case 2:
if (!out->WriteU16(value.format2.axisIndex) ||
!out->WriteU16(value.format2.flags) ||
!out->WriteU16(value.format2.valueNameID) ||
!out->WriteS32(value.format2.nominalValue) ||
!out->WriteS32(value.format2.rangeMinValue) ||
!out->WriteS32(value.format2.rangeMaxValue)) {
return Error("Failed to write axis value");
}
break;
case 3:
if (!out->WriteU16(value.format3.axisIndex) ||
!out->WriteU16(value.format3.flags) ||
!out->WriteU16(value.format3.valueNameID) ||
!out->WriteS32(value.format3.value) ||
!out->WriteS32(value.format3.linkedValue)) {
return Error("Failed to write axis value");
}
break;
case 4:
if (!out->WriteU16(value.format4.axisCount) ||
!out->WriteU16(value.format4.flags) ||
!out->WriteU16(value.format4.valueNameID)) {
return Error("Failed to write axis value");
}
for (unsigned j = 0; j < value.format4.axisValues.size(); j++) {
if (!out->WriteU16(value.format4.axisValues[j].axisIndex) ||
!out->WriteS32(value.format4.axisValues[j].value)) {
return Error("Failed to write axis value");
}
}
break;
default:
return Error("Bad value format");
}
}
return true;
}
} // namespace ots

155
gfx/ots/src/stat.h Normal file
View file

@ -0,0 +1,155 @@
// 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.
#ifndef OTS_STAT_H_
#define OTS_STAT_H_
#include <vector>
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeSTAT Interface
// -----------------------------------------------------------------------------
class OpenTypeSTAT : public Table {
public:
explicit OpenTypeSTAT(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
bool ValidateNameId(uint16_t nameid, bool allowPredefined = true);
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t designAxisSize;
uint16_t designAxisCount;
uint32_t designAxesOffset;
uint16_t axisValueCount;
uint32_t offsetToAxisValueOffsets;
uint16_t elidedFallbackNameID;
struct AxisRecord {
uint32_t axisTag;
uint16_t axisNameID;
uint16_t axisOrdering;
};
std::vector<AxisRecord> designAxes;
typedef int32_t Fixed; /* 16.16 fixed-point value */
struct AxisValueFormat1 {
uint16_t axisIndex;
uint16_t flags;
uint16_t valueNameID;
Fixed value;
static size_t Length() {
return 3 * sizeof(uint16_t) + sizeof(Fixed);
}
};
struct AxisValueFormat2 {
uint16_t axisIndex;
uint16_t flags;
uint16_t valueNameID;
Fixed nominalValue;
Fixed rangeMinValue;
Fixed rangeMaxValue;
static size_t Length() {
return 3 * sizeof(uint16_t) + 3 * sizeof(Fixed);
}
};
struct AxisValueFormat3 {
uint16_t axisIndex;
uint16_t flags;
uint16_t valueNameID;
Fixed value;
Fixed linkedValue;
static size_t Length() {
return 3 * sizeof(uint16_t) + 2 * sizeof(Fixed);
}
};
struct AxisValueFormat4 {
uint16_t axisCount;
uint16_t flags;
uint16_t valueNameID;
struct AxisValue {
uint16_t axisIndex;
Fixed value;
};
std::vector<AxisValue> axisValues;
size_t Length() const {
return 3 * sizeof(uint16_t) + axisValues.size() * (sizeof(uint16_t) + sizeof(Fixed));
}
};
struct AxisValueRecord {
uint16_t format;
union {
AxisValueFormat1 format1;
AxisValueFormat2 format2;
AxisValueFormat3 format3;
AxisValueFormat4 format4;
};
explicit AxisValueRecord(uint16_t format_)
: format(format_)
{
if (format == 4) {
new (&this->format4) AxisValueFormat4();
}
}
AxisValueRecord(const AxisValueRecord& other_)
: format(other_.format)
{
switch (format) {
case 1:
format1 = other_.format1;
break;
case 2:
format2 = other_.format2;
break;
case 3:
format3 = other_.format3;
break;
case 4:
new (&this->format4) AxisValueFormat4();
format4 = other_.format4;
break;
}
}
~AxisValueRecord() {
if (format == 4) {
this->format4.~AxisValueFormat4();
}
}
uint32_t Length() const {
switch (format) {
case 1:
return sizeof(uint16_t) + format1.Length();
case 2:
return sizeof(uint16_t) + format2.Length();
case 3:
return sizeof(uint16_t) + format3.Length();
case 4:
return sizeof(uint16_t) + format4.Length();
default:
// can't happen
return 0;
}
}
};
std::vector<AxisValueRecord> axisValues;
};
} // namespace ots
#endif // OTS_STAT_H_

261
gfx/ots/src/variations.cc Normal file
View file

@ -0,0 +1,261 @@
// 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 shortDeltaCount;
if (!subtable.ReadU16(&itemCount) ||
!subtable.ReadU16(&shortDeltaCount) ||
!subtable.ReadU16(regionIndexCount)) {
return OTS_FAILURE_MSG("Failed to read variation data subtable header");
}
for (unsigned i = 0; i < *regionIndexCount; i++) {
uint16_t regionIndex;
if (!subtable.ReadU16(&regionIndex) || regionIndex >= regionCount) {
return OTS_FAILURE_MSG("Bad region index");
}
}
if (!subtable.Skip(size_t(itemCount) * (size_t(shortDeltaCount) + 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,
&regionCount)) {
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,
&regionIndexCount)) {
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("Invalid tuple coordinate");
}
}
}
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("Invalid tuple coordinate");
}
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("Invalid tuple coordinate");
}
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

26
gfx/ots/src/variations.h Normal file
View file

@ -0,0 +1,26 @@
// 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.
#ifndef OTS_VARIATIONS_H_
#define OTS_VARIATIONS_H_
#include <vector>
#include "ots.h"
// Utility functions for OpenType variations common table formats.
namespace ots {
bool ParseItemVariationStore(const Font* font,
const uint8_t* data, const size_t length,
std::vector<uint16_t>* out_region_index_count = NULL);
bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length);
bool ParseVariationData(const Font* font, const uint8_t* data, size_t length,
size_t axisCount, size_t sharedTupleCount);
} // namespace ots
#endif // OTS_VARIATIONS_H_

95
gfx/ots/src/vvar.cc Normal file
View file

@ -0,0 +1,95 @@
// 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 "vvar.h"
#include "variations.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeVVAR
// -----------------------------------------------------------------------------
bool OpenTypeVVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
uint16_t majorVersion;
uint16_t minorVersion;
uint32_t itemVariationStoreOffset;
uint32_t advanceHeightMappingOffset;
uint32_t tsbMappingOffset;
uint32_t bsbMappingOffset;
uint32_t vOrgMappingOffset;
if (!table.ReadU16(&majorVersion) ||
!table.ReadU16(&minorVersion) ||
!table.ReadU32(&itemVariationStoreOffset) ||
!table.ReadU32(&advanceHeightMappingOffset) ||
!table.ReadU32(&tsbMappingOffset) ||
!table.ReadU32(&bsbMappingOffset) ||
!table.ReadU32(&vOrgMappingOffset)) {
return DropVariations("Failed to read table header");
}
if (majorVersion != 1) {
return DropVariations("Unknown table version");
}
if (itemVariationStoreOffset > length ||
advanceHeightMappingOffset > length ||
tsbMappingOffset > length ||
bsbMappingOffset > length ||
vOrgMappingOffset > length) {
return DropVariations("Invalid subtable offset");
}
if (!ParseItemVariationStore(GetFont(), data + itemVariationStoreOffset,
length - itemVariationStoreOffset)) {
return DropVariations("Failed to parse item variation store");
}
if (advanceHeightMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + advanceHeightMappingOffset,
length - advanceHeightMappingOffset)) {
return DropVariations("Failed to parse advance height mappings");
}
}
if (tsbMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + tsbMappingOffset,
length - tsbMappingOffset)) {
return DropVariations("Failed to parse TSB mappings");
}
}
if (bsbMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + bsbMappingOffset,
length - bsbMappingOffset)) {
return DropVariations("Failed to parse BSB mappings");
}
}
if (vOrgMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + vOrgMappingOffset,
length - vOrgMappingOffset)) {
return DropVariations("Failed to parse vOrg mappings");
}
}
this->m_data = data;
this->m_length = length;
return true;
}
bool OpenTypeVVAR::Serialize(OTSStream* out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write VVAR table");
}
return true;
}
} // namespace ots

31
gfx/ots/src/vvar.h Normal file
View file

@ -0,0 +1,31 @@
// 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.
#ifndef OTS_VVAR_H_
#define OTS_VVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeVVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeVVAR : public Table {
public:
explicit OpenTypeVVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_VVAR_H_