summaryrefslogtreecommitdiffstats
path: root/gfx/ots/src
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /gfx/ots/src
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/ots/src')
-rw-r--r--gfx/ots/src/cff.cc1052
-rw-r--r--gfx/ots/src/cff.h46
-rw-r--r--gfx/ots/src/cff_type2_charstring.cc912
-rw-r--r--gfx/ots/src/cff_type2_charstring.h101
-rw-r--r--gfx/ots/src/cmap.cc1088
-rw-r--r--gfx/ots/src/cmap.h74
-rw-r--r--gfx/ots/src/cvt.cc65
-rw-r--r--gfx/ots/src/cvt.h19
-rw-r--r--gfx/ots/src/fpgm.cc59
-rw-r--r--gfx/ots/src/fpgm.h19
-rw-r--r--gfx/ots/src/gasp.cc119
-rw-r--r--gfx/ots/src/gasp.h24
-rw-r--r--gfx/ots/src/gdef.cc373
-rw-r--r--gfx/ots/src/gdef.h36
-rw-r--r--gfx/ots/src/glyf.cc303
-rw-r--r--gfx/ots/src/glyf.h22
-rw-r--r--gfx/ots/src/gpos.cc817
-rw-r--r--gfx/ots/src/gpos.h29
-rw-r--r--gfx/ots/src/gsub.cc674
-rw-r--r--gfx/ots/src/gsub.h29
-rw-r--r--gfx/ots/src/hdmx.cc147
-rw-r--r--gfx/ots/src/hdmx.h29
-rw-r--r--gfx/ots/src/head.cc159
-rw-r--r--gfx/ots/src/head.h29
-rw-r--r--gfx/ots/src/hhea.cc58
-rw-r--r--gfx/ots/src/hhea.h19
-rw-r--r--gfx/ots/src/hmtx.cc56
-rw-r--r--gfx/ots/src/hmtx.h19
-rw-r--r--gfx/ots/src/kern.cc208
-rw-r--r--gfx/ots/src/kern.h40
-rw-r--r--gfx/ots/src/layout.cc1521
-rw-r--r--gfx/ots/src/layout.h76
-rw-r--r--gfx/ots/src/loca.cc109
-rw-r--r--gfx/ots/src/loca.h20
-rw-r--r--gfx/ots/src/ltsh.cc97
-rw-r--r--gfx/ots/src/ltsh.h21
-rw-r--r--gfx/ots/src/math.cc619
-rw-r--r--gfx/ots/src/math_.h25
-rw-r--r--gfx/ots/src/maxp.cc125
-rw-r--r--gfx/ots/src/maxp.h35
-rw-r--r--gfx/ots/src/metrics.cc165
-rw-r--r--gfx/ots/src/metrics.h54
-rw-r--r--gfx/ots/src/moz.build60
-rw-r--r--gfx/ots/src/name.cc330
-rw-r--r--gfx/ots/src/name.h53
-rw-r--r--gfx/ots/src/os2.cc336
-rw-r--r--gfx/ots/src/os2.h56
-rw-r--r--gfx/ots/src/ots.cc907
-rw-r--r--gfx/ots/src/ots.h294
-rw-r--r--gfx/ots/src/post.cc193
-rw-r--r--gfx/ots/src/post.h29
-rw-r--r--gfx/ots/src/prep.cc59
-rw-r--r--gfx/ots/src/prep.h19
-rw-r--r--gfx/ots/src/vdmx.cc186
-rw-r--r--gfx/ots/src/vdmx.h45
-rw-r--r--gfx/ots/src/vhea.cc60
-rw-r--r--gfx/ots/src/vhea.h20
-rw-r--r--gfx/ots/src/vmtx.cc57
-rw-r--r--gfx/ots/src/vmtx.h20
-rw-r--r--gfx/ots/src/vorg.cc111
-rw-r--r--gfx/ots/src/vorg.h28
61 files changed, 12355 insertions, 0 deletions
diff --git a/gfx/ots/src/cff.cc b/gfx/ots/src/cff.cc
new file mode 100644
index 000000000..23b6dadac
--- /dev/null
+++ b/gfx/ots/src/cff.cc
@@ -0,0 +1,1052 @@
+// Copyright (c) 2012 The Chromium 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 "cff.h"
+
+#include <cstring>
+#include <utility>
+#include <vector>
+
+#include "maxp.h"
+#include "cff_type2_charstring.h"
+
+// CFF - PostScript font program (Compact Font Format) table
+// http://www.microsoft.com/typography/otspec/cff.htm
+// http://www.microsoft.com/typography/otspec/cffspec.htm
+
+#define TABLE_NAME "CFF"
+
+namespace {
+
+enum DICT_OPERAND_TYPE {
+ DICT_OPERAND_INTEGER,
+ DICT_OPERAND_REAL,
+ DICT_OPERATOR,
+};
+
+enum DICT_DATA_TYPE {
+ DICT_DATA_TOPLEVEL,
+ DICT_DATA_FDARRAY,
+};
+
+enum FONT_FORMAT {
+ FORMAT_UNKNOWN,
+ FORMAT_CID_KEYED,
+ FORMAT_OTHER, // Including synthetic fonts
+};
+
+// see Appendix. A
+const size_t kNStdString = 390;
+
+bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
+ if (off_size > 4) {
+ return OTS_FAILURE();
+ }
+
+ uint32_t tmp32 = 0;
+ for (unsigned i = 0; i < off_size; ++i) {
+ uint8_t tmp8 = 0;
+ if (!table->ReadU8(&tmp8)) {
+ return OTS_FAILURE();
+ }
+ tmp32 <<= 8;
+ tmp32 += tmp8;
+ }
+ *offset = tmp32;
+ return true;
+}
+
+bool ParseIndex(ots::Buffer *table, ots::CFFIndex *index) {
+ index->off_size = 0;
+ index->offsets.clear();
+
+ if (!table->ReadU16(&(index->count))) {
+ return OTS_FAILURE();
+ }
+ if (index->count == 0) {
+ // An empty INDEX.
+ index->offset_to_next = table->offset();
+ return true;
+ }
+
+ if (!table->ReadU8(&(index->off_size))) {
+ return OTS_FAILURE();
+ }
+ if ((index->off_size == 0) ||
+ (index->off_size > 4)) {
+ return OTS_FAILURE();
+ }
+
+ const size_t array_size = (index->count + 1) * index->off_size;
+ // less than ((64k + 1) * 4), thus does not overflow.
+ const size_t object_data_offset = table->offset() + array_size;
+ // does not overflow too, since offset() <= 1GB.
+
+ if (object_data_offset >= table->length()) {
+ return OTS_FAILURE();
+ }
+
+ for (unsigned i = 0; i <= index->count; ++i) { // '<=' is not a typo.
+ uint32_t rel_offset = 0;
+ if (!ReadOffset(table, index->off_size, &rel_offset)) {
+ return OTS_FAILURE();
+ }
+ if (rel_offset < 1) {
+ return OTS_FAILURE();
+ }
+ if (i == 0 && rel_offset != 1) {
+ return OTS_FAILURE();
+ }
+
+ if (rel_offset > table->length()) {
+ return OTS_FAILURE();
+ }
+
+ // does not underflow.
+ if (object_data_offset > table->length() - (rel_offset - 1)) {
+ return OTS_FAILURE();
+ }
+
+ index->offsets.push_back(
+ object_data_offset + (rel_offset - 1)); // less than length(), 1GB.
+ }
+
+ for (unsigned i = 1; i < index->offsets.size(); ++i) {
+ // We allow consecutive identical offsets here for zero-length strings.
+ // See http://crbug.com/69341 for more details.
+ if (index->offsets[i] < index->offsets[i - 1]) {
+ return OTS_FAILURE();
+ }
+ }
+
+ index->offset_to_next = index->offsets.back();
+ return true;
+}
+
+bool ParseNameData(
+ ots::Buffer *table, const ots::CFFIndex &index, std::string* out_name) {
+ uint8_t name[256] = {0};
+ if (index.offsets.size() == 0) { // just in case.
+ return OTS_FAILURE();
+ }
+ for (unsigned i = 1; i < index.offsets.size(); ++i) {
+ const size_t length = index.offsets[i] - index.offsets[i - 1];
+ // font names should be no longer than 127 characters.
+ if (length > 127) {
+ return OTS_FAILURE();
+ }
+
+ table->set_offset(index.offsets[i - 1]);
+ if (!table->Read(name, length)) {
+ return OTS_FAILURE();
+ }
+
+ for (size_t j = 0; j < length; ++j) {
+ // setting the first byte to NUL is allowed.
+ if (j == 0 && name[j] == 0) continue;
+ // non-ASCII characters are not recommended (except the first character).
+ if (name[j] < 33 || name[j] > 126) {
+ return OTS_FAILURE();
+ }
+ // [, ], ... are not allowed.
+ if (std::strchr("[](){}<>/% ", name[j])) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+
+ *out_name = reinterpret_cast<char *>(name);
+ return true;
+}
+
+bool CheckOffset(const std::pair<uint32_t, DICT_OPERAND_TYPE>& operand,
+ size_t table_length) {
+ if (operand.second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (operand.first >= table_length) {
+ return OTS_FAILURE();
+ }
+ return true;
+}
+
+bool CheckSid(const std::pair<uint32_t, DICT_OPERAND_TYPE>& operand,
+ size_t sid_max) {
+ if (operand.second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (operand.first > sid_max) {
+ return OTS_FAILURE();
+ }
+ return true;
+}
+
+bool ParseDictDataBcd(
+ ots::Buffer *table,
+ std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+ bool read_decimal_point = false;
+ bool read_e = false;
+
+ uint8_t nibble = 0;
+ size_t count = 0;
+ while (true) {
+ if (!table->ReadU8(&nibble)) {
+ return OTS_FAILURE();
+ }
+ if ((nibble & 0xf0) == 0xf0) {
+ if ((nibble & 0xf) == 0xf) {
+ // TODO(yusukes): would be better to store actual double value,
+ // rather than the dummy integer.
+ operands->push_back(std::make_pair(static_cast<uint32_t>(0),
+ DICT_OPERAND_REAL));
+ return true;
+ }
+ return OTS_FAILURE();
+ }
+ if ((nibble & 0x0f) == 0x0f) {
+ operands->push_back(std::make_pair(static_cast<uint32_t>(0),
+ DICT_OPERAND_REAL));
+ return true;
+ }
+
+ // check number format
+ uint8_t nibbles[2];
+ nibbles[0] = (nibble & 0xf0) >> 8;
+ nibbles[1] = (nibble & 0x0f);
+ for (unsigned i = 0; i < 2; ++i) {
+ if (nibbles[i] == 0xd) { // reserved number
+ return OTS_FAILURE();
+ }
+ if ((nibbles[i] == 0xe) && // minus
+ ((count > 0) || (i > 0))) {
+ return OTS_FAILURE(); // minus sign should be the first character.
+ }
+ if (nibbles[i] == 0xa) { // decimal point
+ if (!read_decimal_point) {
+ read_decimal_point = true;
+ } else {
+ return OTS_FAILURE(); // two or more points.
+ }
+ }
+ if ((nibbles[i] == 0xb) || // E+
+ (nibbles[i] == 0xc)) { // E-
+ if (!read_e) {
+ read_e = true;
+ } else {
+ return OTS_FAILURE(); // two or more E's.
+ }
+ }
+ }
+ ++count;
+ }
+}
+
+bool ParseDictDataEscapedOperator(
+ ots::Buffer *table,
+ std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+ uint8_t op = 0;
+ if (!table->ReadU8(&op)) {
+ return OTS_FAILURE();
+ }
+
+ if ((op <= 14) ||
+ (op >= 17 && op <= 23) ||
+ (op >= 30 && op <= 38)) {
+ operands->push_back(std::make_pair((12U << 8) + op, DICT_OPERATOR));
+ return true;
+ }
+
+ // reserved area.
+ return OTS_FAILURE();
+}
+
+bool ParseDictDataNumber(
+ ots::Buffer *table, uint8_t b0,
+ std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+ uint8_t b1 = 0;
+ uint8_t b2 = 0;
+ uint8_t b3 = 0;
+ uint8_t b4 = 0;
+
+ switch (b0) {
+ case 28: // shortint
+ if (!table->ReadU8(&b1) ||
+ !table->ReadU8(&b2)) {
+ return OTS_FAILURE();
+ }
+ operands->push_back(std::make_pair(
+ static_cast<uint32_t>((b1 << 8) + b2), DICT_OPERAND_INTEGER));
+ return true;
+
+ case 29: // longint
+ if (!table->ReadU8(&b1) ||
+ !table->ReadU8(&b2) ||
+ !table->ReadU8(&b3) ||
+ !table->ReadU8(&b4)) {
+ return OTS_FAILURE();
+ }
+ operands->push_back(std::make_pair(
+ static_cast<uint32_t>((b1 << 24) + (b2 << 16) + (b3 << 8) + b4),
+ DICT_OPERAND_INTEGER));
+ return true;
+
+ case 30: // binary coded decimal
+ return ParseDictDataBcd(table, operands);
+
+ default:
+ break;
+ }
+
+ uint32_t result;
+ if (b0 >=32 && b0 <=246) {
+ result = b0 - 139;
+ } else if (b0 >=247 && b0 <= 250) {
+ if (!table->ReadU8(&b1)) {
+ return OTS_FAILURE();
+ }
+ result = (b0 - 247) * 256 + b1 + 108;
+ } else if (b0 >= 251 && b0 <= 254) {
+ if (!table->ReadU8(&b1)) {
+ return OTS_FAILURE();
+ }
+ result = -(b0 - 251) * 256 + b1 - 108;
+ } else {
+ return OTS_FAILURE();
+ }
+
+ operands->push_back(std::make_pair(result, DICT_OPERAND_INTEGER));
+ return true;
+}
+
+bool ParseDictDataReadNext(
+ ots::Buffer *table,
+ std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+ uint8_t op = 0;
+ if (!table->ReadU8(&op)) {
+ return OTS_FAILURE();
+ }
+ if (op <= 21) {
+ if (op == 12) {
+ return ParseDictDataEscapedOperator(table, operands);
+ }
+ operands->push_back(std::make_pair(
+ static_cast<uint32_t>(op), DICT_OPERATOR));
+ return true;
+ } else if (op <= 27 || op == 31 || op == 255) {
+ // reserved area.
+ return OTS_FAILURE();
+ }
+
+ return ParseDictDataNumber(table, op, operands);
+}
+
+bool ParsePrivateDictData(
+ const uint8_t *data,
+ size_t table_length, size_t offset, size_t dict_length,
+ DICT_DATA_TYPE type, ots::OpenTypeCFF *out_cff) {
+ ots::Buffer table(data + offset, dict_length);
+ std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+
+ // Since a Private DICT for FDArray might not have a Local Subr (e.g. Hiragino
+ // Kaku Gothic Std W8), we create an empty Local Subr here to match the size
+ // of FDArray the size of |local_subrs_per_font|.
+ if (type == DICT_DATA_FDARRAY) {
+ out_cff->local_subrs_per_font.push_back(new ots::CFFIndex);
+ }
+
+ while (table.offset() < dict_length) {
+ if (!ParseDictDataReadNext(&table, &operands)) {
+ return OTS_FAILURE();
+ }
+ if (operands.empty()) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() > 48) {
+ // An operator may be preceded by up to a maximum of 48 operands.
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERATOR) {
+ continue;
+ }
+
+ // got operator
+ const uint32_t op = operands.back().first;
+ operands.pop_back();
+
+ switch (op) {
+ // hints
+ case 6: // BlueValues
+ case 7: // OtherBlues
+ case 8: // FamilyBlues
+ case 9: // FamilyOtherBlues
+ if (operands.empty() || (operands.size() % 2) != 0) {
+ return OTS_FAILURE();
+ }
+ break;
+
+ // array
+ case (12U << 8) + 12: // StemSnapH (delta)
+ case (12U << 8) + 13: // StemSnapV (delta)
+ if (operands.empty()) {
+ return OTS_FAILURE();
+ }
+ break;
+
+ // number
+ case 10: // StdHW
+ case 11: // StdVW
+ case 20: // defaultWidthX
+ case 21: // nominalWidthX
+ case (12U << 8) + 9: // BlueScale
+ case (12U << 8) + 10: // BlueShift
+ case (12U << 8) + 11: // BlueFuzz
+ case (12U << 8) + 17: // LanguageGroup
+ case (12U << 8) + 18: // ExpansionFactor
+ case (12U << 8) + 19: // initialRandomSeed
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ break;
+
+ // Local Subrs INDEX, offset(self)
+ case 19: {
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first >= 1024 * 1024 * 1024) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first + offset >= table_length) {
+ return OTS_FAILURE();
+ }
+ // parse "16. Local Subrs INDEX"
+ ots::Buffer cff_table(data, table_length);
+ cff_table.set_offset(operands.back().first + offset);
+ ots::CFFIndex *local_subrs_index = NULL;
+ if (type == DICT_DATA_FDARRAY) {
+ if (out_cff->local_subrs_per_font.empty()) {
+ return OTS_FAILURE(); // not reached.
+ }
+ local_subrs_index = out_cff->local_subrs_per_font.back();
+ } else { // type == DICT_DATA_TOPLEVEL
+ if (out_cff->local_subrs) {
+ return OTS_FAILURE(); // two or more local_subrs?
+ }
+ local_subrs_index = new ots::CFFIndex;
+ out_cff->local_subrs = local_subrs_index;
+ }
+ if (!ParseIndex(&cff_table, local_subrs_index)) {
+ return OTS_FAILURE();
+ }
+ break;
+ }
+
+ // boolean
+ case (12U << 8) + 14: // ForceBold
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first >= 2) {
+ return OTS_FAILURE();
+ }
+ break;
+
+ default:
+ return OTS_FAILURE();
+ }
+ operands.clear();
+ }
+
+ return true;
+}
+
+bool ParseDictData(const uint8_t *data, size_t table_length,
+ const ots::CFFIndex &index, uint16_t glyphs,
+ size_t sid_max, DICT_DATA_TYPE type,
+ ots::OpenTypeCFF *out_cff) {
+ for (unsigned i = 1; i < index.offsets.size(); ++i) {
+ if (type == DICT_DATA_TOPLEVEL) {
+ out_cff->char_strings_array.push_back(new ots::CFFIndex);
+ }
+ size_t dict_length = index.offsets[i] - index.offsets[i - 1];
+ ots::Buffer table(data + index.offsets[i - 1], dict_length);
+
+ std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+
+ FONT_FORMAT font_format = FORMAT_UNKNOWN;
+ bool have_ros = false;
+ uint16_t charstring_glyphs = 0;
+ size_t charset_offset = 0;
+
+ while (table.offset() < dict_length) {
+ if (!ParseDictDataReadNext(&table, &operands)) {
+ return OTS_FAILURE();
+ }
+ if (operands.empty()) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() > 48) {
+ // An operator may be preceded by up to a maximum of 48 operands.
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERATOR) continue;
+
+ // got operator
+ const uint32_t op = operands.back().first;
+ operands.pop_back();
+
+ switch (op) {
+ // SID
+ case 0: // version
+ case 1: // Notice
+ case 2: // Copyright
+ case 3: // FullName
+ case 4: // FamilyName
+ case (12U << 8) + 0: // Copyright
+ case (12U << 8) + 21: // PostScript
+ case (12U << 8) + 22: // BaseFontName
+ case (12U << 8) + 38: // FontName
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckSid(operands.back(), sid_max)) {
+ return OTS_FAILURE();
+ }
+ break;
+
+ // array
+ case 5: // FontBBox
+ case 14: // XUID
+ case (12U << 8) + 7: // FontMatrix
+ case (12U << 8) + 23: // BaseFontBlend (delta)
+ if (operands.empty()) {
+ return OTS_FAILURE();
+ }
+ break;
+
+ // number
+ case 13: // UniqueID
+ case (12U << 8) + 2: // ItalicAngle
+ case (12U << 8) + 3: // UnderlinePosition
+ case (12U << 8) + 4: // UnderlineThickness
+ case (12U << 8) + 5: // PaintType
+ case (12U << 8) + 8: // StrokeWidth
+ case (12U << 8) + 20: // SyntheticBase
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ break;
+ case (12U << 8) + 31: // CIDFontVersion
+ case (12U << 8) + 32: // CIDFontRevision
+ case (12U << 8) + 33: // CIDFontType
+ case (12U << 8) + 34: // CIDCount
+ case (12U << 8) + 35: // UIDBase
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (font_format != FORMAT_CID_KEYED) {
+ return OTS_FAILURE();
+ }
+ break;
+ case (12U << 8) + 6: // CharstringType
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if(operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first != 2) {
+ // We only support the "Type 2 Charstring Format."
+ // TODO(yusukes): Support Type 1 format? Is that still in use?
+ return OTS_FAILURE();
+ }
+ break;
+
+ // boolean
+ case (12U << 8) + 1: // isFixedPitch
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first >= 2) {
+ return OTS_FAILURE();
+ }
+ break;
+
+ // offset(0)
+ case 15: // charset
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first <= 2) {
+ // predefined charset, ISOAdobe, Expert or ExpertSubset, is used.
+ break;
+ }
+ if (!CheckOffset(operands.back(), table_length)) {
+ return OTS_FAILURE();
+ }
+ if (charset_offset) {
+ return OTS_FAILURE(); // multiple charset tables?
+ }
+ charset_offset = operands.back().first;
+ break;
+
+ case 16: { // Encoding
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first <= 1) {
+ break; // predefined encoding, "Standard" or "Expert", is used.
+ }
+ if (!CheckOffset(operands.back(), table_length)) {
+ return OTS_FAILURE();
+ }
+
+ // parse sub dictionary INDEX.
+ ots::Buffer cff_table(data, table_length);
+ cff_table.set_offset(operands.back().first);
+ uint8_t format = 0;
+ if (!cff_table.ReadU8(&format)) {
+ return OTS_FAILURE();
+ }
+ if (format & 0x80) {
+ // supplemental encoding is not supported at the moment.
+ return OTS_FAILURE();
+ }
+ // TODO(yusukes): support & parse supplemental encoding tables.
+ break;
+ }
+
+ case 17: { // CharStrings
+ if (type != DICT_DATA_TOPLEVEL) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckOffset(operands.back(), table_length)) {
+ return OTS_FAILURE();
+ }
+ // parse "14. CharStrings INDEX"
+ ots::Buffer cff_table(data, table_length);
+ cff_table.set_offset(operands.back().first);
+ ots::CFFIndex *charstring_index = out_cff->char_strings_array.back();
+ if (!ParseIndex(&cff_table, charstring_index)) {
+ return OTS_FAILURE();
+ }
+ if (charstring_index->count < 2) {
+ return OTS_FAILURE();
+ }
+ if (charstring_glyphs) {
+ return OTS_FAILURE(); // multiple charstring tables?
+ }
+ charstring_glyphs = charstring_index->count;
+ if (charstring_glyphs != glyphs) {
+ return OTS_FAILURE(); // CFF and maxp have different number of glyphs?
+ }
+ break;
+ }
+
+ case (12U << 8) + 36: { // FDArray
+ if (type != DICT_DATA_TOPLEVEL) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckOffset(operands.back(), table_length)) {
+ return OTS_FAILURE();
+ }
+
+ // parse sub dictionary INDEX.
+ ots::Buffer cff_table(data, table_length);
+ cff_table.set_offset(operands.back().first);
+ ots::CFFIndex sub_dict_index;
+ if (!ParseIndex(&cff_table, &sub_dict_index)) {
+ return OTS_FAILURE();
+ }
+ if (!ParseDictData(data, table_length,
+ sub_dict_index,
+ glyphs, sid_max, DICT_DATA_FDARRAY,
+ out_cff)) {
+ return OTS_FAILURE();
+ }
+ if (out_cff->font_dict_length != 0) {
+ return OTS_FAILURE(); // two or more FDArray found.
+ }
+ out_cff->font_dict_length = sub_dict_index.count;
+ break;
+ }
+
+ case (12U << 8) + 37: { // FDSelect
+ if (type != DICT_DATA_TOPLEVEL) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckOffset(operands.back(), table_length)) {
+ return OTS_FAILURE();
+ }
+
+ // parse FDSelect data structure
+ ots::Buffer cff_table(data, table_length);
+ cff_table.set_offset(operands.back().first);
+ uint8_t format = 0;
+ if (!cff_table.ReadU8(&format)) {
+ return OTS_FAILURE();
+ }
+ if (format == 0) {
+ for (uint16_t j = 0; j < glyphs; ++j) {
+ uint8_t fd_index = 0;
+ if (!cff_table.ReadU8(&fd_index)) {
+ return OTS_FAILURE();
+ }
+ (out_cff->fd_select)[j] = fd_index;
+ }
+ } else if (format == 3) {
+ uint16_t n_ranges = 0;
+ if (!cff_table.ReadU16(&n_ranges)) {
+ return OTS_FAILURE();
+ }
+ if (n_ranges == 0) {
+ return OTS_FAILURE();
+ }
+
+ uint16_t last_gid = 0;
+ uint8_t fd_index = 0;
+ for (unsigned j = 0; j < n_ranges; ++j) {
+ uint16_t first = 0; // GID
+ if (!cff_table.ReadU16(&first)) {
+ return OTS_FAILURE();
+ }
+
+ // Sanity checks.
+ if ((j == 0) && (first != 0)) {
+ return OTS_FAILURE();
+ }
+ if ((j != 0) && (last_gid >= first)) {
+ return OTS_FAILURE(); // not increasing order.
+ }
+
+ // Copy the mapping to |out_cff->fd_select|.
+ if (j != 0) {
+ for (uint16_t k = last_gid; k < first; ++k) {
+ if (!out_cff->fd_select.insert(
+ std::make_pair(k, fd_index)).second) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+
+ if (!cff_table.ReadU8(&fd_index)) {
+ return OTS_FAILURE();
+ }
+ last_gid = first;
+ // TODO(yusukes): check GID?
+ }
+ uint16_t sentinel = 0;
+ if (!cff_table.ReadU16(&sentinel)) {
+ return OTS_FAILURE();
+ }
+ if (last_gid >= sentinel) {
+ return OTS_FAILURE();
+ }
+ for (uint16_t k = last_gid; k < sentinel; ++k) {
+ if (!out_cff->fd_select.insert(
+ std::make_pair(k, fd_index)).second) {
+ return OTS_FAILURE();
+ }
+ }
+ } else {
+ // unknown format
+ return OTS_FAILURE();
+ }
+ break;
+ }
+
+ // Private DICT (2 * number)
+ case 18: {
+ if (operands.size() != 2) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ const uint32_t private_offset = operands.back().first;
+ operands.pop_back();
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ const uint32_t private_length = operands.back().first;
+ if (private_offset > table_length) {
+ return OTS_FAILURE();
+ }
+ if (private_length >= table_length) {
+ return OTS_FAILURE();
+ }
+ if (private_length + private_offset > table_length) {
+ return OTS_FAILURE();
+ }
+ // parse "15. Private DICT Data"
+ if (!ParsePrivateDictData(data, table_length,
+ private_offset, private_length,
+ type, out_cff)) {
+ return OTS_FAILURE();
+ }
+ break;
+ }
+
+ // ROS
+ case (12U << 8) + 30:
+ if (font_format != FORMAT_UNKNOWN) {
+ return OTS_FAILURE();
+ }
+ font_format = FORMAT_CID_KEYED;
+ if (operands.size() != 3) {
+ return OTS_FAILURE();
+ }
+ // check SIDs
+ operands.pop_back(); // ignore the first number.
+ if (!CheckSid(operands.back(), sid_max)) {
+ return OTS_FAILURE();
+ }
+ operands.pop_back();
+ if (!CheckSid(operands.back(), sid_max)) {
+ return OTS_FAILURE();
+ }
+ if (have_ros) {
+ return OTS_FAILURE(); // multiple ROS tables?
+ }
+ have_ros = true;
+ break;
+
+ default:
+ return OTS_FAILURE();
+ }
+ operands.clear();
+
+ if (font_format == FORMAT_UNKNOWN) {
+ font_format = FORMAT_OTHER;
+ }
+ }
+
+ // parse "13. Charsets"
+ if (charset_offset) {
+ ots::Buffer cff_table(data, table_length);
+ cff_table.set_offset(charset_offset);
+ uint8_t format = 0;
+ if (!cff_table.ReadU8(&format)) {
+ return OTS_FAILURE();
+ }
+ switch (format) {
+ case 0:
+ for (uint16_t j = 1 /* .notdef is omitted */; j < glyphs; ++j) {
+ uint16_t sid = 0;
+ if (!cff_table.ReadU16(&sid)) {
+ return OTS_FAILURE();
+ }
+ if (!have_ros && (sid > sid_max)) {
+ return OTS_FAILURE();
+ }
+ // TODO(yusukes): check CIDs when have_ros is true.
+ }
+ break;
+
+ case 1:
+ case 2: {
+ uint32_t total = 1; // .notdef is omitted.
+ while (total < glyphs) {
+ uint16_t sid = 0;
+ if (!cff_table.ReadU16(&sid)) {
+ return OTS_FAILURE();
+ }
+ if (!have_ros && (sid > sid_max)) {
+ return OTS_FAILURE();
+ }
+ // TODO(yusukes): check CIDs when have_ros is true.
+
+ if (format == 1) {
+ uint8_t left = 0;
+ if (!cff_table.ReadU8(&left)) {
+ return OTS_FAILURE();
+ }
+ total += (left + 1);
+ } else {
+ uint16_t left = 0;
+ if (!cff_table.ReadU16(&left)) {
+ return OTS_FAILURE();
+ }
+ total += (left + 1);
+ }
+ }
+ break;
+ }
+
+ default:
+ return OTS_FAILURE();
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+bool ots_cff_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ font->cff = new OpenTypeCFF;
+ font->cff->data = data;
+ font->cff->length = length;
+ font->cff->font_dict_length = 0;
+ font->cff->local_subrs = NULL;
+
+ // parse "6. Header" in the Adobe Compact Font Format Specification
+ uint8_t major = 0;
+ uint8_t minor = 0;
+ uint8_t hdr_size = 0;
+ uint8_t off_size = 0;
+ if (!table.ReadU8(&major)) {
+ return OTS_FAILURE();
+ }
+ if (!table.ReadU8(&minor)) {
+ return OTS_FAILURE();
+ }
+ if (!table.ReadU8(&hdr_size)) {
+ return OTS_FAILURE();
+ }
+ if (!table.ReadU8(&off_size)) {
+ return OTS_FAILURE();
+ }
+ if ((off_size == 0) || (off_size > 4)) {
+ return OTS_FAILURE();
+ }
+
+ if ((major != 1) ||
+ (minor != 0) ||
+ (hdr_size != 4)) {
+ return OTS_FAILURE();
+ }
+ if (hdr_size >= length) {
+ return OTS_FAILURE();
+ }
+
+ // parse "7. Name INDEX"
+ table.set_offset(hdr_size);
+ CFFIndex name_index;
+ if (!ParseIndex(&table, &name_index)) {
+ return OTS_FAILURE();
+ }
+ if (!ParseNameData(&table, name_index, &(font->cff->name))) {
+ return OTS_FAILURE();
+ }
+
+ // parse "8. Top DICT INDEX"
+ table.set_offset(name_index.offset_to_next);
+ CFFIndex top_dict_index;
+ if (!ParseIndex(&table, &top_dict_index)) {
+ return OTS_FAILURE();
+ }
+ if (name_index.count != top_dict_index.count) {
+ return OTS_FAILURE();
+ }
+
+ // parse "10. String INDEX"
+ table.set_offset(top_dict_index.offset_to_next);
+ CFFIndex string_index;
+ if (!ParseIndex(&table, &string_index)) {
+ return OTS_FAILURE();
+ }
+ if (string_index.count >= 65000 - kNStdString) {
+ return OTS_FAILURE();
+ }
+
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+ const size_t sid_max = string_index.count + kNStdString;
+ // string_index.count == 0 is allowed.
+
+ // parse "9. Top DICT Data"
+ if (!ParseDictData(data, length, top_dict_index,
+ num_glyphs, sid_max,
+ DICT_DATA_TOPLEVEL, font->cff)) {
+ return OTS_FAILURE();
+ }
+
+ // parse "16. Global Subrs INDEX"
+ table.set_offset(string_index.offset_to_next);
+ CFFIndex global_subrs_index;
+ if (!ParseIndex(&table, &global_subrs_index)) {
+ return OTS_FAILURE();
+ }
+
+ // Check if all fd_index in FDSelect are valid.
+ std::map<uint16_t, uint8_t>::const_iterator iter;
+ std::map<uint16_t, uint8_t>::const_iterator end = font->cff->fd_select.end();
+ for (iter = font->cff->fd_select.begin(); iter != end; ++iter) {
+ if (iter->second >= font->cff->font_dict_length) {
+ return OTS_FAILURE();
+ }
+ }
+
+ // Check if all charstrings (font hinting code for each glyph) are valid.
+ for (size_t i = 0; i < font->cff->char_strings_array.size(); ++i) {
+ if (!ValidateType2CharStringIndex(font,
+ *(font->cff->char_strings_array.at(i)),
+ global_subrs_index,
+ font->cff->fd_select,
+ font->cff->local_subrs_per_font,
+ font->cff->local_subrs,
+ &table)) {
+ return OTS_FAILURE_MSG("Failed validating charstring set %d", (int) i);
+ }
+ }
+
+ return true;
+}
+
+bool ots_cff_should_serialise(Font *font) {
+ return font->cff != NULL;
+}
+
+bool ots_cff_serialise(OTSStream *out, Font *font) {
+ // TODO(yusukes): would be better to transcode the data,
+ // rather than simple memcpy.
+ if (!out->Write(font->cff->data, font->cff->length)) {
+ return OTS_FAILURE();
+ }
+ return true;
+}
+
+void ots_cff_reuse(Font *font, Font *other) {
+ font->cff = other->cff;
+ font->cff_reused = true;
+}
+
+void ots_cff_free(Font *font) {
+ if (font->cff) {
+ for (size_t i = 0; i < font->cff->char_strings_array.size(); ++i) {
+ delete (font->cff->char_strings_array)[i];
+ }
+ for (size_t i = 0; i < font->cff->local_subrs_per_font.size(); ++i) {
+ delete (font->cff->local_subrs_per_font)[i];
+ }
+ delete font->cff->local_subrs;
+ delete font->cff;
+ }
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/cff.h b/gfx/ots/src/cff.h
new file mode 100644
index 000000000..5584acc62
--- /dev/null
+++ b/gfx/ots/src/cff.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 The Chromium 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_CFF_H_
+#define OTS_CFF_H_
+
+#include "ots.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace ots {
+
+struct CFFIndex {
+ CFFIndex()
+ : count(0), off_size(0), offset_to_next(0) {}
+ uint16_t count;
+ uint8_t off_size;
+ std::vector<uint32_t> offsets;
+ uint32_t offset_to_next;
+};
+
+struct OpenTypeCFF {
+ const uint8_t *data;
+ size_t length;
+ // 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;
+
+ // A list of char strings.
+ std::vector<CFFIndex *> char_strings_array;
+ // 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;
+};
+
+} // namespace ots
+
+#endif // OTS_CFF_H_
diff --git a/gfx/ots/src/cff_type2_charstring.cc b/gfx/ots/src/cff_type2_charstring.cc
new file mode 100644
index 000000000..8c5544b2e
--- /dev/null
+++ b/gfx/ots/src/cff_type2_charstring.cc
@@ -0,0 +1,912 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A parser for the Type 2 Charstring Format.
+// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
+
+#include "cff_type2_charstring.h"
+
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <stack>
+#include <string>
+#include <utility>
+
+#define TABLE_NAME "CFF"
+
+namespace {
+
+// Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical
+// 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;
+
+// |dummy_result| should be a huge positive integer so callsubr and callgsubr
+// 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);
+
+#ifdef DUMP_T2CHARSTRING
+// Converts |op| to a string and returns it.
+const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
+ switch (op) {
+ case ots::kHStem:
+ return "HStem";
+ case ots::kVStem:
+ return "VStem";
+ case ots::kVMoveTo:
+ return "VMoveTo";
+ case ots::kRLineTo:
+ return "RLineTo";
+ case ots::kHLineTo:
+ return "HLineTo";
+ case ots::kVLineTo:
+ return "VLineTo";
+ case ots::kRRCurveTo:
+ return "RRCurveTo";
+ case ots::kCallSubr:
+ return "CallSubr";
+ case ots::kReturn:
+ return "Return";
+ case ots::kEndChar:
+ return "EndChar";
+ case ots::kHStemHm:
+ return "HStemHm";
+ case ots::kHintMask:
+ return "HintMask";
+ case ots::kCntrMask:
+ return "CntrMask";
+ case ots::kRMoveTo:
+ return "RMoveTo";
+ case ots::kHMoveTo:
+ return "HMoveTo";
+ case ots::kVStemHm:
+ return "VStemHm";
+ case ots::kRCurveLine:
+ return "RCurveLine";
+ case ots::kRLineCurve:
+ return "RLineCurve";
+ case ots::kVVCurveTo:
+ return "VVCurveTo";
+ case ots::kHHCurveTo:
+ return "HHCurveTo";
+ case ots::kCallGSubr:
+ return "CallGSubr";
+ case ots::kVHCurveTo:
+ return "VHCurveTo";
+ case ots::kHVCurveTo:
+ return "HVCurveTo";
+ case ots::kDotSection:
+ return "DotSection";
+ case ots::kAnd:
+ return "And";
+ case ots::kOr:
+ return "Or";
+ case ots::kNot:
+ return "Not";
+ case ots::kAbs:
+ return "Abs";
+ case ots::kAdd:
+ return "Add";
+ case ots::kSub:
+ return "Sub";
+ case ots::kDiv:
+ return "Div";
+ case ots::kNeg:
+ return "Neg";
+ case ots::kEq:
+ return "Eq";
+ case ots::kDrop:
+ return "Drop";
+ case ots::kPut:
+ return "Put";
+ case ots::kGet:
+ return "Get";
+ case ots::kIfElse:
+ return "IfElse";
+ case ots::kRandom:
+ return "Random";
+ case ots::kMul:
+ return "Mul";
+ case ots::kSqrt:
+ return "Sqrt";
+ case ots::kDup:
+ return "Dup";
+ case ots::kExch:
+ return "Exch";
+ case ots::kIndex:
+ return "Index";
+ case ots::kRoll:
+ return "Roll";
+ case ots::kHFlex:
+ return "HFlex";
+ case ots::kFlex:
+ return "Flex";
+ case ots::kHFlex1:
+ return "HFlex1";
+ case ots::kFlex1:
+ return "Flex1";
+ }
+
+ return "UNKNOWN";
+}
+#endif
+
+// 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) {
+ uint8_t v = 0;
+ if (!char_string->ReadU8(&v)) {
+ return OTS_FAILURE();
+ }
+ *out_is_operator = false;
+
+ // The conversion algorithm is described in Adobe Technical Note #5177, page
+ // 13, Table 1.
+ if (v <= 11) {
+ *out_number = v;
+ *out_is_operator = true;
+ } else if (v == 12) {
+ uint16_t result = (v << 8);
+ if (!char_string->ReadU8(&v)) {
+ return OTS_FAILURE();
+ }
+ result += v;
+ *out_number = result;
+ *out_is_operator = true;
+ } else if (v <= 27) {
+ // Special handling for v==19 and v==20 are implemented in
+ // ExecuteType2CharStringOperator().
+ *out_number = v;
+ *out_is_operator = true;
+ } else if (v == 28) {
+ if (!char_string->ReadU8(&v)) {
+ return OTS_FAILURE();
+ }
+ uint16_t result = (v << 8);
+ if (!char_string->ReadU8(&v)) {
+ return OTS_FAILURE();
+ }
+ result += v;
+ *out_number = result;
+ } else if (v <= 31) {
+ *out_number = v;
+ *out_is_operator = true;
+ } else if (v <= 246) {
+ *out_number = static_cast<int32_t>(v) - 139;
+ } else if (v <= 250) {
+ uint8_t w = 0;
+ if (!char_string->ReadU8(&w)) {
+ return OTS_FAILURE();
+ }
+ *out_number = ((static_cast<int32_t>(v) - 247) * 256) +
+ static_cast<int32_t>(w) + 108;
+ } else if (v <= 254) {
+ uint8_t w = 0;
+ if (!char_string->ReadU8(&w)) {
+ return OTS_FAILURE();
+ }
+ *out_number = -((static_cast<int32_t>(v) - 251) * 256) -
+ static_cast<int32_t>(w) - 108;
+ } else if (v == 255) {
+ // TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255,
+ // we should treat the following 4-bytes as a 16.16 fixed-point number
+ // rather than 32bit signed int.
+ if (!char_string->Skip(4)) {
+ return OTS_FAILURE();
+ }
+ *out_number = dummy_result;
+ } else {
+ return OTS_FAILURE();
+ }
+
+ 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
+// |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) {
+ const size_t stack_size = argument_stack->size();
+
+ switch (op) {
+ case ots::kCallSubr:
+ case ots::kCallGSubr: {
+ const ots::CFFIndex& subrs_index =
+ (op == ots::kCallSubr ? local_subrs_index : global_subrs_index);
+
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ int32_t subr_number = argument_stack->top();
+ argument_stack->pop();
+ if (subr_number == dummy_result) {
+ // For safety, we allow subr calls only with immediate subr numbers for
+ // now. For example, we allow "123 callgsubr", but does not allow "100 12
+ // add callgsubr". Please note that arithmetic and conditional operators
+ // always push the |dummy_result| in this implementation.
+ return OTS_FAILURE();
+ }
+
+ // See Adobe Technical Note #5176 (CFF), "16. Local/GlobalSubrs INDEXes."
+ int32_t bias = 32768;
+ if (subrs_index.count < 1240) {
+ bias = 107;
+ } else if (subrs_index.count < 33900) {
+ bias = 1131;
+ }
+ subr_number += bias;
+
+ // Sanity checks of |subr_number|.
+ if (subr_number < 0) {
+ return OTS_FAILURE();
+ }
+ if (subr_number >= kMaxSubrsCount) {
+ return OTS_FAILURE();
+ }
+ if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) {
+ return OTS_FAILURE(); // The number is out-of-bounds.
+ }
+
+ // Prepare ots::Buffer where we're going to jump.
+ const size_t length =
+ subrs_index.offsets[subr_number + 1] - subrs_index.offsets[subr_number];
+ if (length > kMaxCharStringLength) {
+ return OTS_FAILURE();
+ }
+ const size_t offset = subrs_index.offsets[subr_number];
+ cff_table->set_offset(offset);
+ if (!cff_table->Skip(length)) {
+ return OTS_FAILURE();
+ }
+ 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);
+ }
+
+ case ots::kReturn:
+ return true;
+
+ case ots::kEndChar:
+ *out_found_endchar = true;
+ *in_out_found_width = true; // just in case.
+ return true;
+
+ case ots::kHStem:
+ case ots::kVStem:
+ case ots::kHStemHm:
+ case ots::kVStemHm: {
+ bool successful = false;
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ if ((stack_size % 2) == 0) {
+ successful = true;
+ } else if ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) {
+ // The -1 is for "width" argument. For details, see Adobe Technical Note
+ // #5177, page 16, note 4.
+ successful = true;
+ }
+ (*in_out_num_stems) += (stack_size / 2);
+ if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ *in_out_found_width = true; // always set true since "w" might be 0 byte.
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kRMoveTo: {
+ bool successful = false;
+ if (stack_size == 2) {
+ successful = true;
+ } else if ((!(*in_out_found_width)) && (stack_size - 1 == 2)) {
+ successful = true;
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ *in_out_found_width = true;
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kVMoveTo:
+ case ots::kHMoveTo: {
+ bool successful = false;
+ if (stack_size == 1) {
+ successful = true;
+ } else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) {
+ successful = true;
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ *in_out_found_width = true;
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kHintMask:
+ case ots::kCntrMask: {
+ bool successful = false;
+ if (stack_size == 0) {
+ successful = true;
+ } else if ((!(*in_out_found_width)) && (stack_size == 1)) {
+ // A number for "width" is found.
+ successful = true;
+ } else if ((!(*in_out_found_width)) || // in this case, any sizes are ok.
+ ((stack_size % 2) == 0)) {
+ // The numbers are vstem definition.
+ // See Adobe Technical Note #5177, page 24, hintmask.
+ (*in_out_num_stems) += (stack_size / 2);
+ if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
+ return OTS_FAILURE();
+ }
+ successful = true;
+ }
+ if (!successful) {
+ return OTS_FAILURE();
+ }
+
+ if ((*in_out_num_stems) == 0) {
+ return OTS_FAILURE();
+ }
+ const size_t mask_bytes = (*in_out_num_stems + 7) / 8;
+ if (!char_string->Skip(mask_bytes)) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ *in_out_found_width = true;
+ return true;
+ }
+
+ case ots::kRLineTo:
+ if (!(*in_out_found_width)) {
+ // The first stack-clearing operator should be one of hstem, hstemhm,
+ // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or
+ // endchar. For details, see Adobe Technical Note #5177, page 16, note 4.
+ return OTS_FAILURE();
+ }
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ if ((stack_size % 2) != 0) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kHLineTo:
+ case ots::kVLineTo:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kRRCurveTo:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 6) {
+ return OTS_FAILURE();
+ }
+ if ((stack_size % 6) != 0) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kRCurveLine:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 8) {
+ return OTS_FAILURE();
+ }
+ if (((stack_size - 2) % 6) != 0) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kRLineCurve:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 8) {
+ return OTS_FAILURE();
+ }
+ if (((stack_size - 6) % 2) != 0) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kVVCurveTo:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 4) {
+ return OTS_FAILURE();
+ }
+ if (((stack_size % 4) != 0) &&
+ (((stack_size - 1) % 4) != 0)) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kHHCurveTo: {
+ bool successful = false;
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 4) {
+ return OTS_FAILURE();
+ }
+ if ((stack_size % 4) == 0) {
+ // {dxa dxb dyb dxc}+
+ successful = true;
+ } else if (((stack_size - 1) % 4) == 0) {
+ // dy1? {dxa dxb dyb dxc}+
+ successful = true;
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kVHCurveTo:
+ case ots::kHVCurveTo: {
+ bool successful = false;
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 4) {
+ return OTS_FAILURE();
+ }
+ if (((stack_size - 4) % 8) == 0) {
+ // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}*
+ successful = true;
+ } else if ((stack_size >= 5) &&
+ ((stack_size - 5) % 8) == 0) {
+ // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf
+ successful = true;
+ } else if ((stack_size >= 8) &&
+ ((stack_size - 8) % 8) == 0) {
+ // {dxa dxb dyb dyc dyd dxe dye dxf}+
+ successful = true;
+ } else if ((stack_size >= 9) &&
+ ((stack_size - 9) % 8) == 0) {
+ // {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
+ successful = true;
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kDotSection:
+ // Deprecated operator but harmless, we probably should drop it some how.
+ if (stack_size != 0) {
+ return OTS_FAILURE();
+ }
+ return true;
+
+ case ots::kAnd:
+ case ots::kOr:
+ case ots::kEq:
+ case ots::kAdd:
+ case ots::kSub:
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kNot:
+ case ots::kAbs:
+ case ots::kNeg:
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kDiv:
+ // TODO(yusukes): Should detect div-by-zero errors.
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kDrop:
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ return true;
+
+ case ots::kPut:
+ case ots::kGet:
+ case ots::kIndex:
+ // For now, just call OTS_FAILURE since there is no way to check whether the
+ // index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType
+ // fonts I have (except malicious ones!) use the operators.
+ // TODO(yusukes): Implement them in a secure way.
+ return OTS_FAILURE();
+
+ case ots::kRoll:
+ // Likewise, just call OTS_FAILURE for kRoll since there is no way to check
+ // whether |N| is smaller than the current stack depth or not.
+ // TODO(yusukes): Implement them in a secure way.
+ return OTS_FAILURE();
+
+ case ots::kRandom:
+ // For now, we don't handle the 'random' operator since the operator makes
+ // it hard to analyze hinting code statically.
+ return OTS_FAILURE();
+
+ case ots::kIfElse:
+ if (stack_size < 4) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kMul:
+ // TODO(yusukes): Should detect overflows.
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kSqrt:
+ // TODO(yusukes): Should check if the argument is negative.
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kDup:
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ argument_stack->push(dummy_result);
+ if (argument_stack->size() > kMaxArgumentStack) {
+ return OTS_FAILURE();
+ }
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kExch:
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kHFlex:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 7) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kFlex:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 13) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kHFlex1:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 9) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kFlex1:
+ if (!(*in_out_found_width)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 11) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+ }
+
+ return OTS_FAILURE_MSG("Undefined operator: %d (0x%x)", op, op);
+}
+
+// Executes |char_string| and updates |argument_stack|.
+//
+// call_depth: The current call depth. Initial value is zero.
+// global_subrs_index: Global subroutines.
+// local_subrs_index: Local subroutines for the current glyph.
+// cff_table: A whole CFF table which contains all global and local subroutines.
+// char_string: A charstring we'll execute. |char_string| can be a main routine
+// in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr.
+// argument_stack: The stack which an operator in |char_string| operates.
+// out_found_endchar: true is set if |char_string| contains 'endchar'.
+// 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) {
+ if (call_depth > kMaxSubrNesting) {
+ return OTS_FAILURE();
+ }
+ *out_found_endchar = false;
+
+ 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,
+ &operator_or_operand,
+ &is_operator)) {
+ return OTS_FAILURE();
+ }
+
+#ifdef DUMP_T2CHARSTRING
+ /*
+ You can dump all operators and operands (except mask bytes for hintmask
+ and cntrmask) by the following code:
+ */
+
+ if (!is_operator) {
+ std::fprintf(stderr, "#%d# ", operator_or_operand);
+ } else {
+ std::fprintf(stderr, "#%s#\n",
+ Type2CharStringOperatorToString(
+ ots::Type2CharStringOperator(operator_or_operand))
+ );
+ }
+#endif
+
+ if (!is_operator) {
+ argument_stack->push(operator_or_operand);
+ if (argument_stack->size() > kMaxArgumentStack) {
+ 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)) {
+ return OTS_FAILURE();
+ }
+ if (*out_found_endchar) {
+ return true;
+ }
+ if (operator_or_operand == ots::kReturn) {
+ return true;
+ }
+ }
+
+ // No endchar operator is found.
+ 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,
+ uint16_t glyph_index, // 0-origin
+ const ots::CFFIndex **out_local_subrs_to_use) {
+ *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())) {
+ // 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()) {
+ return OTS_FAILURE();
+ }
+ const uint8_t fd_index = iter->second;
+ if (fd_index >= local_subrs_per_font.size()) {
+ return OTS_FAILURE();
+ }
+ *out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
+ } else if (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;
+ } else {
+ // Just return NULL.
+ *out_local_subrs_to_use = NULL;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+bool ValidateType2CharStringIndex(
+ ots::Font *font,
+ const CFFIndex& char_strings_index,
+ 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) {
+ if (char_strings_index.offsets.size() == 0) {
+ return OTS_FAILURE(); // no charstring.
+ }
+
+ // 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
+ // for the |i|-th glyph.
+ const size_t length =
+ char_strings_index.offsets[i] - char_strings_index.offsets[i - 1];
+ if (length > kMaxCharStringLength) {
+ return OTS_FAILURE();
+ }
+ const size_t offset = char_strings_index.offsets[i - 1];
+ cff_table->set_offset(offset);
+ if (!cff_table->Skip(length)) {
+ return OTS_FAILURE();
+ }
+ Buffer char_string(cff_table->buffer() + offset, length);
+
+ // 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,
+ glyph_index,
+ &local_subrs_to_use)) {
+ return OTS_FAILURE();
+ }
+ // If |local_subrs_to_use| is still NULL, use an empty one.
+ CFFIndex default_empty_subrs;
+ if (!local_subrs_to_use){
+ local_subrs_to_use = &default_empty_subrs;
+ }
+
+ // Check a charstring for the |i|-th glyph.
+ std::stack<int32_t> argument_stack;
+ bool found_endchar = false;
+ bool found_width = false;
+ 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)) {
+ return OTS_FAILURE();
+ }
+ if (!found_endchar) {
+ return OTS_FAILURE();
+ }
+ }
+ return true;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/cff_type2_charstring.h b/gfx/ots/src/cff_type2_charstring.h
new file mode 100644
index 000000000..be44bc72c
--- /dev/null
+++ b/gfx/ots/src/cff_type2_charstring.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2010 The Chromium 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_CFF_TYPE2_CHARSTRING_H_
+#define OTS_CFF_TYPE2_CHARSTRING_H_
+
+#include "cff.h"
+#include "ots.h"
+
+#include <map>
+#include <vector>
+
+namespace ots {
+
+// 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
+//
+// The validation will fail if one of the following conditions is met:
+// 1. The code uses more than 48 values of argument stack.
+// 2. The code uses deeply nested subroutine calls (more than 10 levels.)
+// 3. The code passes invalid number of operands to an operator.
+// 4. The code calls an undefined global or local subroutine.
+// 5. The code uses one of the following operators that are unlikely used in
+// an ordinary fonts, and could be dangerous: random, put, get, index, roll.
+//
+// Arguments:
+// global_subrs_index: Global subroutines which could be called by a charstring
+// in |char_strings_index|.
+// fd_select: A map from glyph # to font #.
+// local_subrs_per_font: A list of Local Subrs associated with FDArrays. Can be
+// empty.
+// 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,
+ 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 {
+ kHStem = 1,
+ kVStem = 3,
+ kVMoveTo = 4,
+ kRLineTo = 5,
+ kHLineTo = 6,
+ kVLineTo = 7,
+ kRRCurveTo = 8,
+ kCallSubr = 10,
+ kReturn = 11,
+ kEndChar = 14,
+ kHStemHm = 18,
+ kHintMask = 19,
+ kCntrMask = 20,
+ kRMoveTo = 21,
+ kHMoveTo = 22,
+ kVStemHm = 23,
+ kRCurveLine = 24,
+ kRLineCurve = 25,
+ kVVCurveTo = 26,
+ kHHCurveTo = 27,
+ kCallGSubr = 29,
+ kVHCurveTo = 30,
+ kHVCurveTo = 31,
+ kDotSection = 12 << 8,
+ kAnd = (12 << 8) + 3,
+ kOr = (12 << 8) + 4,
+ kNot = (12 << 8) + 5,
+ kAbs = (12 << 8) + 9,
+ kAdd = (12 << 8) + 10,
+ kSub = (12 << 8) + 11,
+ kDiv = (12 << 8) + 12,
+ kNeg = (12 << 8) + 14,
+ kEq = (12 << 8) + 15,
+ kDrop = (12 << 8) + 18,
+ kPut = (12 << 8) + 20,
+ kGet = (12 << 8) + 21,
+ kIfElse = (12 << 8) + 22,
+ kRandom = (12 << 8) + 23,
+ kMul = (12 << 8) + 24,
+ kSqrt = (12 << 8) + 26,
+ kDup = (12 << 8) + 27,
+ kExch = (12 << 8) + 28,
+ kIndex = (12 << 8) + 29,
+ kRoll = (12 << 8) + 30,
+ kHFlex = (12 << 8) + 34,
+ kFlex = (12 << 8) + 35,
+ kHFlex1 = (12 << 8) + 36,
+ kFlex1 = (12 << 8) + 37,
+ // Operators that are undocumented, such as 'blend', will be rejected.
+};
+
+} // namespace ots
+
+#endif // OTS_CFF_TYPE2_CHARSTRING_H_
diff --git a/gfx/ots/src/cmap.cc b/gfx/ots/src/cmap.cc
new file mode 100644
index 000000000..325f8e0d1
--- /dev/null
+++ b/gfx/ots/src/cmap.cc
@@ -0,0 +1,1088 @@
+// Copyright (c) 2009 The Chromium 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 "cmap.h"
+
+#include <algorithm>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "maxp.h"
+#include "os2.h"
+
+// cmap - Character To Glyph Index Mapping Table
+// http://www.microsoft.com/typography/otspec/cmap.htm
+
+#define TABLE_NAME "cmap"
+
+namespace {
+
+struct CMAPSubtableHeader {
+ uint16_t platform;
+ uint16_t encoding;
+ uint32_t offset;
+ uint16_t format;
+ uint32_t length;
+ uint32_t language;
+};
+
+struct Subtable314Range {
+ uint16_t start_range;
+ uint16_t end_range;
+ int16_t id_delta;
+ uint16_t id_range_offset;
+ uint32_t id_range_offset_offset;
+};
+
+// Glyph array size for the Mac Roman (format 0) table.
+const size_t kFormat0ArraySize = 256;
+
+// The upper limit of the Unicode code point.
+const uint32_t kUnicodeUpperLimit = 0x10FFFF;
+
+// The maximum number of UVS records (See below).
+const uint32_t kMaxCMAPSelectorRecords = 259;
+// The range of UVSes are:
+// 0x180B-0x180D (3 code points)
+// 0xFE00-0xFE0F (16 code points)
+// 0xE0100-0xE01EF (240 code points)
+const uint32_t kMongolianVSStart = 0x180B;
+const uint32_t kMongolianVSEnd = 0x180D;
+const uint32_t kVSStart = 0xFE00;
+const uint32_t kVSEnd = 0xFE0F;
+const uint32_t kIVSStart = 0xE0100;
+const uint32_t kIVSEnd = 0xE01EF;
+const uint32_t kUVSUpperLimit = 0xFFFFFF;
+
+// Parses Format 4 tables
+bool ParseFormat4(ots::Font *font, int platform, int encoding,
+ const uint8_t *data, size_t length, uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
+ // whole thing and recompacting it, we validate it and include it verbatim
+ // in the output.
+
+ if (!font->os2) {
+ return OTS_FAILURE_MSG("Required OS/2 table missing");
+ }
+
+ if (!subtable.Skip(4)) {
+ return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable");
+ }
+ uint16_t language = 0;
+ if (!subtable.ReadU16(&language)) {
+ return OTS_FAILURE_MSG("Can't read language");
+ }
+ if (language) {
+ // Platform ID 3 (windows) subtables should have language '0'.
+ return OTS_FAILURE_MSG("Languages should be 0 (%d)", language);
+ }
+
+ uint16_t segcountx2, search_range, entry_selector, range_shift;
+ segcountx2 = search_range = entry_selector = range_shift = 0;
+ if (!subtable.ReadU16(&segcountx2) ||
+ !subtable.ReadU16(&search_range) ||
+ !subtable.ReadU16(&entry_selector) ||
+ !subtable.ReadU16(&range_shift)) {
+ return OTS_FAILURE_MSG("Failed to read subcmap structure");
+ }
+
+ if (segcountx2 & 1 || search_range & 1) {
+ return OTS_FAILURE_MSG("Bad subcmap structure");
+ }
+ const uint16_t segcount = segcountx2 >> 1;
+ // There must be at least one segment according the spec.
+ if (segcount < 1) {
+ return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount);
+ }
+
+ // log2segcount is the maximal x s.t. 2^x < segcount
+ unsigned log2segcount = 0;
+ while (1u << (log2segcount + 1) <= segcount) {
+ log2segcount++;
+ }
+
+ const uint16_t expected_search_range = 2 * 1u << log2segcount;
+ if (expected_search_range != search_range) {
+ return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range);
+ }
+
+ if (entry_selector != log2segcount) {
+ return OTS_FAILURE_MSG("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
+ }
+
+ const uint16_t expected_range_shift = segcountx2 - search_range;
+ if (range_shift != expected_range_shift) {
+ return OTS_FAILURE_MSG("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
+ }
+
+ std::vector<Subtable314Range> ranges(segcount);
+
+ for (unsigned i = 0; i < segcount; ++i) {
+ if (!subtable.ReadU16(&ranges[i].end_range)) {
+ return OTS_FAILURE_MSG("Failed to read segment %d", i);
+ }
+ }
+
+ uint16_t padding;
+ if (!subtable.ReadU16(&padding)) {
+ return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding");
+ }
+ if (padding) {
+ return OTS_FAILURE_MSG("Non zero cmap subtable segment padding (%d)", padding);
+ }
+
+ for (unsigned i = 0; i < segcount; ++i) {
+ if (!subtable.ReadU16(&ranges[i].start_range)) {
+ return OTS_FAILURE_MSG("Failed to read segment start range %d", i);
+ }
+ }
+ for (unsigned i = 0; i < segcount; ++i) {
+ if (!subtable.ReadS16(&ranges[i].id_delta)) {
+ return OTS_FAILURE_MSG("Failed to read segment delta %d", i);
+ }
+ }
+ for (unsigned i = 0; i < segcount; ++i) {
+ ranges[i].id_range_offset_offset = subtable.offset();
+ if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
+ return OTS_FAILURE_MSG("Failed to read segment range offset %d", i);
+ }
+
+ if (ranges[i].id_range_offset & 1) {
+ // Some font generators seem to put 65535 on id_range_offset
+ // for 0xFFFF-0xFFFF range.
+ // (e.g., many fonts in http://www.princexml.com/fonts/)
+ if (i == segcount - 1u) {
+ OTS_WARNING("bad id_range_offset");
+ ranges[i].id_range_offset = 0;
+ // The id_range_offset value in the transcoded font will not change
+ // since this table is not actually "transcoded" yet.
+ } else {
+ return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset);
+ }
+ }
+ }
+
+ // ranges must be ascending order, based on the end_code. Ranges may not
+ // overlap.
+ for (unsigned i = 1; i < segcount; ++i) {
+ if ((i == segcount - 1u) &&
+ (ranges[i - 1].start_range == 0xffff) &&
+ (ranges[i - 1].end_range == 0xffff) &&
+ (ranges[i].start_range == 0xffff) &&
+ (ranges[i].end_range == 0xffff)) {
+ // Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
+ // We'll accept them as an exception.
+ OTS_WARNING("multiple 0xffff terminators found");
+ continue;
+ }
+
+ // Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
+ // unsorted table...
+ if (ranges[i].end_range <= ranges[i - 1].end_range) {
+ return OTS_FAILURE_MSG("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
+ }
+ if (ranges[i].start_range <= ranges[i - 1].end_range) {
+ return OTS_FAILURE_MSG("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
+ }
+
+ // On many fonts, the value of {first, last}_char_index are incorrect.
+ // Fix them.
+ if (font->os2->first_char_index != 0xFFFF &&
+ ranges[i].start_range != 0xFFFF &&
+ font->os2->first_char_index > ranges[i].start_range) {
+ font->os2->first_char_index = ranges[i].start_range;
+ }
+ if (font->os2->last_char_index != 0xFFFF &&
+ ranges[i].end_range != 0xFFFF &&
+ font->os2->last_char_index < ranges[i].end_range) {
+ font->os2->last_char_index = ranges[i].end_range;
+ }
+ }
+
+ // The last range must end at 0xffff
+ if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
+ return OTS_FAILURE_MSG("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
+ ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
+ }
+
+ // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of
+ // each code-point defined in the table and make sure that they are all valid
+ // glyphs and that we don't access anything out-of-bounds.
+ for (unsigned i = 0; i < segcount; ++i) {
+ for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
+ const uint16_t code_point = static_cast<uint16_t>(cp);
+ if (ranges[i].id_range_offset == 0) {
+ // this is explictly allowed to overflow in the spec
+ const uint16_t glyph = code_point + ranges[i].id_delta;
+ if (glyph >= num_glyphs) {
+ return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+ }
+ } else {
+ const uint16_t range_delta = code_point - ranges[i].start_range;
+ // this might seem odd, but it's true. The offset is relative to the
+ // location of the offset value itself.
+ const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset +
+ ranges[i].id_range_offset +
+ range_delta * 2;
+ // We need to be able to access a 16-bit value from this offset
+ if (glyph_id_offset + 1 >= length) {
+ return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
+ }
+ uint16_t glyph;
+ std::memcpy(&glyph, data + glyph_id_offset, 2);
+ glyph = ntohs(glyph);
+ if (glyph >= num_glyphs) {
+ return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+ }
+ }
+ }
+ }
+
+ // We accept the table.
+ // TODO(yusukes): transcode the subtable.
+ if (platform == 3 && encoding == 0) {
+ font->cmap->subtable_3_0_4_data = data;
+ font->cmap->subtable_3_0_4_length = length;
+ } else if (platform == 3 && encoding == 1) {
+ font->cmap->subtable_3_1_4_data = data;
+ font->cmap->subtable_3_1_4_length = length;
+ } else if (platform == 0 && encoding == 3) {
+ font->cmap->subtable_0_3_4_data = data;
+ font->cmap->subtable_0_3_4_length = length;
+ } else {
+ return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
+ }
+
+ return true;
+}
+
+bool Parse31012(ots::Font *font,
+ const uint8_t *data, size_t length, uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Format 12 tables are simple. We parse these and fully serialise them
+ // later.
+
+ if (!subtable.Skip(8)) {
+ return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable");
+ }
+ uint32_t language = 0;
+ if (!subtable.ReadU32(&language)) {
+ return OTS_FAILURE_MSG("can't read format 12 subtable language");
+ }
+ if (language) {
+ return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language);
+ }
+
+ uint32_t num_groups = 0;
+ if (!subtable.ReadU32(&num_groups)) {
+ return OTS_FAILURE_MSG("can't read number of format 12 subtable groups");
+ }
+ if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
+ return OTS_FAILURE_MSG("Bad format 12 subtable group count %d", num_groups);
+ }
+
+ std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+ = font->cmap->subtable_3_10_12;
+ groups.resize(num_groups);
+
+ for (unsigned i = 0; i < num_groups; ++i) {
+ if (!subtable.ReadU32(&groups[i].start_range) ||
+ !subtable.ReadU32(&groups[i].end_range) ||
+ !subtable.ReadU32(&groups[i].start_glyph_id)) {
+ return OTS_FAILURE_MSG("can't read format 12 subtable group");
+ }
+
+ if (groups[i].start_range > kUnicodeUpperLimit ||
+ groups[i].end_range > kUnicodeUpperLimit ||
+ groups[i].start_glyph_id > 0xFFFF) {
+ return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
+ groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+ }
+
+ // We assert that the glyph value is within range. Because of the range
+ // limits, above, we don't need to worry about overflow.
+ if (groups[i].end_range < groups[i].start_range) {
+ return OTS_FAILURE_MSG("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
+ groups[i].end_range, groups[i].start_range);
+ }
+ if ((groups[i].end_range - groups[i].start_range) +
+ groups[i].start_glyph_id > num_glyphs) {
+ return OTS_FAILURE_MSG("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
+ }
+ }
+
+ // the groups must be sorted by start code and may not overlap
+ for (unsigned i = 1; i < num_groups; ++i) {
+ if (groups[i].start_range <= groups[i - 1].start_range) {
+ return OTS_FAILURE_MSG("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
+ groups[i].start_range, groups[i-1].start_range);
+ }
+ if (groups[i].start_range <= groups[i - 1].end_range) {
+ return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
+ groups[i].start_range, groups[i-1].end_range);
+ }
+ }
+
+ return true;
+}
+
+bool Parse31013(ots::Font *font,
+ const uint8_t *data, size_t length, uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Format 13 tables are simple. We parse these and fully serialise them
+ // later.
+
+ if (!subtable.Skip(8)) {
+ return OTS_FAILURE_MSG("Bad cmap subtable length");
+ }
+ uint32_t language = 0;
+ if (!subtable.ReadU32(&language)) {
+ return OTS_FAILURE_MSG("Can't read cmap subtable language");
+ }
+ if (language) {
+ return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
+ }
+
+ uint32_t num_groups = 0;
+ if (!subtable.ReadU32(&num_groups)) {
+ return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable");
+ }
+
+ // We limit the number of groups in the same way as in 3.10.12 tables. See
+ // the comment there in
+ if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
+ return OTS_FAILURE_MSG("Bad format 13 subtable group count %d", num_groups);
+ }
+
+ std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+ = font->cmap->subtable_3_10_13;
+ groups.resize(num_groups);
+
+ for (unsigned i = 0; i < num_groups; ++i) {
+ if (!subtable.ReadU32(&groups[i].start_range) ||
+ !subtable.ReadU32(&groups[i].end_range) ||
+ !subtable.ReadU32(&groups[i].start_glyph_id)) {
+ return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable");
+ }
+
+ // We conservatively limit all of the values to protect some parsers from
+ // overflows
+ if (groups[i].start_range > kUnicodeUpperLimit ||
+ groups[i].end_range > kUnicodeUpperLimit ||
+ groups[i].start_glyph_id > 0xFFFF) {
+ return OTS_FAILURE_MSG("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+ }
+
+ if (groups[i].start_glyph_id >= num_glyphs) {
+ return OTS_FAILURE_MSG("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
+ }
+ }
+
+ // the groups must be sorted by start code and may not overlap
+ for (unsigned i = 1; i < num_groups; ++i) {
+ if (groups[i].start_range <= groups[i - 1].start_range) {
+ return OTS_FAILURE_MSG("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
+ }
+ if (groups[i].start_range <= groups[i - 1].end_range) {
+ return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
+ }
+ }
+
+ return true;
+}
+
+bool Parse0514(ots::Font *font,
+ const uint8_t *data, size_t length, uint16_t num_glyphs) {
+ // Unicode Variation Selector table
+ ots::Buffer subtable(data, length);
+
+ // Format 14 tables are simple. We parse these and fully serialise them
+ // later.
+
+ // Skip format (USHORT) and length (ULONG)
+ if (!subtable.Skip(6)) {
+ return OTS_FAILURE_MSG("Can't read start of cmap subtable");
+ }
+
+ uint32_t num_records = 0;
+ if (!subtable.ReadU32(&num_records)) {
+ return OTS_FAILURE_MSG("Can't read number of records in cmap subtable");
+ }
+ if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
+ return OTS_FAILURE_MSG("Bad format 14 subtable records count %d", num_records);
+ }
+
+ std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
+ = font->cmap->subtable_0_5_14;
+ records.resize(num_records);
+
+ for (unsigned i = 0; i < num_records; ++i) {
+ if (!subtable.ReadU24(&records[i].var_selector) ||
+ !subtable.ReadU32(&records[i].default_offset) ||
+ !subtable.ReadU32(&records[i].non_default_offset)) {
+ return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i);
+ }
+ // Checks the value of variation selector
+ if (!((records[i].var_selector >= kMongolianVSStart &&
+ records[i].var_selector <= kMongolianVSEnd) ||
+ (records[i].var_selector >= kVSStart &&
+ records[i].var_selector <= kVSEnd) ||
+ (records[i].var_selector >= kIVSStart &&
+ records[i].var_selector <= kIVSEnd))) {
+ return OTS_FAILURE_MSG("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
+ }
+ if (i > 0 &&
+ records[i-1].var_selector >= records[i].var_selector) {
+ return OTS_FAILURE_MSG("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
+ }
+
+ // Checks offsets
+ if (!records[i].default_offset && !records[i].non_default_offset) {
+ return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i);
+ }
+ if (records[i].default_offset &&
+ records[i].default_offset >= length) {
+ return OTS_FAILURE_MSG("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
+ }
+ if (records[i].non_default_offset &&
+ records[i].non_default_offset >= length) {
+ return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
+ }
+ }
+
+ for (unsigned i = 0; i < num_records; ++i) {
+ // Checks default UVS table
+ if (records[i].default_offset) {
+ subtable.set_offset(records[i].default_offset);
+ uint32_t num_ranges = 0;
+ if (!subtable.ReadU32(&num_ranges)) {
+ return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i);
+ }
+ if (num_ranges == 0 || subtable.remaining() / 4 < num_ranges) {
+ return OTS_FAILURE_MSG("Bad number of ranges (%d) in record %d", num_ranges, i);
+ }
+
+ uint32_t last_unicode_value = 0;
+ std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges
+ = records[i].ranges;
+ ranges.resize(num_ranges);
+
+ for (unsigned j = 0; j < num_ranges; ++j) {
+ if (!subtable.ReadU24(&ranges[j].unicode_value) ||
+ !subtable.ReadU8(&ranges[j].additional_count)) {
+ return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i);
+ }
+ const uint32_t check_value =
+ ranges[j].unicode_value + ranges[j].additional_count;
+ if (ranges[j].unicode_value == 0 ||
+ ranges[j].unicode_value > kUnicodeUpperLimit ||
+ check_value > kUVSUpperLimit ||
+ (last_unicode_value &&
+ ranges[j].unicode_value <= last_unicode_value)) {
+ return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
+ }
+ last_unicode_value = check_value;
+ }
+ }
+
+ // Checks non default UVS table
+ if (records[i].non_default_offset) {
+ subtable.set_offset(records[i].non_default_offset);
+ uint32_t num_mappings = 0;
+ if (!subtable.ReadU32(&num_mappings)) {
+ return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i);
+ }
+ if (num_mappings == 0 || subtable.remaining() / 5 < num_mappings) {
+ return OTS_FAILURE_MSG("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
+ }
+
+ uint32_t last_unicode_value = 0;
+ std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings
+ = records[i].mappings;
+ mappings.resize(num_mappings);
+
+ for (unsigned j = 0; j < num_mappings; ++j) {
+ if (!subtable.ReadU24(&mappings[j].unicode_value) ||
+ !subtable.ReadU16(&mappings[j].glyph_id)) {
+ return OTS_FAILURE_MSG("Can't read mapping %d in variation selector record %d", j, i);
+ }
+ if (mappings[j].glyph_id == 0 ||
+ mappings[j].unicode_value == 0 ||
+ mappings[j].unicode_value > kUnicodeUpperLimit ||
+ (last_unicode_value &&
+ mappings[j].unicode_value <= last_unicode_value)) {
+ return OTS_FAILURE_MSG("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
+ }
+ last_unicode_value = mappings[j].unicode_value;
+ }
+ }
+ }
+
+ if (subtable.offset() != length) {
+ return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
+ }
+ font->cmap->subtable_0_5_14_length = subtable.offset();
+ return true;
+}
+
+bool Parse100(ots::Font *font, const uint8_t *data, size_t length) {
+ // Mac Roman table
+ ots::Buffer subtable(data, length);
+
+ if (!subtable.Skip(4)) {
+ return OTS_FAILURE_MSG("Bad cmap subtable");
+ }
+ uint16_t language = 0;
+ if (!subtable.ReadU16(&language)) {
+ return OTS_FAILURE_MSG("Can't read language in cmap subtable");
+ }
+ if (language) {
+ // simsun.ttf has non-zero language id.
+ OTS_WARNING("language id should be zero: %u", language);
+ }
+
+ font->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
+ for (size_t i = 0; i < kFormat0ArraySize; ++i) {
+ uint8_t glyph_id = 0;
+ if (!subtable.ReadU8(&glyph_id)) {
+ return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i);
+ }
+ font->cmap->subtable_1_0_0.push_back(glyph_id);
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ font->cmap = new OpenTypeCMAP;
+
+ uint16_t version = 0;
+ uint16_t num_tables = 0;
+ if (!table.ReadU16(&version) ||
+ !table.ReadU16(&num_tables)) {
+ return OTS_FAILURE_MSG("Can't read structure of cmap");
+ }
+
+ if (version != 0) {
+ return OTS_FAILURE_MSG("Non zero cmap version (%d)", version);
+ }
+ if (!num_tables) {
+ return OTS_FAILURE_MSG("No subtables in cmap!");
+ }
+
+ std::vector<CMAPSubtableHeader> subtable_headers;
+
+ // read the subtable headers
+ subtable_headers.reserve(num_tables);
+ for (unsigned i = 0; i < num_tables; ++i) {
+ CMAPSubtableHeader subt;
+
+ if (!table.ReadU16(&subt.platform) ||
+ !table.ReadU16(&subt.encoding) ||
+ !table.ReadU32(&subt.offset)) {
+ return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i);
+ }
+
+ subtable_headers.push_back(subt);
+ }
+
+ const size_t data_offset = table.offset();
+
+ // make sure that all the offsets are valid.
+ for (unsigned i = 0; i < num_tables; ++i) {
+ if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
+ return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i);
+ }
+ if (subtable_headers[i].offset < data_offset ||
+ subtable_headers[i].offset >= length) {
+ return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
+ }
+ }
+
+ // the format of the table is the first couple of bytes in the table. The
+ // length of the table is stored in a format-specific way.
+ for (unsigned i = 0; i < num_tables; ++i) {
+ table.set_offset(subtable_headers[i].offset);
+ if (!table.ReadU16(&subtable_headers[i].format)) {
+ return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i);
+ }
+
+ uint16_t len = 0;
+ uint16_t lang = 0;
+ switch (subtable_headers[i].format) {
+ case 0:
+ case 4:
+ if (!table.ReadU16(&len)) {
+ return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+ }
+ if (!table.ReadU16(&lang)) {
+ return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+ }
+ subtable_headers[i].length = len;
+ subtable_headers[i].language = lang;
+ break;
+ case 12:
+ case 13:
+ if (!table.Skip(2)) {
+ return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i);
+ }
+ if (!table.ReadU32(&subtable_headers[i].length)) {
+ return OTS_FAILURE_MSG("Can read cmap subtable %d length", i);
+ }
+ if (!table.ReadU32(&subtable_headers[i].language)) {
+ return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+ }
+ break;
+ case 14:
+ if (!table.ReadU32(&subtable_headers[i].length)) {
+ return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+ }
+ subtable_headers[i].language = 0;
+ break;
+ default:
+ subtable_headers[i].length = 0;
+ subtable_headers[i].language = 0;
+ break;
+ }
+ }
+
+ // check if the table is sorted first by platform ID, then by encoding ID.
+ for (unsigned i = 1; i < num_tables; ++i) {
+ if (subtable_headers[i - 1].platform > subtable_headers[i].platform ||
+ (subtable_headers[i - 1].platform == subtable_headers[i].platform &&
+ (subtable_headers[i - 1].encoding > subtable_headers[i].encoding ||
+ (subtable_headers[i - 1].encoding == subtable_headers[i].encoding &&
+ subtable_headers[i - 1].language > subtable_headers[i].language))))
+ OTS_WARNING("subtable %d with platform ID %d, encoding ID %d, language ID %d "
+ "following subtable with platform ID %d, encoding ID %d, language ID %d",
+ i,
+ subtable_headers[i].platform,
+ subtable_headers[i].encoding,
+ subtable_headers[i].language,
+ subtable_headers[i - 1].platform,
+ subtable_headers[i - 1].encoding,
+ subtable_headers[i - 1].language);
+ }
+
+ // Now, verify that all the lengths are sane
+ for (unsigned i = 0; i < num_tables; ++i) {
+ if (!subtable_headers[i].length) continue;
+ if (subtable_headers[i].length > 1024 * 1024 * 1024) {
+ return OTS_FAILURE_MSG("Bad cmap subtable %d length", i);
+ }
+ // We know that both the offset and length are < 1GB, so the following
+ // addition doesn't overflow
+ const uint32_t end_byte
+ = subtable_headers[i].offset + subtable_headers[i].length;
+ if (end_byte > length) {
+ return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
+ }
+ }
+
+ // check that the cmap subtables are not overlapping.
+ std::set<std::pair<uint32_t, uint32_t> > uniq_checker;
+ std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
+ for (unsigned i = 0; i < num_tables; ++i) {
+ const uint32_t end_byte
+ = subtable_headers[i].offset + subtable_headers[i].length;
+
+ if (!uniq_checker.insert(std::make_pair(subtable_headers[i].offset,
+ end_byte)).second) {
+ // Sometimes Unicode table and MS table share exactly the same data.
+ // We'll allow this.
+ continue;
+ }
+ overlap_checker.push_back(
+ std::make_pair(subtable_headers[i].offset,
+ static_cast<uint8_t>(1) /* start */));
+ overlap_checker.push_back(
+ std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */));
+ }
+ std::sort(overlap_checker.begin(), overlap_checker.end());
+ int overlap_count = 0;
+ for (unsigned i = 0; i < overlap_checker.size(); ++i) {
+ overlap_count += (overlap_checker[i].second ? 1 : -1);
+ if (overlap_count > 1) {
+ return OTS_FAILURE_MSG("Excessive overlap count %d", overlap_count);
+ }
+ }
+
+ // we grab the number of glyphs in the file from the maxp table to make sure
+ // that the character map isn't referencing anything beyound this range.
+ if (!font->maxp) {
+ return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap.");
+ }
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+
+ // We only support a subset of the possible character map tables. Microsoft
+ // 'strongly recommends' that everyone supports the Unicode BMP table with
+ // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables:
+ // Platform ID Encoding ID Format
+ // 0 0 4 (Unicode Default)
+ // 0 1 4 (Unicode 1.1)
+ // 0 3 4 (Unicode BMP)
+ // 0 3 12 (Unicode UCS-4)
+ // 0 5 14 (Unicode Variation Sequences)
+ // 1 0 0 (Mac Roman)
+ // 3 0 4 (MS Symbol)
+ // 3 1 4 (MS Unicode BMP)
+ // 3 10 12 (MS Unicode UCS-4)
+ // 3 10 13 (MS UCS-4 Fallback mapping)
+ //
+ // Note:
+ // * 0-0-4 and 0-1-4 tables are (usually) written as a 3-1-4 table. If 3-1-4 table
+ // also exists, the 0-0-4 or 0-1-4 tables are ignored.
+ // * Unlike 0-0-4 table, 0-3-4 table is written as a 0-3-4 table.
+ // Some fonts which include 0-5-14 table seems to be required 0-3-4
+ // table. The 0-3-4 table will be wriiten even if 3-1-4 table also exists.
+ // * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also
+ // exists, the 0-3-12 table is ignored.
+ //
+
+ for (unsigned i = 0; i < num_tables; ++i) {
+ if (subtable_headers[i].platform == 0) {
+ // Unicode platform
+
+ if ((subtable_headers[i].encoding == 0 || subtable_headers[i].encoding == 1) &&
+ (subtable_headers[i].format == 4)) {
+ // parse and output the 0-0-4 and 0-1-4 tables as 3-1-4 table. Sometimes the 0-0-4
+ // table actually points to MS symbol data and thus should be parsed as
+ // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
+ // recovered in ots_cmap_serialise().
+ if (!ParseFormat4(font, 3, 1, data + subtable_headers[i].offset,
+ subtable_headers[i].length, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+ }
+ } else if ((subtable_headers[i].encoding == 3) &&
+ (subtable_headers[i].format == 4)) {
+ // parse and output the 0-3-4 table as 0-3-4 table.
+ if (!ParseFormat4(font, 0, 3, data + subtable_headers[i].offset,
+ subtable_headers[i].length, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+ }
+ } else if ((subtable_headers[i].encoding == 3) &&
+ (subtable_headers[i].format == 12)) {
+ // parse and output the 0-3-12 table as 3-10-12 table.
+ if (!Parse31012(font, data + subtable_headers[i].offset,
+ subtable_headers[i].length, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i);
+ }
+ } else if ((subtable_headers[i].encoding == 5) &&
+ (subtable_headers[i].format == 14)) {
+ if (!Parse0514(font, data + subtable_headers[i].offset,
+ subtable_headers[i].length, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i);
+ }
+ }
+ } else if (subtable_headers[i].platform == 1) {
+ // Mac platform
+
+ if ((subtable_headers[i].encoding == 0) &&
+ (subtable_headers[i].format == 0)) {
+ // parse and output the 1-0-0 table.
+ if (!Parse100(font, data + subtable_headers[i].offset,
+ subtable_headers[i].length)) {
+ return OTS_FAILURE();
+ }
+ }
+ } else if (subtable_headers[i].platform == 3) {
+ // MS platform
+
+ switch (subtable_headers[i].encoding) {
+ case 0:
+ case 1:
+ if (subtable_headers[i].format == 4) {
+ // parse 3-0-4 or 3-1-4 table.
+ if (!ParseFormat4(font, subtable_headers[i].platform,
+ subtable_headers[i].encoding,
+ data + subtable_headers[i].offset,
+ subtable_headers[i].length, num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ }
+ break;
+ case 10:
+ if (subtable_headers[i].format == 12) {
+ font->cmap->subtable_3_10_12.clear();
+ if (!Parse31012(font, data + subtable_headers[i].offset,
+ subtable_headers[i].length, num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ } else if (subtable_headers[i].format == 13) {
+ font->cmap->subtable_3_10_13.clear();
+ if (!Parse31013(font, data + subtable_headers[i].offset,
+ subtable_headers[i].length, num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ots_cmap_should_serialise(Font *font) {
+ return font->cmap != NULL;
+}
+
+bool ots_cmap_serialise(OTSStream *out, Font *font) {
+ const bool have_034 = font->cmap->subtable_0_3_4_data != NULL;
+ const bool have_0514 = font->cmap->subtable_0_5_14.size() != 0;
+ const bool have_100 = font->cmap->subtable_1_0_0.size() != 0;
+ const bool have_304 = font->cmap->subtable_3_0_4_data != NULL;
+ // MS Symbol and MS Unicode tables should not co-exist.
+ // See the comment above in 0-0-4 parser.
+ const bool have_314 = (!have_304) && font->cmap->subtable_3_1_4_data;
+ const bool have_31012 = font->cmap->subtable_3_10_12.size() != 0;
+ const bool have_31013 = font->cmap->subtable_3_10_13.size() != 0;
+ const uint16_t num_subtables = static_cast<uint16_t>(have_034) +
+ static_cast<uint16_t>(have_0514) +
+ static_cast<uint16_t>(have_100) +
+ static_cast<uint16_t>(have_304) +
+ static_cast<uint16_t>(have_314) +
+ static_cast<uint16_t>(have_31012) +
+ static_cast<uint16_t>(have_31013);
+ const off_t table_start = out->Tell();
+
+ // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
+ // (e.g., old fonts for Mac). We don't support them.
+ if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
+ return OTS_FAILURE_MSG("no supported subtables were found");
+ }
+
+ if (!out->WriteU16(0) ||
+ !out->WriteU16(num_subtables)) {
+ return OTS_FAILURE();
+ }
+
+ const off_t record_offset = out->Tell();
+ if (!out->Pad(num_subtables * 8)) {
+ return OTS_FAILURE();
+ }
+
+ const off_t offset_034 = out->Tell();
+ if (have_034) {
+ if (!out->Write(font->cmap->subtable_0_3_4_data,
+ font->cmap->subtable_0_3_4_length)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ const off_t offset_0514 = out->Tell();
+ if (have_0514) {
+ const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
+ = font->cmap->subtable_0_5_14;
+ const unsigned num_records = records.size();
+ if (!out->WriteU16(14) ||
+ !out->WriteU32(font->cmap->subtable_0_5_14_length) ||
+ !out->WriteU32(num_records)) {
+ return OTS_FAILURE();
+ }
+ for (unsigned i = 0; i < num_records; ++i) {
+ if (!out->WriteU24(records[i].var_selector) ||
+ !out->WriteU32(records[i].default_offset) ||
+ !out->WriteU32(records[i].non_default_offset)) {
+ return OTS_FAILURE();
+ }
+ }
+ for (unsigned i = 0; i < num_records; ++i) {
+ if (records[i].default_offset) {
+ const std::vector<ots::OpenTypeCMAPSubtableVSRange> &ranges
+ = records[i].ranges;
+ const unsigned num_ranges = ranges.size();
+ if (!out->Seek(records[i].default_offset + offset_0514) ||
+ !out->WriteU32(num_ranges)) {
+ return OTS_FAILURE();
+ }
+ for (unsigned j = 0; j < num_ranges; ++j) {
+ if (!out->WriteU24(ranges[j].unicode_value) ||
+ !out->WriteU8(ranges[j].additional_count)) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+ if (records[i].non_default_offset) {
+ const std::vector<ots::OpenTypeCMAPSubtableVSMapping> &mappings
+ = records[i].mappings;
+ const unsigned num_mappings = mappings.size();
+ if (!out->Seek(records[i].non_default_offset + offset_0514) ||
+ !out->WriteU32(num_mappings)) {
+ return OTS_FAILURE();
+ }
+ for (unsigned j = 0; j < num_mappings; ++j) {
+ if (!out->WriteU24(mappings[j].unicode_value) ||
+ !out->WriteU16(mappings[j].glyph_id)) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+ }
+ }
+
+ const off_t offset_100 = out->Tell();
+ if (have_100) {
+ if (!out->WriteU16(0) || // format
+ !out->WriteU16(6 + kFormat0ArraySize) || // length
+ !out->WriteU16(0)) { // language
+ return OTS_FAILURE();
+ }
+ if (!out->Write(&(font->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ const off_t offset_304 = out->Tell();
+ if (have_304) {
+ if (!out->Write(font->cmap->subtable_3_0_4_data,
+ font->cmap->subtable_3_0_4_length)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ const off_t offset_314 = out->Tell();
+ if (have_314) {
+ if (!out->Write(font->cmap->subtable_3_1_4_data,
+ font->cmap->subtable_3_1_4_length)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ const off_t offset_31012 = out->Tell();
+ if (have_31012) {
+ std::vector<OpenTypeCMAPSubtableRange> &groups
+ = font->cmap->subtable_3_10_12;
+ const unsigned num_groups = groups.size();
+ if (!out->WriteU16(12) ||
+ !out->WriteU16(0) ||
+ !out->WriteU32(num_groups * 12 + 16) ||
+ !out->WriteU32(0) ||
+ !out->WriteU32(num_groups)) {
+ return OTS_FAILURE();
+ }
+
+ for (unsigned i = 0; i < num_groups; ++i) {
+ if (!out->WriteU32(groups[i].start_range) ||
+ !out->WriteU32(groups[i].end_range) ||
+ !out->WriteU32(groups[i].start_glyph_id)) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+
+ const off_t offset_31013 = out->Tell();
+ if (have_31013) {
+ std::vector<OpenTypeCMAPSubtableRange> &groups
+ = font->cmap->subtable_3_10_13;
+ const unsigned num_groups = groups.size();
+ if (!out->WriteU16(13) ||
+ !out->WriteU16(0) ||
+ !out->WriteU32(num_groups * 12 + 16) ||
+ !out->WriteU32(0) ||
+ !out->WriteU32(num_groups)) {
+ return OTS_FAILURE();
+ }
+
+ for (unsigned i = 0; i < num_groups; ++i) {
+ if (!out->WriteU32(groups[i].start_range) ||
+ !out->WriteU32(groups[i].end_range) ||
+ !out->WriteU32(groups[i].start_glyph_id)) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+
+ const off_t table_end = out->Tell();
+
+ // Now seek back and write the table of offsets
+ if (!out->Seek(record_offset)) {
+ return OTS_FAILURE();
+ }
+
+ if (have_034) {
+ if (!out->WriteU16(0) ||
+ !out->WriteU16(3) ||
+ !out->WriteU32(offset_034 - table_start)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ if (have_0514) {
+ if (!out->WriteU16(0) ||
+ !out->WriteU16(5) ||
+ !out->WriteU32(offset_0514 - table_start)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ if (have_100) {
+ if (!out->WriteU16(1) ||
+ !out->WriteU16(0) ||
+ !out->WriteU32(offset_100 - table_start)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ if (have_304) {
+ if (!out->WriteU16(3) ||
+ !out->WriteU16(0) ||
+ !out->WriteU32(offset_304 - table_start)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ if (have_314) {
+ if (!out->WriteU16(3) ||
+ !out->WriteU16(1) ||
+ !out->WriteU32(offset_314 - table_start)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ if (have_31012) {
+ if (!out->WriteU16(3) ||
+ !out->WriteU16(10) ||
+ !out->WriteU32(offset_31012 - table_start)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ if (have_31013) {
+ if (!out->WriteU16(3) ||
+ !out->WriteU16(10) ||
+ !out->WriteU32(offset_31013 - table_start)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ if (!out->Seek(table_end)) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+void ots_cmap_reuse(Font *font, Font *other) {
+ font->cmap = other->cmap;
+ font->cmap_reused = true;
+}
+
+void ots_cmap_free(Font *font) {
+ delete font->cmap;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/cmap.h b/gfx/ots/src/cmap.h
new file mode 100644
index 000000000..5b09556b7
--- /dev/null
+++ b/gfx/ots/src/cmap.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2009 The Chromium 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_CMAP_H_
+#define OTS_CMAP_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCMAPSubtableRange {
+ uint32_t start_range;
+ uint32_t end_range;
+ uint32_t start_glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRange {
+ uint32_t unicode_value;
+ uint8_t additional_count;
+};
+
+struct OpenTypeCMAPSubtableVSMapping {
+ uint32_t unicode_value;
+ uint16_t glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRecord {
+ uint32_t var_selector;
+ uint32_t default_offset;
+ uint32_t non_default_offset;
+ std::vector<OpenTypeCMAPSubtableVSRange> ranges;
+ std::vector<OpenTypeCMAPSubtableVSMapping> mappings;
+};
+
+struct OpenTypeCMAP {
+ OpenTypeCMAP()
+ : subtable_0_3_4_data(NULL),
+ subtable_0_3_4_length(0),
+ subtable_0_5_14_length(0),
+ subtable_3_0_4_data(NULL),
+ subtable_3_0_4_length(0),
+ subtable_3_1_4_data(NULL),
+ subtable_3_1_4_length(0) {
+ }
+
+ // Platform 0, Encoding 3, Format 4, Unicode BMP table.
+ const uint8_t *subtable_0_3_4_data;
+ size_t subtable_0_3_4_length;
+
+ // Platform 0, Encoding 5, Format 14, Unicode Variation Sequence table.
+ size_t subtable_0_5_14_length;
+ std::vector<OpenTypeCMAPSubtableVSRecord> subtable_0_5_14;
+
+ // Platform 3, Encoding 0, Format 4, MS Symbol table.
+ const uint8_t *subtable_3_0_4_data;
+ size_t subtable_3_0_4_length;
+ // Platform 3, Encoding 1, Format 4, MS Unicode BMP table.
+ const uint8_t *subtable_3_1_4_data;
+ size_t subtable_3_1_4_length;
+
+ // Platform 3, Encoding 10, Format 12, MS Unicode UCS-4 table.
+ std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_12;
+ // Platform 3, Encoding 10, Format 13, MS UCS-4 Fallback table.
+ std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
+ // Platform 1, Encoding 0, Format 0, Mac Roman table.
+ std::vector<uint8_t> subtable_1_0_0;
+};
+
+} // namespace ots
+
+#endif
diff --git a/gfx/ots/src/cvt.cc b/gfx/ots/src/cvt.cc
new file mode 100644
index 000000000..1402e7c06
--- /dev/null
+++ b/gfx/ots/src/cvt.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2009 The Chromium 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 "cvt.h"
+
+// cvt - Control Value Table
+// http://www.microsoft.com/typography/otspec/cvt.htm
+
+#define TABLE_NAME "cvt"
+
+namespace ots {
+
+bool ots_cvt_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypeCVT *cvt = new OpenTypeCVT;
+ font->cvt = cvt;
+
+ if (length >= 128 * 1024u) {
+ return OTS_FAILURE_MSG("Length (%d) > 120K"); // almost all cvt tables are less than 4k bytes.
+ }
+
+ if (length % 2 != 0) {
+ return OTS_FAILURE_MSG("Uneven cvt length (%d)", length);
+ }
+
+ if (!table.Skip(length)) {
+ return OTS_FAILURE_MSG("Length too high");
+ }
+
+ cvt->data = data;
+ cvt->length = length;
+ return true;
+}
+
+bool ots_cvt_should_serialise(Font *font) {
+ if (!font->glyf) {
+ return false; // this table is not for CFF fonts.
+ }
+ return font->cvt != NULL;
+}
+
+bool ots_cvt_serialise(OTSStream *out, Font *font) {
+ const OpenTypeCVT *cvt = font->cvt;
+
+ if (!out->Write(cvt->data, cvt->length)) {
+ return OTS_FAILURE_MSG("Failed to write CVT table");
+ }
+
+ return true;
+}
+
+void ots_cvt_reuse(Font *font, Font *other) {
+ font->cvt = other->cvt;
+ font->cvt_reused = true;
+}
+
+void ots_cvt_free(Font *font) {
+ delete font->cvt;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/cvt.h b/gfx/ots/src/cvt.h
new file mode 100644
index 000000000..3c25f06f6
--- /dev/null
+++ b/gfx/ots/src/cvt.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium 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_CVT_H_
+#define OTS_CVT_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCVT {
+ const uint8_t *data;
+ uint32_t length;
+};
+
+} // namespace ots
+
+#endif // OTS_CVT_H_
diff --git a/gfx/ots/src/fpgm.cc b/gfx/ots/src/fpgm.cc
new file mode 100644
index 000000000..faa9a2392
--- /dev/null
+++ b/gfx/ots/src/fpgm.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2009 The Chromium 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 "fpgm.h"
+
+// fpgm - Font Program
+// http://www.microsoft.com/typography/otspec/fpgm.htm
+
+#define TABLE_NAME "fpgm"
+
+namespace ots {
+
+bool ots_fpgm_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypeFPGM *fpgm = new OpenTypeFPGM;
+ font->fpgm = fpgm;
+
+ if (length >= 128 * 1024u) {
+ return OTS_FAILURE_MSG("length (%ld) > 120", length); // almost all fpgm tables are less than 5k bytes.
+ }
+
+ if (!table.Skip(length)) {
+ return OTS_FAILURE_MSG("Bad fpgm length");
+ }
+
+ fpgm->data = data;
+ fpgm->length = length;
+ return true;
+}
+
+bool ots_fpgm_should_serialise(Font *font) {
+ if (!font->glyf) return false; // this table is not for CFF fonts.
+ return font->fpgm != NULL;
+}
+
+bool ots_fpgm_serialise(OTSStream *out, Font *font) {
+ const OpenTypeFPGM *fpgm = font->fpgm;
+
+ if (!out->Write(fpgm->data, fpgm->length)) {
+ return OTS_FAILURE_MSG("Failed to write fpgm");
+ }
+
+ return true;
+}
+
+void ots_fpgm_reuse(Font *font, Font *other) {
+ font->fpgm = other->fpgm;
+ font->fpgm_reused = true;
+}
+
+void ots_fpgm_free(Font *font) {
+ delete font->fpgm;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/fpgm.h b/gfx/ots/src/fpgm.h
new file mode 100644
index 000000000..8fabac36d
--- /dev/null
+++ b/gfx/ots/src/fpgm.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium 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_FPGM_H_
+#define OTS_FPGM_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeFPGM {
+ const uint8_t *data;
+ uint32_t length;
+};
+
+} // namespace ots
+
+#endif // OTS_FPGM_H_
diff --git a/gfx/ots/src/gasp.cc b/gfx/ots/src/gasp.cc
new file mode 100644
index 000000000..5ebf5d84b
--- /dev/null
+++ b/gfx/ots/src/gasp.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2009 The Chromium 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 "gasp.h"
+
+// gasp - Grid-fitting And Scan-conversion Procedure
+// http://www.microsoft.com/typography/otspec/gasp.htm
+
+#define TABLE_NAME "gasp"
+
+#define DROP_THIS_TABLE(...) \
+ do { \
+ OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
+ OTS_FAILURE_MSG("Table discarded"); \
+ delete font->gasp; \
+ font->gasp = 0; \
+ } while (0)
+
+namespace ots {
+
+bool ots_gasp_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypeGASP *gasp = new OpenTypeGASP;
+ font->gasp = gasp;
+
+ uint16_t num_ranges = 0;
+ if (!table.ReadU16(&gasp->version) ||
+ !table.ReadU16(&num_ranges)) {
+ return OTS_FAILURE_MSG("Failed to read table header");
+ }
+
+ if (gasp->version > 1) {
+ // Lots of Linux fonts have bad version numbers...
+ DROP_THIS_TABLE("bad version: %u", gasp->version);
+ return true;
+ }
+
+ if (num_ranges == 0) {
+ DROP_THIS_TABLE("num_ranges is zero");
+ return true;
+ }
+
+ gasp->gasp_ranges.reserve(num_ranges);
+ for (unsigned i = 0; i < num_ranges; ++i) {
+ uint16_t max_ppem = 0;
+ uint16_t behavior = 0;
+ if (!table.ReadU16(&max_ppem) ||
+ !table.ReadU16(&behavior)) {
+ return OTS_FAILURE_MSG("Failed to read subrange %d", i);
+ }
+ if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
+ // The records in the gaspRange[] array must be sorted in order of
+ // increasing rangeMaxPPEM value.
+ DROP_THIS_TABLE("ranges are not sorted");
+ return true;
+ }
+ if ((i == num_ranges - 1u) && // never underflow.
+ (max_ppem != 0xffffu)) {
+ DROP_THIS_TABLE("The last record should be 0xFFFF as a sentinel value "
+ "for rangeMaxPPEM");
+ return true;
+ }
+
+ if (behavior >> 8) {
+ OTS_WARNING("undefined bits are used: %x", behavior);
+ // mask undefined bits.
+ behavior &= 0x000fu;
+ }
+
+ if (gasp->version == 0 && (behavior >> 2) != 0) {
+ OTS_WARNING("changed the version number to 1");
+ gasp->version = 1;
+ }
+
+ gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
+ }
+
+ return true;
+}
+
+bool ots_gasp_should_serialise(Font *font) {
+ return font->gasp != NULL;
+}
+
+bool ots_gasp_serialise(OTSStream *out, Font *font) {
+ const OpenTypeGASP *gasp = font->gasp;
+
+ const uint16_t num_ranges = static_cast<uint16_t>(gasp->gasp_ranges.size());
+ if (num_ranges != gasp->gasp_ranges.size() ||
+ !out->WriteU16(gasp->version) ||
+ !out->WriteU16(num_ranges)) {
+ return OTS_FAILURE_MSG("failed to write gasp header");
+ }
+
+ for (uint16_t i = 0; i < num_ranges; ++i) {
+ if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
+ !out->WriteU16(gasp->gasp_ranges[i].second)) {
+ return OTS_FAILURE_MSG("Failed to write gasp subtable %d", i);
+ }
+ }
+
+ return true;
+}
+
+void ots_gasp_reuse(Font *font, Font *other) {
+ font->gasp = other->gasp;
+ font->gasp_reused = true;
+}
+
+void ots_gasp_free(Font *font) {
+ delete font->gasp;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/gfx/ots/src/gasp.h b/gfx/ots/src/gasp.h
new file mode 100644
index 000000000..48d7e7c16
--- /dev/null
+++ b/gfx/ots/src/gasp.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2009 The Chromium 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_GASP_H_
+#define OTS_GASP_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGASP {
+ uint16_t version;
+ // A array of (max PPEM, GASP behavior) pairs.
+ std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;
+};
+
+} // namespace ots
+
+#endif // OTS_GASP_H_
diff --git a/gfx/ots/src/gdef.cc b/gfx/ots/src/gdef.cc
new file mode 100644
index 000000000..71c6fc592
--- /dev/null
+++ b/gfx/ots/src/gdef.cc
@@ -0,0 +1,373 @@
+// Copyright (c) 2011 The Chromium 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 "gdef.h"
+
+#include <limits>
+#include <vector>
+
+#include "gpos.h"
+#include "gsub.h"
+#include "layout.h"
+#include "maxp.h"
+
+// GDEF - The Glyph Definition Table
+// http://www.microsoft.com/typography/otspec/gdef.htm
+
+#define TABLE_NAME "GDEF"
+
+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;
+
+bool ParseGlyphClassDefTable(ots::Font *font, const uint8_t *data,
+ size_t length, const uint16_t num_glyphs) {
+ return ots::ParseClassDefTable(font, data, length, num_glyphs,
+ kMaxGlyphClassDefValue);
+}
+
+bool ParseAttachListTable(ots::Font *font, const uint8_t *data,
+ size_t length, const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t offset_coverage = 0;
+ uint16_t glyph_count = 0;
+ if (!subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&glyph_count)) {
+ return OTS_FAILURE_MSG("Failed to read gdef header");
+ }
+ const unsigned attach_points_end =
+ 2 * static_cast<unsigned>(glyph_count) + 4;
+ if (attach_points_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad glyph count in gdef");
+ }
+ if (offset_coverage == 0 || offset_coverage >= length ||
+ offset_coverage < attach_points_end) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+ }
+ if (glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph count %u", glyph_count);
+ }
+
+ std::vector<uint16_t> attach_points;
+ attach_points.resize(glyph_count);
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ if (!subtable.ReadU16(&attach_points[i])) {
+ return OTS_FAILURE_MSG("Can't read attachment point %d", i);
+ }
+ if (attach_points[i] >= length ||
+ attach_points[i] < attach_points_end) {
+ return OTS_FAILURE_MSG("Bad attachment point %d of %d", i, attach_points[i]);
+ }
+ }
+
+ // Parse coverage table
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Bad coverage table");
+ }
+
+ // Parse attach point table
+ for (unsigned i = 0; i < attach_points.size(); ++i) {
+ subtable.set_offset(attach_points[i]);
+ uint16_t point_count = 0;
+ if (!subtable.ReadU16(&point_count)) {
+ return OTS_FAILURE_MSG("Can't read point count %d", i);
+ }
+ if (point_count == 0) {
+ return OTS_FAILURE_MSG("zero point count %d", i);
+ }
+ uint16_t last_point_index = 0;
+ uint16_t point_index = 0;
+ for (unsigned j = 0; j < point_count; ++j) {
+ if (!subtable.ReadU16(&point_index)) {
+ return OTS_FAILURE_MSG("Can't read point index %d in point %d", j, i);
+ }
+ // Contour point indeces are in increasing numerical order
+ if (last_point_index != 0 && last_point_index >= point_index) {
+ return OTS_FAILURE_MSG("bad contour indeces: %u >= %u",
+ last_point_index, point_index);
+ }
+ last_point_index = point_index;
+ }
+ }
+ return true;
+}
+
+bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
+ size_t length, const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+ uint16_t offset_coverage = 0;
+ uint16_t lig_glyph_count = 0;
+ if (!subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&lig_glyph_count)) {
+ return OTS_FAILURE_MSG("Can't read caret structure");
+ }
+ const unsigned lig_glyphs_end =
+ 2 * static_cast<unsigned>(lig_glyph_count) + 4;
+ if (lig_glyphs_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad caret structure");
+ }
+ if (offset_coverage == 0 || offset_coverage >= length ||
+ offset_coverage < lig_glyphs_end) {
+ return OTS_FAILURE_MSG("Bad caret coverate offset %d", offset_coverage);
+ }
+ if (lig_glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("bad ligature glyph count: %u", lig_glyph_count);
+ }
+
+ std::vector<uint16_t> lig_glyphs;
+ lig_glyphs.resize(lig_glyph_count);
+ for (unsigned i = 0; i < lig_glyph_count; ++i) {
+ if (!subtable.ReadU16(&lig_glyphs[i])) {
+ return OTS_FAILURE_MSG("Can't read ligature glyph location %d", i);
+ }
+ if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
+ return OTS_FAILURE_MSG("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
+ }
+ }
+
+ // Parse coverage table
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Can't parse caret coverage table");
+ }
+
+ // Parse ligature glyph table
+ for (unsigned i = 0; i < lig_glyphs.size(); ++i) {
+ subtable.set_offset(lig_glyphs[i]);
+ uint16_t caret_count = 0;
+ if (!subtable.ReadU16(&caret_count)) {
+ return OTS_FAILURE_MSG("Can't read caret count for glyph %d", i);
+ }
+ if (caret_count == 0) {
+ return OTS_FAILURE_MSG("bad caret value count: %u", caret_count);
+ }
+
+ std::vector<uint16_t> caret_value_offsets;
+ caret_value_offsets.resize(caret_count);
+ unsigned caret_value_offsets_end = 2 * static_cast<unsigned>(caret_count) + 2;
+ for (unsigned j = 0; j < caret_count; ++j) {
+ if (!subtable.ReadU16(&caret_value_offsets[j])) {
+ return OTS_FAILURE_MSG("Can't read caret offset %d for glyph %d", j, i);
+ }
+ if (caret_value_offsets[j] >= length || caret_value_offsets[j] < caret_value_offsets_end) {
+ return OTS_FAILURE_MSG("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
+ }
+ }
+
+ // Parse caret values table
+ for (unsigned j = 0; j < caret_count; ++j) {
+ subtable.set_offset(lig_glyphs[i] + caret_value_offsets[j]);
+ uint16_t caret_format = 0;
+ if (!subtable.ReadU16(&caret_format)) {
+ return OTS_FAILURE_MSG("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 OTS_FAILURE_MSG("bad caret value format: %u", caret_format);
+ }
+ // CaretValueFormats contain a 2-byte field which could be
+ // arbitrary value.
+ if (!subtable.Skip(2)) {
+ return OTS_FAILURE_MSG("Bad caret value table structure %d in glyph %d", j, i);
+ }
+ }
+ }
+ return true;
+}
+
+bool ParseMarkAttachClassDefTable(ots::Font *font, const uint8_t *data,
+ size_t length, const uint16_t num_glyphs) {
+ return ots::ParseClassDefTable(font, data, length, num_glyphs, kMaxClassDefValue);
+}
+
+bool ParseMarkGlyphSetsDefTable(ots::Font *font, const uint8_t *data,
+ size_t length, const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+ uint16_t format = 0;
+ uint16_t mark_set_count = 0;
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&mark_set_count)) {
+ return OTS_FAILURE_MSG("Can' read mark glyph table structure");
+ }
+ if (format != 1) {
+ return OTS_FAILURE_MSG("bad mark glyph set table format: %u", format);
+ }
+
+ const unsigned mark_sets_end = 2 * static_cast<unsigned>(mark_set_count) + 4;
+ if (mark_sets_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad mark_set %d", mark_sets_end);
+ }
+ for (unsigned i = 0; i < mark_set_count; ++i) {
+ uint32_t offset_coverage = 0;
+ if (!subtable.ReadU32(&offset_coverage)) {
+ return OTS_FAILURE_MSG("Can't read covrage location for mark set %d", i);
+ }
+ if (offset_coverage >= length ||
+ offset_coverage < mark_sets_end) {
+ return OTS_FAILURE_MSG("Bad coverage location %d for mark set %d", offset_coverage, i);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table for mark set %d", i);
+ }
+ }
+ font->gdef->num_mark_glyph_sets = mark_set_count;
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+bool ots_gdef_parse(Font *font, const uint8_t *data, size_t length) {
+ // Grab the number of glyphs in the font from the maxp table to check
+ // GlyphIDs in GDEF table.
+ if (!font->maxp) {
+ return OTS_FAILURE_MSG("No maxp table in font, needed by GDEF");
+ }
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+
+ Buffer table(data, length);
+
+ OpenTypeGDEF *gdef = new OpenTypeGDEF;
+ font->gdef = gdef;
+
+ uint32_t version = 0;
+ if (!table.ReadU32(&version)) {
+ return OTS_FAILURE_MSG("Incomplete table");
+ }
+ if (version < 0x00010000 || version == 0x00010001) {
+ return OTS_FAILURE_MSG("Bad version");
+ }
+
+ if (version >= 0x00010002) {
+ gdef->version_2 = true;
+ }
+
+ uint16_t offset_glyph_class_def = 0;
+ uint16_t offset_attach_list = 0;
+ uint16_t offset_lig_caret_list = 0;
+ uint16_t offset_mark_attach_class_def = 0;
+ if (!table.ReadU16(&offset_glyph_class_def) ||
+ !table.ReadU16(&offset_attach_list) ||
+ !table.ReadU16(&offset_lig_caret_list) ||
+ !table.ReadU16(&offset_mark_attach_class_def)) {
+ return OTS_FAILURE_MSG("Incomplete table");
+ }
+ uint16_t offset_mark_glyph_sets_def = 0;
+ if (gdef->version_2) {
+ if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
+ return OTS_FAILURE_MSG("Incomplete table");
+ }
+ }
+
+ unsigned gdef_header_end = 4 + 4 * 2;
+ if (gdef->version_2)
+ gdef_header_end += 2;
+
+ // Parse subtables
+ if (offset_glyph_class_def) {
+ if (offset_glyph_class_def >= length ||
+ offset_glyph_class_def < gdef_header_end) {
+ return OTS_FAILURE_MSG("Invalid offset to glyph classes");
+ }
+ if (!ParseGlyphClassDefTable(font, data + offset_glyph_class_def,
+ length - offset_glyph_class_def,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Invalid glyph classes");
+ }
+ gdef->has_glyph_class_def = true;
+ }
+
+ if (offset_attach_list) {
+ if (offset_attach_list >= length ||
+ offset_attach_list < gdef_header_end) {
+ return OTS_FAILURE_MSG("Invalid offset to attachment list");
+ }
+ if (!ParseAttachListTable(font, data + offset_attach_list,
+ length - offset_attach_list,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Invalid attachment list");
+ }
+ }
+
+ if (offset_lig_caret_list) {
+ if (offset_lig_caret_list >= length ||
+ offset_lig_caret_list < gdef_header_end) {
+ return OTS_FAILURE_MSG("Invalid offset to ligature caret list");
+ }
+ if (!ParseLigCaretListTable(font, data + offset_lig_caret_list,
+ length - offset_lig_caret_list,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Invalid ligature caret list");
+ }
+ }
+
+ if (offset_mark_attach_class_def) {
+ if (offset_mark_attach_class_def >= length ||
+ offset_mark_attach_class_def < gdef_header_end) {
+ return OTS_FAILURE_MSG("Invalid offset to mark attachment list");
+ }
+ if (!ParseMarkAttachClassDefTable(font,
+ data + offset_mark_attach_class_def,
+ length - offset_mark_attach_class_def,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Invalid mark attachment list");
+ }
+ gdef->has_mark_attachment_class_def = true;
+ }
+
+ if (offset_mark_glyph_sets_def) {
+ if (offset_mark_glyph_sets_def >= length ||
+ offset_mark_glyph_sets_def < gdef_header_end) {
+ return OTS_FAILURE_MSG("invalid offset to mark glyph sets");
+ }
+ if (!ParseMarkGlyphSetsDefTable(font,
+ data + offset_mark_glyph_sets_def,
+ length - offset_mark_glyph_sets_def,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Invalid mark glyph sets");
+ }
+ gdef->has_mark_glyph_sets_def = true;
+ }
+ gdef->data = data;
+ gdef->length = length;
+ return true;
+}
+
+bool ots_gdef_should_serialise(Font *font) {
+ return font->gdef != NULL && font->gdef->data != NULL;
+}
+
+bool ots_gdef_serialise(OTSStream *out, Font *font) {
+ if (!out->Write(font->gdef->data, font->gdef->length)) {
+ return OTS_FAILURE_MSG("Failed to write GDEF table");
+ }
+
+ return true;
+}
+
+void ots_gdef_reuse(Font *font, Font *other) {
+ font->gdef = other->gdef;
+ font->gdef_reused = true;
+}
+
+void ots_gdef_free(Font *font) {
+ delete font->gdef;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/gdef.h b/gfx/ots/src/gdef.h
new file mode 100644
index 000000000..f46f419c7
--- /dev/null
+++ b/gfx/ots/src/gdef.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium 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_GDEF_H_
+#define OTS_GDEF_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGDEF {
+ OpenTypeGDEF()
+ : 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),
+ data(NULL),
+ length(0) {
+ }
+
+ 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;
+
+ const uint8_t *data;
+ size_t length;
+};
+
+} // namespace ots
+
+#endif
+
diff --git a/gfx/ots/src/glyf.cc b/gfx/ots/src/glyf.cc
new file mode 100644
index 000000000..311916dc0
--- /dev/null
+++ b/gfx/ots/src/glyf.cc
@@ -0,0 +1,303 @@
+// Copyright (c) 2009 The Chromium 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 "glyf.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "head.h"
+#include "loca.h"
+#include "maxp.h"
+
+// glyf - Glyph Data
+// http://www.microsoft.com/typography/otspec/glyf.htm
+
+#define TABLE_NAME "glyf"
+
+namespace {
+
+bool ParseFlagsForSimpleGlyph(ots::Font *font,
+ ots::Buffer *table,
+ uint32_t gly_length,
+ uint32_t num_flags,
+ uint32_t *flags_count_logical,
+ uint32_t *flags_count_physical,
+ uint32_t *xy_coordinates_length) {
+ uint8_t flag = 0;
+ if (!table->ReadU8(&flag)) {
+ return OTS_FAILURE_MSG("Can't read flag");
+ }
+
+ uint32_t delta = 0;
+ if (flag & (1u << 1)) { // x-Short
+ ++delta;
+ } else if (!(flag & (1u << 4))) {
+ delta += 2;
+ }
+
+ if (flag & (1u << 2)) { // y-Short
+ ++delta;
+ } else if (!(flag & (1u << 5))) {
+ delta += 2;
+ }
+
+ if (flag & (1u << 3)) { // repeat
+ if (*flags_count_logical + 1 >= num_flags) {
+ return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags);
+ }
+ uint8_t repeat = 0;
+ if (!table->ReadU8(&repeat)) {
+ return OTS_FAILURE_MSG("Can't read repeat value");
+ }
+ if (repeat == 0) {
+ return OTS_FAILURE_MSG("Zero repeat");
+ }
+ delta += (delta * repeat);
+
+ *flags_count_logical += repeat;
+ if (*flags_count_logical >= num_flags) {
+ return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags);
+ }
+ ++(*flags_count_physical);
+ }
+
+ if ((flag & (1u << 6)) || (flag & (1u << 7))) { // reserved flags
+ return OTS_FAILURE_MSG("Bad glyph flag value (%d), reserved flags must be set to zero", flag);
+ }
+
+ *xy_coordinates_length += delta;
+ if (gly_length < *xy_coordinates_length) {
+ return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length);
+ }
+
+ return true;
+}
+
+bool ParseSimpleGlyph(ots::Font *font, const uint8_t *data,
+ ots::Buffer *table, int16_t num_contours,
+ uint32_t gly_offset, uint32_t gly_length,
+ uint32_t *new_size) {
+ ots::OpenTypeGLYF *glyf = font->glyf;
+
+ // read the end-points array
+ uint16_t num_flags = 0;
+ for (int i = 0; i < num_contours; ++i) {
+ uint16_t tmp_index = 0;
+ if (!table->ReadU16(&tmp_index)) {
+ return OTS_FAILURE_MSG("Can't read contour index %d", i);
+ }
+ if (tmp_index == 0xffffu) {
+ return OTS_FAILURE_MSG("Bad contour index %d", i);
+ }
+ // check if the indices are monotonically increasing
+ if (i && (tmp_index + 1 <= num_flags)) {
+ return OTS_FAILURE_MSG("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
+ }
+ num_flags = tmp_index + 1;
+ }
+
+ uint16_t bytecode_length = 0;
+ if (!table->ReadU16(&bytecode_length)) {
+ return OTS_FAILURE_MSG("Can't read bytecode length");
+ }
+ if ((font->maxp->version_1) &&
+ (font->maxp->max_size_glyf_instructions < bytecode_length)) {
+ return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length);
+ }
+
+ const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
+ if (gly_length < (gly_header_length + bytecode_length)) {
+ return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length);
+ }
+
+ glyf->iov.push_back(std::make_pair(
+ data + gly_offset,
+ static_cast<size_t>(gly_header_length + bytecode_length)));
+
+ if (!table->Skip(bytecode_length)) {
+ return OTS_FAILURE_MSG("Can't skip bytecode of length %d", bytecode_length);
+ }
+
+ uint32_t flags_count_physical = 0; // on memory
+ uint32_t xy_coordinates_length = 0;
+ for (uint32_t flags_count_logical = 0;
+ flags_count_logical < num_flags;
+ ++flags_count_logical, ++flags_count_physical) {
+ if (!ParseFlagsForSimpleGlyph(font,
+ table,
+ gly_length,
+ num_flags,
+ &flags_count_logical,
+ &flags_count_physical,
+ &xy_coordinates_length)) {
+ return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical);
+ }
+ }
+
+ if (gly_length < (gly_header_length + bytecode_length +
+ flags_count_physical + xy_coordinates_length)) {
+ return OTS_FAILURE_MSG("Glyph too short %d", gly_length);
+ }
+
+ if (gly_length - (gly_header_length + bytecode_length +
+ flags_count_physical + xy_coordinates_length) > 3) {
+ // We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
+ // zero-padded length.
+ return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length);
+ }
+
+ glyf->iov.push_back(std::make_pair(
+ data + gly_offset + gly_header_length + bytecode_length,
+ static_cast<size_t>(flags_count_physical + xy_coordinates_length)));
+
+ *new_size
+ = gly_header_length + flags_count_physical + xy_coordinates_length + bytecode_length;
+
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ if (!font->maxp || !font->loca || !font->head) {
+ return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table");
+ }
+
+ OpenTypeGLYF *glyf = new OpenTypeGLYF;
+ font->glyf = glyf;
+
+ const unsigned num_glyphs = font->maxp->num_glyphs;
+ std::vector<uint32_t> &offsets = font->loca->offsets;
+
+ if (offsets.size() != num_glyphs + 1) {
+ return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
+ }
+
+ std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
+ uint32_t current_offset = 0;
+
+ for (unsigned i = 0; i < num_glyphs; ++i) {
+ const unsigned gly_offset = offsets[i];
+ // The LOCA parser checks that these values are monotonic
+ const unsigned gly_length = offsets[i + 1] - offsets[i];
+ if (!gly_length) {
+ // this glyph has no outline (e.g. the space charactor)
+ resulting_offsets[i] = current_offset;
+ continue;
+ }
+
+ if (gly_offset >= length) {
+ return OTS_FAILURE_MSG("Glyph %d offset %d too high %ld", i, gly_offset, length);
+ }
+ // Since these are unsigned types, the compiler is not allowed to assume
+ // that they never overflow.
+ if (gly_offset + gly_length < gly_offset) {
+ return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length);
+ }
+ if (gly_offset + gly_length > length) {
+ return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length);
+ }
+
+ table.set_offset(gly_offset);
+ int16_t num_contours, xmin, ymin, xmax, ymax;
+ if (!table.ReadS16(&num_contours) ||
+ !table.ReadS16(&xmin) ||
+ !table.ReadS16(&ymin) ||
+ !table.ReadS16(&xmax) ||
+ !table.ReadS16(&ymax)) {
+ return OTS_FAILURE_MSG("Can't read glyph %d header", i);
+ }
+
+ if (num_contours <= -2) {
+ // -2, -3, -4, ... are reserved for future use.
+ return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i);
+ }
+
+ // workaround for fonts in http://www.princexml.com/fonts/
+ if ((xmin == 32767) &&
+ (xmax == -32767) &&
+ (ymin == 32767) &&
+ (ymax == -32767)) {
+ OTS_WARNING("bad xmin/xmax/ymin/ymax values");
+ xmin = xmax = ymin = ymax = 0;
+ }
+
+ if (xmin > xmax || ymin > ymax) {
+ return OTS_FAILURE_MSG("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
+ }
+
+ unsigned new_size = 0;
+ if (num_contours >= 0) {
+ // this is a simple glyph and might contain bytecode
+ if (!ParseSimpleGlyph(font, data, &table,
+ num_contours, gly_offset, gly_length, &new_size)) {
+ return OTS_FAILURE_MSG("Failed to parse glyph %d", i);
+ }
+ } else {
+ // it's a composite glyph without any bytecode. Enqueue the whole thing
+ glyf->iov.push_back(std::make_pair(data + gly_offset,
+ static_cast<size_t>(gly_length)));
+ new_size = gly_length;
+ }
+
+ resulting_offsets[i] = current_offset;
+ // glyphs must be four byte aligned
+ // TODO(yusukes): investigate whether this padding is really necessary.
+ // Which part of the spec requires this?
+ const unsigned padding = (4 - (new_size & 3)) % 4;
+ if (padding) {
+ glyf->iov.push_back(std::make_pair(
+ reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"),
+ static_cast<size_t>(padding)));
+ new_size += padding;
+ }
+ current_offset += new_size;
+ }
+ resulting_offsets[num_glyphs] = current_offset;
+
+ const uint16_t max16 = std::numeric_limits<uint16_t>::max();
+ if ((*std::max_element(resulting_offsets.begin(),
+ resulting_offsets.end()) >= (max16 * 2u)) &&
+ (font->head->index_to_loc_format != 1)) {
+ OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
+ font->head->index_to_loc_format = 1;
+ }
+
+ font->loca->offsets = resulting_offsets;
+ return true;
+}
+
+bool ots_glyf_should_serialise(Font *font) {
+ return font->glyf != NULL;
+}
+
+bool ots_glyf_serialise(OTSStream *out, Font *font) {
+ const OpenTypeGLYF *glyf = font->glyf;
+
+ for (unsigned i = 0; i < glyf->iov.size(); ++i) {
+ if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
+ return OTS_FAILURE_MSG("Falied to write glyph %d", i);
+ }
+ }
+
+ return true;
+}
+
+void ots_glyf_reuse(Font *font, Font *other) {
+ font->glyf = other->glyf;
+ font->glyf_reused = true;
+}
+
+void ots_glyf_free(Font *font) {
+ delete font->glyf;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/glyf.h b/gfx/ots/src/glyf.h
new file mode 100644
index 000000000..9a8baf5ec
--- /dev/null
+++ b/gfx/ots/src/glyf.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2009 The Chromium 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_GLYF_H_
+#define OTS_GLYF_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGLYF {
+ std::vector<std::pair<const uint8_t*, size_t> > iov;
+};
+
+} // namespace ots
+
+#endif // OTS_GLYF_H_
diff --git a/gfx/ots/src/gpos.cc b/gfx/ots/src/gpos.cc
new file mode 100644
index 000000000..83d9ab053
--- /dev/null
+++ b/gfx/ots/src/gpos.cc
@@ -0,0 +1,817 @@
+// Copyright (c) 2011 The Chromium 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 "gpos.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// GPOS - The Glyph Positioning Table
+// http://www.microsoft.com/typography/otspec/gpos.htm
+
+#define TABLE_NAME "GPOS"
+
+namespace {
+
+enum GPOS_TYPE {
+ GPOS_TYPE_SINGLE_ADJUSTMENT = 1,
+ GPOS_TYPE_PAIR_ADJUSTMENT = 2,
+ GPOS_TYPE_CURSIVE_ATTACHMENT = 3,
+ GPOS_TYPE_MARK_TO_BASE_ATTACHMENT = 4,
+ GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5,
+ GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6,
+ GPOS_TYPE_CONTEXT_POSITIONING = 7,
+ GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8,
+ GPOS_TYPE_EXTENSION_POSITIONING = 9,
+ GPOS_TYPE_RESERVED = 10
+};
+
+// The size of gpos header.
+const unsigned kGposHeaderSize = 10;
+// 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,
+ const uint8_t *data, const size_t length);
+bool ParsePairAdjustment(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseCursiveAttachment(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseMarkToBaseAttachment(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseMarkToLigatureAttachment(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseMarkToMarkAttachment(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseContextPositioning(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseChainedContextPositioning(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseExtensionPositioning(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = {
+ {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment},
+ {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment},
+ {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment},
+ {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment},
+ {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment},
+ {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment},
+ {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning},
+ {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning},
+ {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning}
+};
+
+const ots::LookupSubtableParser kGposLookupSubtableParser = {
+ arraysize(kGposTypeParsers),
+ GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers
+};
+
+// Shared Tables: ValueRecord, Anchor Table, and MarkArray
+
+bool ParseValueRecord(const ots::Font *font,
+ ots::Buffer* subtable, const uint8_t *data,
+ const size_t length, const uint16_t value_format) {
+ // Check existence of adjustment fields.
+ for (unsigned i = 0; i < 4; ++i) {
+ if ((value_format >> i) & 0x1) {
+ // Just read the field since these fileds could take an arbitrary values.
+ if (!subtable->Skip(2)) {
+ return OTS_FAILURE_MSG("Failed to read value reacord component");
+ }
+ }
+ }
+
+ // Check existence of offsets to device table.
+ for (unsigned i = 0; i < 4; ++i) {
+ if ((value_format >> (i + 4)) & 0x1) {
+ uint16_t offset = 0;
+ if (!subtable->ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read value record offset");
+ }
+ if (offset) {
+ // TODO(bashi): Is it possible that device tables locate before
+ // this record? No fonts contain such offset AKAIF.
+ if (offset >= length) {
+ return OTS_FAILURE_MSG("Value record offset too high %d >= %ld", offset, length);
+ }
+ if (!ots::ParseDeviceTable(font, data + offset, length - offset)) {
+ return OTS_FAILURE_MSG("Failed to parse device table in value record");
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool ParseAnchorTable(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ // Read format and skip 2 2-byte fields that could be arbitrary values.
+ if (!subtable.ReadU16(&format) ||
+ !subtable.Skip(4)) {
+ return OTS_FAILURE_MSG("Faled to read anchor table");
+ }
+
+ if (format == 0 || format > kMaxAnchorFormat) {
+ return OTS_FAILURE_MSG("Bad Anchor table format %d", format);
+ }
+
+ // Format 2 and 3 has additional fields.
+ if (format == 2) {
+ // Format 2 provides an index to a glyph contour point, which will take
+ // arbitrary value.
+ uint16_t anchor_point = 0;
+ if (!subtable.ReadU16(&anchor_point)) {
+ return OTS_FAILURE_MSG("Failed to read anchor point in format 2 Anchor Table");
+ }
+ } else if (format == 3) {
+ uint16_t offset_x_device = 0;
+ uint16_t offset_y_device = 0;
+ if (!subtable.ReadU16(&offset_x_device) ||
+ !subtable.ReadU16(&offset_y_device)) {
+ return OTS_FAILURE_MSG("Failed to read device table offsets in format 3 anchor table");
+ }
+ const unsigned format_end = static_cast<unsigned>(10);
+ if (offset_x_device) {
+ if (offset_x_device < format_end || offset_x_device >= length) {
+ return OTS_FAILURE_MSG("Bad x device table offset %d", offset_x_device);
+ }
+ if (!ots::ParseDeviceTable(font, data + offset_x_device,
+ length - offset_x_device)) {
+ return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
+ }
+ }
+ if (offset_y_device) {
+ if (offset_y_device < format_end || offset_y_device >= length) {
+ return OTS_FAILURE_MSG("Bad y device table offset %d", offset_y_device);
+ }
+ if (!ots::ParseDeviceTable(font, data + offset_y_device,
+ length - offset_y_device)) {
+ return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
+ }
+ }
+ }
+ return true;
+}
+
+bool ParseMarkArrayTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t class_count) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t mark_count = 0;
+ if (!subtable.ReadU16(&mark_count)) {
+ return OTS_FAILURE_MSG("Can't read mark table length");
+ }
+
+ // MarkRecord consists of 4-bytes.
+ const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2;
+ if (mark_records_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad mark table length");
+ }
+ for (unsigned i = 0; i < mark_count; ++i) {
+ uint16_t class_value = 0;
+ uint16_t offset_mark_anchor = 0;
+ if (!subtable.ReadU16(&class_value) ||
+ !subtable.ReadU16(&offset_mark_anchor)) {
+ return OTS_FAILURE_MSG("Can't read mark table %d", i);
+ }
+ // |class_value| may take arbitrary values including 0 here so we don't
+ // check the value.
+ if (offset_mark_anchor < mark_records_end ||
+ offset_mark_anchor >= length) {
+ return OTS_FAILURE_MSG("Bad mark anchor offset %d for mark table %d", offset_mark_anchor, i);
+ }
+ if (!ParseAnchorTable(font, data + offset_mark_anchor,
+ length - offset_mark_anchor)) {
+ return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i);
+ }
+ }
+
+ return true;
+}
+
+// Lookup Type 1:
+// Single Adjustment Positioning Subtable
+bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
+ const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage = 0;
+ uint16_t value_format = 0;
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&value_format)) {
+ return OTS_FAILURE_MSG("Can't read single adjustment information");
+ }
+
+ if (format == 1) {
+ // Format 1 exactly one value record.
+ if (!ParseValueRecord(font, &subtable, data, length, value_format)) {
+ return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table");
+ }
+ } else if (format == 2) {
+ uint16_t value_count = 0;
+ if (!subtable.ReadU16(&value_count)) {
+ return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table");
+ }
+ for (unsigned i = 0; i < value_count; ++i) {
+ if (!ParseValueRecord(font, &subtable, data, length, value_format)) {
+ return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i);
+ }
+ }
+ } else {
+ return OTS_FAILURE_MSG("Bad format %d in single adjustment table", format);
+ }
+
+ if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d in single adjustment table", offset_coverage);
+ }
+
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage,
+ font->maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table");
+ }
+
+ return true;
+}
+
+bool ParsePairSetTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t value_format1,
+ const uint16_t value_format2,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t value_count = 0;
+ if (!subtable.ReadU16(&value_count)) {
+ return OTS_FAILURE_MSG("Failed to read pair set table structure");
+ }
+ for (unsigned i = 0; i < value_count; ++i) {
+ // Check pair value record.
+ uint16_t glyph_id = 0;
+ if (!subtable.ReadU16(&glyph_id)) {
+ return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i);
+ }
+ if (glyph_id >= num_glyphs) {
+ return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs);
+ }
+ if (!ParseValueRecord(font, &subtable, data, length, value_format1)) {
+ return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table");
+ }
+ if (!ParseValueRecord(font, &subtable, data, length, value_format2)) {
+ return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table");
+ }
+ }
+ return true;
+}
+
+bool ParsePairPosFormat1(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t value_format1,
+ const uint16_t value_format2,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Skip 8 bytes that are already read before.
+ if (!subtable.Skip(8)) {
+ return OTS_FAILURE_MSG("Failed to read pair pos table structure");
+ }
+
+ uint16_t pair_set_count = 0;
+ if (!subtable.ReadU16(&pair_set_count)) {
+ return OTS_FAILURE_MSG("Failed to read pair pos set count");
+ }
+
+ const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10;
+ if (pair_pos_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad pair set length %d", pair_pos_end);
+ }
+ for (unsigned i = 0; i < pair_set_count; ++i) {
+ uint16_t pair_set_offset = 0;
+ if (!subtable.ReadU16(&pair_set_offset)) {
+ return OTS_FAILURE_MSG("Failed to read pair set offset for pair set %d", i);
+ }
+ if (pair_set_offset < pair_pos_end || pair_set_offset >= length) {
+ return OTS_FAILURE_MSG("Bad pair set offset %d for pair set %d", pair_set_offset, i);
+ }
+ // Check pair set tables
+ if (!ParsePairSetTable(font, data + pair_set_offset, length - pair_set_offset,
+ value_format1, value_format2,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse pair set table %d", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParsePairPosFormat2(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t value_format1,
+ const uint16_t value_format2,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Skip 8 bytes that are already read before.
+ if (!subtable.Skip(8)) {
+ return OTS_FAILURE_MSG("Failed to read pair pos format 2 structure");
+ }
+
+ uint16_t offset_class_def1 = 0;
+ uint16_t offset_class_def2 = 0;
+ uint16_t class1_count = 0;
+ uint16_t class2_count = 0;
+ if (!subtable.ReadU16(&offset_class_def1) ||
+ !subtable.ReadU16(&offset_class_def2) ||
+ !subtable.ReadU16(&class1_count) ||
+ !subtable.ReadU16(&class2_count)) {
+ return OTS_FAILURE_MSG("Failed to read pair pos format 2 data");
+ }
+
+ // Check class 1 records.
+ for (unsigned i = 0; i < class1_count; ++i) {
+ // Check class 2 records.
+ for (unsigned j = 0; j < class2_count; ++j) {
+ if (value_format1 && !ParseValueRecord(font, &subtable, data, length,
+ value_format1)) {
+ return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
+ }
+ if (value_format2 && !ParseValueRecord(font, &subtable, data, length,
+ value_format2)) {
+ return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
+ }
+ }
+ }
+
+ // Check class definition tables.
+ if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
+ offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
+ return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2);
+ }
+ if (!ots::ParseClassDefTable(font, data + offset_class_def1,
+ length - offset_class_def1,
+ num_glyphs, 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)) {
+ return OTS_FAILURE_MSG("Failed to parse class definition table 2");
+ }
+
+ return true;
+}
+
+// Lookup Type 2:
+// Pair Adjustment Positioning Subtable
+bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
+ const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage = 0;
+ uint16_t value_format1 = 0;
+ uint16_t value_format2 = 0;
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&value_format1) ||
+ !subtable.ReadU16(&value_format2)) {
+ return OTS_FAILURE_MSG("Failed to read pair adjustment structure");
+ }
+
+ if (format == 1) {
+ if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2,
+ font->maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse pair pos format 1");
+ }
+ } else if (format == 2) {
+ if (!ParsePairPosFormat2(font, data, length, value_format1, value_format2,
+ font->maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse pair format 2");
+ }
+ } else {
+ return OTS_FAILURE_MSG("Bad pos pair format %d", format);
+ }
+
+ if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad pair pos offset coverage %d", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage,
+ font->maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table");
+ }
+
+ return true;
+}
+
+// Lookup Type 3
+// Cursive Attachment Positioning Subtable
+bool ParseCursiveAttachment(const ots::Font *font, const uint8_t *data,
+ const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage = 0;
+ uint16_t entry_exit_count = 0;
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&entry_exit_count)) {
+ return OTS_FAILURE_MSG("Failed to read cursive attachment structure");
+ }
+
+ if (format != 1) {
+ return OTS_FAILURE_MSG("Bad cursive attachment format %d", format);
+ }
+
+ // Check entry exit records.
+ const unsigned entry_exit_records_end =
+ 2 * static_cast<unsigned>(entry_exit_count) + 6;
+ if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad entry exit record end %d", entry_exit_records_end);
+ }
+ for (unsigned i = 0; i < entry_exit_count; ++i) {
+ uint16_t offset_entry_anchor = 0;
+ uint16_t offset_exit_anchor = 0;
+ if (!subtable.ReadU16(&offset_entry_anchor) ||
+ !subtable.ReadU16(&offset_exit_anchor)) {
+ return OTS_FAILURE_MSG("Can't read entry exit record %d", i);
+ }
+ // These offsets could be NULL.
+ if (offset_entry_anchor) {
+ if (offset_entry_anchor < entry_exit_records_end ||
+ offset_entry_anchor >= length) {
+ return OTS_FAILURE_MSG("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i);
+ }
+ if (!ParseAnchorTable(font, data + offset_entry_anchor,
+ length - offset_entry_anchor)) {
+ return OTS_FAILURE_MSG("Failed to parse entry anchor table in entry exit record %d", i);
+ }
+ }
+ if (offset_exit_anchor) {
+ if (offset_exit_anchor < entry_exit_records_end ||
+ offset_exit_anchor >= length) {
+ return OTS_FAILURE_MSG("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i);
+ }
+ if (!ParseAnchorTable(font, data + offset_exit_anchor,
+ length - offset_exit_anchor)) {
+ return OTS_FAILURE_MSG("Failed to parse exit anchor table in entry exit record %d", i);
+ }
+ }
+ }
+
+ if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset in cursive attachment %d", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage,
+ font->maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment");
+ }
+
+ return true;
+}
+
+bool ParseAnchorArrayTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t class_count) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t record_count = 0;
+ if (!subtable.ReadU16(&record_count)) {
+ return OTS_FAILURE_MSG("Can't read anchor array length");
+ }
+
+ const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) *
+ static_cast<unsigned>(class_count) + 2;
+ if (anchor_array_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of anchor array %d", anchor_array_end);
+ }
+ for (unsigned i = 0; i < record_count; ++i) {
+ for (unsigned j = 0; j < class_count; ++j) {
+ uint16_t offset_record = 0;
+ if (!subtable.ReadU16(&offset_record)) {
+ return OTS_FAILURE_MSG("Can't read anchor array record offset for class %d and record %d", j, i);
+ }
+ // |offset_record| could be NULL.
+ if (offset_record) {
+ if (offset_record < anchor_array_end || offset_record >= length) {
+ return OTS_FAILURE_MSG("Bad record offset %d in class %d, record %d", offset_record, j, i);
+ }
+ if (!ParseAnchorTable(font, data + offset_record,
+ length - offset_record)) {
+ return OTS_FAILURE_MSG("Failed to parse anchor table for class %d, record %d", j, i);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool ParseLigatureArrayTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t class_count) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t ligature_count = 0;
+ if (!subtable.ReadU16(&ligature_count)) {
+ return OTS_FAILURE_MSG("Failed to read ligature count");
+ }
+ for (unsigned i = 0; i < ligature_count; ++i) {
+ uint16_t offset_ligature_attach = 0;
+ if (!subtable.ReadU16(&offset_ligature_attach)) {
+ return OTS_FAILURE_MSG("Can't read ligature offset %d", i);
+ }
+ if (offset_ligature_attach < 2 || offset_ligature_attach >= length) {
+ return OTS_FAILURE_MSG("Bad ligature attachment offset %d in ligature %d", offset_ligature_attach, i);
+ }
+ if (!ParseAnchorArrayTable(font, data + offset_ligature_attach,
+ length - offset_ligature_attach, class_count)) {
+ return OTS_FAILURE_MSG("Failed to parse anchor table for ligature %d", i);
+ }
+ }
+ return true;
+}
+
+// Common parser for Lookup Type 4, 5 and 6.
+bool ParseMarkToAttachmentSubtables(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const GPOS_TYPE type) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage1 = 0;
+ uint16_t offset_coverage2 = 0;
+ uint16_t class_count = 0;
+ uint16_t offset_mark_array = 0;
+ uint16_t offset_type_specific_array = 0;
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage1) ||
+ !subtable.ReadU16(&offset_coverage2) ||
+ !subtable.ReadU16(&class_count) ||
+ !subtable.ReadU16(&offset_mark_array) ||
+ !subtable.ReadU16(&offset_type_specific_array)) {
+ return OTS_FAILURE_MSG("Failed to read mark attachment subtable header");
+ }
+
+ if (format != 1) {
+ return OTS_FAILURE_MSG("bad mark attachment subtable format %d", format);
+ }
+
+ const unsigned header_end = static_cast<unsigned>(subtable.offset());
+ if (header_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end);
+ }
+ if (offset_coverage1 < header_end || offset_coverage1 >= length) {
+ return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage1,
+ length - offset_coverage1,
+ font->maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse converge 1 table");
+ }
+ if (offset_coverage2 < header_end || offset_coverage2 >= length) {
+ return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage2,
+ length - offset_coverage2,
+ font->maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table 2");
+ }
+
+ if (offset_mark_array < header_end || offset_mark_array >= length) {
+ return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array);
+ }
+ if (!ParseMarkArrayTable(font, data + offset_mark_array,
+ length - offset_mark_array, class_count)) {
+ return OTS_FAILURE_MSG("Failed to parse mark array");
+ }
+
+ if (offset_type_specific_array < header_end ||
+ offset_type_specific_array >= length) {
+ return OTS_FAILURE_MSG("Bad type specific array offset %d", offset_type_specific_array);
+ }
+ if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT ||
+ type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) {
+ if (!ParseAnchorArrayTable(font, data + offset_type_specific_array,
+ length - offset_type_specific_array,
+ class_count)) {
+ return OTS_FAILURE_MSG("Failed to parse anchor array");
+ }
+ } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) {
+ if (!ParseLigatureArrayTable(font, data + offset_type_specific_array,
+ length - offset_type_specific_array,
+ class_count)) {
+ return OTS_FAILURE_MSG("Failed to parse ligature array");
+ }
+ } else {
+ return OTS_FAILURE_MSG("Bad attachment type %d", type);
+ }
+
+ return true;
+}
+
+// Lookup Type 4:
+// MarkToBase Attachment Positioning Subtable
+bool ParseMarkToBaseAttachment(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ return ParseMarkToAttachmentSubtables(font, data, length,
+ GPOS_TYPE_MARK_TO_BASE_ATTACHMENT);
+}
+
+// Lookup Type 5:
+// MarkToLigature Attachment Positioning Subtable
+bool ParseMarkToLigatureAttachment(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ return ParseMarkToAttachmentSubtables(font, data, length,
+ GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT);
+}
+
+// Lookup Type 6:
+// MarkToMark Attachment Positioning Subtable
+bool ParseMarkToMarkAttachment(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ return ParseMarkToAttachmentSubtables(font, data, length,
+ GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
+}
+
+// Lookup Type 7:
+// Contextual Positioning Subtables
+bool ParseContextPositioning(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs,
+ font->gpos->num_lookups);
+}
+
+// Lookup Type 8:
+// Chaining Contexual Positioning Subtable
+bool ParseChainedContextPositioning(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ return ots::ParseChainingContextSubtable(font, data, length,
+ font->maxp->num_glyphs,
+ font->gpos->num_lookups);
+}
+
+// Lookup Type 9:
+// Extension Positioning
+bool ParseExtensionPositioning(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ return ots::ParseExtensionSubtable(font, data, length,
+ &kGposLookupSubtableParser);
+}
+
+} // namespace
+
+namespace ots {
+
+// As far as I checked, following fonts contain invalid GPOS table and
+// OTS will drop their GPOS table.
+//
+// # invalid delta format in device table
+// samanata.ttf
+//
+// # bad size range in device table
+// Sarai_07.ttf
+//
+// # bad offset to PairSetTable
+// chandas1-2.ttf
+//
+// # bad offset to FeatureTable
+// glrso12.ttf
+// gllr12.ttf
+// glbo12.ttf
+// glb12.ttf
+// glro12.ttf
+// glbso12.ttf
+// glrc12.ttf
+// glrsc12.ttf
+// glbs12.ttf
+// glrs12.ttf
+// glr12.ttf
+//
+// # ScriptRecords aren't sorted by tag
+// Garogier_unhinted.otf
+//
+// # bad start coverage index in CoverageFormat2
+// AndBasR.ttf
+// CharisSILB.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILR.ttf
+// DoulosSILR.ttf
+// GenBasBI.ttf
+// GenBasI.ttf
+// GenBkBasI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// Padauk-Bold.ttf
+// Padauk.ttf
+//
+// # Contour point indexes aren't sorted
+// Arial Unicode.ttf
+
+bool ots_gpos_parse(Font *font, const uint8_t *data, size_t length) {
+ // Parsing GPOS table requires num_glyphs which is contained in maxp table.
+ if (!font->maxp) {
+ return OTS_FAILURE_MSG("missing maxp table needed in GPOS");
+ }
+
+ Buffer table(data, length);
+
+ OpenTypeGPOS *gpos = new OpenTypeGPOS;
+ font->gpos = gpos;
+
+ uint32_t version = 0;
+ uint16_t offset_script_list = 0;
+ uint16_t offset_feature_list = 0;
+ uint16_t offset_lookup_list = 0;
+ if (!table.ReadU32(&version) ||
+ !table.ReadU16(&offset_script_list) ||
+ !table.ReadU16(&offset_feature_list) ||
+ !table.ReadU16(&offset_lookup_list)) {
+ return OTS_FAILURE_MSG("Incomplete table");
+ }
+
+ if (version != 0x00010000) {
+ return OTS_FAILURE_MSG("Bad version");
+ }
+
+ if (offset_lookup_list) {
+ if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
+ return OTS_FAILURE_MSG("Bad lookup list offset in table header");
+ }
+
+ if (!ParseLookupListTable(font, data + offset_lookup_list,
+ length - offset_lookup_list,
+ &kGposLookupSubtableParser,
+ &gpos->num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup list table");
+ }
+ }
+
+ uint16_t num_features = 0;
+ if (offset_feature_list) {
+ if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
+ return OTS_FAILURE_MSG("Bad feature list offset in table header");
+ }
+
+ if (!ParseFeatureListTable(font, data + offset_feature_list,
+ length - offset_feature_list, gpos->num_lookups,
+ &num_features)) {
+ return OTS_FAILURE_MSG("Failed to parse feature list table");
+ }
+ }
+
+ if (offset_script_list) {
+ if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
+ return OTS_FAILURE_MSG("Bad script list offset in table header");
+ }
+
+ if (!ParseScriptListTable(font, data + offset_script_list,
+ length - offset_script_list, num_features)) {
+ return OTS_FAILURE_MSG("Failed to parse script list table");
+ }
+ }
+
+ gpos->data = data;
+ gpos->length = length;
+ return true;
+}
+
+bool ots_gpos_should_serialise(Font *font) {
+ return font->gpos != NULL && font->gpos->data != NULL;
+}
+
+bool ots_gpos_serialise(OTSStream *out, Font *font) {
+ if (!out->Write(font->gpos->data, font->gpos->length)) {
+ return OTS_FAILURE_MSG("Failed to write GPOS table");
+ }
+
+ return true;
+}
+
+void ots_gpos_reuse(Font *font, Font *other) {
+ font->gpos = other->gpos;
+ font->gpos_reused = true;
+}
+
+void ots_gpos_free(Font *font) {
+ delete font->gpos;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/gpos.h b/gfx/ots/src/gpos.h
new file mode 100644
index 000000000..3a4034f6f
--- /dev/null
+++ b/gfx/ots/src/gpos.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium 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_GPOS_H_
+#define OTS_GPOS_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGPOS {
+ OpenTypeGPOS()
+ : num_lookups(0),
+ data(NULL),
+ length(0) {
+ }
+
+ // Number of lookups in GPOS table
+ uint16_t num_lookups;
+
+ const uint8_t *data;
+ size_t length;
+};
+
+} // namespace ots
+
+#endif
+
diff --git a/gfx/ots/src/gsub.cc b/gfx/ots/src/gsub.cc
new file mode 100644
index 000000000..9baf2e88b
--- /dev/null
+++ b/gfx/ots/src/gsub.cc
@@ -0,0 +1,674 @@
+// Copyright (c) 2011 The Chromium 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 "gsub.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// GSUB - The Glyph Substitution Table
+// http://www.microsoft.com/typography/otspec/gsub.htm
+
+#define TABLE_NAME "GSUB"
+
+namespace {
+
+// The GSUB header size
+const size_t kGsubHeaderSize = 4 + 3 * 2;
+
+enum GSUB_TYPE {
+ GSUB_TYPE_SINGLE = 1,
+ GSUB_TYPE_MULTIPLE = 2,
+ GSUB_TYPE_ALTERNATE = 3,
+ GSUB_TYPE_LIGATURE = 4,
+ GSUB_TYPE_CONTEXT = 5,
+ GSUB_TYPE_CHANGING_CONTEXT = 6,
+ GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
+ GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
+ GSUB_TYPE_RESERVED = 9
+};
+
+// Lookup type parsers.
+bool ParseSingleSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseMutipleSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseAlternateSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseLigatureSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseContextSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseChainingContextSubstitution(const ots::Font *font,
+ const uint8_t *data,
+ const size_t length);
+bool ParseExtensionSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length);
+bool ParseReverseChainingContextSingleSubstitution(
+ const ots::Font *font, const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
+ {GSUB_TYPE_SINGLE, ParseSingleSubstitution},
+ {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution},
+ {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution},
+ {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution},
+ {GSUB_TYPE_CONTEXT, ParseContextSubstitution},
+ {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution},
+ {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution},
+ {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE,
+ ParseReverseChainingContextSingleSubstitution}
+};
+
+const ots::LookupSubtableParser kGsubLookupSubtableParser = {
+ arraysize(kGsubTypeParsers),
+ GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
+};
+
+// Lookup Type 1:
+// Single Substitution Subtable
+bool ParseSingleSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage = 0;
+
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage)) {
+ return OTS_FAILURE_MSG("Failed to read single subst table header");
+ }
+
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+ if (format == 1) {
+ // Parse SingleSubstFormat1
+ int16_t delta_glyph_id = 0;
+ if (!subtable.ReadS16(&delta_glyph_id)) {
+ return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table");
+ }
+ if (std::abs(delta_glyph_id) >= num_glyphs) {
+ return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
+ }
+ } else if (format == 2) {
+ // Parse SingleSubstFormat2
+ uint16_t glyph_count = 0;
+ if (!subtable.ReadU16(&glyph_count)) {
+ return OTS_FAILURE_MSG("Failed to read glyph cound in format 2 single subst table");
+ }
+ if (glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
+ }
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ uint16_t substitute = 0;
+ if (!subtable.ReadU16(&substitute)) {
+ return OTS_FAILURE_MSG("Failed to read substitution %d in format 2 single subst table", i);
+ }
+ if (substitute >= num_glyphs) {
+ return OTS_FAILURE_MSG("too large substitute: %u", substitute);
+ }
+ }
+ } else {
+ return OTS_FAILURE_MSG("Bad single subst table format %d", format);
+ }
+
+ if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %x", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table");
+ }
+
+ return true;
+}
+
+bool ParseSequenceTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t glyph_count = 0;
+ if (!subtable.ReadU16(&glyph_count)) {
+ return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
+ }
+ if (glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs);
+ }
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ uint16_t substitute = 0;
+ if (!subtable.ReadU16(&substitute)) {
+ return OTS_FAILURE_MSG("Failedt o read substitution %d in sequence table", i);
+ }
+ if (substitute >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad subsitution (%d) %d > %d", i, substitute, num_glyphs);
+ }
+ }
+
+ return true;
+}
+
+// Lookup Type 2:
+// Multiple Substitution Subtable
+bool ParseMutipleSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage = 0;
+ uint16_t sequence_count = 0;
+
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&sequence_count)) {
+ return OTS_FAILURE_MSG("Can't read header of multiple subst table");
+ }
+
+ if (format != 1) {
+ return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
+ }
+
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+ const unsigned sequence_end = static_cast<unsigned>(6) +
+ sequence_count * 2;
+ if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad segence end %d, in multiple subst", sequence_end);
+ }
+ for (unsigned i = 0; i < sequence_count; ++i) {
+ uint16_t offset_sequence = 0;
+ if (!subtable.ReadU16(&offset_sequence)) {
+ return OTS_FAILURE_MSG("Failed to read sequence offset for sequence %d", i);
+ }
+ if (offset_sequence < sequence_end || offset_sequence >= length) {
+ return OTS_FAILURE_MSG("Bad sequence offset %d for sequence %d", offset_sequence, i);
+ }
+ if (!ParseSequenceTable(font, data + offset_sequence, length - offset_sequence,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse sequence table %d", i);
+ }
+ }
+
+ if (offset_coverage < sequence_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table");
+ }
+
+ return true;
+}
+
+bool ParseAlternateSetTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t glyph_count = 0;
+ if (!subtable.ReadU16(&glyph_count)) {
+ return OTS_FAILURE_MSG("Failed to read alternate set header");
+ }
+ if (glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs);
+ }
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ uint16_t alternate = 0;
+ if (!subtable.ReadU16(&alternate)) {
+ return OTS_FAILURE_MSG("Can't read alternate %d", i);
+ }
+ if (alternate >= num_glyphs) {
+ return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
+ }
+ }
+ return true;
+}
+
+// Lookup Type 3:
+// Alternate Substitution Subtable
+bool ParseAlternateSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage = 0;
+ uint16_t alternate_set_count = 0;
+
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&alternate_set_count)) {
+ return OTS_FAILURE_MSG("Can't read alternate subst header");
+ }
+
+ if (format != 1) {
+ return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
+ }
+
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+ const unsigned alternate_set_end = static_cast<unsigned>(6) +
+ alternate_set_count * 2;
+ if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end);
+ }
+ for (unsigned i = 0; i < alternate_set_count; ++i) {
+ uint16_t offset_alternate_set = 0;
+ if (!subtable.ReadU16(&offset_alternate_set)) {
+ return OTS_FAILURE_MSG("Can't read alternate set offset for set %d", i);
+ }
+ if (offset_alternate_set < alternate_set_end ||
+ offset_alternate_set >= length) {
+ return OTS_FAILURE_MSG("Bad alternate set offset %d for set %d", offset_alternate_set, i);
+ }
+ if (!ParseAlternateSetTable(font, data + offset_alternate_set,
+ length - offset_alternate_set,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse alternate set");
+ }
+ }
+
+ if (offset_coverage < alternate_set_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table");
+ }
+
+ return true;
+}
+
+bool ParseLigatureTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t lig_glyph = 0;
+ uint16_t comp_count = 0;
+
+ if (!subtable.ReadU16(&lig_glyph) ||
+ !subtable.ReadU16(&comp_count)) {
+ return OTS_FAILURE_MSG("Failed to read ligatuer table header");
+ }
+
+ if (lig_glyph >= num_glyphs) {
+ return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph);
+ }
+ if (comp_count == 0 || comp_count > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad component count of %d", comp_count);
+ }
+ for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
+ uint16_t component = 0;
+ if (!subtable.ReadU16(&component)) {
+ return OTS_FAILURE_MSG("Can't read ligature component %d", i);
+ }
+ if (component >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component);
+ }
+ }
+
+ return true;
+}
+
+bool ParseLigatureSetTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t ligature_count = 0;
+
+ if (!subtable.ReadU16(&ligature_count)) {
+ return OTS_FAILURE_MSG("Can't read ligature count in ligature set");
+ }
+
+ const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
+ if (ligature_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end);
+ }
+ for (unsigned i = 0; i < ligature_count; ++i) {
+ uint16_t offset_ligature = 0;
+ if (!subtable.ReadU16(&offset_ligature)) {
+ return OTS_FAILURE_MSG("Failed to read ligature offset %d", i);
+ }
+ if (offset_ligature < ligature_end || offset_ligature >= length) {
+ return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i);
+ }
+ if (!ParseLigatureTable(font, data + offset_ligature, length - offset_ligature,
+ num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
+ }
+ }
+
+ return true;
+}
+
+// Lookup Type 4:
+// Ligature Substitution Subtable
+bool ParseLigatureSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage = 0;
+ uint16_t lig_set_count = 0;
+
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&lig_set_count)) {
+ return OTS_FAILURE_MSG("Failed to read ligature substitution header");
+ }
+
+ if (format != 1) {
+ return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
+ }
+
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+ const unsigned ligature_set_end = static_cast<unsigned>(6) +
+ lig_set_count * 2;
+ if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
+ }
+ for (unsigned i = 0; i < lig_set_count; ++i) {
+ uint16_t offset_ligature_set = 0;
+ if (!subtable.ReadU16(&offset_ligature_set)) {
+ return OTS_FAILURE_MSG("Can't read ligature set offset %d", i);
+ }
+ if (offset_ligature_set < ligature_set_end ||
+ offset_ligature_set >= length) {
+ return OTS_FAILURE_MSG("Bad ligature set offset %d for set %d", offset_ligature_set, i);
+ }
+ if (!ParseLigatureSetTable(font, data + offset_ligature_set,
+ length - offset_ligature_set, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse ligature set %d", i);
+ }
+ }
+
+ if (offset_coverage < ligature_set_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table");
+ }
+
+ return true;
+}
+
+// Lookup Type 5:
+// Contextual Substitution Subtable
+bool ParseContextSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs,
+ font->gsub->num_lookups);
+}
+
+// Lookup Type 6:
+// Chaining Contextual Substitution Subtable
+bool ParseChainingContextSubstitution(const ots::Font *font,
+ const uint8_t *data,
+ const size_t length) {
+ return ots::ParseChainingContextSubtable(font, data, length,
+ font->maxp->num_glyphs,
+ font->gsub->num_lookups);
+}
+
+// Lookup Type 7:
+// Extension Substition
+bool ParseExtensionSubstitution(const ots::Font *font,
+ const uint8_t *data, const size_t length) {
+ return ots::ParseExtensionSubtable(font, data, length,
+ &kGsubLookupSubtableParser);
+}
+
+// Lookup Type 8:
+// Reverse Chaining Contexual Single Substitution Subtable
+bool ParseReverseChainingContextSingleSubstitution(
+ const ots::Font *font, const uint8_t *data, const size_t length) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t offset_coverage = 0;
+
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&offset_coverage)) {
+ return OTS_FAILURE_MSG("Failed to read reverse chaining header");
+ }
+
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+
+ uint16_t backtrack_glyph_count = 0;
+ if (!subtable.ReadU16(&backtrack_glyph_count)) {
+ return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table");
+ }
+ if (backtrack_glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad backtrack glyph count of %d", backtrack_glyph_count);
+ }
+ std::vector<uint16_t> offsets_backtrack;
+ offsets_backtrack.reserve(backtrack_glyph_count);
+ for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+ uint16_t offset = 0;
+ if (!subtable.ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read backtrack offset %d", i);
+ }
+ offsets_backtrack.push_back(offset);
+ }
+
+ uint16_t lookahead_glyph_count = 0;
+ if (!subtable.ReadU16(&lookahead_glyph_count)) {
+ return OTS_FAILURE_MSG("Failed to read look ahead glyph count");
+ }
+ if (lookahead_glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad look ahead glyph count %d", lookahead_glyph_count);
+ }
+ std::vector<uint16_t> offsets_lookahead;
+ offsets_lookahead.reserve(lookahead_glyph_count);
+ for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+ uint16_t offset = 0;
+ if (!subtable.ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Can't read look ahead offset %d", i);
+ }
+ offsets_lookahead.push_back(offset);
+ }
+
+ uint16_t glyph_count = 0;
+ if (!subtable.ReadU16(&glyph_count)) {
+ return OTS_FAILURE_MSG("Can't read glyph count in reverse chaining table");
+ }
+ if (glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph count of %d", glyph_count);
+ }
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ uint16_t substitute = 0;
+ if (!subtable.ReadU16(&substitute)) {
+ return OTS_FAILURE_MSG("Failed to read substitution %d reverse chaining table", i);
+ }
+ if (substitute >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
+ }
+ }
+
+ const unsigned substitute_end = static_cast<unsigned>(10) +
+ (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
+ if (substitute_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad substitute end offset in reverse chaining table");
+ }
+
+ if (offset_coverage < substitute_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d in reverse chaining table", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table in reverse chaining table");
+ }
+
+ for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+ if (offsets_backtrack[i] < substitute_end ||
+ offsets_backtrack[i] >= length) {
+ return OTS_FAILURE_MSG("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
+ }
+ if (!ots::ParseCoverageTable(font, data + offsets_backtrack[i],
+ length - offsets_backtrack[i], num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
+ }
+ }
+
+ for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+ if (offsets_lookahead[i] < substitute_end ||
+ offsets_lookahead[i] >= length) {
+ return OTS_FAILURE_MSG("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
+ }
+ if (!ots::ParseCoverageTable(font, data + offsets_lookahead[i],
+ length - offsets_lookahead[i], num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in reverse chaining table", i);
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+// As far as I checked, following fonts contain invalid values in GSUB table.
+// OTS will drop their GSUB table.
+//
+// # too large substitute (value is 0xFFFF)
+// kaiu.ttf
+// mingliub2.ttf
+// mingliub1.ttf
+// mingliub0.ttf
+// GraublauWeb.otf
+// GraublauWebBold.otf
+//
+// # too large alternate (value is 0xFFFF)
+// ManchuFont.ttf
+//
+// # bad offset to lang sys table (NULL offset)
+// DejaVuMonoSansBold.ttf
+// DejaVuMonoSansBoldOblique.ttf
+// DejaVuMonoSansOblique.ttf
+// DejaVuSansMono-BoldOblique.ttf
+// DejaVuSansMono-Oblique.ttf
+// DejaVuSansMono-Bold.ttf
+//
+// # bad start coverage index
+// GenBasBI.ttf
+// GenBasI.ttf
+// AndBasR.ttf
+// GenBkBasI.ttf
+// CharisSILR.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILB.ttf
+// DoulosSILR.ttf
+// CharisSILBI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// GenBkBasBI.ttf
+// GenBasB.ttf
+// GenBasR.ttf
+//
+// # glyph range is overlapping
+// KacstTitleL.ttf
+// KacstDecorative.ttf
+// KacstTitle.ttf
+// KacstArt.ttf
+// KacstPoster.ttf
+// KacstQurn.ttf
+// KacstDigital.ttf
+// KacstBook.ttf
+// KacstFarsi.ttf
+
+bool ots_gsub_parse(Font *font, const uint8_t *data, size_t length) {
+ // Parsing gsub table requires |font->maxp->num_glyphs|
+ if (!font->maxp) {
+ return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
+ }
+
+ Buffer table(data, length);
+
+ OpenTypeGSUB *gsub = new OpenTypeGSUB;
+ font->gsub = gsub;
+
+ uint32_t version = 0;
+ uint16_t offset_script_list = 0;
+ uint16_t offset_feature_list = 0;
+ uint16_t offset_lookup_list = 0;
+ if (!table.ReadU32(&version) ||
+ !table.ReadU16(&offset_script_list) ||
+ !table.ReadU16(&offset_feature_list) ||
+ !table.ReadU16(&offset_lookup_list)) {
+ return OTS_FAILURE_MSG("Incomplete table");
+ }
+
+ if (version != 0x00010000) {
+ return OTS_FAILURE_MSG("Bad version");
+ }
+
+ if (offset_lookup_list) {
+ if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
+ return OTS_FAILURE_MSG("Bad lookup list offset in table header");
+ }
+
+ if (!ParseLookupListTable(font, data + offset_lookup_list,
+ length - offset_lookup_list,
+ &kGsubLookupSubtableParser,
+ &gsub->num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup list table");
+ }
+ }
+
+ uint16_t num_features = 0;
+ if (offset_feature_list) {
+ if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
+ return OTS_FAILURE_MSG("Bad feature list offset in table header");
+ }
+
+ if (!ParseFeatureListTable(font, data + offset_feature_list,
+ length - offset_feature_list, gsub->num_lookups,
+ &num_features)) {
+ return OTS_FAILURE_MSG("Failed to parse feature list table");
+ }
+ }
+
+ if (offset_script_list) {
+ if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
+ return OTS_FAILURE_MSG("Bad script list offset in table header");
+ }
+
+ if (!ParseScriptListTable(font, data + offset_script_list,
+ length - offset_script_list, num_features)) {
+ return OTS_FAILURE_MSG("Failed to parse script list table");
+ }
+ }
+
+ gsub->data = data;
+ gsub->length = length;
+ return true;
+}
+
+bool ots_gsub_should_serialise(Font *font) {
+ return font->gsub != NULL && font->gsub->data != NULL;
+}
+
+bool ots_gsub_serialise(OTSStream *out, Font *font) {
+ if (!out->Write(font->gsub->data, font->gsub->length)) {
+ return OTS_FAILURE_MSG("Failed to write GSUB table");
+ }
+
+ return true;
+}
+
+void ots_gsub_reuse(Font *font, Font *other) {
+ font->gsub = other->gsub;
+ font->gsub_reused = true;
+}
+
+void ots_gsub_free(Font *font) {
+ delete font->gsub;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/gsub.h b/gfx/ots/src/gsub.h
new file mode 100644
index 000000000..f6f8cd3b1
--- /dev/null
+++ b/gfx/ots/src/gsub.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium 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_GSUB_H_
+#define OTS_GSUB_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGSUB {
+ OpenTypeGSUB()
+ : num_lookups(0),
+ data(NULL),
+ length(0) {
+ }
+
+ // Number of lookups in GPSUB table
+ uint16_t num_lookups;
+
+ const uint8_t *data;
+ size_t length;
+};
+
+} // namespace ots
+
+#endif // OTS_GSUB_H_
+
diff --git a/gfx/ots/src/hdmx.cc b/gfx/ots/src/hdmx.cc
new file mode 100644
index 000000000..f57b71f59
--- /dev/null
+++ b/gfx/ots/src/hdmx.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2009 The Chromium 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 "hdmx.h"
+#include "head.h"
+#include "maxp.h"
+
+// hdmx - Horizontal Device Metrics
+// http://www.microsoft.com/typography/otspec/hdmx.htm
+
+#define TABLE_NAME "hdmx"
+
+#define DROP_THIS_TABLE(...) \
+ do { \
+ OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
+ OTS_FAILURE_MSG("Table discarded"); \
+ delete font->hdmx; \
+ font->hdmx = 0; \
+ } while (0)
+
+namespace ots {
+
+bool ots_hdmx_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ font->hdmx = new OpenTypeHDMX;
+ OpenTypeHDMX * const hdmx = font->hdmx;
+
+ if (!font->head || !font->maxp) {
+ return OTS_FAILURE_MSG("Missing maxp or head tables in font, needed by hdmx");
+ }
+
+ if ((font->head->flags & 0x14) == 0) {
+ // http://www.microsoft.com/typography/otspec/recom.htm
+ DROP_THIS_TABLE("the table should not be present when bit 2 and 4 of the "
+ "head->flags are not set");
+ return true;
+ }
+
+ int16_t num_recs;
+ if (!table.ReadU16(&hdmx->version) ||
+ !table.ReadS16(&num_recs) ||
+ !table.ReadS32(&hdmx->size_device_record)) {
+ return OTS_FAILURE_MSG("Failed to read hdmx header");
+ }
+ if (hdmx->version != 0) {
+ DROP_THIS_TABLE("bad version: %u", hdmx->version);
+ return true;
+ }
+ if (num_recs <= 0) {
+ DROP_THIS_TABLE("bad num_recs: %d", num_recs);
+ return true;
+ }
+ const int32_t actual_size_device_record = font->maxp->num_glyphs + 2;
+ if (hdmx->size_device_record < actual_size_device_record) {
+ DROP_THIS_TABLE("bad hdmx->size_device_record: %d", hdmx->size_device_record);
+ return true;
+ }
+
+ hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
+ if (hdmx->pad_len > 3) {
+ return OTS_FAILURE_MSG("Bad padding %d", hdmx->pad_len);
+ }
+
+ uint8_t last_pixel_size = 0;
+ hdmx->records.reserve(num_recs);
+ for (int i = 0; i < num_recs; ++i) {
+ OpenTypeHDMXDeviceRecord rec;
+
+ if (!table.ReadU8(&rec.pixel_size) ||
+ !table.ReadU8(&rec.max_width)) {
+ return OTS_FAILURE_MSG("Failed to read hdmx record %d", i);
+ }
+ if ((i != 0) &&
+ (rec.pixel_size <= last_pixel_size)) {
+ DROP_THIS_TABLE("records are not sorted");
+ return true;
+ }
+ last_pixel_size = rec.pixel_size;
+
+ rec.widths.reserve(font->maxp->num_glyphs);
+ for (unsigned j = 0; j < font->maxp->num_glyphs; ++j) {
+ uint8_t width;
+ if (!table.ReadU8(&width)) {
+ return OTS_FAILURE_MSG("Failed to read glyph width %d in record %d", j, i);
+ }
+ rec.widths.push_back(width);
+ }
+
+ if ((hdmx->pad_len > 0) &&
+ !table.Skip(hdmx->pad_len)) {
+ return OTS_FAILURE_MSG("Failed to skip padding %d", hdmx->pad_len);
+ }
+
+ hdmx->records.push_back(rec);
+ }
+
+ return true;
+}
+
+bool ots_hdmx_should_serialise(Font *font) {
+ if (!font->hdmx) return false;
+ if (!font->glyf) return false; // this table is not for CFF fonts.
+ return true;
+}
+
+bool ots_hdmx_serialise(OTSStream *out, Font *font) {
+ OpenTypeHDMX * const hdmx = font->hdmx;
+
+ const int16_t num_recs = static_cast<int16_t>(hdmx->records.size());
+ if (hdmx->records.size() >
+ static_cast<size_t>(std::numeric_limits<int16_t>::max()) ||
+ !out->WriteU16(hdmx->version) ||
+ !out->WriteS16(num_recs) ||
+ !out->WriteS32(hdmx->size_device_record)) {
+ return OTS_FAILURE_MSG("Failed to write hdmx header");
+ }
+
+ for (int16_t i = 0; i < num_recs; ++i) {
+ const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
+ if (!out->Write(&rec.pixel_size, 1) ||
+ !out->Write(&rec.max_width, 1) ||
+ !out->Write(&rec.widths[0], rec.widths.size())) {
+ return OTS_FAILURE_MSG("Failed to write hdmx record %d", i);
+ }
+ if ((hdmx->pad_len > 0) &&
+ !out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
+ return OTS_FAILURE_MSG("Failed to write hdmx padding of length %d", hdmx->pad_len);
+ }
+ }
+
+ return true;
+}
+
+void ots_hdmx_reuse(Font *font, Font *other) {
+ font->hdmx = other->hdmx;
+ font->hdmx_reused = true;
+}
+
+void ots_hdmx_free(Font *font) {
+ delete font->hdmx;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/gfx/ots/src/hdmx.h b/gfx/ots/src/hdmx.h
new file mode 100644
index 000000000..9ec212437
--- /dev/null
+++ b/gfx/ots/src/hdmx.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium 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_HDMX_H_
+#define OTS_HDMX_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHDMXDeviceRecord {
+ uint8_t pixel_size;
+ uint8_t max_width;
+ std::vector<uint8_t> widths;
+};
+
+struct OpenTypeHDMX {
+ uint16_t version;
+ int32_t size_device_record;
+ int32_t pad_len;
+ std::vector<OpenTypeHDMXDeviceRecord> records;
+};
+
+} // namespace ots
+
+#endif
diff --git a/gfx/ots/src/head.cc b/gfx/ots/src/head.cc
new file mode 100644
index 000000000..229ea71f9
--- /dev/null
+++ b/gfx/ots/src/head.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2009 The Chromium 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 "head.h"
+
+#include <cstring>
+
+// head - Font Header
+// http://www.microsoft.com/typography/otspec/head.htm
+
+#define TABLE_NAME "head"
+
+namespace ots {
+
+bool ots_head_parse(Font* font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ OpenTypeHEAD *head = new OpenTypeHEAD;
+ font->head = head;
+
+ uint32_t version = 0;
+ if (!table.ReadU32(&version) ||
+ !table.ReadU32(&head->revision)) {
+ return OTS_FAILURE_MSG("Failed to read head header");
+ }
+
+ if (version >> 16 != 1) {
+ return OTS_FAILURE_MSG("Bad head table version of %d", version);
+ }
+
+ // Skip the checksum adjustment
+ if (!table.Skip(4)) {
+ return OTS_FAILURE_MSG("Failed to read checksum");
+ }
+
+ uint32_t magic;
+ if (!table.ReadU32(&magic) || magic != 0x5F0F3CF5) {
+ return OTS_FAILURE_MSG("Failed to read font magic number");
+ }
+
+ if (!table.ReadU16(&head->flags)) {
+ return OTS_FAILURE_MSG("Failed to read head flags");
+ }
+
+ // We allow bits 0..4, 11..13
+ head->flags &= 0x381f;
+
+ if (!table.ReadU16(&head->ppem)) {
+ return OTS_FAILURE_MSG("Failed to read pixels per em");
+ }
+
+ // ppem must be in range
+ if (head->ppem < 16 ||
+ head->ppem > 16384) {
+ return OTS_FAILURE_MSG("Bad ppm of %d", head->ppem);
+ }
+
+ // ppem must be a power of two
+#if 0
+ // We don't call ots_failure() for now since lots of TrueType fonts are
+ // not following this rule. Putting OTS_WARNING here is too noisy.
+ if ((head->ppem - 1) & head->ppem) {
+ return OTS_FAILURE_MSG("ppm not a power of two: %d", head->ppem);
+ }
+#endif
+
+ if (!table.ReadR64(&head->created) ||
+ !table.ReadR64(&head->modified)) {
+ return OTS_FAILURE_MSG("Can't read font dates");
+ }
+
+ if (!table.ReadS16(&head->xmin) ||
+ !table.ReadS16(&head->ymin) ||
+ !table.ReadS16(&head->xmax) ||
+ !table.ReadS16(&head->ymax)) {
+ return OTS_FAILURE_MSG("Failed to read font bounding box");
+ }
+
+ if (head->xmin > head->xmax) {
+ return OTS_FAILURE_MSG("Bad x dimension in the font bounding box (%d, %d)", head->xmin, head->xmax);
+ }
+ if (head->ymin > head->ymax) {
+ return OTS_FAILURE_MSG("Bad y dimension in the font bounding box (%d, %d)", head->ymin, head->ymax);
+ }
+
+ if (!table.ReadU16(&head->mac_style)) {
+ return OTS_FAILURE_MSG("Failed to read font style");
+ }
+
+ // We allow bits 0..6
+ head->mac_style &= 0x7f;
+
+ if (!table.ReadU16(&head->min_ppem)) {
+ return OTS_FAILURE_MSG("Failed to read font minimum ppm");
+ }
+
+ // We don't care about the font direction hint
+ if (!table.Skip(2)) {
+ return OTS_FAILURE_MSG("Failed to skip font direction hint");
+ }
+
+ if (!table.ReadS16(&head->index_to_loc_format)) {
+ return OTS_FAILURE_MSG("Failed to read index to loc format");
+ }
+ if (head->index_to_loc_format < 0 ||
+ head->index_to_loc_format > 1) {
+ return OTS_FAILURE_MSG("Bad index to loc format %d", head->index_to_loc_format);
+ }
+
+ int16_t glyph_data_format;
+ if (!table.ReadS16(&glyph_data_format) ||
+ glyph_data_format) {
+ return OTS_FAILURE_MSG("Failed to read glyph data format");
+ }
+
+ return true;
+}
+
+bool ots_head_should_serialise(Font *font) {
+ return font->head != NULL;
+}
+
+bool ots_head_serialise(OTSStream *out, Font *font) {
+ const OpenTypeHEAD *head = font->head;
+ if (!out->WriteU32(0x00010000) ||
+ !out->WriteU32(head->revision) ||
+ !out->WriteU32(0) || // check sum not filled in yet
+ !out->WriteU32(0x5F0F3CF5) ||
+ !out->WriteU16(head->flags) ||
+ !out->WriteU16(head->ppem) ||
+ !out->WriteR64(head->created) ||
+ !out->WriteR64(head->modified) ||
+ !out->WriteS16(head->xmin) ||
+ !out->WriteS16(head->ymin) ||
+ !out->WriteS16(head->xmax) ||
+ !out->WriteS16(head->ymax) ||
+ !out->WriteU16(head->mac_style) ||
+ !out->WriteU16(head->min_ppem) ||
+ !out->WriteS16(2) ||
+ !out->WriteS16(head->index_to_loc_format) ||
+ !out->WriteS16(0)) {
+ return OTS_FAILURE_MSG("Failed to write head table");
+ }
+
+ return true;
+}
+
+void ots_head_reuse(Font *font, Font *other) {
+ font->head = other->head;
+ font->head_reused = true;
+}
+
+void ots_head_free(Font *font) {
+ delete font->head;
+}
+
+} // namespace
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/head.h b/gfx/ots/src/head.h
new file mode 100644
index 000000000..5967c4b89
--- /dev/null
+++ b/gfx/ots/src/head.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium 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_HEAD_H_
+#define OTS_HEAD_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHEAD {
+ uint32_t revision;
+ uint16_t flags;
+ uint16_t ppem;
+ uint64_t created;
+ uint64_t modified;
+
+ int16_t xmin, xmax;
+ int16_t ymin, ymax;
+
+ uint16_t mac_style;
+ uint16_t min_ppem;
+ int16_t index_to_loc_format;
+};
+
+} // namespace ots
+
+#endif // OTS_HEAD_H_
diff --git a/gfx/ots/src/hhea.cc b/gfx/ots/src/hhea.cc
new file mode 100644
index 000000000..624287280
--- /dev/null
+++ b/gfx/ots/src/hhea.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2009 The Chromium 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 "hhea.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// hhea - Horizontal Header
+// http://www.microsoft.com/typography/otspec/hhea.htm
+
+#define TABLE_NAME "hhea"
+
+namespace ots {
+
+bool ots_hhea_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ OpenTypeHHEA *hhea = new OpenTypeHHEA;
+ font->hhea = hhea;
+
+ if (!table.ReadU32(&hhea->header.version)) {
+ return OTS_FAILURE_MSG("Failed to read hhea version");
+ }
+ if (hhea->header.version >> 16 != 1) {
+ return OTS_FAILURE_MSG("Bad hhea version of %d", hhea->header.version);
+ }
+
+ if (!ParseMetricsHeader(font, &table, &hhea->header)) {
+ return OTS_FAILURE_MSG("Failed to parse horizontal metrics");
+ }
+
+ return true;
+}
+
+bool ots_hhea_should_serialise(Font *font) {
+ return font->hhea != NULL;
+}
+
+bool ots_hhea_serialise(OTSStream *out, Font *font) {
+ if (!SerialiseMetricsHeader(font, out, &font->hhea->header)) {
+ return OTS_FAILURE_MSG("Failed to serialise horizontal metrics");
+ }
+ return true;
+}
+
+void ots_hhea_reuse(Font *font, Font *other) {
+ font->hhea = other->hhea;
+ font->hhea_reused = true;
+}
+
+void ots_hhea_free(Font *font) {
+ delete font->hhea;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/hhea.h b/gfx/ots/src/hhea.h
new file mode 100644
index 000000000..bdea9aa0d
--- /dev/null
+++ b/gfx/ots/src/hhea.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium 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_HHEA_H_
+#define OTS_HHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHHEA {
+ OpenTypeMetricsHeader header;
+};
+
+} // namespace ots
+
+#endif // OTS_HHEA_H_
diff --git a/gfx/ots/src/hmtx.cc b/gfx/ots/src/hmtx.cc
new file mode 100644
index 000000000..667d1fe6f
--- /dev/null
+++ b/gfx/ots/src/hmtx.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2009 The Chromium 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 "hmtx.h"
+
+#include "hhea.h"
+#include "maxp.h"
+
+// hmtx - Horizontal Metrics
+// http://www.microsoft.com/typography/otspec/hmtx.htm
+
+#define TABLE_NAME "hmtx"
+
+namespace ots {
+
+bool ots_hmtx_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ OpenTypeHMTX *hmtx = new OpenTypeHMTX;
+ font->hmtx = hmtx;
+
+ if (!font->hhea || !font->maxp) {
+ return OTS_FAILURE_MSG("Missing hhea or maxp tables in font, needed by hmtx");
+ }
+
+ if (!ParseMetricsTable(font, &table, font->maxp->num_glyphs,
+ &font->hhea->header, &hmtx->metrics)) {
+ return OTS_FAILURE_MSG("Failed to parse hmtx metrics");
+ }
+
+ return true;
+}
+
+bool ots_hmtx_should_serialise(Font *font) {
+ return font->hmtx != NULL;
+}
+
+bool ots_hmtx_serialise(OTSStream *out, Font *font) {
+ if (!SerialiseMetricsTable(font, out, &font->hmtx->metrics)) {
+ return OTS_FAILURE_MSG("Failed to serialise htmx metrics");
+ }
+ return true;
+}
+
+void ots_hmtx_reuse(Font *font, Font *other) {
+ font->hmtx = other->hmtx;
+ font->hmtx_reused = true;
+}
+
+void ots_hmtx_free(Font *font) {
+ delete font->hmtx;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/hmtx.h b/gfx/ots/src/hmtx.h
new file mode 100644
index 000000000..435949c5e
--- /dev/null
+++ b/gfx/ots/src/hmtx.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium 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_HMTX_H_
+#define OTS_HMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHMTX {
+ OpenTypeMetricsTable metrics;
+};
+
+} // namespace ots
+
+#endif // OTS_HMTX_H_
diff --git a/gfx/ots/src/kern.cc b/gfx/ots/src/kern.cc
new file mode 100644
index 000000000..d4ae8fcc4
--- /dev/null
+++ b/gfx/ots/src/kern.cc
@@ -0,0 +1,208 @@
+// Copyright (c) 2009 The Chromium 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 "kern.h"
+
+// kern - Kerning
+// http://www.microsoft.com/typography/otspec/kern.htm
+
+#define TABLE_NAME "kern"
+
+#define DROP_THIS_TABLE(msg_) \
+ do { \
+ OTS_FAILURE_MSG(msg_ ", table discarded"); \
+ delete font->kern; \
+ font->kern = 0; \
+ } while (0)
+
+namespace ots {
+
+bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypeKERN *kern = new OpenTypeKERN;
+ font->kern = kern;
+
+ uint16_t num_tables = 0;
+ if (!table.ReadU16(&kern->version) ||
+ !table.ReadU16(&num_tables)) {
+ return OTS_FAILURE_MSG("Failed to read kern header");
+ }
+
+ if (kern->version > 0) {
+ DROP_THIS_TABLE("bad table version");
+ return true;
+ }
+
+ if (num_tables == 0) {
+ DROP_THIS_TABLE("num_tables is zero");
+ return true;
+ }
+
+ kern->subtables.reserve(num_tables);
+ for (unsigned i = 0; i < num_tables; ++i) {
+ OpenTypeKERNFormat0 subtable;
+ uint16_t sub_length = 0;
+
+ if (!table.ReadU16(&subtable.version) ||
+ !table.ReadU16(&sub_length)) {
+ return OTS_FAILURE_MSG("Failed to read kern subtable %d header", i);
+ }
+
+ if (subtable.version > 0) {
+ OTS_WARNING("Bad subtable version: %d", subtable.version);
+ continue;
+ }
+
+ const size_t current_offset = table.offset();
+ if (current_offset - 4 + sub_length > length) {
+ return OTS_FAILURE_MSG("Bad kern subtable %d offset %ld", i, current_offset);
+ }
+
+ if (!table.ReadU16(&subtable.coverage)) {
+ return OTS_FAILURE_MSG("Cailed to read kern subtable %d coverage", i);
+ }
+
+ if (!(subtable.coverage & 0x1)) {
+ OTS_WARNING(
+ "We don't support vertical data as the renderer doesn't support it.");
+ continue;
+ }
+ if (subtable.coverage & 0xF0) {
+ DROP_THIS_TABLE("Reserved fields should zero-filled");
+ return true;
+ }
+ const uint32_t format = (subtable.coverage & 0xFF00) >> 8;
+ if (format != 0) {
+ OTS_WARNING("Format %d is not supported.", format);
+ continue;
+ }
+
+ // Parse the format 0 field.
+ uint16_t num_pairs = 0;
+ if (!table.ReadU16(&num_pairs) ||
+ !table.ReadU16(&subtable.search_range) ||
+ !table.ReadU16(&subtable.entry_selector) ||
+ !table.ReadU16(&subtable.range_shift)) {
+ return OTS_FAILURE_MSG("Failed to read kern subtable %d format 0 fields", i);
+ }
+
+ if (!num_pairs) {
+ DROP_THIS_TABLE("Zero length subtable is found");
+ return true;
+ }
+
+ // Sanity checks for search_range, entry_selector, and range_shift. See the
+ // comment in ots.cc for details.
+ const size_t kFormat0PairSize = 6; // left, right, and value. 2 bytes each.
+ if (num_pairs > (65536 / kFormat0PairSize)) {
+ // Some fonts (e.g. calibri.ttf, pykes_peak_zero.ttf) have pairs >= 10923.
+ DROP_THIS_TABLE("Too large subtable");
+ return true;
+ }
+ unsigned max_pow2 = 0;
+ while (1u << (max_pow2 + 1) <= num_pairs) {
+ ++max_pow2;
+ }
+ const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize;
+ if (subtable.search_range != expected_search_range) {
+ OTS_WARNING("bad search range");
+ subtable.search_range = expected_search_range;
+ }
+ if (subtable.entry_selector != max_pow2) {
+ return OTS_FAILURE_MSG("Bad subtable %d entry selector %d", i, subtable.entry_selector);
+ }
+ const uint16_t expected_range_shift =
+ kFormat0PairSize * num_pairs - subtable.search_range;
+ if (subtable.range_shift != expected_range_shift) {
+ OTS_WARNING("bad range shift");
+ subtable.range_shift = expected_range_shift;
+ }
+
+ // Read kerning pairs.
+ subtable.pairs.reserve(num_pairs);
+ uint32_t last_pair = 0;
+ for (unsigned j = 0; j < num_pairs; ++j) {
+ OpenTypeKERNFormat0Pair kerning_pair;
+ if (!table.ReadU16(&kerning_pair.left) ||
+ !table.ReadU16(&kerning_pair.right) ||
+ !table.ReadS16(&kerning_pair.value)) {
+ return OTS_FAILURE_MSG("Failed to read subtable %d kerning pair %d", i, j);
+ }
+ const uint32_t current_pair
+ = (kerning_pair.left << 16) + kerning_pair.right;
+ if (j != 0 && current_pair <= last_pair) {
+ // Many free fonts don't follow this rule, so we don't call OTS_FAILURE
+ // in order to support these fonts.
+ DROP_THIS_TABLE("Kerning pairs are not sorted");
+ return true;
+ }
+ last_pair = current_pair;
+ subtable.pairs.push_back(kerning_pair);
+ }
+
+ kern->subtables.push_back(subtable);
+ }
+
+ if (!kern->subtables.size()) {
+ DROP_THIS_TABLE("All subtables are removed");
+ return true;
+ }
+
+ return true;
+}
+
+bool ots_kern_should_serialise(Font *font) {
+ if (!font->glyf) return false; // this table is not for CFF fonts.
+ return font->kern != NULL;
+}
+
+bool ots_kern_serialise(OTSStream *out, Font *font) {
+ const OpenTypeKERN *kern = font->kern;
+
+ const uint16_t num_subtables = static_cast<uint16_t>(kern->subtables.size());
+ if (num_subtables != kern->subtables.size() ||
+ !out->WriteU16(kern->version) ||
+ !out->WriteU16(num_subtables)) {
+ return OTS_FAILURE_MSG("Can't write kern table header");
+ }
+
+ for (uint16_t i = 0; i < num_subtables; ++i) {
+ const size_t length = 14 + (6 * kern->subtables[i].pairs.size());
+ if (length > std::numeric_limits<uint16_t>::max() ||
+ !out->WriteU16(kern->subtables[i].version) ||
+ !out->WriteU16(static_cast<uint16_t>(length)) ||
+ !out->WriteU16(kern->subtables[i].coverage) ||
+ !out->WriteU16(
+ static_cast<uint16_t>(kern->subtables[i].pairs.size())) ||
+ !out->WriteU16(kern->subtables[i].search_range) ||
+ !out->WriteU16(kern->subtables[i].entry_selector) ||
+ !out->WriteU16(kern->subtables[i].range_shift)) {
+ return OTS_FAILURE_MSG("Failed to write kern subtable %d", i);
+ }
+ for (unsigned j = 0; j < kern->subtables[i].pairs.size(); ++j) {
+ if (!out->WriteU16(kern->subtables[i].pairs[j].left) ||
+ !out->WriteU16(kern->subtables[i].pairs[j].right) ||
+ !out->WriteS16(kern->subtables[i].pairs[j].value)) {
+ return OTS_FAILURE_MSG("Failed to write kern pair %d for subtable %d", j, i);
+ }
+ }
+ }
+
+ return true;
+}
+
+void ots_kern_reuse(Font *font, Font *other) {
+ font->kern = other->kern;
+ font->kern_reused = true;
+}
+
+void ots_kern_free(Font *font) {
+ delete font->kern;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/gfx/ots/src/kern.h b/gfx/ots/src/kern.h
new file mode 100644
index 000000000..9350ef7f8
--- /dev/null
+++ b/gfx/ots/src/kern.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium 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_KERN_H_
+#define OTS_KERN_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeKERNFormat0Pair {
+ uint16_t left;
+ uint16_t right;
+ int16_t value;
+};
+
+struct OpenTypeKERNFormat0 {
+ uint16_t version;
+ uint16_t coverage;
+ uint16_t search_range;
+ uint16_t entry_selector;
+ uint16_t range_shift;
+ std::vector<OpenTypeKERNFormat0Pair> pairs;
+};
+
+// Format 2 is not supported. Since the format is not supported by Windows,
+// WebFonts unlikely use it. I've checked thousands of proprietary fonts and
+// free fonts, and found no font uses the format.
+
+struct OpenTypeKERN {
+ uint16_t version;
+ std::vector<OpenTypeKERNFormat0> subtables;
+};
+
+} // namespace ots
+
+#endif // OTS_KERN_H_
diff --git a/gfx/ots/src/layout.cc b/gfx/ots/src/layout.cc
new file mode 100644
index 000000000..1d87b53d4
--- /dev/null
+++ b/gfx/ots/src/layout.cc
@@ -0,0 +1,1521 @@
+// Copyright (c) 2011 The Chromium 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 <limits>
+#include <vector>
+
+#include "gdef.h"
+
+// OpenType Layout Common Table Formats
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+#define TABLE_NAME "Layout" // XXX: use individual table names
+
+namespace {
+
+// The 'DFLT' tag of script table.
+const uint32_t kScriptTableTagDflt = 0x44464c54;
+// The value which represents there is no required feature index.
+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;
+
+struct ScriptRecord {
+ uint32_t tag;
+ uint16_t offset;
+};
+
+struct LangSysRecord {
+ uint32_t tag;
+ uint16_t offset;
+};
+
+struct FeatureRecord {
+ uint32_t tag;
+ uint16_t offset;
+};
+
+bool ParseLangSysTable(const ots::Font *font,
+ ots::Buffer *subtable, const uint32_t tag,
+ const uint16_t num_features) {
+ uint16_t offset_lookup_order = 0;
+ uint16_t req_feature_index = 0;
+ uint16_t feature_count = 0;
+ if (!subtable->ReadU16(&offset_lookup_order) ||
+ !subtable->ReadU16(&req_feature_index) ||
+ !subtable->ReadU16(&feature_count)) {
+ return OTS_FAILURE_MSG("Failed to read langsys header for tag %c%c%c%c", OTS_UNTAG(tag));
+ }
+ // |offset_lookup_order| is reserved and should be NULL.
+ if (offset_lookup_order != 0) {
+ return OTS_FAILURE_MSG("Bad lookup offset order %d for langsys tag %c%c%c%c", offset_lookup_order, OTS_UNTAG(tag));
+ }
+ if (req_feature_index != kNoRequiredFeatureIndexDefined &&
+ req_feature_index >= num_features) {
+ return OTS_FAILURE_MSG("Bad required features index %d for langsys tag %c%c%c%c", req_feature_index, OTS_UNTAG(tag));
+ }
+ if (feature_count > num_features) {
+ return OTS_FAILURE_MSG("Bad feature count %d for langsys tag %c%c%c%c", feature_count, OTS_UNTAG(tag));
+ }
+
+ for (unsigned i = 0; i < feature_count; ++i) {
+ uint16_t feature_index = 0;
+ if (!subtable->ReadU16(&feature_index)) {
+ return OTS_FAILURE_MSG("Failed to read feature index %d for langsys tag %c%c%c%c", i, OTS_UNTAG(tag));
+ }
+ if (feature_index >= num_features) {
+ return OTS_FAILURE_MSG("Bad feature index %d for feature %d for langsys tag %c%c%c%c", feature_index, i, OTS_UNTAG(tag));
+ }
+ }
+ return true;
+}
+
+bool ParseScriptTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint32_t tag, const uint16_t num_features) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t offset_default_lang_sys = 0;
+ uint16_t lang_sys_count = 0;
+ if (!subtable.ReadU16(&offset_default_lang_sys) ||
+ !subtable.ReadU16(&lang_sys_count)) {
+ return OTS_FAILURE_MSG("Failed to read script header for script tag %c%c%c%c", OTS_UNTAG(tag));
+ }
+
+ // The spec requires a script table for 'DFLT' tag must contain non-NULL
+ // |offset_default_lang_sys| and |lang_sys_count| == 0
+ // https://www.microsoft.com/typography/otspec/chapter2.htm
+ if (tag == kScriptTableTagDflt) {
+ if (offset_default_lang_sys == 0) {
+ return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. DefaultLangSys is NULL");
+ }
+ if (lang_sys_count != 0) {
+ return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. LangSysCount is not zero: %d", lang_sys_count);
+ }
+ }
+
+ const unsigned lang_sys_record_end =
+ 6 * static_cast<unsigned>(lang_sys_count) + 4;
+ if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of langsys record %d for script tag %c%c%c%c", lang_sys_record_end, OTS_UNTAG(tag));
+ }
+
+ std::vector<LangSysRecord> lang_sys_records;
+ lang_sys_records.resize(lang_sys_count);
+ uint32_t last_tag = 0;
+ for (unsigned i = 0; i < lang_sys_count; ++i) {
+ if (!subtable.ReadU32(&lang_sys_records[i].tag) ||
+ !subtable.ReadU16(&lang_sys_records[i].offset)) {
+ return OTS_FAILURE_MSG("Failed to read langsys record header %d for script tag %c%c%c%c", i, OTS_UNTAG(tag));
+ }
+ // The record array must store the records alphabetically by tag
+ if (last_tag != 0 && last_tag > lang_sys_records[i].tag) {
+ return OTS_FAILURE_MSG("Bad last tag %d for langsys record %d for script tag %c%c%c%c", last_tag, i, OTS_UNTAG(tag));
+ }
+ if (lang_sys_records[i].offset < lang_sys_record_end ||
+ lang_sys_records[i].offset >= length) {
+ return OTS_FAILURE_MSG("bad offset to lang sys table: %x",
+ lang_sys_records[i].offset);
+ }
+ last_tag = lang_sys_records[i].tag;
+ }
+
+ // Check lang sys tables
+ for (unsigned i = 0; i < lang_sys_count; ++i) {
+ subtable.set_offset(lang_sys_records[i].offset);
+ if (!ParseLangSysTable(font, &subtable, lang_sys_records[i].tag, num_features)) {
+ return OTS_FAILURE_MSG("Failed to parse langsys table %d (%c%c%c%c) for script tag %c%c%c%c", i, OTS_UNTAG(lang_sys_records[i].tag), OTS_UNTAG(tag));
+ }
+ }
+
+ return true;
+}
+
+bool ParseFeatureTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t offset_feature_params = 0;
+ uint16_t lookup_count = 0;
+ if (!subtable.ReadU16(&offset_feature_params) ||
+ !subtable.ReadU16(&lookup_count)) {
+ return OTS_FAILURE_MSG("Failed to read feature table header");
+ }
+
+ const unsigned feature_table_end =
+ 2 * static_cast<unsigned>(lookup_count) + 4;
+ if (feature_table_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of feature table %d", feature_table_end);
+ }
+ // |offset_feature_params| is generally set to NULL.
+ if (offset_feature_params != 0 &&
+ (offset_feature_params < feature_table_end ||
+ offset_feature_params >= length)) {
+ return OTS_FAILURE_MSG("Bad feature params offset %d", offset_feature_params);
+ }
+
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ uint16_t lookup_index = 0;
+ if (!subtable.ReadU16(&lookup_index)) {
+ return OTS_FAILURE_MSG("Failed to read lookup index for lookup %d", i);
+ }
+ // lookup index starts with 0.
+ if (lookup_index >= num_lookups) {
+ return OTS_FAILURE_MSG("Bad lookup index %d for lookup %d", lookup_index, i);
+ }
+ }
+ return true;
+}
+
+bool ParseLookupTable(ots::Font *font, const uint8_t *data,
+ const size_t length,
+ const ots::LookupSubtableParser* parser) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t lookup_type = 0;
+ uint16_t lookup_flag = 0;
+ uint16_t subtable_count = 0;
+ if (!subtable.ReadU16(&lookup_type) ||
+ !subtable.ReadU16(&lookup_flag) ||
+ !subtable.ReadU16(&subtable_count)) {
+ return OTS_FAILURE_MSG("Failed to read lookup table header");
+ }
+
+ if (lookup_type == 0 || lookup_type > parser->num_types) {
+ return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
+ }
+
+ // Check lookup flags.
+ if ((lookup_flag & kGdefRequiredFlags) &&
+ (!font->gdef || !font->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) &&
+ (!font->gdef || !font->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 (!font->gdef || !font->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;
+ }
+
+ std::vector<uint16_t> subtables;
+ subtables.reserve(subtable_count);
+ // If the |kUseMarkFilteringSetBit| of |lookup_flag| is set,
+ // extra 2 bytes will follow after subtable offset array.
+ const unsigned lookup_table_end = 2 * static_cast<unsigned>(subtable_count) +
+ (use_mark_filtering_set ? 8 : 6);
+ if (lookup_table_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of lookup %d", lookup_table_end);
+ }
+ for (unsigned i = 0; i < subtable_count; ++i) {
+ uint16_t offset_subtable = 0;
+ if (!subtable.ReadU16(&offset_subtable)) {
+ return OTS_FAILURE_MSG("Failed to read subtable offset %d", i);
+ }
+ if (offset_subtable < lookup_table_end ||
+ offset_subtable >= length) {
+ return OTS_FAILURE_MSG("Bad subtable offset %d for subtable %d", offset_subtable, i);
+ }
+ subtables.push_back(offset_subtable);
+ }
+ if (subtables.size() != subtable_count) {
+ return OTS_FAILURE_MSG("Bad subtable size %ld", subtables.size());
+ }
+
+ if (use_mark_filtering_set) {
+ uint16_t mark_filtering_set = 0;
+ if (!subtable.ReadU16(&mark_filtering_set)) {
+ return OTS_FAILURE_MSG("Failed to read mark filtering set");
+ }
+ if (font->gdef->num_mark_glyph_sets == 0 ||
+ mark_filtering_set >= font->gdef->num_mark_glyph_sets) {
+ return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
+ }
+ }
+
+ // Parse lookup subtables for this lookup type.
+ for (unsigned i = 0; i < subtable_count; ++i) {
+ if (!parser->Parse(font, data + subtables[i], length - subtables[i],
+ lookup_type)) {
+ return OTS_FAILURE_MSG("Failed to parse subtable %d", i);
+ }
+ }
+ return true;
+}
+
+bool ParseClassDefFormat1(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_classes) {
+ ots::Buffer subtable(data, length);
+
+ // Skip format field.
+ if (!subtable.Skip(2)) {
+ return OTS_FAILURE_MSG("Failed to skip class definition header");
+ }
+
+ uint16_t start_glyph = 0;
+ if (!subtable.ReadU16(&start_glyph)) {
+ return OTS_FAILURE_MSG("Failed to read starting glyph of class definition");
+ }
+ if (start_glyph > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad starting glyph %d in class definition", start_glyph);
+ }
+
+ uint16_t glyph_count = 0;
+ if (!subtable.ReadU16(&glyph_count)) {
+ return OTS_FAILURE_MSG("Failed to read glyph count in class definition");
+ }
+ if (glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count);
+ }
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ uint16_t class_value = 0;
+ if (!subtable.ReadU16(&class_value)) {
+ return OTS_FAILURE_MSG("Failed to read class value for glyph %d in class definition", i);
+ }
+ if (class_value > num_classes) {
+ return OTS_FAILURE_MSG("Bad class value %d for glyph %d in class definition", class_value, i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseClassDefFormat2(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_classes) {
+ ots::Buffer subtable(data, length);
+
+ // Skip format field.
+ if (!subtable.Skip(2)) {
+ return OTS_FAILURE_MSG("Failed to skip format of class defintion header");
+ }
+
+ uint16_t range_count = 0;
+ if (!subtable.ReadU16(&range_count)) {
+ return OTS_FAILURE_MSG("Failed to read range count in class definition");
+ }
+ if (range_count > num_glyphs) {
+ return OTS_FAILURE_MSG("bad range count: %u", range_count);
+ }
+
+ uint16_t last_end = 0;
+ for (unsigned i = 0; i < range_count; ++i) {
+ uint16_t start = 0;
+ uint16_t end = 0;
+ uint16_t class_value = 0;
+ if (!subtable.ReadU16(&start) ||
+ !subtable.ReadU16(&end) ||
+ !subtable.ReadU16(&class_value)) {
+ return OTS_FAILURE_MSG("Failed to read class definition reange %d", i);
+ }
+ if (start > end || (last_end && start <= last_end)) {
+ return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i);
+ }
+ if (class_value > num_classes) {
+ return OTS_FAILURE_MSG("bad class value: %u", class_value);
+ }
+ last_end = end;
+ }
+
+ return true;
+}
+
+bool ParseCoverageFormat1(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t expected_num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Skip format field.
+ if (!subtable.Skip(2)) {
+ return OTS_FAILURE_MSG("Failed to skip coverage format");
+ }
+
+ uint16_t glyph_count = 0;
+ if (!subtable.ReadU16(&glyph_count)) {
+ return OTS_FAILURE_MSG("Failed to read glyph count in coverage");
+ }
+ if (glyph_count > num_glyphs) {
+ return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count);
+ }
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ uint16_t glyph = 0;
+ if (!subtable.ReadU16(&glyph)) {
+ return OTS_FAILURE_MSG("Failed to read glyph %d in coverage", i);
+ }
+ if (glyph > num_glyphs) {
+ return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+ }
+ }
+
+ if (expected_num_glyphs && expected_num_glyphs != glyph_count) {
+ return OTS_FAILURE_MSG("unexpected number of glyphs: %u", glyph_count);
+ }
+
+ return true;
+}
+
+bool ParseCoverageFormat2(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t expected_num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Skip format field.
+ if (!subtable.Skip(2)) {
+ return OTS_FAILURE_MSG("Failed to skip format of coverage type 2");
+ }
+
+ uint16_t range_count = 0;
+ if (!subtable.ReadU16(&range_count)) {
+ return OTS_FAILURE_MSG("Failed to read range count in coverage");
+ }
+ if (range_count > num_glyphs) {
+ return OTS_FAILURE_MSG("bad range count: %u", range_count);
+ }
+ uint16_t last_end = 0;
+ uint16_t last_start_coverage_index = 0;
+ for (unsigned i = 0; i < range_count; ++i) {
+ uint16_t start = 0;
+ uint16_t end = 0;
+ uint16_t start_coverage_index = 0;
+ if (!subtable.ReadU16(&start) ||
+ !subtable.ReadU16(&end) ||
+ !subtable.ReadU16(&start_coverage_index)) {
+ return OTS_FAILURE_MSG("Failed to read range %d in coverage", i);
+ }
+
+ // Some of the Adobe Pro fonts have ranges that overlap by one element: the
+ // start of one range is equal to the end of the previous range. Therefore
+ // the < in the following condition should be <= were it not for this.
+ // See crbug.com/134135.
+ if (start > end || (last_end && start < last_end)) {
+ return OTS_FAILURE_MSG("glyph range is overlapping.");
+ }
+ if (start_coverage_index != last_start_coverage_index) {
+ return OTS_FAILURE_MSG("bad start coverage index.");
+ }
+ last_end = end;
+ last_start_coverage_index += end - start + 1;
+ }
+
+ if (expected_num_glyphs &&
+ expected_num_glyphs != last_start_coverage_index) {
+ return OTS_FAILURE_MSG("unexpected number of glyphs: %u", last_start_coverage_index);
+ }
+
+ return true;
+}
+
+// Parsers for Contextual subtables in GSUB/GPOS tables.
+
+bool ParseLookupRecord(const ots::Font *font,
+ ots::Buffer *subtable, const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ uint16_t sequence_index = 0;
+ uint16_t lookup_list_index = 0;
+ if (!subtable->ReadU16(&sequence_index) ||
+ !subtable->ReadU16(&lookup_list_index)) {
+ return OTS_FAILURE_MSG("Failed to read header for lookup record");
+ }
+ if (sequence_index >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad sequence index %d in lookup record", sequence_index);
+ }
+ if (lookup_list_index >= num_lookups) {
+ return OTS_FAILURE_MSG("Bad lookup list index %d in lookup record", lookup_list_index);
+ }
+ return true;
+}
+
+bool ParseRuleSubtable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t glyph_count = 0;
+ uint16_t lookup_count = 0;
+ if (!subtable.ReadU16(&glyph_count) ||
+ !subtable.ReadU16(&lookup_count)) {
+ return OTS_FAILURE_MSG("Failed to read rule subtable header");
+ }
+
+ if (glyph_count == 0 || glyph_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph count %d in rule subtable", glyph_count);
+ }
+ for (unsigned i = 0; i < glyph_count - static_cast<unsigned>(1); ++i) {
+ uint16_t glyph_id = 0;
+ if (!subtable.ReadU16(&glyph_id)) {
+ return OTS_FAILURE_MSG("Failed to read glyph %d", i);
+ }
+ if (glyph_id > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph %d for entry %d", glyph_id, i);
+ }
+ }
+
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ if (!ParseLookupRecord(font, &subtable, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup record %d", i);
+ }
+ }
+ return true;
+}
+
+bool ParseRuleSetTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t rule_count = 0;
+ if (!subtable.ReadU16(&rule_count)) {
+ return OTS_FAILURE_MSG("Failed to read rule count in rule set");
+ }
+ const unsigned rule_end = 2 * static_cast<unsigned>(rule_count) + 2;
+ if (rule_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of rule %d in rule set", rule_end);
+ }
+
+ for (unsigned i = 0; i < rule_count; ++i) {
+ uint16_t offset_rule = 0;
+ if (!subtable.ReadU16(&offset_rule)) {
+ return OTS_FAILURE_MSG("Failed to read rule offset for rule set %d", i);
+ }
+ if (offset_rule < rule_end || offset_rule >= length) {
+ return OTS_FAILURE_MSG("Bad rule offset %d in set %d", offset_rule, i);
+ }
+ if (!ParseRuleSubtable(font, data + offset_rule, length - offset_rule,
+ num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse rule set %d", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseContextFormat1(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t offset_coverage = 0;
+ uint16_t rule_set_count = 0;
+ // Skip format field.
+ if (!subtable.Skip(2) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&rule_set_count)) {
+ return OTS_FAILURE_MSG("Failed to read header of context format 1");
+ }
+
+ const unsigned rule_set_end = static_cast<unsigned>(6) +
+ rule_set_count * 2;
+ if (rule_set_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of rule set %d of context format 1", rule_set_end);
+ }
+ if (offset_coverage < rule_set_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d in context format 1", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table in context format 1");
+ }
+
+ for (unsigned i = 0; i < rule_set_count; ++i) {
+ uint16_t offset_rule = 0;
+ if (!subtable.ReadU16(&offset_rule)) {
+ return OTS_FAILURE_MSG("Failed to read rule offset %d in context format 1", i);
+ }
+ if (offset_rule < rule_set_end || offset_rule >= length) {
+ return OTS_FAILURE_MSG("Bad rule offset %d in rule %d in context format 1", offset_rule, i);
+ }
+ if (!ParseRuleSetTable(font, data + offset_rule, length - offset_rule,
+ num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse rule set %d in context format 1", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseClassRuleTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t glyph_count = 0;
+ uint16_t lookup_count = 0;
+ if (!subtable.ReadU16(&glyph_count) ||
+ !subtable.ReadU16(&lookup_count)) {
+ return OTS_FAILURE_MSG("Failed to read header of class rule table");
+ }
+
+ if (glyph_count == 0 || glyph_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph count %d in class rule table", glyph_count);
+ }
+
+ // ClassRule table contains an array of classes. Each value of classes
+ // could take arbitrary values including zero so we don't check these value.
+ const unsigned num_classes = glyph_count - static_cast<unsigned>(1);
+ if (!subtable.Skip(2 * num_classes)) {
+ return OTS_FAILURE_MSG("Failed to skip classes in class rule table");
+ }
+
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ if (!ParseLookupRecord(font, &subtable, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup record %d in class rule table", i);
+ }
+ }
+ return true;
+}
+
+bool ParseClassSetTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t class_rule_count = 0;
+ if (!subtable.ReadU16(&class_rule_count)) {
+ return OTS_FAILURE_MSG("Failed to read class rule count in class set table");
+ }
+ const unsigned class_rule_end =
+ 2 * static_cast<unsigned>(class_rule_count) + 2;
+ if (class_rule_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("bad class rule end %d in class set table", class_rule_end);
+ }
+ for (unsigned i = 0; i < class_rule_count; ++i) {
+ uint16_t offset_class_rule = 0;
+ if (!subtable.ReadU16(&offset_class_rule)) {
+ return OTS_FAILURE_MSG("Failed to read class rule offset %d in class set table", i);
+ }
+ if (offset_class_rule < class_rule_end || offset_class_rule >= length) {
+ return OTS_FAILURE_MSG("Bad class rule offset %d in class %d", offset_class_rule, i);
+ }
+ if (!ParseClassRuleTable(font, data + offset_class_rule,
+ length - offset_class_rule, num_glyphs,
+ num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse class rule table %d", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseContextFormat2(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t offset_coverage = 0;
+ uint16_t offset_class_def = 0;
+ uint16_t class_set_cnt = 0;
+ // Skip format field.
+ if (!subtable.Skip(2) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&offset_class_def) ||
+ !subtable.ReadU16(&class_set_cnt)) {
+ return OTS_FAILURE_MSG("Failed to read header for context format 2");
+ }
+
+ const unsigned class_set_end = 2 * static_cast<unsigned>(class_set_cnt) + 8;
+ if (class_set_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of class set %d for context format 2", class_set_end);
+ }
+ if (offset_coverage < class_set_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d in context format 2", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table in context format 2");
+ }
+
+ if (offset_class_def < class_set_end || offset_class_def >= length) {
+ return OTS_FAILURE_MSG("bad class definition offset %d in context format 2", offset_class_def);
+ }
+ if (!ots::ParseClassDefTable(font, data + offset_class_def,
+ length - offset_class_def,
+ num_glyphs, kMaxClassDefValue)) {
+ return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2");
+ }
+
+ for (unsigned i = 0; i < class_set_cnt; ++i) {
+ uint16_t offset_class_rule = 0;
+ if (!subtable.ReadU16(&offset_class_rule)) {
+ return OTS_FAILURE_MSG("Failed to read class rule offset %d in context format 2", i);
+ }
+ if (offset_class_rule) {
+ if (offset_class_rule < class_set_end || offset_class_rule >= length) {
+ return OTS_FAILURE_MSG("Bad class rule offset %d for rule %d in context format 2", offset_class_rule, i);
+ }
+ if (!ParseClassSetTable(font, data + offset_class_rule,
+ length - offset_class_rule, num_glyphs,
+ num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse class set %d in context format 2", i);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ParseContextFormat3(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t glyph_count = 0;
+ uint16_t lookup_count = 0;
+ // Skip format field.
+ if (!subtable.Skip(2) ||
+ !subtable.ReadU16(&glyph_count) ||
+ !subtable.ReadU16(&lookup_count)) {
+ return OTS_FAILURE_MSG("Failed to read header in context format 3");
+ }
+
+ if (glyph_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph count %d in context format 3", glyph_count);
+ }
+ const unsigned lookup_record_end = 2 * static_cast<unsigned>(glyph_count) +
+ 4 * static_cast<unsigned>(lookup_count) + 6;
+ if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of lookup %d in context format 3", lookup_record_end);
+ }
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ uint16_t offset_coverage = 0;
+ if (!subtable.ReadU16(&offset_coverage)) {
+ return OTS_FAILURE_MSG("Failed to read coverage offset %d in conxtext format 3", i);
+ }
+ if (offset_coverage < lookup_record_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d for glyph %d in context format 3", offset_coverage, i);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table for glyph %d in context format 3", i);
+ }
+ }
+
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ if (!ParseLookupRecord(font, &subtable, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup record %d in context format 3", i);
+ }
+ }
+
+ return true;
+}
+
+// Parsers for Chaning Contextual subtables in GSUB/GPOS tables.
+
+bool ParseChainRuleSubtable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t backtrack_count = 0;
+ if (!subtable.ReadU16(&backtrack_count)) {
+ return OTS_FAILURE_MSG("Failed to read backtrack count in chain rule subtable");
+ }
+ if (backtrack_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad backtrack count %d in chain rule subtable", backtrack_count);
+ }
+ for (unsigned i = 0; i < backtrack_count; ++i) {
+ uint16_t glyph_id = 0;
+ if (!subtable.ReadU16(&glyph_id)) {
+ return OTS_FAILURE_MSG("Failed to read backtrack glyph %d in chain rule subtable", i);
+ }
+ if (glyph_id > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph id %d for bactrack glyph %d in chain rule subtable", glyph_id, i);
+ }
+ }
+
+ uint16_t input_count = 0;
+ if (!subtable.ReadU16(&input_count)) {
+ return OTS_FAILURE_MSG("Failed to read input count in chain rule subtable");
+ }
+ if (input_count == 0 || input_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad input count %d in chain rule subtable", input_count);
+ }
+ for (unsigned i = 0; i < input_count - static_cast<unsigned>(1); ++i) {
+ uint16_t glyph_id = 0;
+ if (!subtable.ReadU16(&glyph_id)) {
+ return OTS_FAILURE_MSG("Failed to read input glyph %d in chain rule subtable", i);
+ }
+ if (glyph_id > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph id %d for input glyph %d in chain rule subtable", glyph_id, i);
+ }
+ }
+
+ uint16_t lookahead_count = 0;
+ if (!subtable.ReadU16(&lookahead_count)) {
+ return OTS_FAILURE_MSG("Failed to read lookahead count in chain rule subtable");
+ }
+ if (lookahead_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad lookahead count %d in chain rule subtable", lookahead_count);
+ }
+ for (unsigned i = 0; i < lookahead_count; ++i) {
+ uint16_t glyph_id = 0;
+ if (!subtable.ReadU16(&glyph_id)) {
+ return OTS_FAILURE_MSG("Failed to read lookahead glyph %d in chain rule subtable", i);
+ }
+ if (glyph_id > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad glyph id %d for lookadhead glyph %d in chain rule subtable", glyph_id, i);
+ }
+ }
+
+ uint16_t lookup_count = 0;
+ if (!subtable.ReadU16(&lookup_count)) {
+ return OTS_FAILURE_MSG("Failed to read lookup count in chain rule subtable");
+ }
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ if (!ParseLookupRecord(font, &subtable, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain rule subtable", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseChainRuleSetTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t chain_rule_count = 0;
+ if (!subtable.ReadU16(&chain_rule_count)) {
+ return OTS_FAILURE_MSG("Failed to read rule count in chain rule set");
+ }
+ const unsigned chain_rule_end =
+ 2 * static_cast<unsigned>(chain_rule_count) + 2;
+ if (chain_rule_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of chain rule %d in chain rule set", chain_rule_end);
+ }
+ for (unsigned i = 0; i < chain_rule_count; ++i) {
+ uint16_t offset_chain_rule = 0;
+ if (!subtable.ReadU16(&offset_chain_rule)) {
+ return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain rule set", i);
+ }
+ if (offset_chain_rule < chain_rule_end || offset_chain_rule >= length) {
+ return OTS_FAILURE_MSG("Bad chain rule offset %d for chain rule %d in chain rule set", offset_chain_rule, i);
+ }
+ if (!ParseChainRuleSubtable(font, data + offset_chain_rule,
+ length - offset_chain_rule,
+ num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse chain rule %d in chain rule set", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseChainContextFormat1(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t offset_coverage = 0;
+ uint16_t chain_rule_set_count = 0;
+ // Skip format field.
+ if (!subtable.Skip(2) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&chain_rule_set_count)) {
+ return OTS_FAILURE_MSG("Failed to read header of chain context format 1");
+ }
+
+ const unsigned chain_rule_set_end =
+ 2 * static_cast<unsigned>(chain_rule_set_count) + 6;
+ if (chain_rule_set_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad chain rule end %d in chain context format 1", chain_rule_set_end);
+ }
+ if (offset_coverage < chain_rule_set_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 1", chain_rule_set_end);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table for chain context format 1");
+ }
+
+ for (unsigned i = 0; i < chain_rule_set_count; ++i) {
+ uint16_t offset_chain_rule_set = 0;
+ if (!subtable.ReadU16(&offset_chain_rule_set)) {
+ return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain context format 1", i);
+ }
+ if (offset_chain_rule_set < chain_rule_set_end ||
+ offset_chain_rule_set >= length) {
+ return OTS_FAILURE_MSG("Bad chain rule set offset %d for chain rule set %d in chain context format 1", offset_chain_rule_set, i);
+ }
+ if (!ParseChainRuleSetTable(font, data + offset_chain_rule_set,
+ length - offset_chain_rule_set,
+ num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse chain rule set %d in chain context format 1", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseChainClassRuleSubtable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ // In this subtable, we don't check the value of classes for now since
+ // these could take arbitrary values.
+
+ uint16_t backtrack_count = 0;
+ if (!subtable.ReadU16(&backtrack_count)) {
+ return OTS_FAILURE_MSG("Failed to read backtrack count in chain class rule subtable");
+ }
+ if (backtrack_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad backtrack count %d in chain class rule subtable", backtrack_count);
+ }
+ if (!subtable.Skip(2 * backtrack_count)) {
+ return OTS_FAILURE_MSG("Failed to skip backtrack offsets in chain class rule subtable");
+ }
+
+ uint16_t input_count = 0;
+ if (!subtable.ReadU16(&input_count)) {
+ return OTS_FAILURE_MSG("Failed to read input count in chain class rule subtable");
+ }
+ if (input_count == 0 || input_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad input count %d in chain class rule subtable", input_count);
+ }
+ if (!subtable.Skip(2 * (input_count - 1))) {
+ return OTS_FAILURE_MSG("Failed to skip input offsets in chain class rule subtable");
+ }
+
+ uint16_t lookahead_count = 0;
+ if (!subtable.ReadU16(&lookahead_count)) {
+ return OTS_FAILURE_MSG("Failed to read lookahead count in chain class rule subtable");
+ }
+ if (lookahead_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad lookahead count %d in chain class rule subtable", lookahead_count);
+ }
+ if (!subtable.Skip(2 * lookahead_count)) {
+ return OTS_FAILURE_MSG("Failed to skip lookahead offsets in chain class rule subtable");
+ }
+
+ uint16_t lookup_count = 0;
+ if (!subtable.ReadU16(&lookup_count)) {
+ return OTS_FAILURE_MSG("Failed to read lookup count in chain class rule subtable");
+ }
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ if (!ParseLookupRecord(font, &subtable, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain class rule subtable", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseChainClassSetTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t chain_class_rule_count = 0;
+ if (!subtable.ReadU16(&chain_class_rule_count)) {
+ return OTS_FAILURE_MSG("Failed to read rule count in chain class set");
+ }
+ const unsigned chain_class_rule_end =
+ 2 * static_cast<unsigned>(chain_class_rule_count) + 2;
+ if (chain_class_rule_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of chain class set %d in chain class set", chain_class_rule_end);
+ }
+ for (unsigned i = 0; i < chain_class_rule_count; ++i) {
+ uint16_t offset_chain_class_rule = 0;
+ if (!subtable.ReadU16(&offset_chain_class_rule)) {
+ return OTS_FAILURE_MSG("Failed to read chain class rule offset %d in chain class set", i);
+ }
+ if (offset_chain_class_rule < chain_class_rule_end ||
+ offset_chain_class_rule >= length) {
+ return OTS_FAILURE_MSG("Bad chain class rule offset %d for chain class %d in chain class set", offset_chain_class_rule, i);
+ }
+ if (!ParseChainClassRuleSubtable(font, data + offset_chain_class_rule,
+ length - offset_chain_class_rule,
+ num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse chain class rule %d in chain class set", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseChainContextFormat2(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t offset_coverage = 0;
+ uint16_t offset_backtrack_class_def = 0;
+ uint16_t offset_input_class_def = 0;
+ uint16_t offset_lookahead_class_def = 0;
+ uint16_t chain_class_set_count = 0;
+ // Skip format field.
+ if (!subtable.Skip(2) ||
+ !subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&offset_backtrack_class_def) ||
+ !subtable.ReadU16(&offset_input_class_def) ||
+ !subtable.ReadU16(&offset_lookahead_class_def) ||
+ !subtable.ReadU16(&chain_class_set_count)) {
+ return OTS_FAILURE_MSG("Failed to read header of chain context format 2");
+ }
+
+ const unsigned chain_class_set_end =
+ 2 * static_cast<unsigned>(chain_class_set_count) + 12;
+ if (chain_class_set_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad chain class set end %d in chain context format 2", chain_class_set_end);
+ }
+ if (offset_coverage < chain_class_set_end || offset_coverage >= length) {
+ return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 2", offset_coverage);
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage, num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse coverage table in chain context format 2");
+ }
+
+ // Classes for backtrack/lookahead sequences might not be defined.
+ if (offset_backtrack_class_def) {
+ if (offset_backtrack_class_def < chain_class_set_end ||
+ offset_backtrack_class_def >= length) {
+ return OTS_FAILURE_MSG("Bad backtrack class offset %d in chain context format 2", offset_backtrack_class_def);
+ }
+ if (!ots::ParseClassDefTable(font, data + offset_backtrack_class_def,
+ length - offset_backtrack_class_def,
+ num_glyphs, kMaxClassDefValue)) {
+ return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2");
+ }
+ }
+
+ if (offset_input_class_def < chain_class_set_end ||
+ offset_input_class_def >= length) {
+ return OTS_FAILURE_MSG("Bad input class defn offset %d in chain context format 2", offset_input_class_def);
+ }
+ if (!ots::ParseClassDefTable(font, data + offset_input_class_def,
+ length - offset_input_class_def,
+ num_glyphs, kMaxClassDefValue)) {
+ return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2");
+ }
+
+ if (offset_lookahead_class_def) {
+ if (offset_lookahead_class_def < chain_class_set_end ||
+ offset_lookahead_class_def >= length) {
+ return OTS_FAILURE_MSG("Bad lookahead class defn offset %d in chain context format 2", offset_lookahead_class_def);
+ }
+ if (!ots::ParseClassDefTable(font, data + offset_lookahead_class_def,
+ length - offset_lookahead_class_def,
+ num_glyphs, kMaxClassDefValue)) {
+ return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2");
+ }
+ }
+
+ for (unsigned i = 0; i < chain_class_set_count; ++i) {
+ uint16_t offset_chain_class_set = 0;
+ if (!subtable.ReadU16(&offset_chain_class_set)) {
+ return OTS_FAILURE_MSG("Failed to read chain class set offset %d", i);
+ }
+ // |offset_chain_class_set| could be NULL.
+ if (offset_chain_class_set) {
+ if (offset_chain_class_set < chain_class_set_end ||
+ offset_chain_class_set >= length) {
+ return OTS_FAILURE_MSG("Bad chain set class offset %d for chain set %d in chain context format 2", offset_chain_class_set, i);
+ }
+ if (!ParseChainClassSetTable(font, data + offset_chain_class_set,
+ length - offset_chain_class_set,
+ num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse chain class set table %d in chain context format 2", i);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ParseChainContextFormat3(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ ots::Buffer subtable(data, length);
+
+ uint16_t backtrack_count = 0;
+ // Skip format field.
+ if (!subtable.Skip(2) ||
+ !subtable.ReadU16(&backtrack_count)) {
+ return OTS_FAILURE_MSG("Failed to read backtrack count in chain context format 3");
+ }
+
+ if (backtrack_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad backtrack count %d in chain context format 3", backtrack_count);
+ }
+ std::vector<uint16_t> offsets_backtrack;
+ offsets_backtrack.reserve(backtrack_count);
+ for (unsigned i = 0; i < backtrack_count; ++i) {
+ uint16_t offset = 0;
+ if (!subtable.ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read backtrack offset %d in chain context format 3", i);
+ }
+ offsets_backtrack.push_back(offset);
+ }
+ if (offsets_backtrack.size() != backtrack_count) {
+ return OTS_FAILURE_MSG("Bad backtrack offsets size %ld in chain context format 3", offsets_backtrack.size());
+ }
+
+ uint16_t input_count = 0;
+ if (!subtable.ReadU16(&input_count)) {
+ return OTS_FAILURE_MSG("Failed to read input count in chain context format 3");
+ }
+ if (input_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad input count %d in chain context format 3", input_count);
+ }
+ std::vector<uint16_t> offsets_input;
+ offsets_input.reserve(input_count);
+ for (unsigned i = 0; i < input_count; ++i) {
+ uint16_t offset = 0;
+ if (!subtable.ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read input offset %d in chain context format 3", i);
+ }
+ offsets_input.push_back(offset);
+ }
+ if (offsets_input.size() != input_count) {
+ return OTS_FAILURE_MSG("Bad input offsets size %ld in chain context format 3", offsets_input.size());
+ }
+
+ uint16_t lookahead_count = 0;
+ if (!subtable.ReadU16(&lookahead_count)) {
+ return OTS_FAILURE_MSG("Failed ot read lookahead count in chain context format 3");
+ }
+ if (lookahead_count >= num_glyphs) {
+ return OTS_FAILURE_MSG("Bad lookahead count %d in chain context format 3", lookahead_count);
+ }
+ std::vector<uint16_t> offsets_lookahead;
+ offsets_lookahead.reserve(lookahead_count);
+ for (unsigned i = 0; i < lookahead_count; ++i) {
+ uint16_t offset = 0;
+ if (!subtable.ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read lookahead offset %d in chain context format 3", i);
+ }
+ offsets_lookahead.push_back(offset);
+ }
+ if (offsets_lookahead.size() != lookahead_count) {
+ return OTS_FAILURE_MSG("Bad lookahead offsets size %ld in chain context format 3", offsets_lookahead.size());
+ }
+
+ uint16_t lookup_count = 0;
+ if (!subtable.ReadU16(&lookup_count)) {
+ return OTS_FAILURE_MSG("Failed to read lookup count in chain context format 3");
+ }
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ if (!ParseLookupRecord(font, &subtable, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup %d in chain context format 3", i);
+ }
+ }
+
+ const unsigned lookup_record_end =
+ 2 * (static_cast<unsigned>(backtrack_count) +
+ static_cast<unsigned>(input_count) +
+ static_cast<unsigned>(lookahead_count)) +
+ 4 * static_cast<unsigned>(lookup_count) + 10;
+ if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of lookup record %d in chain context format 3", lookup_record_end);
+ }
+ for (unsigned i = 0; i < backtrack_count; ++i) {
+ if (offsets_backtrack[i] < lookup_record_end ||
+ offsets_backtrack[i] >= length) {
+ return OTS_FAILURE_MSG("Bad backtrack offset of %d for backtrack %d in chain context format 3", offsets_backtrack[i], i);
+ }
+ if (!ots::ParseCoverageTable(font, data + offsets_backtrack[i],
+ length - offsets_backtrack[i], num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse backtrack coverage %d in chain context format 3", i);
+ }
+ }
+ for (unsigned i = 0; i < input_count; ++i) {
+ if (offsets_input[i] < lookup_record_end || offsets_input[i] >= length) {
+ return OTS_FAILURE_MSG("Bad input offset %d for input %d in chain context format 3", offsets_input[i], i);
+ }
+ if (!ots::ParseCoverageTable(font, data + offsets_input[i],
+ length - offsets_input[i], num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse input coverage table %d in chain context format 3", i);
+ }
+ }
+ for (unsigned i = 0; i < lookahead_count; ++i) {
+ if (offsets_lookahead[i] < lookup_record_end ||
+ offsets_lookahead[i] >= length) {
+ return OTS_FAILURE_MSG("Bad lookadhead offset %d for lookahead %d in chain context format 3", offsets_lookahead[i], i);
+ }
+ if (!ots::ParseCoverageTable(font, data + offsets_lookahead[i],
+ length - offsets_lookahead[i], num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in chain context format 3", i);
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+bool LookupSubtableParser::Parse(const Font *font, const uint8_t *data,
+ const size_t length,
+ const uint16_t lookup_type) const {
+ for (unsigned i = 0; i < num_types; ++i) {
+ if (parsers[i].type == lookup_type && parsers[i].parse) {
+ if (!parsers[i].parse(font, data, length)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup subtable %d", i);
+ }
+ return true;
+ }
+ }
+ return OTS_FAILURE_MSG("No lookup subtables to parse");
+}
+
+// Parsing ScriptListTable requires number of features so we need to
+// parse FeatureListTable before calling this function.
+bool ParseScriptListTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_features) {
+ Buffer subtable(data, length);
+
+ uint16_t script_count = 0;
+ if (!subtable.ReadU16(&script_count)) {
+ return OTS_FAILURE_MSG("Failed to read script count in script list table");
+ }
+
+ const unsigned script_record_end =
+ 6 * static_cast<unsigned>(script_count) + 2;
+ if (script_record_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of script record %d in script list table", script_record_end);
+ }
+ std::vector<ScriptRecord> script_list;
+ script_list.reserve(script_count);
+ uint32_t last_tag = 0;
+ for (unsigned i = 0; i < script_count; ++i) {
+ ScriptRecord record;
+ if (!subtable.ReadU32(&record.tag) ||
+ !subtable.ReadU16(&record.offset)) {
+ return OTS_FAILURE_MSG("Failed to read script record %d in script list table", i);
+ }
+ // Script tags should be arranged alphabetically by tag
+ if (last_tag != 0 && last_tag > record.tag) {
+ // Several fonts don't arrange tags alphabetically.
+ // It seems that the order of tags might not be a security issue
+ // so we just warn it.
+ OTS_WARNING("tags aren't arranged alphabetically.");
+ }
+ last_tag = record.tag;
+ if (record.offset < script_record_end || record.offset >= length) {
+ return OTS_FAILURE_MSG("Bad record offset %d for script %c%c%c%c entry %d in script list table", record.offset, OTS_UNTAG(record.tag), i);
+ }
+ script_list.push_back(record);
+ }
+ if (script_list.size() != script_count) {
+ return OTS_FAILURE_MSG("Bad script list size %ld in script list table", script_list.size());
+ }
+
+ // Check script records.
+ for (unsigned i = 0; i < script_count; ++i) {
+ if (!ParseScriptTable(font, data + script_list[i].offset,
+ length - script_list[i].offset,
+ script_list[i].tag, num_features)) {
+ return OTS_FAILURE_MSG("Failed to parse script table %d", i);
+ }
+ }
+
+ return true;
+}
+
+// Parsing FeatureListTable requires number of lookups so we need to parse
+// LookupListTable before calling this function.
+bool ParseFeatureListTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_lookups,
+ uint16_t* num_features) {
+ Buffer subtable(data, length);
+
+ uint16_t feature_count = 0;
+ if (!subtable.ReadU16(&feature_count)) {
+ return OTS_FAILURE_MSG("Failed to read feature count");
+ }
+
+ std::vector<FeatureRecord> feature_records;
+ feature_records.resize(feature_count);
+ const unsigned feature_record_end =
+ 6 * static_cast<unsigned>(feature_count) + 2;
+ if (feature_record_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of feature record %d", feature_record_end);
+ }
+ uint32_t last_tag = 0;
+ for (unsigned i = 0; i < feature_count; ++i) {
+ if (!subtable.ReadU32(&feature_records[i].tag) ||
+ !subtable.ReadU16(&feature_records[i].offset)) {
+ return OTS_FAILURE_MSG("Failed to read feature header %d", i);
+ }
+ // Feature record array should be arranged alphabetically by tag
+ if (last_tag != 0 && last_tag > feature_records[i].tag) {
+ // Several fonts don't arrange tags alphabetically.
+ // It seems that the order of tags might not be a security issue
+ // so we just warn it.
+ OTS_WARNING("tags aren't arranged alphabetically.");
+ }
+ last_tag = feature_records[i].tag;
+ if (feature_records[i].offset < feature_record_end ||
+ feature_records[i].offset >= length) {
+ return OTS_FAILURE_MSG("Bad feature offset %d for feature %d %c%c%c%c", feature_records[i].offset, i, OTS_UNTAG(feature_records[i].tag));
+ }
+ }
+
+ for (unsigned i = 0; i < feature_count; ++i) {
+ if (!ParseFeatureTable(font, data + feature_records[i].offset,
+ length - feature_records[i].offset, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse feature table %d", i);
+ }
+ }
+ *num_features = feature_count;
+ return true;
+}
+
+// For parsing GPOS/GSUB tables, this function should be called at first to
+// obtain the number of lookups because parsing FeatureTableList requires
+// the number.
+bool ParseLookupListTable(Font *font, const uint8_t *data,
+ const size_t length,
+ const LookupSubtableParser* parser,
+ uint16_t *num_lookups) {
+ Buffer subtable(data, length);
+
+ if (!subtable.ReadU16(num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to read number of lookups");
+ }
+
+ std::vector<uint16_t> lookups;
+ lookups.reserve(*num_lookups);
+ const unsigned lookup_end =
+ 2 * static_cast<unsigned>(*num_lookups) + 2;
+ if (lookup_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE_MSG("Bad end of lookups %d", lookup_end);
+ }
+ for (unsigned i = 0; i < *num_lookups; ++i) {
+ uint16_t offset = 0;
+ if (!subtable.ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read lookup offset %d", i);
+ }
+ if (offset < lookup_end || offset >= length) {
+ return OTS_FAILURE_MSG("Bad lookup offset %d for lookup %d", offset, i);
+ }
+ lookups.push_back(offset);
+ }
+ if (lookups.size() != *num_lookups) {
+ return OTS_FAILURE_MSG("Bad lookup offsets list size %ld", lookups.size());
+ }
+
+ for (unsigned i = 0; i < *num_lookups; ++i) {
+ if (!ParseLookupTable(font, data + lookups[i], length - lookups[i],
+ parser)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup %d", i);
+ }
+ }
+
+ return true;
+}
+
+bool ParseClassDefTable(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_classes) {
+ Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ if (!subtable.ReadU16(&format)) {
+ return OTS_FAILURE_MSG("Failed to read class defn format");
+ }
+ if (format == 1) {
+ return ParseClassDefFormat1(font, data, length, num_glyphs, num_classes);
+ } else if (format == 2) {
+ return ParseClassDefFormat2(font, data, length, num_glyphs, num_classes);
+ }
+
+ return OTS_FAILURE_MSG("Bad class defn format %d", format);
+}
+
+bool ParseCoverageTable(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t expected_num_glyphs) {
+ Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ if (!subtable.ReadU16(&format)) {
+ return OTS_FAILURE_MSG("Failed to read coverage table format");
+ }
+ if (format == 1) {
+ return ParseCoverageFormat1(font, data, length, num_glyphs, expected_num_glyphs);
+ } else if (format == 2) {
+ return ParseCoverageFormat2(font, data, length, num_glyphs, expected_num_glyphs);
+ }
+
+ return OTS_FAILURE_MSG("Bad coverage table format %d", format);
+}
+
+bool ParseDeviceTable(const ots::Font *font,
+ const uint8_t *data, size_t length) {
+ Buffer subtable(data, length);
+
+ uint16_t start_size = 0;
+ uint16_t end_size = 0;
+ uint16_t delta_format = 0;
+ if (!subtable.ReadU16(&start_size) ||
+ !subtable.ReadU16(&end_size) ||
+ !subtable.ReadU16(&delta_format)) {
+ return OTS_FAILURE_MSG("Failed to read device table header");
+ }
+ if (start_size > end_size) {
+ return OTS_FAILURE_MSG("bad 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);
+ }
+ // The number of delta values per uint16. The device table should contain
+ // at least |num_units| * 2 bytes compressed data.
+ const unsigned num_units = (end_size - start_size) /
+ (1 << (4 - delta_format)) + 1;
+ // Just skip |num_units| * 2 bytes since the compressed data could take
+ // arbitrary values.
+ if (!subtable.Skip(num_units * 2)) {
+ return OTS_FAILURE_MSG("Failed to skip data in device table");
+ }
+ return true;
+}
+
+bool ParseContextSubtable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ if (!subtable.ReadU16(&format)) {
+ return OTS_FAILURE_MSG("Failed to read context subtable format");
+ }
+
+ if (format == 1) {
+ if (!ParseContextFormat1(font, data, length, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse context format 1 subtable");
+ }
+ } else if (format == 2) {
+ if (!ParseContextFormat2(font, data, length, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse context format 2 subtable");
+ }
+ } else if (format == 3) {
+ if (!ParseContextFormat3(font, data, length, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse context format 3 subtable");
+ }
+ } else {
+ return OTS_FAILURE_MSG("Bad context subtable format %d", format);
+ }
+
+ return true;
+}
+
+bool ParseChainingContextSubtable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups) {
+ Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ if (!subtable.ReadU16(&format)) {
+ return OTS_FAILURE_MSG("Failed to read chaining context subtable format");
+ }
+
+ if (format == 1) {
+ if (!ParseChainContextFormat1(font, data, length, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse chaining context format 1 subtable");
+ }
+ } else if (format == 2) {
+ if (!ParseChainContextFormat2(font, data, length, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse chaining context format 2 subtable");
+ }
+ } else if (format == 3) {
+ if (!ParseChainContextFormat3(font, data, length, num_glyphs, num_lookups)) {
+ return OTS_FAILURE_MSG("Failed to parse chaining context format 3 subtable");
+ }
+ } else {
+ return OTS_FAILURE_MSG("Bad chaining context subtable format %d", format);
+ }
+
+ return true;
+}
+
+bool ParseExtensionSubtable(const Font *font,
+ const uint8_t *data, const size_t length,
+ const LookupSubtableParser* parser) {
+ Buffer subtable(data, length);
+
+ uint16_t format = 0;
+ uint16_t lookup_type = 0;
+ uint32_t offset_extension = 0;
+ if (!subtable.ReadU16(&format) ||
+ !subtable.ReadU16(&lookup_type) ||
+ !subtable.ReadU32(&offset_extension)) {
+ return OTS_FAILURE_MSG("Failed to read extension table header");
+ }
+
+ if (format != 1) {
+ return OTS_FAILURE_MSG("Bad extension table format %d", format);
+ }
+ // |lookup_type| should be other than |parser->extension_type|.
+ if (lookup_type < 1 || lookup_type > parser->num_types ||
+ lookup_type == parser->extension_type) {
+ return OTS_FAILURE_MSG("Bad lookup type %d in extension table", lookup_type);
+ }
+
+ const unsigned format_end = static_cast<unsigned>(8);
+ if (offset_extension < format_end ||
+ offset_extension >= length) {
+ return OTS_FAILURE_MSG("Bad extension offset %d", offset_extension);
+ }
+
+ // Parse the extension subtable of |lookup_type|.
+ if (!parser->Parse(font, data + offset_extension, length - offset_extension,
+ lookup_type)) {
+ return OTS_FAILURE_MSG("Failed to parse lookup from extension lookup");
+ }
+
+ return true;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/layout.h b/gfx/ots/src/layout.h
new file mode 100644
index 000000000..d195646d4
--- /dev/null
+++ b/gfx/ots/src/layout.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 The Chromium 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_LAYOUT_H_
+#define OTS_LAYOUT_H_
+
+#include "ots.h"
+
+// Utility functions for OpenType layout common table formats.
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+namespace ots {
+
+
+struct LookupSubtableParser {
+ struct TypeParser {
+ uint16_t type;
+ bool (*parse)(const Font *font, const uint8_t *data,
+ const size_t length);
+ };
+ size_t num_types;
+ uint16_t extension_type;
+ const TypeParser *parsers;
+
+ bool Parse(const Font *font, const uint8_t *data,
+ const size_t length, const uint16_t lookup_type) const;
+};
+
+bool ParseScriptListTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_features);
+
+bool ParseFeatureListTable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_lookups,
+ uint16_t *num_features);
+
+bool ParseLookupListTable(Font *font, const uint8_t *data,
+ const size_t length,
+ const LookupSubtableParser* parser,
+ uint16_t* num_lookups);
+
+bool ParseClassDefTable(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_classes);
+
+bool ParseCoverageTable(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t expected_num_glyphs = 0);
+
+bool ParseDeviceTable(const ots::Font *font,
+ const uint8_t *data, size_t length);
+
+// Parser for 'Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseContextSubtable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups);
+
+// Parser for 'Chaining Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseChainingContextSubtable(const ots::Font *font,
+ const uint8_t *data, const size_t length,
+ const uint16_t num_glyphs,
+ const uint16_t num_lookups);
+
+bool ParseExtensionSubtable(const Font *font,
+ const uint8_t *data, const size_t length,
+ const LookupSubtableParser* parser);
+
+} // namespace ots
+
+#endif // OTS_LAYOUT_H_
+
diff --git a/gfx/ots/src/loca.cc b/gfx/ots/src/loca.cc
new file mode 100644
index 000000000..aae04c25a
--- /dev/null
+++ b/gfx/ots/src/loca.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2009 The Chromium 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 "loca.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// loca - Index to Location
+// http://www.microsoft.com/typography/otspec/loca.htm
+
+#define TABLE_NAME "loca"
+
+namespace ots {
+
+bool ots_loca_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ // We can't do anything useful in validating this data except to ensure that
+ // the values are monotonically increasing.
+
+ OpenTypeLOCA *loca = new OpenTypeLOCA;
+ font->loca = loca;
+
+ if (!font->maxp || !font->head) {
+ return OTS_FAILURE_MSG("maxp or head tables missing from font, needed by loca");
+ }
+
+ const unsigned num_glyphs = font->maxp->num_glyphs;
+ unsigned last_offset = 0;
+ loca->offsets.resize(num_glyphs + 1);
+ // maxp->num_glyphs is uint16_t, thus the addition never overflows.
+
+ if (font->head->index_to_loc_format == 0) {
+ // Note that the <= here (and below) is correct. There is one more offset
+ // than the number of glyphs in order to give the length of the final
+ // glyph.
+ for (unsigned i = 0; i <= num_glyphs; ++i) {
+ uint16_t offset = 0;
+ if (!table.ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+ }
+ if (offset < last_offset) {
+ return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+ }
+ last_offset = offset;
+ loca->offsets[i] = offset * 2;
+ }
+ } else {
+ for (unsigned i = 0; i <= num_glyphs; ++i) {
+ uint32_t offset = 0;
+ if (!table.ReadU32(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+ }
+ if (offset < last_offset) {
+ return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+ }
+ last_offset = offset;
+ loca->offsets[i] = offset;
+ }
+ }
+
+ return true;
+}
+
+bool ots_loca_should_serialise(Font *font) {
+ return font->loca != NULL;
+}
+
+bool ots_loca_serialise(OTSStream *out, Font *font) {
+ const OpenTypeLOCA *loca = font->loca;
+ const OpenTypeHEAD *head = font->head;
+
+ if (!head) {
+ return OTS_FAILURE_MSG("Missing head table in font needed by loca");
+ }
+
+ if (head->index_to_loc_format == 0) {
+ for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+ const uint16_t offset = static_cast<uint16_t>(loca->offsets[i] >> 1);
+ if ((offset != (loca->offsets[i] >> 1)) ||
+ !out->WriteU16(offset)) {
+ return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+ }
+ }
+ } else {
+ for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+ if (!out->WriteU32(loca->offsets[i])) {
+ return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+ }
+ }
+ }
+
+ return true;
+}
+
+void ots_loca_reuse(Font *font, Font *other) {
+ font->loca = other->loca;
+ font->loca_reused = true;
+}
+
+void ots_loca_free(Font *font) {
+ delete font->loca;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/loca.h b/gfx/ots/src/loca.h
new file mode 100644
index 000000000..255ef06ec
--- /dev/null
+++ b/gfx/ots/src/loca.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium 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_LOCA_H_
+#define OTS_LOCA_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeLOCA {
+ std::vector<uint32_t> offsets;
+};
+
+} // namespace ots
+
+#endif // OTS_LOCA_H_
diff --git a/gfx/ots/src/ltsh.cc b/gfx/ots/src/ltsh.cc
new file mode 100644
index 000000000..5b34cf4ad
--- /dev/null
+++ b/gfx/ots/src/ltsh.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2009 The Chromium 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 "ltsh.h"
+
+#include "maxp.h"
+
+// LTSH - Linear Threshold
+// http://www.microsoft.com/typography/otspec/ltsh.htm
+
+#define TABLE_NAME "LTSH"
+
+#define DROP_THIS_TABLE(...) \
+ do { \
+ OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
+ OTS_FAILURE_MSG("Table discarded"); \
+ delete font->ltsh; \
+ font->ltsh = 0; \
+ } while (0)
+
+namespace ots {
+
+bool ots_ltsh_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ if (!font->maxp) {
+ return OTS_FAILURE_MSG("Missing maxp table from font needed by ltsh");
+ }
+
+ OpenTypeLTSH *ltsh = new OpenTypeLTSH;
+ font->ltsh = ltsh;
+
+ uint16_t num_glyphs = 0;
+ if (!table.ReadU16(&ltsh->version) ||
+ !table.ReadU16(&num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to read ltsh header");
+ }
+
+ if (ltsh->version != 0) {
+ DROP_THIS_TABLE("bad version: %u", ltsh->version);
+ return true;
+ }
+
+ if (num_glyphs != font->maxp->num_glyphs) {
+ DROP_THIS_TABLE("bad num_glyphs: %u", num_glyphs);
+ return true;
+ }
+
+ ltsh->ypels.reserve(num_glyphs);
+ for (unsigned i = 0; i < num_glyphs; ++i) {
+ uint8_t pel = 0;
+ if (!table.ReadU8(&pel)) {
+ return OTS_FAILURE_MSG("Failed to read pixels for glyph %d", i);
+ }
+ ltsh->ypels.push_back(pel);
+ }
+
+ return true;
+}
+
+bool ots_ltsh_should_serialise(Font *font) {
+ if (!font->glyf) return false; // this table is not for CFF fonts.
+ return font->ltsh != NULL;
+}
+
+bool ots_ltsh_serialise(OTSStream *out, Font *font) {
+ const OpenTypeLTSH *ltsh = font->ltsh;
+
+ const uint16_t num_ypels = static_cast<uint16_t>(ltsh->ypels.size());
+ if (num_ypels != ltsh->ypels.size() ||
+ !out->WriteU16(ltsh->version) ||
+ !out->WriteU16(num_ypels)) {
+ return OTS_FAILURE_MSG("Failed to write pels size");
+ }
+ for (uint16_t i = 0; i < num_ypels; ++i) {
+ if (!out->Write(&(ltsh->ypels[i]), 1)) {
+ return OTS_FAILURE_MSG("Failed to write pixel size for glyph %d", i);
+ }
+ }
+
+ return true;
+}
+
+void ots_ltsh_reuse(Font *font, Font *other) {
+ font->ltsh = other->ltsh;
+ font->ltsh_reused = true;
+}
+
+void ots_ltsh_free(Font *font) {
+ delete font->ltsh;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/gfx/ots/src/ltsh.h b/gfx/ots/src/ltsh.h
new file mode 100644
index 000000000..23d97d784
--- /dev/null
+++ b/gfx/ots/src/ltsh.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium 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_LTSH_H_
+#define OTS_LTSH_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeLTSH {
+ uint16_t version;
+ std::vector<uint8_t> ypels;
+};
+
+} // namespace ots
+
+#endif // OTS_LTSH_H_
diff --git a/gfx/ots/src/math.cc b/gfx/ots/src/math.cc
new file mode 100644
index 000000000..36417dc29
--- /dev/null
+++ b/gfx/ots/src/math.cc
@@ -0,0 +1,619 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// We use an underscore to avoid confusion with the standard math.h library.
+#include "math_.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// MATH - The MATH Table
+// The specification is not yet public but has been submitted to the MPEG group
+// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font
+// Format" Color Font Technology and MATH layout support'. Meanwhile, you can
+// contact Microsoft's engineer Murray Sargent to obtain a copy.
+
+#define TABLE_NAME "MATH"
+
+namespace {
+
+// The size of MATH header.
+// Version
+// MathConstants
+// MathGlyphInfo
+// MathVariants
+const unsigned kMathHeaderSize = 4 + 3 * 2;
+
+// The size of the MathGlyphInfo header.
+// MathItalicsCorrectionInfo
+// MathTopAccentAttachment
+// ExtendedShapeCoverage
+// MathKernInfo
+const unsigned kMathGlyphInfoHeaderSize = 4 * 2;
+
+// The size of the MathValueRecord.
+// Value
+// DeviceTable
+const unsigned kMathValueRecordSize = 2 * 2;
+
+// The size of the GlyphPartRecord.
+// glyph
+// StartConnectorLength
+// EndConnectorLength
+// FullAdvance
+// PartFlags
+const unsigned kGlyphPartRecordSize = 5 * 2;
+
+// Shared Table: MathValueRecord
+
+bool ParseMathValueRecord(const ots::Font *font,
+ ots::Buffer* subtable, const uint8_t *data,
+ const size_t length) {
+ // Check the Value field.
+ if (!subtable->Skip(2)) {
+ return OTS_FAILURE();
+ }
+
+ // Check the offset to device table.
+ uint16_t offset = 0;
+ if (!subtable->ReadU16(&offset)) {
+ return OTS_FAILURE();
+ }
+ if (offset) {
+ if (offset >= length) {
+ return OTS_FAILURE();
+ }
+ if (!ots::ParseDeviceTable(font, data + offset, length - offset)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ return true;
+}
+
+bool ParseMathConstantsTable(const ots::Font *font,
+ const uint8_t *data, size_t length) {
+ ots::Buffer subtable(data, length);
+
+ // Part 1: int16 or uint16 constants.
+ // ScriptPercentScaleDown
+ // ScriptScriptPercentScaleDown
+ // DelimitedSubFormulaMinHeight
+ // DisplayOperatorMinHeight
+ if (!subtable.Skip(4 * 2)) {
+ return OTS_FAILURE();
+ }
+
+ // Part 2: MathValueRecord constants.
+ // MathLeading
+ // AxisHeight
+ // AccentBaseHeight
+ // FlattenedAccentBaseHeight
+ // SubscriptShiftDown
+ // SubscriptTopMax
+ // SubscriptBaselineDropMin
+ // SuperscriptShiftUp
+ // SuperscriptShiftUpCramped
+ // SuperscriptBottomMin
+ //
+ // SuperscriptBaselineDropMax
+ // SubSuperscriptGapMin
+ // SuperscriptBottomMaxWithSubscript
+ // SpaceAfterScript
+ // UpperLimitGapMin
+ // UpperLimitBaselineRiseMin
+ // LowerLimitGapMin
+ // LowerLimitBaselineDropMin
+ // StackTopShiftUp
+ // StackTopDisplayStyleShiftUp
+ //
+ // StackBottomShiftDown
+ // StackBottomDisplayStyleShiftDown
+ // StackGapMin
+ // StackDisplayStyleGapMin
+ // StretchStackTopShiftUp
+ // StretchStackBottomShiftDown
+ // StretchStackGapAboveMin
+ // StretchStackGapBelowMin
+ // FractionNumeratorShiftUp
+ // FractionNumeratorDisplayStyleShiftUp
+ //
+ // FractionDenominatorShiftDown
+ // FractionDenominatorDisplayStyleShiftDown
+ // FractionNumeratorGapMin
+ // FractionNumDisplayStyleGapMin
+ // FractionRuleThickness
+ // FractionDenominatorGapMin
+ // FractionDenomDisplayStyleGapMin
+ // SkewedFractionHorizontalGap
+ // SkewedFractionVerticalGap
+ // OverbarVerticalGap
+ //
+ // OverbarRuleThickness
+ // OverbarExtraAscender
+ // UnderbarVerticalGap
+ // UnderbarRuleThickness
+ // UnderbarExtraDescender
+ // RadicalVerticalGap
+ // RadicalDisplayStyleVerticalGap
+ // RadicalRuleThickness
+ // RadicalExtraAscender
+ // RadicalKernBeforeDegree
+ //
+ // RadicalKernAfterDegree
+ for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
+ if (!ParseMathValueRecord(font, &subtable, data, length)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ // Part 3: uint16 constant
+ // RadicalDegreeBottomRaisePercent
+ if (!subtable.Skip(2)) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
+ ots::Buffer* subtable,
+ const uint8_t *data,
+ const size_t length,
+ const uint16_t num_glyphs) {
+ // Check the header.
+ uint16_t offset_coverage = 0;
+ uint16_t sequence_count = 0;
+ if (!subtable->ReadU16(&offset_coverage) ||
+ !subtable->ReadU16(&sequence_count)) {
+ return OTS_FAILURE();
+ }
+
+ const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+ sequence_count * kMathValueRecordSize;
+ if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE();
+ }
+
+ // Check coverage table.
+ if (offset_coverage < sequence_end || offset_coverage >= length) {
+ return OTS_FAILURE();
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage,
+ num_glyphs, sequence_count)) {
+ return OTS_FAILURE();
+ }
+
+ // Check sequence.
+ for (unsigned i = 0; i < sequence_count; ++i) {
+ if (!ParseMathValueRecord(font, subtable, data, length)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ return true;
+}
+
+bool ParseMathItalicsCorrectionInfoTable(const ots::Font *font,
+ const uint8_t *data,
+ size_t length,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+ return ParseMathValueRecordSequenceForGlyphs(font, &subtable, data, length,
+ num_glyphs);
+}
+
+bool ParseMathTopAccentAttachmentTable(const ots::Font *font,
+ const uint8_t *data,
+ size_t length,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+ return ParseMathValueRecordSequenceForGlyphs(font, &subtable, data, length,
+ num_glyphs);
+}
+
+bool ParseMathKernTable(const ots::Font *font,
+ const uint8_t *data, size_t length) {
+ ots::Buffer subtable(data, length);
+
+ // Check the Height count.
+ uint16_t height_count = 0;
+ if (!subtable.ReadU16(&height_count)) {
+ return OTS_FAILURE();
+ }
+
+ // Check the Correction Heights.
+ for (unsigned i = 0; i < height_count; ++i) {
+ if (!ParseMathValueRecord(font, &subtable, data, length)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ // Check the Kern Values.
+ for (unsigned i = 0; i <= height_count; ++i) {
+ if (!ParseMathValueRecord(font, &subtable, data, length)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ return true;
+}
+
+bool ParseMathKernInfoTable(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Check the header.
+ uint16_t offset_coverage = 0;
+ uint16_t sequence_count = 0;
+ if (!subtable.ReadU16(&offset_coverage) ||
+ !subtable.ReadU16(&sequence_count)) {
+ return OTS_FAILURE();
+ }
+
+ const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+ sequence_count * 4 * 2;
+ if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE();
+ }
+
+ // Check coverage table.
+ if (offset_coverage < sequence_end || offset_coverage >= length) {
+ return OTS_FAILURE();
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage, length - offset_coverage,
+ num_glyphs, sequence_count)) {
+ return OTS_FAILURE();
+ }
+
+ // Check sequence of MathKernInfoRecord
+ for (unsigned i = 0; i < sequence_count; ++i) {
+ // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
+ for (unsigned j = 0; j < 4; ++j) {
+ uint16_t offset_math_kern = 0;
+ if (!subtable.ReadU16(&offset_math_kern)) {
+ return OTS_FAILURE();
+ }
+ if (offset_math_kern) {
+ if (offset_math_kern < sequence_end || offset_math_kern >= length ||
+ !ParseMathKernTable(font, data + offset_math_kern,
+ length - offset_math_kern)) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ParseMathGlyphInfoTable(const ots::Font *font,
+ const uint8_t *data, size_t length,
+ const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Check Header.
+ uint16_t offset_math_italics_correction_info = 0;
+ uint16_t offset_math_top_accent_attachment = 0;
+ uint16_t offset_extended_shaped_coverage = 0;
+ uint16_t offset_math_kern_info = 0;
+ if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
+ !subtable.ReadU16(&offset_math_top_accent_attachment) ||
+ !subtable.ReadU16(&offset_extended_shaped_coverage) ||
+ !subtable.ReadU16(&offset_math_kern_info)) {
+ return OTS_FAILURE();
+ }
+
+ // Check subtables.
+ // The specification does not say whether the offsets for
+ // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may
+ // be NULL, but that's the case in some fonts (e.g STIX) so we accept that.
+ if (offset_math_italics_correction_info) {
+ if (offset_math_italics_correction_info >= length ||
+ offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
+ !ParseMathItalicsCorrectionInfoTable(
+ font, data + offset_math_italics_correction_info,
+ length - offset_math_italics_correction_info,
+ num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ }
+ if (offset_math_top_accent_attachment) {
+ if (offset_math_top_accent_attachment >= length ||
+ offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
+ !ParseMathTopAccentAttachmentTable(font, data +
+ offset_math_top_accent_attachment,
+ length -
+ offset_math_top_accent_attachment,
+ num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ }
+ if (offset_extended_shaped_coverage) {
+ if (offset_extended_shaped_coverage >= length ||
+ offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
+ !ots::ParseCoverageTable(font, data + offset_extended_shaped_coverage,
+ length - offset_extended_shaped_coverage,
+ num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ }
+ if (offset_math_kern_info) {
+ if (offset_math_kern_info >= length ||
+ offset_math_kern_info < kMathGlyphInfoHeaderSize ||
+ !ParseMathKernInfoTable(font, data + offset_math_kern_info,
+ length - offset_math_kern_info, num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ return true;
+}
+
+bool ParseGlyphAssemblyTable(const ots::Font *font,
+ const uint8_t *data,
+ size_t length, const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Check the header.
+ uint16_t part_count = 0;
+ if (!ParseMathValueRecord(font, &subtable, data, length) ||
+ !subtable.ReadU16(&part_count)) {
+ return OTS_FAILURE();
+ }
+
+ const unsigned sequence_end = kMathValueRecordSize +
+ static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
+ if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE();
+ }
+
+ // Check the sequence of GlyphPartRecord.
+ for (unsigned i = 0; i < part_count; ++i) {
+ uint16_t glyph = 0;
+ uint16_t part_flags = 0;
+ if (!subtable.ReadU16(&glyph) ||
+ !subtable.Skip(2 * 3) ||
+ !subtable.ReadU16(&part_flags)) {
+ return OTS_FAILURE();
+ }
+ if (glyph >= num_glyphs) {
+ return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+ }
+ if (part_flags & ~0x00000001) {
+ return OTS_FAILURE_MSG("unknown part flag: %u", part_flags);
+ }
+ }
+
+ return true;
+}
+
+bool ParseMathGlyphConstructionTable(const ots::Font *font,
+ const uint8_t *data,
+ size_t length, const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Check the header.
+ uint16_t offset_glyph_assembly = 0;
+ uint16_t variant_count = 0;
+ if (!subtable.ReadU16(&offset_glyph_assembly) ||
+ !subtable.ReadU16(&variant_count)) {
+ return OTS_FAILURE();
+ }
+
+ const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+ variant_count * 2 * 2;
+ if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE();
+ }
+
+ // Check the GlyphAssembly offset.
+ if (offset_glyph_assembly) {
+ if (offset_glyph_assembly >= length ||
+ offset_glyph_assembly < sequence_end) {
+ return OTS_FAILURE();
+ }
+ if (!ParseGlyphAssemblyTable(font, data + offset_glyph_assembly,
+ length - offset_glyph_assembly, num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ // Check the sequence of MathGlyphVariantRecord.
+ for (unsigned i = 0; i < variant_count; ++i) {
+ uint16_t glyph = 0;
+ if (!subtable.ReadU16(&glyph) ||
+ !subtable.Skip(2)) {
+ return OTS_FAILURE();
+ }
+ if (glyph >= num_glyphs) {
+ return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+ }
+ }
+
+ return true;
+}
+
+bool ParseMathGlyphConstructionSequence(const ots::Font *font,
+ ots::Buffer* subtable,
+ const uint8_t *data,
+ size_t length,
+ const uint16_t num_glyphs,
+ uint16_t offset_coverage,
+ uint16_t glyph_count,
+ const unsigned sequence_end) {
+ // Zero glyph count, nothing to parse.
+ if (!glyph_count) {
+ return true;
+ }
+
+ // Check coverage table.
+ if (offset_coverage < sequence_end || offset_coverage >= length) {
+ return OTS_FAILURE();
+ }
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
+ length - offset_coverage,
+ num_glyphs, glyph_count)) {
+ return OTS_FAILURE();
+ }
+
+ // Check sequence of MathGlyphConstruction.
+ for (unsigned i = 0; i < glyph_count; ++i) {
+ uint16_t offset_glyph_construction = 0;
+ if (!subtable->ReadU16(&offset_glyph_construction)) {
+ return OTS_FAILURE();
+ }
+ if (offset_glyph_construction < sequence_end ||
+ offset_glyph_construction >= length ||
+ !ParseMathGlyphConstructionTable(font, data + offset_glyph_construction,
+ length - offset_glyph_construction,
+ num_glyphs)) {
+ return OTS_FAILURE();
+ }
+ }
+
+ return true;
+}
+
+bool ParseMathVariantsTable(const ots::Font *font,
+ const uint8_t *data,
+ size_t length, const uint16_t num_glyphs) {
+ ots::Buffer subtable(data, length);
+
+ // Check the header.
+ uint16_t offset_vert_glyph_coverage = 0;
+ uint16_t offset_horiz_glyph_coverage = 0;
+ uint16_t vert_glyph_count = 0;
+ uint16_t horiz_glyph_count = 0;
+ if (!subtable.Skip(2) || // MinConnectorOverlap
+ !subtable.ReadU16(&offset_vert_glyph_coverage) ||
+ !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
+ !subtable.ReadU16(&vert_glyph_count) ||
+ !subtable.ReadU16(&horiz_glyph_count)) {
+ return OTS_FAILURE();
+ }
+
+ const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
+ horiz_glyph_count * 2;
+ if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+ return OTS_FAILURE();
+ }
+
+ if (!ParseMathGlyphConstructionSequence(font, &subtable, data, length, num_glyphs,
+ offset_vert_glyph_coverage,
+ vert_glyph_count,
+ sequence_end) ||
+ !ParseMathGlyphConstructionSequence(font, &subtable, data, length, num_glyphs,
+ offset_horiz_glyph_coverage,
+ horiz_glyph_count,
+ sequence_end)) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+} // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+ do { \
+ OTS_FAILURE_MSG(msg_ ", table discarded"); \
+ font->math->data = 0; \
+ font->math->length = 0; \
+ } while (0)
+
+namespace ots {
+
+bool ots_math_parse(Font *font, const uint8_t *data, size_t length) {
+ // Grab the number of glyphs in the font from the maxp table to check
+ // GlyphIDs in MATH table.
+ if (!font->maxp) {
+ return OTS_FAILURE();
+ }
+ const uint16_t num_glyphs = font->maxp->num_glyphs;
+
+ Buffer table(data, length);
+
+ OpenTypeMATH* math = new OpenTypeMATH;
+ font->math = math;
+
+ uint32_t version = 0;
+ if (!table.ReadU32(&version)) {
+ return OTS_FAILURE();
+ }
+ if (version != 0x00010000) {
+ DROP_THIS_TABLE("bad MATH version");
+ return true;
+ }
+
+ uint16_t offset_math_constants = 0;
+ uint16_t offset_math_glyph_info = 0;
+ uint16_t offset_math_variants = 0;
+ if (!table.ReadU16(&offset_math_constants) ||
+ !table.ReadU16(&offset_math_glyph_info) ||
+ !table.ReadU16(&offset_math_variants)) {
+ return OTS_FAILURE();
+ }
+
+ if (offset_math_constants >= length ||
+ offset_math_constants < kMathHeaderSize ||
+ offset_math_glyph_info >= length ||
+ offset_math_glyph_info < kMathHeaderSize ||
+ offset_math_variants >= length ||
+ offset_math_variants < kMathHeaderSize) {
+ DROP_THIS_TABLE("bad offset in MATH header");
+ return true;
+ }
+
+ if (!ParseMathConstantsTable(font, data + offset_math_constants,
+ length - offset_math_constants)) {
+ DROP_THIS_TABLE("failed to parse MathConstants table");
+ return true;
+ }
+ if (!ParseMathGlyphInfoTable(font, data + offset_math_glyph_info,
+ length - offset_math_glyph_info, num_glyphs)) {
+ DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
+ return true;
+ }
+ if (!ParseMathVariantsTable(font, data + offset_math_variants,
+ length - offset_math_variants, num_glyphs)) {
+ DROP_THIS_TABLE("failed to parse MathVariants table");
+ return true;
+ }
+
+ math->data = data;
+ math->length = length;
+ return true;
+}
+
+bool ots_math_should_serialise(Font *font) {
+ return font->math != NULL && font->math->data != NULL;
+}
+
+bool ots_math_serialise(OTSStream *out, Font *font) {
+ if (!out->Write(font->math->data, font->math->length)) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+void ots_math_reuse(Font *font, Font *other) {
+ font->math = other->math;
+ font->math_reused = true;
+}
+
+void ots_math_free(Font *font) {
+ delete font->math;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/gfx/ots/src/math_.h b/gfx/ots/src/math_.h
new file mode 100644
index 000000000..91c54dbe1
--- /dev/null
+++ b/gfx/ots/src/math_.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2014 The Chromium 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_MATH_H_
+#define OTS_MATH_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMATH {
+ OpenTypeMATH()
+ : data(NULL),
+ length(0) {
+ }
+
+ const uint8_t *data;
+ size_t length;
+};
+
+} // namespace ots
+
+#endif
+
diff --git a/gfx/ots/src/maxp.cc b/gfx/ots/src/maxp.cc
new file mode 100644
index 000000000..41e29a745
--- /dev/null
+++ b/gfx/ots/src/maxp.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2009 The Chromium 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 "maxp.h"
+
+// maxp - Maximum Profile
+// http://www.microsoft.com/typography/otspec/maxp.htm
+
+#define TABLE_NAME "maxp"
+
+namespace ots {
+
+bool ots_maxp_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypeMAXP *maxp = new OpenTypeMAXP;
+ font->maxp = maxp;
+
+ uint32_t version = 0;
+ if (!table.ReadU32(&version)) {
+ return OTS_FAILURE_MSG("Failed to read version of maxp table");
+ }
+
+ if (version >> 16 > 1) {
+ return OTS_FAILURE_MSG("Bad maxp version %d", version);
+ }
+
+ if (!table.ReadU16(&maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to read number of glyphs from maxp table");
+ }
+
+ if (!maxp->num_glyphs) {
+ return OTS_FAILURE_MSG("Bad number of glyphs 0 in maxp table");
+ }
+
+ if (version >> 16 == 1) {
+ maxp->version_1 = true;
+ if (!table.ReadU16(&maxp->max_points) ||
+ !table.ReadU16(&maxp->max_contours) ||
+ !table.ReadU16(&maxp->max_c_points) ||
+ !table.ReadU16(&maxp->max_c_contours) ||
+ !table.ReadU16(&maxp->max_zones) ||
+ !table.ReadU16(&maxp->max_t_points) ||
+ !table.ReadU16(&maxp->max_storage) ||
+ !table.ReadU16(&maxp->max_fdefs) ||
+ !table.ReadU16(&maxp->max_idefs) ||
+ !table.ReadU16(&maxp->max_stack) ||
+ !table.ReadU16(&maxp->max_size_glyf_instructions) ||
+ !table.ReadU16(&maxp->max_c_components) ||
+ !table.ReadU16(&maxp->max_c_depth)) {
+ return OTS_FAILURE_MSG("Failed to read maxp table");
+ }
+
+ if (maxp->max_zones == 0) {
+ // workaround for ipa*.ttf Japanese fonts.
+ OTS_WARNING("bad max_zones: %u", maxp->max_zones);
+ maxp->max_zones = 1;
+ } else if (maxp->max_zones == 3) {
+ // workaround for Ecolier-*.ttf fonts.
+ OTS_WARNING("bad max_zones: %u", maxp->max_zones);
+ maxp->max_zones = 2;
+ }
+
+ if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) {
+ return OTS_FAILURE_MSG("Bad max zones %d in maxp", maxp->max_zones);
+ }
+ } else {
+ maxp->version_1 = false;
+ }
+
+ return true;
+}
+
+bool ots_maxp_should_serialise(Font *font) {
+ return font->maxp != NULL;
+}
+
+bool ots_maxp_serialise(OTSStream *out, Font *font) {
+ const OpenTypeMAXP *maxp = font->maxp;
+
+ if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
+ !out->WriteU16(maxp->num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to write maxp version or number of glyphs");
+ }
+
+ if (!maxp->version_1) return true;
+
+ if (!out->WriteU16(maxp->max_points) ||
+ !out->WriteU16(maxp->max_contours) ||
+ !out->WriteU16(maxp->max_c_points) ||
+ !out->WriteU16(maxp->max_c_contours)) {
+ return OTS_FAILURE_MSG("Failed to write maxp");
+ }
+
+ if (!out->WriteU16(maxp->max_zones) ||
+ !out->WriteU16(maxp->max_t_points) ||
+ !out->WriteU16(maxp->max_storage) ||
+ !out->WriteU16(maxp->max_fdefs) ||
+ !out->WriteU16(maxp->max_idefs) ||
+ !out->WriteU16(maxp->max_stack) ||
+ !out->WriteU16(maxp->max_size_glyf_instructions)) {
+ return OTS_FAILURE_MSG("Failed to write more maxp");
+ }
+
+ if (!out->WriteU16(maxp->max_c_components) ||
+ !out->WriteU16(maxp->max_c_depth)) {
+ return OTS_FAILURE_MSG("Failed to write yet more maxp");
+ }
+
+ return true;
+}
+
+void ots_maxp_reuse(Font *font, Font *other) {
+ font->maxp = other->maxp;
+ font->maxp_reused = true;
+}
+
+void ots_maxp_free(Font *font) {
+ delete font->maxp;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/maxp.h b/gfx/ots/src/maxp.h
new file mode 100644
index 000000000..efca0c91a
--- /dev/null
+++ b/gfx/ots/src/maxp.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2009 The Chromium 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_MAXP_H_
+#define OTS_MAXP_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMAXP {
+ uint16_t num_glyphs;
+ bool version_1;
+
+ uint16_t max_points;
+ uint16_t max_contours;
+ uint16_t max_c_points;
+ uint16_t max_c_contours;
+
+ uint16_t max_zones;
+ uint16_t max_t_points;
+ uint16_t max_storage;
+ uint16_t max_fdefs;
+ uint16_t max_idefs;
+ uint16_t max_stack;
+ uint16_t max_size_glyf_instructions;
+
+ uint16_t max_c_components;
+ uint16_t max_c_depth;
+};
+
+} // namespace ots
+
+#endif // OTS_MAXP_H_
diff --git a/gfx/ots/src/metrics.cc b/gfx/ots/src/metrics.cc
new file mode 100644
index 000000000..cd89f4555
--- /dev/null
+++ b/gfx/ots/src/metrics.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2011 The Chromium 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 "metrics.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// OpenType horizontal and vertical common header format
+// http://www.microsoft.com/typography/otspec/hhea.htm
+// http://www.microsoft.com/typography/otspec/vhea.htm
+
+#define TABLE_NAME "metrics" // XXX: use individual table names
+
+namespace ots {
+
+bool ParseMetricsHeader(Font *font, Buffer *table,
+ OpenTypeMetricsHeader *header) {
+ if (!table->ReadS16(&header->ascent) ||
+ !table->ReadS16(&header->descent) ||
+ !table->ReadS16(&header->linegap) ||
+ !table->ReadU16(&header->adv_width_max) ||
+ !table->ReadS16(&header->min_sb1) ||
+ !table->ReadS16(&header->min_sb2) ||
+ !table->ReadS16(&header->max_extent) ||
+ !table->ReadS16(&header->caret_slope_rise) ||
+ !table->ReadS16(&header->caret_slope_run) ||
+ !table->ReadS16(&header->caret_offset)) {
+ return OTS_FAILURE_MSG("Failed to read metrics header");
+ }
+
+ if (header->ascent < 0) {
+ OTS_WARNING("bad ascent: %d", header->ascent);
+ header->ascent = 0;
+ }
+ if (header->linegap < 0) {
+ OTS_WARNING("bad linegap: %d", header->linegap);
+ header->linegap = 0;
+ }
+
+ if (!font->head) {
+ return OTS_FAILURE_MSG("Missing head font table");
+ }
+
+ // if the font is non-slanted, caret_offset should be zero.
+ if (!(font->head->mac_style & 2) &&
+ (header->caret_offset != 0)) {
+ OTS_WARNING("bad caret offset: %d", header->caret_offset);
+ header->caret_offset = 0;
+ }
+
+ // skip the reserved bytes
+ if (!table->Skip(8)) {
+ return OTS_FAILURE_MSG("Failed to skip reserverd bytes");
+ }
+
+ int16_t data_format;
+ if (!table->ReadS16(&data_format)) {
+ return OTS_FAILURE_MSG("Failed to read data format");
+ }
+ if (data_format) {
+ return OTS_FAILURE_MSG("Bad data format %d", data_format);
+ }
+
+ if (!table->ReadU16(&header->num_metrics)) {
+ return OTS_FAILURE_MSG("Failed to read number of metrics");
+ }
+
+ if (!font->maxp) {
+ return OTS_FAILURE_MSG("Missing maxp font table");
+ }
+
+ if (header->num_metrics > font->maxp->num_glyphs) {
+ return OTS_FAILURE_MSG("Bad number of metrics %d", header->num_metrics);
+ }
+
+ return true;
+}
+
+bool SerialiseMetricsHeader(const ots::Font *font,
+ OTSStream *out,
+ const OpenTypeMetricsHeader *header) {
+ if (!out->WriteU32(header->version) ||
+ !out->WriteS16(header->ascent) ||
+ !out->WriteS16(header->descent) ||
+ !out->WriteS16(header->linegap) ||
+ !out->WriteU16(header->adv_width_max) ||
+ !out->WriteS16(header->min_sb1) ||
+ !out->WriteS16(header->min_sb2) ||
+ !out->WriteS16(header->max_extent) ||
+ !out->WriteS16(header->caret_slope_rise) ||
+ !out->WriteS16(header->caret_slope_run) ||
+ !out->WriteS16(header->caret_offset) ||
+ !out->WriteR64(0) || // reserved
+ !out->WriteS16(0) || // metric data format
+ !out->WriteU16(header->num_metrics)) {
+ return OTS_FAILURE_MSG("Failed to write metrics");
+ }
+
+ return true;
+}
+
+bool ParseMetricsTable(const ots::Font *font,
+ Buffer *table,
+ const uint16_t num_glyphs,
+ const OpenTypeMetricsHeader *header,
+ OpenTypeMetricsTable *metrics) {
+ // |num_metrics| is a uint16_t, so it's bounded < 65536. This limits that
+ // amount of memory that we'll allocate for this to a sane amount.
+ const unsigned num_metrics = header->num_metrics;
+
+ if (num_metrics > num_glyphs) {
+ return OTS_FAILURE_MSG("Bad number of metrics %d", num_metrics);
+ }
+ if (!num_metrics) {
+ return OTS_FAILURE_MSG("No metrics!");
+ }
+ const unsigned num_sbs = num_glyphs - num_metrics;
+
+ metrics->entries.reserve(num_metrics);
+ for (unsigned i = 0; i < num_metrics; ++i) {
+ uint16_t adv = 0;
+ int16_t sb = 0;
+ if (!table->ReadU16(&adv) || !table->ReadS16(&sb)) {
+ return OTS_FAILURE_MSG("Failed to read metric %d", i);
+ }
+ metrics->entries.push_back(std::make_pair(adv, sb));
+ }
+
+ metrics->sbs.reserve(num_sbs);
+ for (unsigned i = 0; i < num_sbs; ++i) {
+ int16_t sb;
+ if (!table->ReadS16(&sb)) {
+ // Some Japanese fonts (e.g., mona.ttf) fail this test.
+ return OTS_FAILURE_MSG("Failed to read side bearing %d", i + num_metrics);
+ }
+ metrics->sbs.push_back(sb);
+ }
+
+ return true;
+}
+
+bool SerialiseMetricsTable(const ots::Font *font,
+ OTSStream *out,
+ const OpenTypeMetricsTable *metrics) {
+ for (unsigned i = 0; i < metrics->entries.size(); ++i) {
+ if (!out->WriteU16(metrics->entries[i].first) ||
+ !out->WriteS16(metrics->entries[i].second)) {
+ return OTS_FAILURE_MSG("Failed to write metric %d", i);
+ }
+ }
+
+ for (unsigned i = 0; i < metrics->sbs.size(); ++i) {
+ if (!out->WriteS16(metrics->sbs[i])) {
+ return OTS_FAILURE_MSG("Failed to write side bearing %ld", i + metrics->entries.size());
+ }
+ }
+
+ return true;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/metrics.h b/gfx/ots/src/metrics.h
new file mode 100644
index 000000000..767be2fdb
--- /dev/null
+++ b/gfx/ots/src/metrics.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium 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_METRICS_H_
+#define OTS_METRICS_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMetricsHeader {
+ uint32_t version;
+ int16_t ascent;
+ int16_t descent;
+ int16_t linegap;
+ uint16_t adv_width_max;
+ int16_t min_sb1;
+ int16_t min_sb2;
+ int16_t max_extent;
+ int16_t caret_slope_rise;
+ int16_t caret_slope_run;
+ int16_t caret_offset;
+ uint16_t num_metrics;
+};
+
+struct OpenTypeMetricsTable {
+ std::vector<std::pair<uint16_t, int16_t> > entries;
+ std::vector<int16_t> sbs;
+};
+
+bool ParseMetricsHeader(Font *font, Buffer *table,
+ OpenTypeMetricsHeader *header);
+bool SerialiseMetricsHeader(const ots::Font *font,
+ OTSStream *out,
+ const OpenTypeMetricsHeader *header);
+
+bool ParseMetricsTable(const ots::Font *font,
+ Buffer *table,
+ const uint16_t num_glyphs,
+ const OpenTypeMetricsHeader *header,
+ OpenTypeMetricsTable *metrics);
+bool SerialiseMetricsTable(const ots::Font *font,
+ OTSStream *out,
+ const OpenTypeMetricsTable *metrics);
+
+} // namespace ots
+
+#endif // OTS_METRICS_H_
+
diff --git a/gfx/ots/src/moz.build b/gfx/ots/src/moz.build
new file mode 100644
index 000000000..f8e60d5f8
--- /dev/null
+++ b/gfx/ots/src/moz.build
@@ -0,0 +1,60 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ '../include/opentype-sanitiser.h',
+ '../include/ots-memory-stream.h',
+]
+
+SOURCES += [
+ # don't unify sources that use a (file-specific) DROP_THIS_TABLE macro
+ 'gasp.cc',
+ 'gdef.cc',
+ 'gpos.cc',
+ 'gsub.cc',
+ 'hdmx.cc',
+ 'kern.cc',
+ 'ltsh.cc',
+ 'math.cc',
+ 'vdmx.cc',
+ 'vorg.cc',
+]
+
+UNIFIED_SOURCES += [
+ 'cff.cc',
+ 'cff_type2_charstring.cc',
+ 'cmap.cc',
+ 'cvt.cc',
+ 'fpgm.cc',
+ 'glyf.cc',
+ 'head.cc',
+ 'hhea.cc',
+ 'hmtx.cc',
+ 'layout.cc',
+ 'loca.cc',
+ 'maxp.cc',
+ 'metrics.cc',
+ 'name.cc',
+ 'os2.cc',
+ 'ots.cc',
+ 'post.cc',
+ 'prep.cc',
+ 'vhea.cc',
+ 'vmtx.cc',
+]
+
+# We allow warnings for third-party code that can be updated from upstream.
+ALLOW_COMPILER_WARNINGS = True
+
+FINAL_LIBRARY = 'gkmedias'
+
+DEFINES['PACKAGE_VERSION'] = '"moz"'
+DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
+
+USE_LIBS += [
+ 'brotli',
+ 'woff2',
+]
diff --git a/gfx/ots/src/name.cc b/gfx/ots/src/name.cc
new file mode 100644
index 000000000..e55be7537
--- /dev/null
+++ b/gfx/ots/src/name.cc
@@ -0,0 +1,330 @@
+// Copyright (c) 2011 The Chromium 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 "name.h"
+
+#include <algorithm>
+#include <cstring>
+
+// name - Naming Table
+// http://www.microsoft.com/typography/otspec/name.htm
+
+#define TABLE_NAME "name"
+
+namespace {
+
+bool ValidInPsName(char c) {
+ return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
+}
+
+bool CheckPsNameAscii(const std::string& name) {
+ for (unsigned i = 0; i < name.size(); ++i) {
+ if (!ValidInPsName(name[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CheckPsNameUtf16Be(const std::string& name) {
+ if ((name.size() & 1) != 0)
+ return false;
+
+ for (unsigned i = 0; i < name.size(); i += 2) {
+ if (name[i] != 0) {
+ return false;
+ }
+ if (!ValidInPsName(name[i+1])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void AssignToUtf16BeFromAscii(std::string* target,
+ const std::string& source) {
+ target->resize(source.size() * 2);
+ for (unsigned i = 0, j = 0; i < source.size(); i++) {
+ (*target)[j++] = '\0';
+ (*target)[j++] = source[i];
+ }
+}
+
+} // namespace
+
+
+namespace ots {
+
+bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypeNAME* name = new OpenTypeNAME;
+ font->name = name;
+
+ uint16_t format = 0;
+ if (!table.ReadU16(&format) || format > 1) {
+ return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
+ }
+
+ uint16_t count = 0;
+ if (!table.ReadU16(&count)) {
+ return OTS_FAILURE_MSG("Failed to read name count");
+ }
+
+ uint16_t string_offset = 0;
+ if (!table.ReadU16(&string_offset) || string_offset > length) {
+ return OTS_FAILURE_MSG("Failed to read strings offset");
+ }
+ const char* string_base = reinterpret_cast<const char*>(data) +
+ string_offset;
+
+ bool sort_required = false;
+
+ // Read all the names, discarding any with invalid IDs,
+ // and any where the offset/length would be outside the table.
+ // A stricter alternative would be to reject the font if there
+ // are invalid name records, but it's not clear that is necessary.
+ for (unsigned i = 0; i < count; ++i) {
+ NameRecord rec;
+ uint16_t name_length, name_offset = 0;
+ if (!table.ReadU16(&rec.platform_id) ||
+ !table.ReadU16(&rec.encoding_id) ||
+ !table.ReadU16(&rec.language_id) ||
+ !table.ReadU16(&rec.name_id) ||
+ !table.ReadU16(&name_length) ||
+ !table.ReadU16(&name_offset)) {
+ return OTS_FAILURE_MSG("Failed to read name entry %d", i);
+ }
+ // check platform & encoding, discard names with unknown values
+ switch (rec.platform_id) {
+ case 0: // Unicode
+ if (rec.encoding_id > 6) {
+ continue;
+ }
+ break;
+ case 1: // Macintosh
+ if (rec.encoding_id > 32) {
+ continue;
+ }
+ break;
+ case 2: // ISO
+ if (rec.encoding_id > 2) {
+ continue;
+ }
+ break;
+ case 3: // Windows: IDs 7 to 9 are "reserved"
+ if (rec.encoding_id > 6 && rec.encoding_id != 10) {
+ continue;
+ }
+ break;
+ case 4: // Custom (OTF Windows NT compatibility)
+ if (rec.encoding_id > 255) {
+ continue;
+ }
+ break;
+ default: // unknown platform
+ continue;
+ }
+
+ const unsigned name_end = static_cast<unsigned>(string_offset) +
+ name_offset + name_length;
+ if (name_end > length) {
+ continue;
+ }
+ rec.text.resize(name_length);
+ rec.text.assign(string_base + name_offset, name_length);
+
+ if (rec.name_id == 6) {
+ // PostScript name: check that it is valid, if not then discard it
+ if (rec.platform_id == 1) {
+ if (!CheckPsNameAscii(rec.text)) {
+ continue;
+ }
+ } else if (rec.platform_id == 0 || rec.platform_id == 3) {
+ if (!CheckPsNameUtf16Be(rec.text)) {
+ continue;
+ }
+ }
+ }
+
+ if (!name->names.empty() && !(name->names.back() < rec)) {
+ OTS_WARNING("name records are not sorted.");
+ sort_required = true;
+ }
+
+ name->names.push_back(rec);
+ }
+
+ if (format == 1) {
+ // extended name table format with language tags
+ uint16_t lang_tag_count;
+ if (!table.ReadU16(&lang_tag_count)) {
+ return OTS_FAILURE_MSG("Failed to read language tag count");
+ }
+ for (unsigned i = 0; i < lang_tag_count; ++i) {
+ uint16_t tag_length = 0;
+ uint16_t tag_offset = 0;
+ if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
+ return OTS_FAILURE_MSG("Faile to read tag length or offset");
+ }
+ const unsigned tag_end = static_cast<unsigned>(string_offset) +
+ tag_offset + tag_length;
+ if (tag_end > length) {
+ return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
+ }
+ std::string tag(string_base + tag_offset, tag_length);
+ name->lang_tags.push_back(tag);
+ }
+ }
+
+ if (table.offset() > string_offset) {
+ // the string storage apparently overlapped the name/tag records;
+ // consider this font to be badly broken
+ return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
+ }
+
+ // check existence of required name strings (synthesize if necessary)
+ // [0 - copyright - skip]
+ // 1 - family
+ // 2 - subfamily
+ // [3 - unique ID - skip]
+ // 4 - full name
+ // 5 - version
+ // 6 - postscript name
+ static const uint16_t kStdNameCount = 7;
+ static const char* kStdNames[kStdNameCount] = {
+ NULL,
+ "OTS derived font",
+ "Unspecified",
+ NULL,
+ "OTS derived font",
+ "1.000",
+ "OTS-derived-font"
+ };
+
+ // scan the names to check whether the required "standard" ones are present;
+ // 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 = name->names.begin();
+ name_iter != name->names.end(); ++name_iter) {
+ const uint16_t id = name_iter->name_id;
+ if (id >= kStdNameCount || kStdNames[id] == NULL) {
+ continue;
+ }
+ if (name_iter->platform_id == 1) {
+ mac_name[id] = true;
+ continue;
+ }
+ if (name_iter->platform_id == 3) {
+ win_name[id] = true;
+ continue;
+ }
+ }
+
+ for (uint16_t i = 0; i < kStdNameCount; ++i) {
+ if (kStdNames[i] == NULL) {
+ continue;
+ }
+ if (!mac_name[i] && !win_name[i]) {
+ NameRecord mac_rec(1 /* platform_id */, 0 /* encoding_id */,
+ 0 /* language_id */ , i /* name_id */);
+ mac_rec.text.assign(kStdNames[i]);
+
+ NameRecord win_rec(3 /* platform_id */, 1 /* encoding_id */,
+ 1033 /* language_id */ , i /* name_id */);
+ AssignToUtf16BeFromAscii(&win_rec.text, std::string(kStdNames[i]));
+
+ name->names.push_back(mac_rec);
+ name->names.push_back(win_rec);
+ sort_required = true;
+ }
+ }
+
+ if (sort_required) {
+ std::sort(name->names.begin(), name->names.end());
+ }
+
+ return true;
+}
+
+bool ots_name_should_serialise(Font *font) {
+ return font->name != NULL;
+}
+
+bool ots_name_serialise(OTSStream* out, Font *font) {
+ const OpenTypeNAME* name = font->name;
+
+ uint16_t name_count = static_cast<uint16_t>(name->names.size());
+ uint16_t lang_tag_count = static_cast<uint16_t>(name->lang_tags.size());
+ uint16_t format = 0;
+ size_t string_offset = 6 + name_count * 12;
+
+ if (name->lang_tags.size() > 0) {
+ // lang tags require a format-1 name table
+ format = 1;
+ string_offset += 2 + lang_tag_count * 4;
+ }
+ if (string_offset > 0xffff) {
+ return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
+ }
+ if (!out->WriteU16(format) ||
+ !out->WriteU16(name_count) ||
+ !out->WriteU16(static_cast<uint16_t>(string_offset))) {
+ return OTS_FAILURE_MSG("Failed to write name header");
+ }
+
+ std::string string_data;
+ for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
+ name_iter != name->names.end(); ++name_iter) {
+ const NameRecord& rec = *name_iter;
+ if (string_data.size() + rec.text.size() >
+ std::numeric_limits<uint16_t>::max() ||
+ !out->WriteU16(rec.platform_id) ||
+ !out->WriteU16(rec.encoding_id) ||
+ !out->WriteU16(rec.language_id) ||
+ !out->WriteU16(rec.name_id) ||
+ !out->WriteU16(static_cast<uint16_t>(rec.text.size())) ||
+ !out->WriteU16(static_cast<uint16_t>(string_data.size())) ) {
+ return OTS_FAILURE_MSG("Faile to write name entry");
+ }
+ string_data.append(rec.text);
+ }
+
+ if (format == 1) {
+ if (!out->WriteU16(lang_tag_count)) {
+ return OTS_FAILURE_MSG("Faile to write language tag count");
+ }
+ for (std::vector<std::string>::const_iterator tag_iter =
+ name->lang_tags.begin();
+ tag_iter != name->lang_tags.end(); ++tag_iter) {
+ if (string_data.size() + tag_iter->size() >
+ std::numeric_limits<uint16_t>::max() ||
+ !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
+ !out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
+ return OTS_FAILURE_MSG("Failed to write string");
+ }
+ string_data.append(*tag_iter);
+ }
+ }
+
+ if (!out->Write(string_data.data(), string_data.size())) {
+ return OTS_FAILURE_MSG("Faile to write string data");
+ }
+
+ return true;
+}
+
+void ots_name_reuse(Font *font, Font *other) {
+ font->name = other->name;
+ font->name_reused = true;
+}
+
+void ots_name_free(Font *font) {
+ delete font->name;
+}
+
+} // namespace
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/name.h b/gfx/ots/src/name.h
new file mode 100644
index 000000000..a11965f46
--- /dev/null
+++ b/gfx/ots/src/name.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium 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_NAME_H_
+#define OTS_NAME_H_
+
+#include <new>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct NameRecord {
+ NameRecord() {
+ }
+
+ NameRecord(uint16_t platformID, uint16_t encodingID,
+ uint16_t languageID, uint16_t nameID)
+ : platform_id(platformID),
+ encoding_id(encodingID),
+ language_id(languageID),
+ name_id(nameID) {
+ }
+
+ uint16_t platform_id;
+ uint16_t encoding_id;
+ uint16_t language_id;
+ uint16_t name_id;
+ std::string text;
+
+ bool operator<(const NameRecord& rhs) const {
+ if (platform_id < rhs.platform_id) return true;
+ if (platform_id > rhs.platform_id) return false;
+ if (encoding_id < rhs.encoding_id) return true;
+ if (encoding_id > rhs.encoding_id) return false;
+ if (language_id < rhs.language_id) return true;
+ if (language_id > rhs.language_id) return false;
+ return name_id < rhs.name_id;
+ }
+};
+
+struct OpenTypeNAME {
+ std::vector<NameRecord> names;
+ std::vector<std::string> lang_tags;
+};
+
+} // namespace ots
+
+#endif // OTS_NAME_H_
diff --git a/gfx/ots/src/os2.cc b/gfx/ots/src/os2.cc
new file mode 100644
index 000000000..fd5cdd1d6
--- /dev/null
+++ b/gfx/ots/src/os2.cc
@@ -0,0 +1,336 @@
+// Copyright (c) 2009 The Chromium 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 <string>
+
+#include "os2.h"
+#include "head.h"
+
+// OS/2 - OS/2 and Windows Metrics
+// http://www.microsoft.com/typography/otspec/os2.htm
+
+#define TABLE_NAME "OS/2"
+
+namespace ots {
+
+bool ots_os2_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypeOS2 *os2 = new OpenTypeOS2;
+ font->os2 = os2;
+
+ if (!table.ReadU16(&os2->version) ||
+ !table.ReadS16(&os2->avg_char_width) ||
+ !table.ReadU16(&os2->weight_class) ||
+ !table.ReadU16(&os2->width_class) ||
+ !table.ReadU16(&os2->type) ||
+ !table.ReadS16(&os2->subscript_x_size) ||
+ !table.ReadS16(&os2->subscript_y_size) ||
+ !table.ReadS16(&os2->subscript_x_offset) ||
+ !table.ReadS16(&os2->subscript_y_offset) ||
+ !table.ReadS16(&os2->superscript_x_size) ||
+ !table.ReadS16(&os2->superscript_y_size) ||
+ !table.ReadS16(&os2->superscript_x_offset) ||
+ !table.ReadS16(&os2->superscript_y_offset) ||
+ !table.ReadS16(&os2->strikeout_size) ||
+ !table.ReadS16(&os2->strikeout_position) ||
+ !table.ReadS16(&os2->family_class)) {
+ return OTS_FAILURE_MSG("Error reading basic table elements");
+ }
+
+ if (os2->version > 5) {
+ return OTS_FAILURE_MSG("Unsupported table version: %u", os2->version);
+ }
+
+ // Follow WPF Font Selection Model's advice.
+ if (1 <= os2->weight_class && os2->weight_class <= 9) {
+ OTS_WARNING("Bad usWeightClass: %u, changing it to: %u", os2->weight_class, os2->weight_class * 100);
+ os2->weight_class *= 100;
+ }
+ // Ditto.
+ if (os2->weight_class > 999) {
+ OTS_WARNING("Bad usWeightClass: %u, changing it to: %d", os2->weight_class, 999);
+ os2->weight_class = 999;
+ }
+
+ if (os2->width_class < 1) {
+ OTS_WARNING("Bad usWidthClass: %u, changing it to: %d", os2->width_class, 1);
+ os2->width_class = 1;
+ } else if (os2->width_class > 9) {
+ OTS_WARNING("Bad usWidthClass: %u, changing it to: %d", os2->width_class, 9);
+ os2->width_class = 9;
+ }
+
+ // lowest 3 bits of fsType are exclusive.
+ if (os2->type & 0x2) {
+ // mask bits 2 & 3.
+ os2->type &= 0xfff3u;
+ } else if (os2->type & 0x4) {
+ // mask bits 1 & 3.
+ os2->type &= 0xfff4u;
+ } else if (os2->type & 0x8) {
+ // mask bits 1 & 2.
+ os2->type &= 0xfff9u;
+ }
+
+ // mask reserved bits. use only 0..3, 8, 9 bits.
+ os2->type &= 0x30f;
+
+#define SET_TO_ZERO(a, b) \
+ if (os2->b < 0) { \
+ OTS_WARNING("Bad " a ": %d, setting it to zero", os2->b); \
+ os2->b = 0; \
+ }
+
+ SET_TO_ZERO("ySubscriptXSize", subscript_x_size);
+ SET_TO_ZERO("ySubscriptYSize", subscript_y_size);
+ SET_TO_ZERO("ySuperscriptXSize", superscript_x_size);
+ SET_TO_ZERO("ySuperscriptYSize", superscript_y_size);
+ SET_TO_ZERO("yStrikeoutSize", strikeout_size);
+#undef SET_TO_ZERO
+
+ static std::string panose_strings[10] = {
+ "bFamilyType",
+ "bSerifStyle",
+ "bWeight",
+ "bProportion",
+ "bContrast",
+ "bStrokeVariation",
+ "bArmStyle",
+ "bLetterform",
+ "bMidline",
+ "bXHeight",
+ };
+ for (unsigned i = 0; i < 10; ++i) {
+ if (!table.ReadU8(&os2->panose[i])) {
+ return OTS_FAILURE_MSG("Error reading PANOSE %s", panose_strings[i].c_str());
+ }
+ }
+
+ if (!table.ReadU32(&os2->unicode_range_1) ||
+ !table.ReadU32(&os2->unicode_range_2) ||
+ !table.ReadU32(&os2->unicode_range_3) ||
+ !table.ReadU32(&os2->unicode_range_4) ||
+ !table.ReadU32(&os2->vendor_id) ||
+ !table.ReadU16(&os2->selection) ||
+ !table.ReadU16(&os2->first_char_index) ||
+ !table.ReadU16(&os2->last_char_index) ||
+ !table.ReadS16(&os2->typo_ascender) ||
+ !table.ReadS16(&os2->typo_descender) ||
+ !table.ReadS16(&os2->typo_linegap) ||
+ !table.ReadU16(&os2->win_ascent) ||
+ !table.ReadU16(&os2->win_descent)) {
+ return OTS_FAILURE_MSG("Error reading more basic table fields");
+ }
+
+ // If bit 6 is set, then bits 0 and 5 must be clear.
+ if (os2->selection & 0x40) {
+ os2->selection &= 0xffdeu;
+ }
+
+ // the settings of bits 0 and 1 must be reflected in the macStyle bits
+ // in the 'head' table.
+ if (!font->head) {
+ return OTS_FAILURE_MSG("Needed head table is missing from the font");
+ }
+ if ((os2->selection & 0x1) &&
+ !(font->head->mac_style & 0x2)) {
+ OTS_WARNING("adjusting Mac style (italic)");
+ font->head->mac_style |= 0x2;
+ }
+ if ((os2->selection & 0x2) &&
+ !(font->head->mac_style & 0x4)) {
+ OTS_WARNING("adjusting Mac style (underscore)");
+ font->head->mac_style |= 0x4;
+ }
+
+ // While bit 6 on implies that bits 0 and 1 of macStyle are clear,
+ // the reverse is not true.
+ if ((os2->selection & 0x40) &&
+ (font->head->mac_style & 0x3)) {
+ OTS_WARNING("adjusting Mac style (regular)");
+ font->head->mac_style &= 0xfffcu;
+ }
+
+ if ((os2->version < 4) &&
+ (os2->selection & 0x300)) {
+ // bit 8 and 9 must be unset in OS/2 table versions less than 4.
+ return OTS_FAILURE_MSG("Version %d incompatible with selection %d", os2->version, os2->selection);
+ }
+
+ // mask reserved bits. use only 0..9 bits.
+ os2->selection &= 0x3ff;
+
+ if (os2->first_char_index > os2->last_char_index) {
+ return OTS_FAILURE_MSG("first char index %d > last char index %d in os2", os2->first_char_index, os2->last_char_index);
+ }
+ if (os2->typo_linegap < 0) {
+ OTS_WARNING("bad linegap: %d", os2->typo_linegap);
+ os2->typo_linegap = 0;
+ }
+
+ if (os2->version < 1) {
+ // http://www.microsoft.com/typography/otspec/os2ver0.htm
+ return true;
+ }
+
+ if (length < offsetof(OpenTypeOS2, code_page_range_2)) {
+ OTS_WARNING("bad version number: %u", os2->version);
+ // Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version
+ // numbers. Fix them.
+ os2->version = 0;
+ return true;
+ }
+
+ if (!table.ReadU32(&os2->code_page_range_1) ||
+ !table.ReadU32(&os2->code_page_range_2)) {
+ return OTS_FAILURE_MSG("Failed to read codepage ranges");
+ }
+
+ if (os2->version < 2) {
+ // http://www.microsoft.com/typography/otspec/os2ver1.htm
+ return true;
+ }
+
+ if (length < offsetof(OpenTypeOS2, max_context)) {
+ OTS_WARNING("bad version number: %u", os2->version);
+ // some Japanese fonts (e.g., mona.ttf) have weird version number.
+ // fix them.
+ os2->version = 1;
+ return true;
+ }
+
+ if (!table.ReadS16(&os2->x_height) ||
+ !table.ReadS16(&os2->cap_height) ||
+ !table.ReadU16(&os2->default_char) ||
+ !table.ReadU16(&os2->break_char) ||
+ !table.ReadU16(&os2->max_context)) {
+ return OTS_FAILURE_MSG("Failed to read version 2-specific fields");
+ }
+
+ if (os2->x_height < 0) {
+ OTS_WARNING("bad x_height: %d", os2->x_height);
+ os2->x_height = 0;
+ }
+ if (os2->cap_height < 0) {
+ OTS_WARNING("bad cap_height: %d", os2->cap_height);
+ os2->cap_height = 0;
+ }
+
+ if (os2->version < 5) {
+ // http://www.microsoft.com/typography/otspec/os2ver4.htm
+ return true;
+ }
+
+ if (!table.ReadU16(&os2->lower_optical_pointsize) ||
+ !table.ReadU16(&os2->upper_optical_pointsize)) {
+ return OTS_FAILURE_MSG("Failed to read version 5-specific fields");
+ }
+
+ if (os2->lower_optical_pointsize > 0xFFFE) {
+ OTS_WARNING("'usLowerOpticalPointSize' is bigger than 0xFFFE: %d", os2->lower_optical_pointsize);
+ os2->lower_optical_pointsize = 0xFFFE;
+ }
+
+ if (os2->upper_optical_pointsize < 2) {
+ OTS_WARNING("'usUpperOpticalPointSize' is lower than 2: %d", os2->upper_optical_pointsize);
+ os2->upper_optical_pointsize = 2;
+ }
+
+ return true;
+}
+
+bool ots_os2_should_serialise(Font *font) {
+ return font->os2 != NULL;
+}
+
+bool ots_os2_serialise(OTSStream *out, Font *font) {
+ const OpenTypeOS2 *os2 = font->os2;
+
+ if (!out->WriteU16(os2->version) ||
+ !out->WriteS16(os2->avg_char_width) ||
+ !out->WriteU16(os2->weight_class) ||
+ !out->WriteU16(os2->width_class) ||
+ !out->WriteU16(os2->type) ||
+ !out->WriteS16(os2->subscript_x_size) ||
+ !out->WriteS16(os2->subscript_y_size) ||
+ !out->WriteS16(os2->subscript_x_offset) ||
+ !out->WriteS16(os2->subscript_y_offset) ||
+ !out->WriteS16(os2->superscript_x_size) ||
+ !out->WriteS16(os2->superscript_y_size) ||
+ !out->WriteS16(os2->superscript_x_offset) ||
+ !out->WriteS16(os2->superscript_y_offset) ||
+ !out->WriteS16(os2->strikeout_size) ||
+ !out->WriteS16(os2->strikeout_position) ||
+ !out->WriteS16(os2->family_class)) {
+ return OTS_FAILURE_MSG("Failed to write basic OS2 information");
+ }
+
+ for (unsigned i = 0; i < 10; ++i) {
+ if (!out->Write(&os2->panose[i], 1)) {
+ return OTS_FAILURE_MSG("Failed to write os2 panose information");
+ }
+ }
+
+ if (!out->WriteU32(os2->unicode_range_1) ||
+ !out->WriteU32(os2->unicode_range_2) ||
+ !out->WriteU32(os2->unicode_range_3) ||
+ !out->WriteU32(os2->unicode_range_4) ||
+ !out->WriteU32(os2->vendor_id) ||
+ !out->WriteU16(os2->selection) ||
+ !out->WriteU16(os2->first_char_index) ||
+ !out->WriteU16(os2->last_char_index) ||
+ !out->WriteS16(os2->typo_ascender) ||
+ !out->WriteS16(os2->typo_descender) ||
+ !out->WriteS16(os2->typo_linegap) ||
+ !out->WriteU16(os2->win_ascent) ||
+ !out->WriteU16(os2->win_descent)) {
+ return OTS_FAILURE_MSG("Failed to write version 1-specific fields");
+ }
+
+ if (os2->version < 1) {
+ return true;
+ }
+
+ if (!out->WriteU32(os2->code_page_range_1) ||
+ !out->WriteU32(os2->code_page_range_2)) {
+ return OTS_FAILURE_MSG("Failed to write codepage ranges");
+ }
+
+ if (os2->version < 2) {
+ return true;
+ }
+
+ if (!out->WriteS16(os2->x_height) ||
+ !out->WriteS16(os2->cap_height) ||
+ !out->WriteU16(os2->default_char) ||
+ !out->WriteU16(os2->break_char) ||
+ !out->WriteU16(os2->max_context)) {
+ return OTS_FAILURE_MSG("Failed to write version 2-specific fields");
+ }
+
+ if (os2->version < 5) {
+ return true;
+ }
+
+ if (!out->WriteU16(os2->lower_optical_pointsize) ||
+ !out->WriteU16(os2->upper_optical_pointsize)) {
+ return OTS_FAILURE_MSG("Failed to write version 5-specific fields");
+ }
+
+ return true;
+}
+
+void ots_os2_reuse(Font *font, Font *other) {
+ font->os2 = other->os2;
+ font->os2_reused = true;
+}
+
+void ots_os2_free(Font *font) {
+ delete font->os2;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/os2.h b/gfx/ots/src/os2.h
new file mode 100644
index 000000000..01511c5dc
--- /dev/null
+++ b/gfx/ots/src/os2.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2009 The Chromium 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_OS2_H_
+#define OTS_OS2_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeOS2 {
+ uint16_t version;
+ int16_t avg_char_width;
+ uint16_t weight_class;
+ uint16_t width_class;
+ uint16_t type;
+ int16_t subscript_x_size;
+ int16_t subscript_y_size;
+ int16_t subscript_x_offset;
+ int16_t subscript_y_offset;
+ int16_t superscript_x_size;
+ int16_t superscript_y_size;
+ int16_t superscript_x_offset;
+ int16_t superscript_y_offset;
+ int16_t strikeout_size;
+ int16_t strikeout_position;
+ int16_t family_class;
+ uint8_t panose[10];
+ uint32_t unicode_range_1;
+ uint32_t unicode_range_2;
+ uint32_t unicode_range_3;
+ uint32_t unicode_range_4;
+ uint32_t vendor_id;
+ uint16_t selection;
+ uint16_t first_char_index;
+ uint16_t last_char_index;
+ int16_t typo_ascender;
+ int16_t typo_descender;
+ int16_t typo_linegap;
+ uint16_t win_ascent;
+ uint16_t win_descent;
+ uint32_t code_page_range_1;
+ uint32_t code_page_range_2;
+ int16_t x_height;
+ int16_t cap_height;
+ uint16_t default_char;
+ uint16_t break_char;
+ uint16_t max_context;
+ uint16_t lower_optical_pointsize;
+ uint16_t upper_optical_pointsize;
+};
+
+} // namespace ots
+
+#endif // OTS_OS2_H_
diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc
new file mode 100644
index 000000000..15794475a
--- /dev/null
+++ b/gfx/ots/src/ots.cc
@@ -0,0 +1,907 @@
+// Copyright (c) 2009 The Chromium 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 "ots.h"
+
+#include <sys/types.h>
+#include <zlib.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <map>
+#include <vector>
+
+#include "woff2_dec.h"
+
+// The OpenType Font File
+// http://www.microsoft.com/typography/otspec/cmap.htm
+
+namespace {
+
+// Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
+#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
+#define OTS_FAILURE_MSG_HDR(msg_) OTS_FAILURE_MSG_(header, msg_)
+#define OTS_WARNING_MSG_HDR(msg_) OTS_WARNING_MSG_(header, msg_)
+
+
+struct OpenTypeTable {
+ uint32_t tag;
+ uint32_t chksum;
+ uint32_t offset;
+ uint32_t length;
+ uint32_t uncompressed_length;
+};
+
+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;
+}
+
+struct Arena {
+ public:
+ ~Arena() {
+ for (std::vector<uint8_t*>::iterator
+ i = hunks_.begin(); i != hunks_.end(); ++i) {
+ delete[] *i;
+ }
+ }
+
+ uint8_t* Allocate(size_t length) {
+ uint8_t* p = new uint8_t[length];
+ hunks_.push_back(p);
+ return p;
+ }
+
+ private:
+ std::vector<uint8_t*> hunks_;
+};
+
+const struct {
+ uint32_t tag;
+ bool (*parse)(ots::Font *font, const uint8_t *data, size_t length);
+ bool (*serialise)(ots::OTSStream *out, ots::Font *font);
+ bool (*should_serialise)(ots::Font *font);
+ void (*reuse)(ots::Font *font, ots::Font *other);
+ bool required;
+} table_parsers[] = {
+ { OTS_TAG('m','a','x','p'), ots::ots_maxp_parse, ots::ots_maxp_serialise,
+ ots::ots_maxp_should_serialise, ots::ots_maxp_reuse, true },
+ { OTS_TAG('h','e','a','d'), ots::ots_head_parse, ots::ots_head_serialise,
+ ots::ots_head_should_serialise, ots::ots_head_reuse, true },
+ { OTS_TAG('O','S','/','2'), ots::ots_os2_parse, ots::ots_os2_serialise,
+ ots::ots_os2_should_serialise, ots::ots_os2_reuse, true },
+ { OTS_TAG('c','m','a','p'), ots::ots_cmap_parse, ots::ots_cmap_serialise,
+ ots::ots_cmap_should_serialise, ots::ots_cmap_reuse, true },
+ { OTS_TAG('h','h','e','a'), ots::ots_hhea_parse, ots::ots_hhea_serialise,
+ ots::ots_hhea_should_serialise, ots::ots_hhea_reuse, true },
+ { OTS_TAG('h','m','t','x'), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
+ ots::ots_hmtx_should_serialise, ots::ots_hmtx_reuse, true },
+ { OTS_TAG('n','a','m','e'), ots::ots_name_parse, ots::ots_name_serialise,
+ ots::ots_name_should_serialise, ots::ots_name_reuse, true },
+ { OTS_TAG('p','o','s','t'), ots::ots_post_parse, ots::ots_post_serialise,
+ ots::ots_post_should_serialise, ots::ots_post_reuse, true },
+ { OTS_TAG('l','o','c','a'), ots::ots_loca_parse, ots::ots_loca_serialise,
+ ots::ots_loca_should_serialise, ots::ots_loca_reuse, false },
+ { OTS_TAG('g','l','y','f'), ots::ots_glyf_parse, ots::ots_glyf_serialise,
+ ots::ots_glyf_should_serialise, ots::ots_glyf_reuse, false },
+ { OTS_TAG('C','F','F',' '), ots::ots_cff_parse, ots::ots_cff_serialise,
+ ots::ots_cff_should_serialise, ots::ots_cff_reuse, false },
+ { OTS_TAG('V','D','M','X'), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
+ ots::ots_vdmx_should_serialise, ots::ots_vdmx_reuse, false },
+ { OTS_TAG('h','d','m','x'), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
+ ots::ots_hdmx_should_serialise, ots::ots_hdmx_reuse, false },
+ { OTS_TAG('g','a','s','p'), ots::ots_gasp_parse, ots::ots_gasp_serialise,
+ ots::ots_gasp_should_serialise, ots::ots_gasp_reuse, false },
+ { OTS_TAG('c','v','t',' '), ots::ots_cvt_parse, ots::ots_cvt_serialise,
+ ots::ots_cvt_should_serialise, ots::ots_cvt_reuse, false },
+ { OTS_TAG('f','p','g','m'), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
+ ots::ots_fpgm_should_serialise, ots::ots_fpgm_reuse, false },
+ { OTS_TAG('p','r','e','p'), ots::ots_prep_parse, ots::ots_prep_serialise,
+ ots::ots_prep_should_serialise, ots::ots_prep_reuse, false },
+ { OTS_TAG('L','T','S','H'), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
+ ots::ots_ltsh_should_serialise, ots::ots_ltsh_reuse, false },
+ { OTS_TAG('V','O','R','G'), ots::ots_vorg_parse, ots::ots_vorg_serialise,
+ ots::ots_vorg_should_serialise, ots::ots_vorg_reuse, false },
+ { OTS_TAG('k','e','r','n'), ots::ots_kern_parse, ots::ots_kern_serialise,
+ ots::ots_kern_should_serialise, ots::ots_kern_reuse, false },
+ // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
+ // because they could refer GDEF table.
+ { OTS_TAG('G','D','E','F'), ots::ots_gdef_parse, ots::ots_gdef_serialise,
+ ots::ots_gdef_should_serialise, ots::ots_gdef_reuse, false },
+ { OTS_TAG('G','P','O','S'), ots::ots_gpos_parse, ots::ots_gpos_serialise,
+ ots::ots_gpos_should_serialise, ots::ots_gpos_reuse, false },
+ { OTS_TAG('G','S','U','B'), ots::ots_gsub_parse, ots::ots_gsub_serialise,
+ ots::ots_gsub_should_serialise, ots::ots_gsub_reuse, false },
+ { OTS_TAG('v','h','e','a'), ots::ots_vhea_parse, ots::ots_vhea_serialise,
+ ots::ots_vhea_should_serialise, ots::ots_vhea_reuse, false },
+ { OTS_TAG('v','m','t','x'), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+ ots::ots_vmtx_should_serialise, ots::ots_vmtx_reuse, false },
+ { OTS_TAG('M','A','T','H'), ots::ots_math_parse, ots::ots_math_serialise,
+ ots::ots_math_should_serialise, ots::ots_math_reuse, false },
+ { 0, NULL, NULL, NULL, NULL, false },
+};
+
+bool ProcessGeneric(ots::OpenTypeFile *header,
+ ots::Font *font,
+ uint32_t signature,
+ ots::OTSStream *output,
+ const uint8_t *data, size_t length,
+ const std::vector<OpenTypeTable>& tables,
+ ots::Buffer& file);
+
+bool ProcessTTF(ots::OpenTypeFile *header,
+ ots::Font *font,
+ ots::OTSStream *output, const uint8_t *data, size_t length,
+ uint32_t offset = 0) {
+ ots::Buffer file(data + offset, length - offset);
+
+ if (offset > length) {
+ return OTS_FAILURE_MSG_HDR("offset beyond end of file");
+ }
+
+ // we disallow all files > 1GB in size for sanity.
+ if (length > 1024 * 1024 * 1024) {
+ return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+ }
+
+ if (!file.ReadU32(&font->version)) {
+ return OTS_FAILURE_MSG_HDR("error reading version tag");
+ }
+ if (!ots::IsValidVersionTag(font->version)) {
+ return OTS_FAILURE_MSG_HDR("invalid version tag");
+ }
+
+ if (!file.ReadU16(&font->num_tables) ||
+ !file.ReadU16(&font->search_range) ||
+ !file.ReadU16(&font->entry_selector) ||
+ !file.ReadU16(&font->range_shift)) {
+ return OTS_FAILURE_MSG_HDR("error reading table directory search header");
+ }
+
+ // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
+ // overflow num_tables is, at most, 2^16 / 16 = 2^12
+ if (font->num_tables >= 4096 || font->num_tables < 1) {
+ return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables");
+ }
+
+ unsigned max_pow2 = 0;
+ while (1u << (max_pow2 + 1) <= font->num_tables) {
+ max_pow2++;
+ }
+ const uint16_t expected_search_range = (1u << max_pow2) << 4;
+
+ // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
+ // http://www.princexml.com/fonts/ have unexpected search_range value.
+ if (font->search_range != expected_search_range) {
+ OTS_WARNING_MSG_HDR("bad search range");
+ font->search_range = expected_search_range; // Fix the value.
+ }
+
+ // entry_selector is Log2(maximum power of 2 <= numTables)
+ if (font->entry_selector != max_pow2) {
+ return OTS_FAILURE_MSG_HDR("incorrect entrySelector for table directory");
+ }
+
+ // range_shift is NumTables x 16-searchRange. We know that 16*num_tables
+ // doesn't over flow because we range checked it above. Also, we know that
+ // it's > font->search_range by construction of search_range.
+ const uint16_t expected_range_shift =
+ 16 * font->num_tables - font->search_range;
+ if (font->range_shift != expected_range_shift) {
+ OTS_WARNING_MSG_HDR("bad range shift");
+ font->range_shift = expected_range_shift; // the same as above.
+ }
+
+ // Next up is the list of tables.
+ std::vector<OpenTypeTable> tables;
+
+ for (unsigned i = 0; i < font->num_tables; ++i) {
+ OpenTypeTable table;
+ if (!file.ReadU32(&table.tag) ||
+ !file.ReadU32(&table.chksum) ||
+ !file.ReadU32(&table.offset) ||
+ !file.ReadU32(&table.length)) {
+ return OTS_FAILURE_MSG_HDR("error reading table directory");
+ }
+
+ table.uncompressed_length = table.length;
+ tables.push_back(table);
+ }
+
+ return ProcessGeneric(header, font, font->version, output, data, length,
+ tables, file);
+}
+
+bool ProcessTTC(ots::OpenTypeFile *header,
+ ots::OTSStream *output,
+ const uint8_t *data,
+ size_t length,
+ uint32_t index) {
+ ots::Buffer file(data, length);
+
+ // we disallow all files > 1GB in size for sanity.
+ if (length > 1024 * 1024 * 1024) {
+ return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+ }
+
+ uint32_t ttc_tag;
+ if (!file.ReadU32(&ttc_tag)) {
+ return OTS_FAILURE_MSG_HDR("Error reading TTC tag");
+ }
+ if (ttc_tag != OTS_TAG('t','t','c','f')) {
+ return OTS_FAILURE_MSG_HDR("Invalid TTC tag");
+ }
+
+ uint32_t ttc_version;
+ if (!file.ReadU32(&ttc_version)) {
+ return OTS_FAILURE_MSG_HDR("Error reading TTC version");
+ }
+ if (ttc_version != 0x00010000 && ttc_version != 0x00020000) {
+ return OTS_FAILURE_MSG_HDR("Invalid TTC version");
+ }
+
+ uint32_t num_fonts;
+ if (!file.ReadU32(&num_fonts)) {
+ return OTS_FAILURE_MSG_HDR("Error reading number of TTC fonts");
+ }
+ // Limit the allowed number of subfonts to have same memory allocation.
+ if (num_fonts > 0x10000) {
+ return OTS_FAILURE_MSG_HDR("Too many fonts in TTC");
+ }
+
+ std::vector<uint32_t> offsets(num_fonts);
+ for (unsigned i = 0; i < num_fonts; i++) {
+ if (!file.ReadU32(&offsets[i])) {
+ return OTS_FAILURE_MSG_HDR("Error reading offset to OffsetTable");
+ }
+ }
+
+ if (ttc_version == 0x00020000) {
+ // We don't care about these fields of the header:
+ // uint32_t dsig_tag, dsig_length, dsig_offset
+ if (!file.Skip(3 * 4)) {
+ return OTS_FAILURE_MSG_HDR("Error reading DSIG offset and length in TTC font");
+ }
+ }
+
+ if (index == static_cast<uint32_t>(-1)) {
+ if (!output->WriteU32(ttc_tag) ||
+ !output->WriteU32(0x00010000) ||
+ !output->WriteU32(num_fonts) ||
+ !output->Seek((3 + num_fonts) * 4)) {
+ return OTS_FAILURE_MSG_HDR("Error writing output");
+ }
+
+ // Keep references to the fonts processed in the loop below, as we need
+ // them for reused tables.
+ std::vector<ots::Font> fonts(num_fonts, ots::Font(header));
+
+ for (unsigned i = 0; i < num_fonts; i++) {
+ uint32_t out_offset = output->Tell();
+ if (!output->Seek((3 + i) * 4) ||
+ !output->WriteU32(out_offset) ||
+ !output->Seek(out_offset)) {
+ return OTS_FAILURE_MSG_HDR("Error writing output");
+ }
+ if (!ProcessTTF(header, &fonts[i], output, data, length, offsets[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ } else {
+ if (index >= num_fonts) {
+ return OTS_FAILURE_MSG_HDR("Requested font index is bigger than the number of fonts in the TTC file");
+ }
+
+ ots::Font font(header);
+ return ProcessTTF(header, &font, output, data, length, offsets[index]);
+ }
+}
+
+bool ProcessWOFF(ots::OpenTypeFile *header,
+ ots::Font *font,
+ ots::OTSStream *output, const uint8_t *data, size_t length) {
+ ots::Buffer file(data, length);
+
+ // we disallow all files > 1GB in size for sanity.
+ if (length > 1024 * 1024 * 1024) {
+ return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+ }
+
+ uint32_t woff_tag;
+ if (!file.ReadU32(&woff_tag)) {
+ return OTS_FAILURE_MSG_HDR("error reading WOFF marker");
+ }
+
+ if (woff_tag != OTS_TAG('w','O','F','F')) {
+ return OTS_FAILURE_MSG_HDR("invalid WOFF marker");
+ }
+
+ if (!file.ReadU32(&font->version)) {
+ return OTS_FAILURE_MSG_HDR("error reading version tag");
+ }
+ if (!ots::IsValidVersionTag(font->version)) {
+ return OTS_FAILURE_MSG_HDR("invalid version tag");
+ }
+
+ uint32_t reported_length;
+ if (!file.ReadU32(&reported_length) || length != reported_length) {
+ return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header");
+ }
+
+ if (!file.ReadU16(&font->num_tables) || !font->num_tables) {
+ return OTS_FAILURE_MSG_HDR("error reading number of tables");
+ }
+
+ uint16_t reserved_value;
+ if (!file.ReadU16(&reserved_value) || reserved_value) {
+ return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header");
+ }
+
+ uint32_t reported_total_sfnt_size;
+ if (!file.ReadU32(&reported_total_sfnt_size)) {
+ return OTS_FAILURE_MSG_HDR("error reading total sfnt size");
+ }
+
+ // We don't care about these fields of the header:
+ // uint16_t major_version, minor_version
+ if (!file.Skip(2 * 2)) {
+ return OTS_FAILURE_MSG_HDR("Failed to read 'majorVersion' or 'minorVersion'");
+ }
+
+ // Checks metadata block size.
+ uint32_t meta_offset;
+ uint32_t meta_length;
+ uint32_t meta_length_orig;
+ if (!file.ReadU32(&meta_offset) ||
+ !file.ReadU32(&meta_length) ||
+ !file.ReadU32(&meta_length_orig)) {
+ return OTS_FAILURE_MSG_HDR("Failed to read header metadata block fields");
+ }
+ if (meta_offset) {
+ if (meta_offset >= length || length - meta_offset < meta_length) {
+ return OTS_FAILURE_MSG_HDR("Invalid metadata block offset or length");
+ }
+ }
+
+ // Checks private data block size.
+ uint32_t priv_offset;
+ uint32_t priv_length;
+ if (!file.ReadU32(&priv_offset) ||
+ !file.ReadU32(&priv_length)) {
+ return OTS_FAILURE_MSG_HDR("Failed to read header private block fields");
+ }
+ if (priv_offset) {
+ if (priv_offset >= length || length - priv_offset < priv_length) {
+ return OTS_FAILURE_MSG_HDR("Invalid private block offset or length");
+ }
+ }
+
+ // Next up is the list of tables.
+ std::vector<OpenTypeTable> tables;
+
+ uint32_t first_index = 0;
+ uint32_t last_index = 0;
+ // Size of sfnt header plus size of table records.
+ uint64_t total_sfnt_size = 12 + 16 * font->num_tables;
+ for (unsigned i = 0; i < font->num_tables; ++i) {
+ OpenTypeTable table;
+ if (!file.ReadU32(&table.tag) ||
+ !file.ReadU32(&table.offset) ||
+ !file.ReadU32(&table.length) ||
+ !file.ReadU32(&table.uncompressed_length) ||
+ !file.ReadU32(&table.chksum)) {
+ return OTS_FAILURE_MSG_HDR("error reading table directory");
+ }
+
+ total_sfnt_size += ots::Round4(table.uncompressed_length);
+ if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
+ return OTS_FAILURE_MSG_HDR("sfnt size overflow");
+ }
+ tables.push_back(table);
+ if (i == 0 || tables[first_index].offset > table.offset)
+ first_index = i;
+ if (i == 0 || tables[last_index].offset < table.offset)
+ last_index = i;
+ }
+
+ if (reported_total_sfnt_size != total_sfnt_size) {
+ return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch");
+ }
+
+ // Table data must follow immediately after the header.
+ if (tables[first_index].offset != ots::Round4(file.offset())) {
+ return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file");
+ }
+
+ if (tables[last_index].offset >= length ||
+ length - tables[last_index].offset < tables[last_index].length) {
+ return OTS_FAILURE_MSG_HDR("invalid table location/size");
+ }
+ // Blocks must follow immediately after the previous block.
+ // (Except for padding with a maximum of three null bytes)
+ uint64_t block_end = ots::Round4(
+ static_cast<uint64_t>(tables[last_index].offset) +
+ static_cast<uint64_t>(tables[last_index].length));
+ if (block_end > std::numeric_limits<uint32_t>::max()) {
+ return OTS_FAILURE_MSG_HDR("invalid table location/size");
+ }
+ if (meta_offset) {
+ if (block_end != meta_offset) {
+ return OTS_FAILURE_MSG_HDR("Invalid metadata block offset");
+ }
+ block_end = ots::Round4(static_cast<uint64_t>(meta_offset) +
+ static_cast<uint64_t>(meta_length));
+ if (block_end > std::numeric_limits<uint32_t>::max()) {
+ return OTS_FAILURE_MSG_HDR("Invalid metadata block length");
+ }
+ }
+ if (priv_offset) {
+ if (block_end != priv_offset) {
+ return OTS_FAILURE_MSG_HDR("Invalid private block offset");
+ }
+ block_end = ots::Round4(static_cast<uint64_t>(priv_offset) +
+ static_cast<uint64_t>(priv_length));
+ if (block_end > std::numeric_limits<uint32_t>::max()) {
+ return OTS_FAILURE_MSG_HDR("Invalid private block length");
+ }
+ }
+ if (block_end != ots::Round4(length)) {
+ return OTS_FAILURE_MSG_HDR("File length mismatch (trailing junk?)");
+ }
+
+ return ProcessGeneric(header, font, woff_tag, output, data, length, tables, file);
+}
+
+bool ProcessWOFF2(ots::OpenTypeFile *header,
+ ots::OTSStream *output,
+ const uint8_t *data,
+ size_t length,
+ uint32_t index) {
+ size_t decompressed_size = woff2::ComputeWOFF2FinalSize(data, length);
+
+ if (decompressed_size == 0) {
+ return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0");
+ }
+ // decompressed font must be <= 30MB
+ if (decompressed_size > 30 * 1024 * 1024) {
+ return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 font exceeds 30MB");
+ }
+
+ std::string buf(decompressed_size, 0);
+ woff2::WOFF2StringOut out(&buf);
+ if (!woff2::ConvertWOFF2ToTTF(data, length, &out)) {
+ return OTS_FAILURE_MSG_HDR("Failed to convert WOFF 2.0 font to SFNT");
+ }
+ const uint8_t *decompressed = reinterpret_cast<const uint8_t*>(buf.data());
+
+ if (data[4] == 't' && data[5] == 't' && data[6] == 'c' && data[7] == 'f') {
+ return ProcessTTC(header, output, decompressed, out.Size(), index);
+ } else {
+ ots::Font font(header);
+ return ProcessTTF(header, &font, output, decompressed, out.Size());
+ }
+}
+
+ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
+ ots::TableAction action = header->context->GetTableAction(tag);
+
+ if (action == ots::TABLE_ACTION_DEFAULT) {
+ action = ots::TABLE_ACTION_DROP;
+
+ for (unsigned i = 0; ; ++i) {
+ if (table_parsers[i].parse == NULL) break;
+
+ if (table_parsers[i].tag == tag) {
+ action = ots::TABLE_ACTION_SANITIZE;
+ break;
+ }
+ }
+ }
+
+ assert(action != ots::TABLE_ACTION_DEFAULT); // Should never return this.
+ return action;
+}
+
+bool GetTableData(const uint8_t *data,
+ const OpenTypeTable& table,
+ Arena *arena,
+ size_t *table_length,
+ const uint8_t **table_data) {
+ if (table.uncompressed_length != table.length) {
+ // Compressed table. Need to uncompress into memory first.
+ *table_length = table.uncompressed_length;
+ *table_data = (*arena).Allocate(*table_length);
+ uLongf dest_len = *table_length;
+ int r = uncompress((Bytef*) *table_data, &dest_len,
+ data + table.offset, table.length);
+ if (r != Z_OK || dest_len != *table_length) {
+ return false;
+ }
+ } else {
+ // Uncompressed table. We can process directly from memory.
+ *table_data = data + table.offset;
+ *table_length = table.length;
+ }
+
+ return true;
+}
+
+bool ProcessGeneric(ots::OpenTypeFile *header,
+ ots::Font *font,
+ uint32_t signature,
+ ots::OTSStream *output,
+ const uint8_t *data, size_t length,
+ const std::vector<OpenTypeTable>& tables,
+ ots::Buffer& file) {
+ const size_t data_offset = file.offset();
+
+ uint32_t uncompressed_sum = 0;
+
+ for (unsigned i = 0; i < font->num_tables; ++i) {
+ // the tables must be sorted by tag (when taken as big-endian numbers).
+ // This also remove the possibility of duplicate tables.
+ if (i) {
+ const uint32_t this_tag = tables[i].tag;
+ const uint32_t prev_tag = tables[i - 1].tag;
+ if (this_tag <= prev_tag) {
+ OTS_WARNING_MSG_HDR("Table directory is not correctly ordered");
+ }
+ }
+
+ // all tag names must be built from printable ASCII characters
+ if (!CheckTag(tables[i].tag)) {
+ return OTS_FAILURE_MSG_TAG("invalid table tag", tables[i].tag);
+ }
+
+ // tables must be 4-byte aligned
+ if (tables[i].offset & 3) {
+ return OTS_FAILURE_MSG_TAG("misaligned table", tables[i].tag);
+ }
+
+ // and must be within the file
+ if (tables[i].offset < data_offset || tables[i].offset >= length) {
+ return OTS_FAILURE_MSG_TAG("invalid table offset", tables[i].tag);
+ }
+ // disallow all tables with a zero length
+ if (tables[i].length < 1) {
+ // Note: malayalam.ttf has zero length CVT table...
+ return OTS_FAILURE_MSG_TAG("zero-length table", tables[i].tag);
+ }
+ // disallow all tables with a length > 1GB
+ if (tables[i].length > 1024 * 1024 * 1024) {
+ return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", tables[i].tag);
+ }
+ // disallow tables where the uncompressed size is < the compressed size.
+ if (tables[i].uncompressed_length < tables[i].length) {
+ return OTS_FAILURE_MSG_TAG("invalid compressed table", tables[i].tag);
+ }
+ if (tables[i].uncompressed_length > tables[i].length) {
+ // We'll probably be decompressing this table.
+
+ // disallow all tables which uncompress to > 30 MB
+ if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
+ return OTS_FAILURE_MSG_TAG("uncompressed length exceeds 30MB", tables[i].tag);
+ }
+ if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
+ return OTS_FAILURE_MSG_TAG("overflow of uncompressed sum", tables[i].tag);
+ }
+
+ uncompressed_sum += tables[i].uncompressed_length;
+ }
+ // since we required that the file be < 1GB in length, and that the table
+ // length is < 1GB, the following addtion doesn't overflow
+ uint32_t end_byte = tables[i].offset + tables[i].length;
+ // Tables in the WOFF file must be aligned 4-byte boundary.
+ if (signature == OTS_TAG('w','O','F','F')) {
+ end_byte = ots::Round4(end_byte);
+ }
+ if (!end_byte || end_byte > length) {
+ return OTS_FAILURE_MSG_TAG("table overruns end of file", tables[i].tag);
+ }
+ }
+
+ // All decompressed tables uncompressed must be <= 30MB.
+ if (uncompressed_sum > 30 * 1024 * 1024) {
+ return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
+ }
+
+ std::map<uint32_t, OpenTypeTable> table_map;
+ for (unsigned i = 0; i < font->num_tables; ++i) {
+ table_map[tables[i].tag] = tables[i];
+ }
+
+ // check that the tables are not overlapping.
+ std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
+ for (unsigned i = 0; i < font->num_tables; ++i) {
+ overlap_checker.push_back(
+ std::make_pair(tables[i].offset, static_cast<uint8_t>(1) /* start */));
+ overlap_checker.push_back(
+ std::make_pair(tables[i].offset + tables[i].length,
+ static_cast<uint8_t>(0) /* end */));
+ }
+ std::sort(overlap_checker.begin(), overlap_checker.end());
+ int overlap_count = 0;
+ for (unsigned i = 0; i < overlap_checker.size(); ++i) {
+ overlap_count += (overlap_checker[i].second ? 1 : -1);
+ if (overlap_count > 1) {
+ return OTS_FAILURE_MSG_HDR("overlapping tables");
+ }
+ }
+
+ Arena arena;
+
+ for (unsigned i = 0; ; ++i) {
+ if (table_parsers[i].parse == NULL) break;
+
+ uint32_t tag = table_parsers[i].tag;
+ const std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.find(tag);
+
+ ots::TableAction action = GetTableAction(header, tag);
+ if (it == table_map.end()) {
+ if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
+ return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
+ }
+ continue;
+ }
+
+ uint32_t input_offset = it->second.offset;
+ const ots::TableMap::const_iterator ot = header->tables.find(input_offset);
+ if (ot == header->tables.end()) {
+ const uint8_t* table_data;
+ size_t table_length;
+
+ if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
+ return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag);
+ }
+
+ if (action == ots::TABLE_ACTION_SANITIZE &&
+ !table_parsers[i].parse(font, table_data, table_length)) {
+ return OTS_FAILURE();
+ }
+ } else if (action == ots::TABLE_ACTION_SANITIZE) {
+ table_parsers[i].reuse(font, ot->second.first);
+ }
+ }
+
+ if (font->cff) {
+ // 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");
+ }
+ if (font->glyf || font->loca) {
+ // mixing outline formats is not recommended
+ return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
+ }
+ } else {
+ if (!font->glyf || !font->loca) {
+ // No TrueType glyph found.
+#define PASSTHRU_TABLE(tag_) (table_map.find(tag_) != table_map.end() && \
+ GetTableAction(header, tag_) == ots::TABLE_ACTION_PASSTHRU)
+ // We don't sanitise bitmap table, but don't reject bitmap-only fonts if
+ // we keep the tables.
+ if (!PASSTHRU_TABLE(OTS_TAG('C','B','D','T')) ||
+ !PASSTHRU_TABLE(OTS_TAG('C','B','L','C'))) {
+ return OTS_FAILURE_MSG_HDR("no supported glyph shapes table(s) present");
+ }
+#undef PASSTHRU_TABLE
+ }
+ }
+
+ uint16_t num_output_tables = 0;
+ for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
+ it != table_map.end(); ++it) {
+ ots::TableAction action = GetTableAction(header, it->first);
+ if (action == ots::TABLE_ACTION_PASSTHRU) {
+ num_output_tables++;
+ } else {
+ for (unsigned i = 0; table_parsers[i].parse != NULL; ++i) {
+ if (table_parsers[i].tag == it->first &&
+ table_parsers[i].should_serialise(font)) {
+ num_output_tables++;
+ break;
+ }
+ }
+ }
+ }
+
+ uint16_t max_pow2 = 0;
+ while (1u << (max_pow2 + 1) <= num_output_tables) {
+ max_pow2++;
+ }
+ const uint16_t output_search_range = (1u << max_pow2) << 4;
+
+ // most of the errors here are highly unlikely - they'd only occur if the
+ // output stream returns a failure, e.g. lack of space to write
+ output->ResetChecksum();
+ if (!output->WriteU32(font->version) ||
+ !output->WriteU16(num_output_tables) ||
+ !output->WriteU16(output_search_range) ||
+ !output->WriteU16(max_pow2) ||
+ !output->WriteU16((num_output_tables << 4) - output_search_range)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+ const uint32_t offset_table_chksum = output->chksum();
+
+ const size_t table_record_offset = output->Tell();
+ if (!output->Pad(16 * num_output_tables)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+
+ std::vector<ots::OutputTable> out_tables;
+
+ size_t head_table_offset = 0;
+ for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
+ it != table_map.end(); ++it) {
+ uint32_t input_offset = it->second.offset;
+ const ots::TableMap::const_iterator ot = header->tables.find(input_offset);
+ if (ot != header->tables.end()) {
+ ots::OutputTable out = ot->second.second;
+ if (out.tag == OTS_TAG('h','e','a','d')) {
+ head_table_offset = out.offset;
+ }
+ out_tables.push_back(out);
+ } else {
+ ots::OutputTable out;
+ out.tag = it->first;
+ out.offset = output->Tell();
+
+ if (out.tag == OTS_TAG('h','e','a','d')) {
+ head_table_offset = out.offset;
+ }
+
+ ots::TableAction action = GetTableAction(header, it->first);
+ if (action == ots::TABLE_ACTION_PASSTHRU) {
+ output->ResetChecksum();
+ const uint8_t* table_data;
+ size_t table_length;
+
+ if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
+ return OTS_FAILURE_MSG_HDR("Failed to uncompress table");
+ }
+
+ if (!output->Write(table_data, table_length)) {
+ return OTS_FAILURE_MSG_HDR("Failed to serialize table");
+ }
+
+ const size_t end_offset = output->Tell();
+ if (end_offset <= out.offset) {
+ // paranoid check. |end_offset| is supposed to be greater than the offset,
+ // as long as the Tell() interface is implemented correctly.
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+ out.length = end_offset - out.offset;
+
+ // align tables to four bytes
+ if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+ out.chksum = output->chksum();
+ out_tables.push_back(out);
+ header->tables[input_offset] = std::make_pair(font, out);
+ } else {
+ for (unsigned i = 0; table_parsers[i].parse != NULL; ++i) {
+ if (table_parsers[i].tag == it->first &&
+ table_parsers[i].should_serialise(font)) {
+ output->ResetChecksum();
+ if (!table_parsers[i].serialise(output, font)) {
+ return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag);
+ }
+
+ const size_t end_offset = output->Tell();
+ if (end_offset <= out.offset) {
+ // paranoid check. |end_offset| is supposed to be greater than the offset,
+ // as long as the Tell() interface is implemented correctly.
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+ out.length = end_offset - out.offset;
+
+ // align tables to four bytes
+ if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+ out.chksum = output->chksum();
+ out_tables.push_back(out);
+ header->tables[input_offset] = std::make_pair(font, out);
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ const size_t end_of_file = output->Tell();
+
+ // Need to sort the output tables for inclusion in the file
+ std::sort(out_tables.begin(), out_tables.end());
+ if (!output->Seek(table_record_offset)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+
+ output->ResetChecksum();
+ uint32_t tables_chksum = 0;
+ for (unsigned i = 0; i < out_tables.size(); ++i) {
+ if (!output->WriteU32(out_tables[i].tag) ||
+ !output->WriteU32(out_tables[i].chksum) ||
+ !output->WriteU32(out_tables[i].offset) ||
+ !output->WriteU32(out_tables[i].length)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+ tables_chksum += out_tables[i].chksum;
+ }
+ const uint32_t table_record_chksum = output->chksum();
+
+ // http://www.microsoft.com/typography/otspec/otff.htm
+ const uint32_t file_chksum
+ = offset_table_chksum + tables_chksum + table_record_chksum;
+ const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum;
+
+ // seek into the 'head' table and write in the checksum magic value
+ if (!head_table_offset) {
+ return OTS_FAILURE_MSG_HDR("internal error!");
+ }
+ if (!output->Seek(head_table_offset + 8)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+ if (!output->WriteU32(chksum_magic)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+
+ if (!output->Seek(end_of_file)) {
+ return OTS_FAILURE_MSG_HDR("error writing output");
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+bool IsValidVersionTag(uint32_t tag) {
+ return tag == 0x000010000 ||
+ // OpenType fonts with CFF data have 'OTTO' tag.
+ tag == OTS_TAG('O','T','T','O') ||
+ // Older Mac fonts might have 'true' or 'typ1' tag.
+ tag == OTS_TAG('t','r','u','e') ||
+ tag == OTS_TAG('t','y','p','1');
+}
+
+bool OTSContext::Process(OTSStream *output,
+ const uint8_t *data,
+ size_t length,
+ uint32_t index) {
+ OpenTypeFile header;
+ Font font(&header);
+ header.context = this;
+
+ if (length < 4) {
+ return OTS_FAILURE_MSG_(&header, "file less than 4 bytes");
+ }
+
+ bool result;
+ if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
+ result = ProcessWOFF(&header, &font, output, data, length);
+ } else if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') {
+ result = ProcessWOFF2(&header, output, data, length, index);
+ } else if (data[0] == 't' && data[1] == 't' && data[2] == 'c' && data[3] == 'f') {
+ result = ProcessTTC(&header, output, data, length, index);
+ } else {
+ result = ProcessTTF(&header, &font, output, data, length);
+ }
+
+ return result;
+}
+
+} // namespace ots
diff --git a/gfx/ots/src/ots.h b/gfx/ots/src/ots.h
new file mode 100644
index 000000000..2d13f8d6d
--- /dev/null
+++ b/gfx/ots/src/ots.h
@@ -0,0 +1,294 @@
+// Copyright (c) 2009 The Chromium 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_H_
+#define OTS_H_
+
+#include <stddef.h>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <map>
+
+#include "opentype-sanitiser.h"
+
+// arraysize borrowed from base/basictypes.h
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+namespace ots {
+
+#if !defined(OTS_DEBUG)
+#define OTS_FAILURE() false
+#else
+#define OTS_FAILURE() \
+ (\
+ std::fprintf(stderr, "ERROR at %s:%d (%s)\n", \
+ __FILE__, __LINE__, __FUNCTION__) \
+ && false\
+ )
+#endif
+
+// All OTS_FAILURE_* macros ultimately evaluate to 'false', just like the original
+// message-less OTS_FAILURE(), so that the current parser will return 'false' as
+// its result (indicating a failure).
+
+#if !defined(OTS_DEBUG)
+#define OTS_MESSAGE_(level,otf_,...) \
+ (otf_)->context->Message(level,__VA_ARGS__)
+#else
+#define OTS_MESSAGE_(level,otf_,...) \
+ OTS_FAILURE(), \
+ (otf_)->context->Message(level,__VA_ARGS__)
+#endif
+
+// Generate a simple message
+#define OTS_FAILURE_MSG_(otf_,...) \
+ (OTS_MESSAGE_(0,otf_,__VA_ARGS__), false)
+
+#define OTS_WARNING_MSG_(otf_,...) \
+ OTS_MESSAGE_(1,otf_,__VA_ARGS__)
+
+// Generate a message with an associated table tag
+#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
+ (OTS_MESSAGE_(0,otf_,"%c%c%c%c: %s", OTS_UNTAG(tag_), msg_), false)
+
+// Convenience macros for use in files that only handle a single table tag,
+// defined as TABLE_NAME at the top of the file; the 'file' variable is
+// expected to be the current OpenTypeFile pointer.
+#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)
+
+#define OTS_WARNING(...) OTS_WARNING_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)
+
+// -----------------------------------------------------------------------------
+// Buffer helper class
+//
+// This class perform some trival buffer operations while checking for
+// out-of-bounds errors. As a family they return false if anything is amiss,
+// updating the current offset otherwise.
+// -----------------------------------------------------------------------------
+class Buffer {
+ public:
+ Buffer(const uint8_t *buf, size_t len)
+ : buffer_(buf),
+ length_(len),
+ offset_(0) { }
+
+ bool Skip(size_t n_bytes) {
+ return Read(NULL, n_bytes);
+ }
+
+ bool Read(uint8_t *buf, size_t n_bytes) {
+ if (n_bytes > 1024 * 1024 * 1024) {
+ return OTS_FAILURE();
+ }
+ if ((offset_ + n_bytes > length_) ||
+ (offset_ > length_ - n_bytes)) {
+ return OTS_FAILURE();
+ }
+ if (buf) {
+ std::memcpy(buf, buffer_ + offset_, n_bytes);
+ }
+ offset_ += n_bytes;
+ return true;
+ }
+
+ inline bool ReadU8(uint8_t *value) {
+ if (offset_ + 1 > length_) {
+ return OTS_FAILURE();
+ }
+ *value = buffer_[offset_];
+ ++offset_;
+ return true;
+ }
+
+ bool ReadU16(uint16_t *value) {
+ if (offset_ + 2 > length_) {
+ return OTS_FAILURE();
+ }
+ std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
+ *value = ntohs(*value);
+ offset_ += 2;
+ return true;
+ }
+
+ bool ReadS16(int16_t *value) {
+ return ReadU16(reinterpret_cast<uint16_t*>(value));
+ }
+
+ bool ReadU24(uint32_t *value) {
+ if (offset_ + 3 > length_) {
+ return OTS_FAILURE();
+ }
+ *value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
+ static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
+ static_cast<uint32_t>(buffer_[offset_ + 2]);
+ offset_ += 3;
+ return true;
+ }
+
+ bool ReadU32(uint32_t *value) {
+ if (offset_ + 4 > length_) {
+ return OTS_FAILURE();
+ }
+ std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+ *value = ntohl(*value);
+ offset_ += 4;
+ return true;
+ }
+
+ bool ReadS32(int32_t *value) {
+ return ReadU32(reinterpret_cast<uint32_t*>(value));
+ }
+
+ bool ReadR64(uint64_t *value) {
+ if (offset_ + 8 > length_) {
+ return OTS_FAILURE();
+ }
+ std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
+ offset_ += 8;
+ return true;
+ }
+
+ const uint8_t *buffer() const { return buffer_; }
+ size_t offset() const { return offset_; }
+ size_t length() const { return length_; }
+ size_t remaining() const { return length_ - offset_; }
+
+ void set_offset(size_t newoffset) { offset_ = newoffset; }
+
+ private:
+ const uint8_t * const buffer_;
+ const size_t length_;
+ size_t offset_;
+};
+
+// Round a value up to the nearest multiple of 4. Don't round the value in the
+// case that rounding up overflows.
+template<typename T> T Round4(T value) {
+ if (std::numeric_limits<T>::max() - value < 3) {
+ return value;
+ }
+ return (value + 3) & ~3;
+}
+
+template<typename T> T Round2(T value) {
+ if (value == std::numeric_limits<T>::max()) {
+ return value;
+ }
+ return (value + 1) & ~1;
+}
+
+bool IsValidVersionTag(uint32_t tag);
+
+#define FOR_EACH_TABLE_TYPE \
+ F(cff, CFF) \
+ F(cmap, CMAP) \
+ F(cvt, CVT) \
+ F(fpgm, FPGM) \
+ F(gasp, GASP) \
+ F(gdef, GDEF) \
+ F(glyf, GLYF) \
+ F(gpos, GPOS) \
+ F(gsub, GSUB) \
+ F(hdmx, HDMX) \
+ F(head, HEAD) \
+ F(hhea, HHEA) \
+ F(hmtx, HMTX) \
+ F(kern, KERN) \
+ F(loca, LOCA) \
+ F(ltsh, LTSH) \
+ F(math, MATH) \
+ F(maxp, MAXP) \
+ F(name, NAME) \
+ F(os2, OS2) \
+ F(post, POST) \
+ F(prep, PREP) \
+ F(vdmx, VDMX) \
+ F(vorg, VORG) \
+ F(vhea, VHEA) \
+ F(vmtx, VMTX)
+
+#define F(name, capname) struct OpenType##capname;
+FOR_EACH_TABLE_TYPE
+#undef F
+
+struct Font;
+struct OpenTypeFile;
+
+#define F(name, capname) \
+bool ots_##name##_parse(Font *f, const uint8_t *d, size_t l); \
+bool ots_##name##_should_serialise(Font *f); \
+bool ots_##name##_serialise(OTSStream *s, Font *f); \
+void ots_##name##_reuse(Font *f, Font *o);\
+void ots_##name##_free(Font *f);
+FOR_EACH_TABLE_TYPE
+#undef F
+
+struct Font {
+ explicit Font(const OpenTypeFile *f)
+ : file(f),
+ version(0),
+ num_tables(0),
+ search_range(0),
+ entry_selector(0),
+ range_shift(0) {
+#define F(name, capname) \
+ name = NULL; \
+ name##_reused = false;
+ FOR_EACH_TABLE_TYPE
+#undef F
+ }
+
+ ~Font() {
+#define F(name, capname) \
+ if (!name##_reused) {\
+ ots_##name##_free(this); \
+ }
+ FOR_EACH_TABLE_TYPE
+#undef F
+ }
+
+ const OpenTypeFile *file;
+
+ uint32_t version;
+ uint16_t num_tables;
+ uint16_t search_range;
+ uint16_t entry_selector;
+ uint16_t range_shift;
+
+#define F(name, capname) \
+ OpenType##capname *name; \
+ bool name##_reused;
+FOR_EACH_TABLE_TYPE
+#undef F
+};
+
+struct OutputTable {
+ uint32_t tag;
+ size_t offset;
+ size_t length;
+ uint32_t chksum;
+
+ bool operator<(const OutputTable& other) const {
+ return tag < other.tag;
+ }
+};
+
+typedef std::map<uint32_t, std::pair<Font*, OutputTable> > TableMap;
+
+struct OpenTypeFile {
+ OTSContext *context;
+ TableMap tables;
+};
+
+} // namespace ots
+
+#undef FOR_EACH_TABLE_TYPE
+
+#endif // OTS_H_
diff --git a/gfx/ots/src/post.cc b/gfx/ots/src/post.cc
new file mode 100644
index 000000000..a110b2dea
--- /dev/null
+++ b/gfx/ots/src/post.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2009 The Chromium 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 "post.h"
+
+#include "maxp.h"
+
+// post - PostScript
+// http://www.microsoft.com/typography/otspec/post.htm
+
+#define TABLE_NAME "post"
+
+namespace ots {
+
+bool ots_post_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypePOST *post = new OpenTypePOST;
+ font->post = post;
+
+ if (!table.ReadU32(&post->version) ||
+ !table.ReadU32(&post->italic_angle) ||
+ !table.ReadS16(&post->underline) ||
+ !table.ReadS16(&post->underline_thickness) ||
+ !table.ReadU32(&post->is_fixed_pitch)) {
+ return OTS_FAILURE_MSG("Failed to read post header");
+ }
+
+ if (post->underline_thickness < 0) {
+ post->underline_thickness = 1;
+ }
+
+ if (post->version == 0x00010000) {
+ return true;
+ } else if (post->version == 0x00030000) {
+ return true;
+ } else if (post->version != 0x00020000) {
+ // 0x00025000 is deprecated. We don't accept it.
+ return OTS_FAILURE_MSG("Bad post version %x", post->version);
+ }
+
+ // We have a version 2 table with a list of Pascal strings at the end
+
+ // We don't care about the memory usage fields. We'll set all these to zero
+ // when serialising
+ if (!table.Skip(16)) {
+ return OTS_FAILURE_MSG("Failed to skip memory usage in post table");
+ }
+
+ uint16_t num_glyphs = 0;
+ if (!table.ReadU16(&num_glyphs)) {
+ return OTS_FAILURE_MSG("Failed to read number of glyphs");
+ }
+
+ if (!font->maxp) {
+ return OTS_FAILURE_MSG("No maxp table required by post table");
+ }
+
+ if (num_glyphs == 0) {
+ if (font->maxp->num_glyphs > 258) {
+ return OTS_FAILURE_MSG("Can't have no glyphs in the post table if there are more than 256 glyphs in the font");
+ }
+ OTS_WARNING("table version is 1, but no glyf names are found");
+ // workaround for fonts in http://www.fontsquirrel.com/fontface
+ // (e.g., yataghan.ttf).
+ post->version = 0x00010000;
+ return true;
+ }
+
+ if (num_glyphs != font->maxp->num_glyphs) {
+ // Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
+ return OTS_FAILURE_MSG("Bad number of glyphs in post table %d", num_glyphs);
+ }
+
+ post->glyph_name_index.resize(num_glyphs);
+ for (unsigned i = 0; i < num_glyphs; ++i) {
+ if (!table.ReadU16(&post->glyph_name_index[i])) {
+ return OTS_FAILURE_MSG("Failed to read post information for glyph %d", i);
+ }
+ // Note: A strict interpretation of the specification requires name indexes
+ // are less than 32768. This, however, excludes fonts like unifont.ttf
+ // which cover all of unicode.
+ }
+
+ // Now we have an array of Pascal strings. We have to check that they are all
+ // valid and read them in.
+ const size_t strings_offset = table.offset();
+ const uint8_t *strings = data + strings_offset;
+ const uint8_t *strings_end = data + length;
+
+ for (;;) {
+ if (strings == strings_end) break;
+ const unsigned string_length = *strings;
+ if (strings + 1 + string_length > strings_end) {
+ return OTS_FAILURE_MSG("Bad string length %d", string_length);
+ }
+ if (std::memchr(strings + 1, '\0', string_length)) {
+ return OTS_FAILURE_MSG("Bad string of length %d", string_length);
+ }
+ post->names.push_back(
+ std::string(reinterpret_cast<const char*>(strings + 1), string_length));
+ strings += 1 + string_length;
+ }
+ const unsigned num_strings = post->names.size();
+
+ // check that all the references are within bounds
+ for (unsigned i = 0; i < num_glyphs; ++i) {
+ unsigned offset = post->glyph_name_index[i];
+ if (offset < 258) {
+ continue;
+ }
+
+ offset -= 258;
+ if (offset >= num_strings) {
+ return OTS_FAILURE_MSG("Bad string index %d", offset);
+ }
+ }
+
+ return true;
+}
+
+bool ots_post_should_serialise(Font *font) {
+ return font->post != NULL;
+}
+
+bool ots_post_serialise(OTSStream *out, Font *font) {
+ const OpenTypePOST *post = font->post;
+
+ // OpenType with CFF glyphs must have v3 post table.
+ if (post && font->cff && post->version != 0x00030000) {
+ return OTS_FAILURE_MSG("Bad post version %x", post->version);
+ }
+
+ if (!out->WriteU32(post->version) ||
+ !out->WriteU32(post->italic_angle) ||
+ !out->WriteS16(post->underline) ||
+ !out->WriteS16(post->underline_thickness) ||
+ !out->WriteU32(post->is_fixed_pitch) ||
+ !out->WriteU32(0) ||
+ !out->WriteU32(0) ||
+ !out->WriteU32(0) ||
+ !out->WriteU32(0)) {
+ return OTS_FAILURE_MSG("Failed to write post header");
+ }
+
+ if (post->version != 0x00020000) {
+ return true; // v1.0 and v3.0 does not have glyph names.
+ }
+
+ const uint16_t num_indexes =
+ static_cast<uint16_t>(post->glyph_name_index.size());
+ if (num_indexes != post->glyph_name_index.size() ||
+ !out->WriteU16(num_indexes)) {
+ return OTS_FAILURE_MSG("Failed to write number of indices");
+ }
+
+ for (uint16_t i = 0; i < num_indexes; ++i) {
+ if (!out->WriteU16(post->glyph_name_index[i])) {
+ return OTS_FAILURE_MSG("Failed to write name index %d", i);
+ }
+ }
+
+ // Now we just have to write out the strings in the correct order
+ for (unsigned i = 0; i < post->names.size(); ++i) {
+ const std::string& s = post->names[i];
+ const uint8_t string_length = static_cast<uint8_t>(s.size());
+ if (string_length != s.size() ||
+ !out->Write(&string_length, 1)) {
+ return OTS_FAILURE_MSG("Failed to write string %d", i);
+ }
+ // Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
+ // We allow them.
+ if (string_length > 0 && !out->Write(s.data(), string_length)) {
+ return OTS_FAILURE_MSG("Failed to write string length for string %d", i);
+ }
+ }
+
+ return true;
+}
+
+void ots_post_reuse(Font *font, Font *other) {
+ font->post = other->post;
+ font->post_reused = true;
+}
+
+void ots_post_free(Font *font) {
+ delete font->post;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/post.h b/gfx/ots/src/post.h
new file mode 100644
index 000000000..f220d4fc7
--- /dev/null
+++ b/gfx/ots/src/post.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium 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_POST_H_
+#define OTS_POST_H_
+
+#include "ots.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace ots {
+
+struct OpenTypePOST {
+ uint32_t version;
+ uint32_t italic_angle;
+ int16_t underline;
+ int16_t underline_thickness;
+ uint32_t is_fixed_pitch;
+
+ std::vector<uint16_t> glyph_name_index;
+ std::vector<std::string> names;
+};
+
+} // namespace ots
+
+#endif // OTS_POST_H_
diff --git a/gfx/ots/src/prep.cc b/gfx/ots/src/prep.cc
new file mode 100644
index 000000000..1c9b45f91
--- /dev/null
+++ b/gfx/ots/src/prep.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2009 The Chromium 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 "prep.h"
+
+// prep - Control Value Program
+// http://www.microsoft.com/typography/otspec/prep.htm
+
+#define TABLE_NAME "prep"
+
+namespace ots {
+
+bool ots_prep_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ OpenTypePREP *prep = new OpenTypePREP;
+ font->prep = prep;
+
+ if (length >= 128 * 1024u) {
+ return OTS_FAILURE_MSG("table length %ld > 120K", length); // almost all prep tables are less than 9k bytes.
+ }
+
+ if (!table.Skip(length)) {
+ return OTS_FAILURE_MSG("Failed to read table of length %ld", length);
+ }
+
+ prep->data = data;
+ prep->length = length;
+ return true;
+}
+
+bool ots_prep_should_serialise(Font *font) {
+ if (!font->glyf) return false; // this table is not for CFF fonts.
+ return font->prep != NULL;
+}
+
+bool ots_prep_serialise(OTSStream *out, Font *font) {
+ const OpenTypePREP *prep = font->prep;
+
+ if (!out->Write(prep->data, prep->length)) {
+ return OTS_FAILURE_MSG("Failed to write table length");
+ }
+
+ return true;
+}
+
+void ots_prep_reuse(Font *font, Font *other) {
+ font->prep = other->prep;
+ font->prep_reused = true;
+}
+
+void ots_prep_free(Font *font) {
+ delete font->prep;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/prep.h b/gfx/ots/src/prep.h
new file mode 100644
index 000000000..935ca111c
--- /dev/null
+++ b/gfx/ots/src/prep.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium 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_PREP_H_
+#define OTS_PREP_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypePREP {
+ const uint8_t *data;
+ uint32_t length;
+};
+
+} // namespace ots
+
+#endif // OTS_PREP_H_
diff --git a/gfx/ots/src/vdmx.cc b/gfx/ots/src/vdmx.cc
new file mode 100644
index 000000000..cd80946ae
--- /dev/null
+++ b/gfx/ots/src/vdmx.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2009 The Chromium 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 "vdmx.h"
+
+// VDMX - Vertical Device Metrics
+// http://www.microsoft.com/typography/otspec/vdmx.htm
+
+#define TABLE_NAME "VDMX"
+
+#define DROP_THIS_TABLE(...) \
+ do { \
+ OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
+ OTS_FAILURE_MSG("Table discarded"); \
+ delete font->vdmx; \
+ font->vdmx = 0; \
+ } while (0)
+
+namespace ots {
+
+bool ots_vdmx_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ font->vdmx = new OpenTypeVDMX;
+ OpenTypeVDMX * const vdmx = font->vdmx;
+
+ if (!table.ReadU16(&vdmx->version) ||
+ !table.ReadU16(&vdmx->num_recs) ||
+ !table.ReadU16(&vdmx->num_ratios)) {
+ return OTS_FAILURE_MSG("Failed to read table header");
+ }
+
+ if (vdmx->version > 1) {
+ DROP_THIS_TABLE("bad version: %u", vdmx->version);
+ return true; // continue transcoding
+ }
+
+ vdmx->rat_ranges.reserve(vdmx->num_ratios);
+ for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
+ OpenTypeVDMXRatioRecord rec;
+
+ if (!table.ReadU8(&rec.charset) ||
+ !table.ReadU8(&rec.x_ratio) ||
+ !table.ReadU8(&rec.y_start_ratio) ||
+ !table.ReadU8(&rec.y_end_ratio)) {
+ return OTS_FAILURE_MSG("Failed to read ratio header %d", i);
+ }
+
+ if (rec.charset > 1) {
+ DROP_THIS_TABLE("bad charset: %u", rec.charset);
+ return true;
+ }
+
+ if (rec.y_start_ratio > rec.y_end_ratio) {
+ DROP_THIS_TABLE("bad y ratio");
+ return true;
+ }
+
+ // All values set to zero signal the default grouping to use;
+ // if present, this must be the last Ratio group in the table.
+ if ((i < vdmx->num_ratios - 1u) &&
+ (rec.x_ratio == 0) &&
+ (rec.y_start_ratio == 0) &&
+ (rec.y_end_ratio == 0)) {
+ // workaround for fonts which have 2 or more {0, 0, 0} terminators.
+ DROP_THIS_TABLE("superfluous terminator found");
+ return true;
+ }
+
+ vdmx->rat_ranges.push_back(rec);
+ }
+
+ vdmx->offsets.reserve(vdmx->num_ratios);
+ const size_t current_offset = table.offset();
+ // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
+ for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
+ uint16_t offset;
+ if (!table.ReadU16(&offset)) {
+ return OTS_FAILURE_MSG("Failed to read ratio offset %d", i);
+ }
+ if (current_offset + offset >= length) { // thus doesn't overflow.
+ return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset, i);
+ }
+
+ vdmx->offsets.push_back(offset);
+ }
+
+ vdmx->groups.reserve(vdmx->num_recs);
+ for (unsigned i = 0; i < vdmx->num_recs; ++i) {
+ OpenTypeVDMXGroup group;
+ if (!table.ReadU16(&group.recs) ||
+ !table.ReadU8(&group.startsz) ||
+ !table.ReadU8(&group.endsz)) {
+ return OTS_FAILURE_MSG("Failed to read record header %d", i);
+ }
+ group.entries.reserve(group.recs);
+ for (unsigned j = 0; j < group.recs; ++j) {
+ OpenTypeVDMXVTable vt;
+ if (!table.ReadU16(&vt.y_pel_height) ||
+ !table.ReadS16(&vt.y_max) ||
+ !table.ReadS16(&vt.y_min)) {
+ return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i, j);
+ }
+ if (vt.y_max < vt.y_min) {
+ DROP_THIS_TABLE("bad y min/max");
+ return true;
+ }
+
+ // This table must appear in sorted order (sorted by yPelHeight),
+ // but need not be continuous.
+ if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
+ DROP_THIS_TABLE("the table is not sorted");
+ return true;
+ }
+
+ group.entries.push_back(vt);
+ }
+ vdmx->groups.push_back(group);
+ }
+
+ return true;
+}
+
+bool ots_vdmx_should_serialise(Font *font) {
+ if (!font->glyf) return false; // this table is not for CFF fonts.
+ return font->vdmx != NULL;
+}
+
+bool ots_vdmx_serialise(OTSStream *out, Font *font) {
+ OpenTypeVDMX * const vdmx = font->vdmx;
+
+ if (!out->WriteU16(vdmx->version) ||
+ !out->WriteU16(vdmx->num_recs) ||
+ !out->WriteU16(vdmx->num_ratios)) {
+ return OTS_FAILURE_MSG("Failed to write table header");
+ }
+
+ for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
+ const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
+ if (!out->Write(&rec.charset, 1) ||
+ !out->Write(&rec.x_ratio, 1) ||
+ !out->Write(&rec.y_start_ratio, 1) ||
+ !out->Write(&rec.y_end_ratio, 1)) {
+ return OTS_FAILURE_MSG("Failed to write ratio %d", i);
+ }
+ }
+
+ for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
+ if (!out->WriteU16(vdmx->offsets[i])) {
+ return OTS_FAILURE_MSG("Failed to write ratio offset %d", i);
+ }
+ }
+
+ for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
+ const OpenTypeVDMXGroup& group = vdmx->groups[i];
+ if (!out->WriteU16(group.recs) ||
+ !out->Write(&group.startsz, 1) ||
+ !out->Write(&group.endsz, 1)) {
+ return OTS_FAILURE_MSG("Failed to write group %d", i);
+ }
+ for (unsigned j = 0; j < group.entries.size(); ++j) {
+ const OpenTypeVDMXVTable& vt = group.entries[j];
+ if (!out->WriteU16(vt.y_pel_height) ||
+ !out->WriteS16(vt.y_max) ||
+ !out->WriteS16(vt.y_min)) {
+ return OTS_FAILURE_MSG("Failed to write group %d entry %d", i, j);
+ }
+ }
+ }
+
+ return true;
+}
+
+void ots_vdmx_reuse(Font *font, Font *other) {
+ font->vdmx = other->vdmx;
+ font->vdmx_reused = true;
+}
+
+void ots_vdmx_free(Font *font) {
+ delete font->vdmx;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/gfx/ots/src/vdmx.h b/gfx/ots/src/vdmx.h
new file mode 100644
index 000000000..1d959efe5
--- /dev/null
+++ b/gfx/ots/src/vdmx.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2009 The Chromium 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_VDMX_H_
+#define OTS_VDMX_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVDMXRatioRecord {
+ uint8_t charset;
+ uint8_t x_ratio;
+ uint8_t y_start_ratio;
+ uint8_t y_end_ratio;
+};
+
+struct OpenTypeVDMXVTable {
+ uint16_t y_pel_height;
+ int16_t y_max;
+ int16_t y_min;
+};
+
+struct OpenTypeVDMXGroup {
+ uint16_t recs;
+ uint8_t startsz;
+ uint8_t endsz;
+ std::vector<OpenTypeVDMXVTable> entries;
+};
+
+struct OpenTypeVDMX {
+ uint16_t version;
+ uint16_t num_recs;
+ uint16_t num_ratios;
+ std::vector<OpenTypeVDMXRatioRecord> rat_ranges;
+ std::vector<uint16_t> offsets;
+ std::vector<OpenTypeVDMXGroup> groups;
+};
+
+} // namespace ots
+
+#endif // OTS_VDMX_H_
diff --git a/gfx/ots/src/vhea.cc b/gfx/ots/src/vhea.cc
new file mode 100644
index 000000000..e721b971e
--- /dev/null
+++ b/gfx/ots/src/vhea.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium 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 "vhea.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// vhea - Vertical Header Table
+// http://www.microsoft.com/typography/otspec/vhea.htm
+
+#define TABLE_NAME "vhea"
+
+namespace ots {
+
+bool ots_vhea_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ OpenTypeVHEA *vhea = new OpenTypeVHEA;
+ font->vhea = vhea;
+
+ if (!table.ReadU32(&vhea->header.version)) {
+ return OTS_FAILURE_MSG("Failed to read version");
+ }
+ if (vhea->header.version != 0x00010000 &&
+ vhea->header.version != 0x00011000) {
+ return OTS_FAILURE_MSG("Bad vhea version %x", vhea->header.version);
+ }
+
+ if (!ParseMetricsHeader(font, &table, &vhea->header)) {
+ return OTS_FAILURE_MSG("Failed to parse metrics in vhea");
+ }
+
+ return true;
+}
+
+bool ots_vhea_should_serialise(Font *font) {
+ // vhea should'nt serialise when vmtx doesn't exist.
+ return font->vhea != NULL && font->vmtx != NULL;
+}
+
+bool ots_vhea_serialise(OTSStream *out, Font *font) {
+ if (!SerialiseMetricsHeader(font, out, &font->vhea->header)) {
+ return OTS_FAILURE_MSG("Failed to write vhea metrics");
+ }
+ return true;
+}
+
+void ots_vhea_reuse(Font *font, Font *other) {
+ font->vhea = other->vhea;
+ font->vhea_reused = true;
+}
+
+void ots_vhea_free(Font *font) {
+ delete font->vhea;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/vhea.h b/gfx/ots/src/vhea.h
new file mode 100644
index 000000000..f8efde731
--- /dev/null
+++ b/gfx/ots/src/vhea.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium 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_VHEA_H_
+#define OTS_VHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVHEA {
+ OpenTypeMetricsHeader header;
+};
+
+} // namespace ots
+
+#endif // OTS_VHEA_H_
+
diff --git a/gfx/ots/src/vmtx.cc b/gfx/ots/src/vmtx.cc
new file mode 100644
index 000000000..64a706148
--- /dev/null
+++ b/gfx/ots/src/vmtx.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium 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 "vmtx.h"
+
+#include "maxp.h"
+#include "vhea.h"
+
+// vmtx - Vertical Metrics Table
+// http://www.microsoft.com/typography/otspec/vmtx.htm
+
+#define TABLE_NAME "vmtx"
+
+namespace ots {
+
+bool ots_vmtx_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ OpenTypeVMTX *vmtx = new OpenTypeVMTX;
+ font->vmtx = vmtx;
+
+ if (!font->vhea || !font->maxp) {
+ return OTS_FAILURE_MSG("vhea or maxp table missing as needed by vmtx");
+ }
+
+ if (!ParseMetricsTable(font, &table, font->maxp->num_glyphs,
+ &font->vhea->header, &vmtx->metrics)) {
+ return OTS_FAILURE_MSG("Failed to parse vmtx metrics");
+ }
+
+ return true;
+}
+
+bool ots_vmtx_should_serialise(Font *font) {
+ // vmtx should serialise when vhea is preserved.
+ return font->vmtx != NULL && font->vhea != NULL;
+}
+
+bool ots_vmtx_serialise(OTSStream *out, Font *font) {
+ if (!SerialiseMetricsTable(font, out, &font->vmtx->metrics)) {
+ return OTS_FAILURE_MSG("Failed to write vmtx metrics");
+ }
+ return true;
+}
+
+void ots_vmtx_reuse(Font *font, Font *other) {
+ font->vmtx = other->vmtx;
+ font->vmtx_reused = true;
+}
+
+void ots_vmtx_free(Font *font) {
+ delete font->vmtx;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
diff --git a/gfx/ots/src/vmtx.h b/gfx/ots/src/vmtx.h
new file mode 100644
index 000000000..061dc73ed
--- /dev/null
+++ b/gfx/ots/src/vmtx.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium 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_VMTX_H_
+#define OTS_VMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVMTX {
+ OpenTypeMetricsTable metrics;
+};
+
+} // namespace ots
+
+#endif // OTS_VMTX_H_
+
diff --git a/gfx/ots/src/vorg.cc b/gfx/ots/src/vorg.cc
new file mode 100644
index 000000000..358923125
--- /dev/null
+++ b/gfx/ots/src/vorg.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2009 The Chromium 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 "vorg.h"
+
+#include <vector>
+
+// VORG - Vertical Origin Table
+// http://www.microsoft.com/typography/otspec/vorg.htm
+
+#define TABLE_NAME "VORG"
+
+#define DROP_THIS_TABLE(...) \
+ do { \
+ OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
+ OTS_FAILURE_MSG("Table discarded"); \
+ delete font->vorg; \
+ font->vorg = 0; \
+ } while (0)
+
+namespace ots {
+
+bool ots_vorg_parse(Font *font, const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+ font->vorg = new OpenTypeVORG;
+ OpenTypeVORG * const vorg = font->vorg;
+
+ uint16_t num_recs;
+ if (!table.ReadU16(&vorg->major_version) ||
+ !table.ReadU16(&vorg->minor_version) ||
+ !table.ReadS16(&vorg->default_vert_origin_y) ||
+ !table.ReadU16(&num_recs)) {
+ return OTS_FAILURE_MSG("Failed to read header");
+ }
+ if (vorg->major_version != 1) {
+ DROP_THIS_TABLE("bad major version: %u", vorg->major_version);
+ return true;
+ }
+ if (vorg->minor_version != 0) {
+ DROP_THIS_TABLE("bad minor version: %u", vorg->minor_version);
+ return true;
+ }
+
+ // num_recs might be zero (e.g., DFHSMinchoPro5-W3-Demo.otf).
+ if (!num_recs) {
+ return true;
+ }
+
+ uint16_t last_glyph_index = 0;
+ vorg->metrics.reserve(num_recs);
+ for (unsigned i = 0; i < num_recs; ++i) {
+ OpenTypeVORGMetrics rec;
+
+ if (!table.ReadU16(&rec.glyph_index) ||
+ !table.ReadS16(&rec.vert_origin_y)) {
+ return OTS_FAILURE_MSG("Failed to read record %d", i);
+ }
+ if ((i != 0) && (rec.glyph_index <= last_glyph_index)) {
+ DROP_THIS_TABLE("the table is not sorted");
+ return true;
+ }
+ last_glyph_index = rec.glyph_index;
+
+ vorg->metrics.push_back(rec);
+ }
+
+ return true;
+}
+
+bool ots_vorg_should_serialise(Font *font) {
+ if (!font->cff) return false; // this table is not for fonts with TT glyphs.
+ return font->vorg != NULL;
+}
+
+bool ots_vorg_serialise(OTSStream *out, Font *font) {
+ OpenTypeVORG * const vorg = font->vorg;
+
+ const uint16_t num_metrics = static_cast<uint16_t>(vorg->metrics.size());
+ if (num_metrics != vorg->metrics.size() ||
+ !out->WriteU16(vorg->major_version) ||
+ !out->WriteU16(vorg->minor_version) ||
+ !out->WriteS16(vorg->default_vert_origin_y) ||
+ !out->WriteU16(num_metrics)) {
+ return OTS_FAILURE_MSG("Failed to write table header");
+ }
+
+ for (uint16_t i = 0; i < num_metrics; ++i) {
+ const OpenTypeVORGMetrics& rec = vorg->metrics[i];
+ if (!out->WriteU16(rec.glyph_index) ||
+ !out->WriteS16(rec.vert_origin_y)) {
+ return OTS_FAILURE_MSG("Failed to write record %d", i);
+ }
+ }
+
+ return true;
+}
+
+void ots_vorg_reuse(Font *font, Font *other) {
+ font->vorg = other->vorg;
+ font->vorg_reused = true;
+}
+
+void ots_vorg_free(Font *font) {
+ delete font->vorg;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/gfx/ots/src/vorg.h b/gfx/ots/src/vorg.h
new file mode 100644
index 000000000..c3d3ffdae
--- /dev/null
+++ b/gfx/ots/src/vorg.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2009 The Chromium 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_VORG_H_
+#define OTS_VORG_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVORGMetrics {
+ uint16_t glyph_index;
+ int16_t vert_origin_y;
+};
+
+struct OpenTypeVORG {
+ uint16_t major_version;
+ uint16_t minor_version;
+ int16_t default_vert_origin_y;
+ std::vector<OpenTypeVORGMetrics> metrics;
+};
+
+} // namespace ots
+
+#endif // OTS_VORG_H_