зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1369672 - Update OTS to support Graphite table sanitization. r=jfkthame
MozReview-Commit-ID: 4WU4nQcsQgt --HG-- extra : rebase_source : 8a27c738aaccb5bd47bf057de181c4abe210ba93
This commit is contained in:
Родитель
c1383cebf3
Коммит
572c3c94d2
|
@ -2,11 +2,11 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
|
|||
|
||||
Our reference repository is https://github.com/khaledhosny/ots/.
|
||||
|
||||
Current revision: 5f685b8e1fce77347c87f6d98511d53debbe64b2 (5.2.0)
|
||||
Current revision: 57ef618b11aa0409637af04988ccce7e6b92ed0f (5.2.0)
|
||||
|
||||
Upstream files included: LICENSE, src/, include/, tests/*.cc
|
||||
|
||||
Additional files: README.mozilla, src/moz.build
|
||||
|
||||
Additional patch: ots-visibility.patch (bug 711079).
|
||||
Additional patch: ots-config.patch (config.h not needed in mozilla build)
|
||||
Additional patch: ots-lz4.patch
|
||||
|
|
|
@ -176,7 +176,7 @@ class OTSStream {
|
|||
|
||||
enum TableAction {
|
||||
TABLE_ACTION_DEFAULT, // Use OTS's default action for that table
|
||||
TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
|
||||
TABLE_ACTION_SANITIZE, // Sanitize the table, potentially dropping it
|
||||
TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
|
||||
TABLE_ACTION_DROP // Drop the table
|
||||
};
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
diff --git a/gfx/ots/src/ots.h b/gfx/ots/src/ots.h
|
||||
--- a/gfx/ots/src/ots.h
|
||||
+++ b/gfx/ots/src/ots.h
|
||||
@@ -1,16 +1,17 @@
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_H_
|
||||
#define OTS_H_
|
||||
|
||||
-#include "config.h"
|
||||
+// Not needed in the gecko build
|
||||
+// #include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
|
@ -0,0 +1,70 @@
|
|||
diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
|
||||
--- a/gfx/ots/src/glat.cc
|
||||
+++ b/gfx/ots/src/glat.cc
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "glat.h"
|
||||
|
||||
#include "gloc.h"
|
||||
-#include "lz4.h"
|
||||
+#include "mozilla/Compression.h"
|
||||
#include <list>
|
||||
|
||||
namespace ots {
|
||||
@@ -201,13 +201,15 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0);
|
||||
- int ret = LZ4_decompress_safe(
|
||||
- reinterpret_cast<const char*>(data + table.offset()),
|
||||
- reinterpret_cast<char*>(decompressed.data()),
|
||||
- table.remaining(),
|
||||
- decompressed.size());
|
||||
- if (ret < 0) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
+ size_t outputSize = 0;
|
||||
+ if (!mozilla::Compression::LZ4::decompress(
|
||||
+ reinterpret_cast<const char*>(data + table.offset()),
|
||||
+ table.remaining(),
|
||||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
+ decompressed.size(),
|
||||
+ &outputSize) ||
|
||||
+ outputSize != (this->compHead & FULL_SIZE)) {
|
||||
+ return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
|
||||
--- a/gfx/ots/src/silf.cc
|
||||
+++ b/gfx/ots/src/silf.cc
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "silf.h"
|
||||
|
||||
#include "name.h"
|
||||
-#include "lz4.h"
|
||||
+#include "mozilla/Compression.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace ots {
|
||||
@@ -39,13 +39,15 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0);
|
||||
- int ret = LZ4_decompress_safe(
|
||||
- reinterpret_cast<const char*>(data + table.offset()),
|
||||
- reinterpret_cast<char*>(decompressed.data()),
|
||||
- table.remaining(),
|
||||
- decompressed.size());
|
||||
- if (ret < 0) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
+ size_t outputSize = 0;
|
||||
+ if (!mozilla::Compression::LZ4::decompress(
|
||||
+ reinterpret_cast<const char*>(data + table.offset()),
|
||||
+ table.remaining(),
|
||||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
+ decompressed.size(),
|
||||
+ &outputSize) ||
|
||||
+ outputSize != (this->compHead & FULL_SIZE)) {
|
||||
+ return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-sanitiser.h
|
||||
--- a/gfx/ots/include/opentype-sanitiser.h
|
||||
+++ b/gfx/ots/include/opentype-sanitiser.h
|
||||
@@ -1,15 +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.
|
||||
|
||||
@@ -5,6 +5,26 @@
|
||||
#ifndef OPENTYPE_SANITISER_H_
|
||||
#define OPENTYPE_SANITISER_H_
|
||||
|
||||
|
@ -32,17 +28,7 @@ diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-san
|
|||
#if defined(_WIN32)
|
||||
#include <stdlib.h>
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
@@ -187,17 +207,17 @@ class OTSStream {
|
||||
|
||||
enum TableAction {
|
||||
TABLE_ACTION_DEFAULT, // Use OTS's default action for that table
|
||||
TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
|
||||
TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
|
||||
@@ -161,7 +181,7 @@ enum TableAction {
|
||||
TABLE_ACTION_DROP // Drop the table
|
||||
};
|
||||
|
||||
|
@ -51,8 +37,3 @@ diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-san
|
|||
public:
|
||||
OTSContext() {}
|
||||
virtual ~OTSContext() {}
|
||||
|
||||
// Process a given OpenType file and write out a sanitized version
|
||||
// output: a pointer to an object implementing the OTSStream interface. The
|
||||
// sanitisied output will be written to this. In the even of a failure,
|
||||
// partial output may have been written.
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "feat.h"
|
||||
|
||||
#include "name.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeFEAT::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!table.ReadU32(&this->version)) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
if (this->version >> 16 != 1 && this->version >> 16 != 2) {
|
||||
return DropGraphite("Unsupported table version: %u", this->version >> 16);
|
||||
}
|
||||
if (!table.ReadU16(&this->numFeat)) {
|
||||
return DropGraphite("Failed to read numFeat");
|
||||
}
|
||||
if (!table.ReadU16(&this->reserved)) {
|
||||
return DropGraphite("Failed to read reserved");
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
Warning("Nonzero reserved");
|
||||
}
|
||||
if (!table.ReadU32(&this->reserved2)) {
|
||||
return DropGraphite("Failed to read valid reserved2");
|
||||
}
|
||||
if (this->reserved2 != 0) {
|
||||
Warning("Nonzero reserved2");
|
||||
}
|
||||
|
||||
std::unordered_set<size_t> unverified;
|
||||
//this->features.resize(this->numFeat, this);
|
||||
for (unsigned i = 0; i < this->numFeat; ++i) {
|
||||
this->features.emplace_back(this);
|
||||
FeatureDefn& feature = this->features[i];
|
||||
if (!feature.ParsePart(table)) {
|
||||
return DropGraphite("Failed to read features[%u]", i);
|
||||
}
|
||||
this->feature_ids.insert(feature.id);
|
||||
for (unsigned j = 0; j < feature.numSettings; ++j) {
|
||||
size_t offset = feature.offset + j * 4;
|
||||
if (offset < feature.offset || offset > length) {
|
||||
return DropGraphite("Invalid FeatSettingDefn offset %zu/%zu",
|
||||
offset, length);
|
||||
}
|
||||
unverified.insert(offset);
|
||||
// need to verify that this FeatureDefn points to valid
|
||||
// FeatureSettingDefn
|
||||
}
|
||||
}
|
||||
|
||||
while (table.remaining()) {
|
||||
bool used = unverified.erase(table.offset());
|
||||
FeatureSettingDefn featSetting(this);
|
||||
if (!featSetting.ParsePart(table, used)) {
|
||||
return DropGraphite("Failed to read a FeatureSettingDefn");
|
||||
}
|
||||
featSettings.push_back(featSetting);
|
||||
}
|
||||
|
||||
if (!unverified.empty()) {
|
||||
return DropGraphite("%zu incorrect offsets into featSettings",
|
||||
unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU16(this->numFeat) ||
|
||||
!out->WriteU16(this->reserved) ||
|
||||
!out->WriteU32(this->reserved2) ||
|
||||
!SerializeParts(this->features, out) ||
|
||||
!SerializeParts(this->featSettings, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::IsValidFeatureId(uint32_t id) const {
|
||||
return feature_ids.count(id);
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::FeatureDefn::ParsePart(Buffer& table) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
parent->GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
if (!name) {
|
||||
return parent->Error("FeatureDefn: Required name table is missing");
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 2 && !table.ReadU32(&this->id)) {
|
||||
return parent->Error("FeatureDefn: Failed to read id");
|
||||
}
|
||||
if (parent->version >> 16 == 1) {
|
||||
uint16_t id;
|
||||
if (!table.ReadU16(&id)) {
|
||||
return parent->Error("FeatureDefn: Failed to read id");
|
||||
}
|
||||
this->id = id;
|
||||
}
|
||||
if (!table.ReadU16(&this->numSettings)) {
|
||||
return parent->Error("FeatureDefn: Failed to read numSettings");
|
||||
}
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU16(&this->reserved)) {
|
||||
return parent->Error("FeatureDefn: Failed to read reserved");
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
parent->Warning("FeatureDefn: Nonzero reserved");
|
||||
}
|
||||
}
|
||||
if (!table.ReadU32(&this->offset)) {
|
||||
return parent->Error("FeatureDefn: Failed to read offset");
|
||||
} // validity of offset verified in OpenTypeFEAT::Parse
|
||||
if (!table.ReadU16(&this->flags)) {
|
||||
return parent->Error("FeatureDefn: Failed to read flags");
|
||||
}
|
||||
if ((this->flags & RESERVED) != 0) {
|
||||
this->flags &= ~RESERVED;
|
||||
parent->Warning("FeatureDefn: Nonzero (flags & 0x%x) repaired", RESERVED);
|
||||
}
|
||||
if (this->flags & HAS_DEFAULT_SETTING &&
|
||||
(this->flags & DEFAULT_SETTING) >= this->numSettings) {
|
||||
return parent->Error("FeatureDefn: (flags & 0x%x) is set but (flags & 0x%x "
|
||||
"is not a valid setting index", HAS_DEFAULT_SETTING,
|
||||
DEFAULT_SETTING);
|
||||
}
|
||||
if (!table.ReadU16(&this->label)) {
|
||||
return parent->Error("FeatureDefn: Failed to read label");
|
||||
}
|
||||
if (!name->IsValidNameId(this->label)) {
|
||||
if (this->id == 1 && name->IsValidNameId(this->label, true)) {
|
||||
parent->Warning("FeatureDefn: Missing NameRecord repaired for feature"
|
||||
" with id=%u, label=%u", this->id, this->label);
|
||||
}
|
||||
else {
|
||||
return parent->Error("FeatureDefn: Invalid label");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::FeatureDefn::SerializePart(OTSStream* out) const {
|
||||
if ((parent->version >> 16 >= 2 && !out->WriteU32(this->id)) ||
|
||||
(parent->version >> 16 == 1 &&
|
||||
!out->WriteU16(static_cast<uint16_t>(this->id))) ||
|
||||
!out->WriteU16(this->numSettings) ||
|
||||
(parent->version >> 16 >= 2 && !out->WriteU16(this->reserved)) ||
|
||||
!out->WriteU32(this->offset) ||
|
||||
!out->WriteU16(this->flags) ||
|
||||
!out->WriteU16(this->label)) {
|
||||
return parent->Error("FeatureDefn: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::FeatureSettingDefn::ParsePart(Buffer& table, bool used) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
parent->GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
if (!name) {
|
||||
return parent->Error("FeatureSettingDefn: Required name table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadS16(&this->value)) {
|
||||
return parent->Error("FeatureSettingDefn: Failed to read value");
|
||||
}
|
||||
if (!table.ReadU16(&this->label) ||
|
||||
(used && !name->IsValidNameId(this->label))) {
|
||||
return parent->Error("FeatureSettingDefn: Failed to read valid label");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::FeatureSettingDefn::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteS16(this->value) ||
|
||||
!out->WriteU16(this->label)) {
|
||||
return parent->Error("FeatureSettingDefn: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_FEAT_H_
|
||||
#define OTS_FEAT_H_
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeFEAT : public Table {
|
||||
public:
|
||||
explicit OpenTypeFEAT(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
bool IsValidFeatureId(uint32_t id) const;
|
||||
|
||||
private:
|
||||
struct FeatureDefn : public TablePart<OpenTypeFEAT> {
|
||||
explicit FeatureDefn(OpenTypeFEAT* parent)
|
||||
: TablePart<OpenTypeFEAT>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint32_t id;
|
||||
uint16_t numSettings;
|
||||
uint16_t reserved;
|
||||
uint32_t offset;
|
||||
uint16_t flags;
|
||||
static const uint16_t HAS_DEFAULT_SETTING = 0x4000;
|
||||
static const uint16_t RESERVED = 0x3F00;
|
||||
static const uint16_t DEFAULT_SETTING = 0x00FF;
|
||||
uint16_t label;
|
||||
};
|
||||
struct FeatureSettingDefn : public TablePart<OpenTypeFEAT> {
|
||||
explicit FeatureSettingDefn(OpenTypeFEAT* parent)
|
||||
: TablePart<OpenTypeFEAT>(parent) { }
|
||||
bool ParsePart(Buffer& table) { return ParsePart(table, true); }
|
||||
bool ParsePart(Buffer& table, bool used);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
int16_t value;
|
||||
uint16_t label;
|
||||
};
|
||||
uint32_t version;
|
||||
uint16_t numFeat;
|
||||
uint16_t reserved;
|
||||
uint32_t reserved2;
|
||||
std::vector<FeatureDefn> features;
|
||||
std::vector<FeatureSettingDefn> featSettings;
|
||||
std::unordered_set<uint32_t> feature_ids;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_FEAT_H_
|
|
@ -0,0 +1,447 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "glat.h"
|
||||
|
||||
#include "gloc.h"
|
||||
#include "mozilla/Compression.h"
|
||||
#include <list>
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v1
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeGLAT_v1::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_GLOC));
|
||||
if (!gloc) {
|
||||
return DropGraphite("Required Gloc table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& locations = gloc->GetLocations();
|
||||
if (locations.empty()) {
|
||||
return DropGraphite("No locations from Gloc table");
|
||||
}
|
||||
std::list<uint32_t> unverified(locations.begin(), locations.end());
|
||||
while (table.remaining()) {
|
||||
GlatEntry entry(this);
|
||||
if (table.offset() > unverified.front()) {
|
||||
return DropGraphite("Offset check failed for a GlatEntry");
|
||||
}
|
||||
if (table.offset() == unverified.front()) {
|
||||
unverified.pop_front();
|
||||
}
|
||||
if (unverified.empty()) {
|
||||
return DropGraphite("Expected more locations");
|
||||
}
|
||||
if (!entry.ParsePart(table)) {
|
||||
return DropGraphite("Failed to read a GlatEntry");
|
||||
}
|
||||
this->entries.push_back(entry);
|
||||
}
|
||||
|
||||
if (unverified.size() != 1 || unverified.front() != table.offset()) {
|
||||
return DropGraphite("%zu location(s) could not be verified", unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v1::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!SerializeParts(this->entries, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v1::GlatEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->attNum)) {
|
||||
return parent->Error("GlatEntry: Failed to read attNum");
|
||||
}
|
||||
if (!table.ReadU8(&this->num)) {
|
||||
return parent->Error("GlatEntry: Failed to read num");
|
||||
}
|
||||
|
||||
//this->attributes.resize(this->num);
|
||||
for (unsigned i = 0; i < this->num; ++i) {
|
||||
this->attributes.emplace_back();
|
||||
if (!table.ReadS16(&this->attributes[i])) {
|
||||
return parent->Error("GlatEntry: Failed to read attribute %u", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v1::GlatEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->attNum) ||
|
||||
!out->WriteU8(this->num) ||
|
||||
!SerializeParts(this->attributes, out)) {
|
||||
return parent->Error("GlatEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v2
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeGLAT_v2::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_GLOC));
|
||||
if (!gloc) {
|
||||
return DropGraphite("Required Gloc table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& locations = gloc->GetLocations();
|
||||
if (locations.empty()) {
|
||||
return DropGraphite("No locations from Gloc table");
|
||||
}
|
||||
std::list<uint32_t> unverified(locations.begin(), locations.end());
|
||||
while (table.remaining()) {
|
||||
GlatEntry entry(this);
|
||||
if (table.offset() > unverified.front()) {
|
||||
return DropGraphite("Offset check failed for a GlatEntry");
|
||||
}
|
||||
if (table.offset() == unverified.front()) {
|
||||
unverified.pop_front();
|
||||
}
|
||||
if (unverified.empty()) {
|
||||
return DropGraphite("Expected more locations");
|
||||
}
|
||||
if (!entry.ParsePart(table)) {
|
||||
return DropGraphite("Failed to read a GlatEntry");
|
||||
}
|
||||
this->entries.push_back(entry);
|
||||
}
|
||||
|
||||
if (unverified.size() != 1 || unverified.front() != table.offset()) {
|
||||
return DropGraphite("%zu location(s) could not be verified", unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v2::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!SerializeParts(this->entries, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v2::GlatEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadS16(&this->attNum)) {
|
||||
return parent->Error("GlatEntry: Failed to read attNum");
|
||||
}
|
||||
if (!table.ReadS16(&this->num) || this->num < 0) {
|
||||
return parent->Error("GlatEntry: Failed to read valid num");
|
||||
}
|
||||
|
||||
//this->attributes.resize(this->num);
|
||||
for (unsigned i = 0; i < this->num; ++i) {
|
||||
this->attributes.emplace_back();
|
||||
if (!table.ReadS16(&this->attributes[i])) {
|
||||
return parent->Error("GlatEntry: Failed to read attribute %u", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v2::GlatEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteS16(this->attNum) ||
|
||||
!out->WriteS16(this->num) ||
|
||||
!SerializeParts(this->attributes, out)) {
|
||||
return parent->Error("GlatEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v3
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
|
||||
bool prevent_decompression) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_GLOC));
|
||||
if (!gloc) {
|
||||
return DropGraphite("Required Gloc table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 3) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
if (!table.ReadU32(&this->compHead)) {
|
||||
return DropGraphite("Failed to read compression header");
|
||||
}
|
||||
switch ((this->compHead & SCHEME) >> 27) {
|
||||
case 0: // uncompressed
|
||||
break;
|
||||
case 1: { // lz4
|
||||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0);
|
||||
size_t outputSize = 0;
|
||||
if (!mozilla::Compression::LZ4::decompress(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
table.remaining(),
|
||||
reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(),
|
||||
&outputSize) ||
|
||||
outputSize != (this->compHead & FULL_SIZE)) {
|
||||
return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
default:
|
||||
return DropGraphite("Unknown compression scheme");
|
||||
}
|
||||
if (this->compHead & RESERVED) {
|
||||
Warning("Nonzero reserved");
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& locations = gloc->GetLocations();
|
||||
if (locations.empty()) {
|
||||
return DropGraphite("No locations from Gloc table");
|
||||
}
|
||||
std::list<uint32_t> unverified(locations.begin(), locations.end());
|
||||
//this->entries.resize(locations.size() - 1, this);
|
||||
for (size_t i = 0; i < locations.size() - 1; ++i) {
|
||||
this->entries.emplace_back(this);
|
||||
if (table.offset() != unverified.front()) {
|
||||
return DropGraphite("Offset check failed for a GlyphAttrs");
|
||||
}
|
||||
unverified.pop_front();
|
||||
if (!this->entries[i].ParsePart(table,
|
||||
unverified.front() - table.offset())) {
|
||||
// unverified.front() is guaranteed to exist because of the number of
|
||||
// iterations of this loop
|
||||
return DropGraphite("Failed to read a GlyphAttrs");
|
||||
}
|
||||
}
|
||||
|
||||
if (unverified.size() != 1 || unverified.front() != table.offset()) {
|
||||
return DropGraphite("%zu location(s) could not be verified", unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU32(this->compHead) ||
|
||||
!SerializeParts(this->entries, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::ParsePart(Buffer& table, const size_t size) {
|
||||
size_t init_offset = table.offset();
|
||||
if (parent->compHead & OCTABOXES && !octabox.ParsePart(table)) {
|
||||
// parent->flags & 0b1: octaboxes are present flag
|
||||
return parent->Error("GlyphAttrs: Failed to read octabox");
|
||||
}
|
||||
|
||||
while (table.offset() < init_offset + size) {
|
||||
GlatEntry entry(parent);
|
||||
if (!entry.ParsePart(table)) {
|
||||
return parent->Error("GlyphAttrs: Failed to read a GlatEntry");
|
||||
}
|
||||
this->entries.push_back(entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::SerializePart(OTSStream* out) const {
|
||||
if ((parent->compHead & OCTABOXES && !octabox.SerializePart(out)) ||
|
||||
!SerializeParts(this->entries, out)) {
|
||||
return parent->Error("GlyphAttrs: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::
|
||||
OctaboxMetrics::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU16(&this->subbox_bitmap)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read subbox_bitmap");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_neg_min)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read diag_neg_min");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_neg_max) ||
|
||||
this->diag_neg_max < this->diag_neg_min) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read valid diag_neg_max");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_pos_min)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read diag_pos_min");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_pos_max) ||
|
||||
this->diag_pos_max < this->diag_pos_min) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read valid diag_pos_max");
|
||||
}
|
||||
|
||||
unsigned subboxes_len = 0; // count of 1's in this->subbox_bitmap
|
||||
for (uint16_t i = this->subbox_bitmap; i; i >>= 1) {
|
||||
if (i & 0b1) {
|
||||
++subboxes_len;
|
||||
}
|
||||
}
|
||||
//this->subboxes.resize(subboxes_len, parent);
|
||||
for (unsigned i = 0; i < subboxes_len; i++) {
|
||||
this->subboxes.emplace_back(parent);
|
||||
if (!this->subboxes[i].ParsePart(table)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read subbox[%u]", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::
|
||||
OctaboxMetrics::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->subbox_bitmap) ||
|
||||
!out->WriteU8(this->diag_neg_min) ||
|
||||
!out->WriteU8(this->diag_neg_max) ||
|
||||
!out->WriteU8(this->diag_pos_min) ||
|
||||
!out->WriteU8(this->diag_pos_max) ||
|
||||
!SerializeParts(this->subboxes, out)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::OctaboxMetrics::
|
||||
SubboxEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->left)) {
|
||||
return parent->Error("SubboxEntry: Failed to read left");
|
||||
}
|
||||
if (!table.ReadU8(&this->right) || this->right < this->left) {
|
||||
return parent->Error("SubboxEntry: Failed to read valid right");
|
||||
}
|
||||
if (!table.ReadU8(&this->bottom)) {
|
||||
return parent->Error("SubboxEntry: Failed to read bottom");
|
||||
}
|
||||
if (!table.ReadU8(&this->top) || this->top < this->bottom) {
|
||||
return parent->Error("SubboxEntry: Failed to read valid top");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_pos_min)) {
|
||||
return parent->Error("SubboxEntry: Failed to read diag_pos_min");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_pos_max) ||
|
||||
this->diag_pos_max < this->diag_pos_min) {
|
||||
return parent->Error("SubboxEntry: Failed to read valid diag_pos_max");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_neg_min)) {
|
||||
return parent->Error("SubboxEntry: Failed to read diag_neg_min");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_neg_max) ||
|
||||
this->diag_neg_max < this->diag_neg_min) {
|
||||
return parent->Error("SubboxEntry: Failed to read valid diag_neg_max");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::OctaboxMetrics::
|
||||
SubboxEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->left) ||
|
||||
!out->WriteU8(this->right) ||
|
||||
!out->WriteU8(this->bottom) ||
|
||||
!out->WriteU8(this->top) ||
|
||||
!out->WriteU8(this->diag_pos_min) ||
|
||||
!out->WriteU8(this->diag_pos_max) ||
|
||||
!out->WriteU8(this->diag_neg_min) ||
|
||||
!out->WriteU8(this->diag_neg_max)) {
|
||||
return parent->Error("SubboxEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::
|
||||
GlatEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadS16(&this->attNum)) {
|
||||
return parent->Error("GlatEntry: Failed to read attNum");
|
||||
}
|
||||
if (!table.ReadS16(&this->num) || this->num < 0) {
|
||||
return parent->Error("GlatEntry: Failed to read valid num");
|
||||
}
|
||||
|
||||
//this->attributes.resize(this->num);
|
||||
for (unsigned i = 0; i < this->num; ++i) {
|
||||
this->attributes.emplace_back();
|
||||
if (!table.ReadS16(&this->attributes[i])) {
|
||||
return parent->Error("GlatEntry: Failed to read attribute %u", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::
|
||||
GlatEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteS16(this->attNum) ||
|
||||
!out->WriteS16(this->num) ||
|
||||
!SerializeParts(this->attributes, out)) {
|
||||
return parent->Error("GlatEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeGLAT::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
uint32_t version;
|
||||
if (!table.ReadU32(&version)) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
switch (version >> 16) {
|
||||
case 1:
|
||||
this->handler = new OpenTypeGLAT_v1(this->font, this->tag);
|
||||
break;
|
||||
case 2:
|
||||
this->handler = new OpenTypeGLAT_v2(this->font, this->tag);
|
||||
break;
|
||||
case 3: {
|
||||
this->handler = new OpenTypeGLAT_v3(this->font, this->tag);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return DropGraphite("Unsupported table version: %u", version >> 16);
|
||||
}
|
||||
return this->handler->Parse(data, length);
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT::Serialize(OTSStream* out) {
|
||||
if (!this->handler) {
|
||||
return Error("No Glat table parsed");
|
||||
}
|
||||
return this->handler->Serialize(out);
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GLAT_H_
|
||||
#define OTS_GLAT_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_Basic Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT_Basic : public Table {
|
||||
public:
|
||||
explicit OpenTypeGLAT_Basic(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
virtual bool Parse(const uint8_t* data, size_t length) = 0;
|
||||
virtual bool Serialize(OTSStream* out) = 0;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v1
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT_v1 : public OpenTypeGLAT_Basic {
|
||||
public:
|
||||
explicit OpenTypeGLAT_v1(Font* font, uint32_t tag)
|
||||
: OpenTypeGLAT_Basic(font, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
struct GlatEntry : public TablePart<OpenTypeGLAT_v1> {
|
||||
explicit GlatEntry(OpenTypeGLAT_v1* parent)
|
||||
: TablePart<OpenTypeGLAT_v1>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint8_t attNum;
|
||||
uint8_t num;
|
||||
std::vector<int16_t> attributes;
|
||||
};
|
||||
uint32_t version;
|
||||
std::vector<GlatEntry> entries;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v2
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT_v2 : public OpenTypeGLAT_Basic {
|
||||
public:
|
||||
explicit OpenTypeGLAT_v2(Font* font, uint32_t tag)
|
||||
: OpenTypeGLAT_Basic(font, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
struct GlatEntry : public TablePart<OpenTypeGLAT_v2> {
|
||||
explicit GlatEntry(OpenTypeGLAT_v2* parent)
|
||||
: TablePart<OpenTypeGLAT_v2>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
int16_t attNum;
|
||||
int16_t num;
|
||||
std::vector<int16_t> attributes;
|
||||
};
|
||||
uint32_t version;
|
||||
std::vector<GlatEntry> entries;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v3
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT_v3 : public OpenTypeGLAT_Basic {
|
||||
public:
|
||||
explicit OpenTypeGLAT_v3(Font* font, uint32_t tag)
|
||||
: OpenTypeGLAT_Basic(font, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length) {
|
||||
return this->Parse(data, length, false);
|
||||
}
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
bool Parse(const uint8_t* data, size_t length, bool prevent_decompression);
|
||||
struct GlyphAttrs : public TablePart<OpenTypeGLAT_v3> {
|
||||
explicit GlyphAttrs(OpenTypeGLAT_v3* parent)
|
||||
: TablePart<OpenTypeGLAT_v3>(parent), octabox(parent) { }
|
||||
bool ParsePart(Buffer& table) { return false; }
|
||||
bool ParsePart(Buffer& table, const size_t size);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct OctaboxMetrics : public TablePart<OpenTypeGLAT_v3> {
|
||||
explicit OctaboxMetrics(OpenTypeGLAT_v3* parent)
|
||||
: TablePart<OpenTypeGLAT_v3>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct SubboxEntry : public TablePart<OpenTypeGLAT_v3> {
|
||||
explicit SubboxEntry(OpenTypeGLAT_v3* parent)
|
||||
: TablePart<OpenTypeGLAT_v3>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint8_t left;
|
||||
uint8_t right;
|
||||
uint8_t bottom;
|
||||
uint8_t top;
|
||||
uint8_t diag_pos_min;
|
||||
uint8_t diag_pos_max;
|
||||
uint8_t diag_neg_min;
|
||||
uint8_t diag_neg_max;
|
||||
};
|
||||
uint16_t subbox_bitmap;
|
||||
uint8_t diag_neg_min;
|
||||
uint8_t diag_neg_max;
|
||||
uint8_t diag_pos_min;
|
||||
uint8_t diag_pos_max;
|
||||
std::vector<SubboxEntry> subboxes;
|
||||
};
|
||||
struct GlatEntry : public TablePart<OpenTypeGLAT_v3> {
|
||||
explicit GlatEntry(OpenTypeGLAT_v3* parent)
|
||||
: TablePart<OpenTypeGLAT_v3>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
int16_t attNum;
|
||||
int16_t num;
|
||||
std::vector<int16_t> attributes;
|
||||
};
|
||||
OctaboxMetrics octabox;
|
||||
std::vector<GlatEntry> entries;
|
||||
};
|
||||
uint32_t version;
|
||||
uint32_t compHead; // compression header
|
||||
static const uint32_t SCHEME = 0xF8000000;
|
||||
static const uint32_t FULL_SIZE = 0x07FFFFFF;
|
||||
static const uint32_t RESERVED = 0x07FFFFFE;
|
||||
static const uint32_t OCTABOXES = 0x00000001;
|
||||
std::vector<GlyphAttrs> entries;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT : public Table {
|
||||
public:
|
||||
explicit OpenTypeGLAT(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag), font(font), tag(tag) { }
|
||||
OpenTypeGLAT(const OpenTypeGLAT& other) = delete;
|
||||
OpenTypeGLAT& operator=(const OpenTypeGLAT& other) = delete;
|
||||
~OpenTypeGLAT() { delete handler; }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
Font* font;
|
||||
uint32_t tag;
|
||||
OpenTypeGLAT_Basic* handler = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GLAT_H_
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "gloc.h"
|
||||
|
||||
#include "name.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeGLOC::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
if (!name) {
|
||||
return DropGraphite("Required name table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->version)) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
if (this->version >> 16 != 1) {
|
||||
return DropGraphite("Unsupported table version: %u", this->version >> 16);
|
||||
}
|
||||
if (!table.ReadU16(&this->flags) || this->flags > 0b11) {
|
||||
return DropGraphite("Failed to read valid flags");
|
||||
}
|
||||
if (!table.ReadU16(&this->numAttribs)) {
|
||||
return DropGraphite("Failed to read numAttribs");
|
||||
}
|
||||
|
||||
if (this->flags & ATTRIB_IDS && this->numAttribs * sizeof(uint16_t) >
|
||||
table.remaining()) {
|
||||
return DropGraphite("Failed to calulate length of locations");
|
||||
}
|
||||
size_t locations_len = (table.remaining() -
|
||||
(this->flags & ATTRIB_IDS ? this->numAttribs * sizeof(uint16_t) : 0)) /
|
||||
(this->flags & LONG_FORMAT ? sizeof(uint32_t) : sizeof(uint16_t));
|
||||
//this->locations.resize(locations_len);
|
||||
if (this->flags & LONG_FORMAT) {
|
||||
unsigned long last_location = 0;
|
||||
for (size_t i = 0; i < locations_len; ++i) {
|
||||
this->locations.emplace_back();
|
||||
uint32_t& location = this->locations[i];
|
||||
if (!table.ReadU32(&location) || location < last_location) {
|
||||
return DropGraphite("Failed to read valid locations[%lu]", i);
|
||||
}
|
||||
last_location = location;
|
||||
}
|
||||
} else { // short (16-bit) offsets
|
||||
unsigned last_location = 0;
|
||||
for (size_t i = 0; i < locations_len; ++i) {
|
||||
uint16_t location;
|
||||
if (!table.ReadU16(&location) || location < last_location) {
|
||||
return DropGraphite("Failed to read valid locations[%lu]", i);
|
||||
}
|
||||
last_location = location;
|
||||
this->locations.push_back(static_cast<uint32_t>(location));
|
||||
}
|
||||
}
|
||||
if (this->locations.empty()) {
|
||||
return DropGraphite("No locations");
|
||||
}
|
||||
|
||||
if (this->flags & ATTRIB_IDS) { // attribIds array present
|
||||
//this->attribIds.resize(numAttribs);
|
||||
for (unsigned i = 0; i < this->numAttribs; ++i) {
|
||||
this->attribIds.emplace_back();
|
||||
if (!table.ReadU16(&this->attribIds[i]) ||
|
||||
!name->IsValidNameId(this->attribIds[i])) {
|
||||
return DropGraphite("Failed to read valid attribIds[%u]", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLOC::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU16(this->flags) ||
|
||||
!out->WriteU16(this->numAttribs) ||
|
||||
(this->flags & LONG_FORMAT ? !SerializeParts(this->locations, out) :
|
||||
![&] {
|
||||
for (uint32_t location : this->locations) {
|
||||
if (!out->WriteU16(static_cast<uint16_t>(location))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}()) ||
|
||||
(this->flags & ATTRIB_IDS && !SerializeParts(this->attribIds, out))) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& OpenTypeGLOC::GetLocations() {
|
||||
return this->locations;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GLOC_H_
|
||||
#define OTS_GLOC_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeGLOC : public Table {
|
||||
public:
|
||||
explicit OpenTypeGLOC(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
const std::vector<uint32_t>& GetLocations();
|
||||
|
||||
private:
|
||||
uint32_t version;
|
||||
uint16_t flags;
|
||||
static const uint16_t LONG_FORMAT = 0b1;
|
||||
static const uint16_t ATTRIB_IDS = 0b10;
|
||||
uint16_t numAttribs;
|
||||
std::vector<uint32_t> locations;
|
||||
std::vector<uint16_t> attribIds;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GLOC_H_
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GRAPHITE_H_
|
||||
#define OTS_GRAPHITE_H_
|
||||
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ots {
|
||||
|
||||
template<typename ParentType>
|
||||
class TablePart {
|
||||
public:
|
||||
TablePart(ParentType* parent) : parent(parent) { }
|
||||
virtual bool ParsePart(Buffer& table) = 0;
|
||||
virtual bool SerializePart(OTSStream* out) const = 0;
|
||||
protected:
|
||||
ParentType* parent;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool SerializeParts(const std::vector<T>& vec, OTSStream* out) {
|
||||
for (const T& part : vec) {
|
||||
if (!part.SerializePart(out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool SerializeParts(const std::vector<std::vector<T>>& vec, OTSStream* out) {
|
||||
for (const std::vector<T>& part : vec) {
|
||||
if (!SerializeParts(part, out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<uint8_t>& vec, OTSStream* out) {
|
||||
for (uint8_t part : vec) {
|
||||
if (!out->WriteU8(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<uint16_t>& vec, OTSStream* out) {
|
||||
for (uint16_t part : vec) {
|
||||
if (!out->WriteU16(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<int16_t>& vec, OTSStream* out) {
|
||||
for (int16_t part : vec) {
|
||||
if (!out->WriteS16(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<uint32_t>& vec, OTSStream* out) {
|
||||
for (uint32_t part : vec) {
|
||||
if (!out->WriteU32(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<int32_t>& vec, OTSStream* out) {
|
||||
for (int32_t part : vec) {
|
||||
if (!out->WriteS32(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t datasize(std::vector<T> vec) {
|
||||
return sizeof(T) * vec.size();
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GRAPHITE_H_
|
|
@ -10,17 +10,8 @@ EXPORTS += [
|
|||
]
|
||||
|
||||
SOURCES += [
|
||||
# don't unify sources that use a (file-specific) DROP_THIS_TABLE macro
|
||||
'gasp.cc',
|
||||
# needs to be separate because gpos.cc also defines kMaxClassDefValue
|
||||
'gdef.cc',
|
||||
'gpos.cc',
|
||||
'gsub.cc',
|
||||
'hdmx.cc',
|
||||
'kern.cc',
|
||||
'ltsh.cc',
|
||||
'math.cc',
|
||||
'vdmx.cc',
|
||||
'vorg.cc',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -28,13 +19,23 @@ UNIFIED_SOURCES += [
|
|||
'cff_type2_charstring.cc',
|
||||
'cmap.cc',
|
||||
'cvt.cc',
|
||||
'feat.cc',
|
||||
'fpgm.cc',
|
||||
'gasp.cc',
|
||||
'glat.cc',
|
||||
'gloc.cc',
|
||||
'glyf.cc',
|
||||
'gpos.cc',
|
||||
'gsub.cc',
|
||||
'hdmx.cc',
|
||||
'head.cc',
|
||||
'hhea.cc',
|
||||
'hmtx.cc',
|
||||
'kern.cc',
|
||||
'layout.cc',
|
||||
'loca.cc',
|
||||
'ltsh.cc',
|
||||
'math.cc',
|
||||
'maxp.cc',
|
||||
'metrics.cc',
|
||||
'name.cc',
|
||||
|
@ -42,8 +43,13 @@ UNIFIED_SOURCES += [
|
|||
'ots.cc',
|
||||
'post.cc',
|
||||
'prep.cc',
|
||||
'sile.cc',
|
||||
'silf.cc',
|
||||
'sill.cc',
|
||||
'vdmx.cc',
|
||||
'vhea.cc',
|
||||
'vmtx.cc',
|
||||
'vorg.cc',
|
||||
]
|
||||
|
||||
# We allow warnings for third-party code that can be updated from upstream.
|
||||
|
@ -53,6 +59,7 @@ FINAL_LIBRARY = 'gkmedias'
|
|||
|
||||
DEFINES['PACKAGE_VERSION'] = '"moz"'
|
||||
DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
|
||||
DEFINES['OTS_GRAPHITE'] = 1
|
||||
|
||||
USE_LIBS += [
|
||||
'brotli',
|
||||
|
|
|
@ -149,6 +149,7 @@ bool OpenTypeNAME::Parse(const uint8_t* data, size_t length) {
|
|||
}
|
||||
|
||||
this->names.push_back(rec);
|
||||
this->name_ids.insert(rec.name_id);
|
||||
}
|
||||
|
||||
if (format == 1) {
|
||||
|
@ -305,4 +306,50 @@ bool OpenTypeNAME::Serialize(OTSStream* out) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeNAME::IsValidNameId(uint16_t nameID, bool addIfMissing) {
|
||||
if (addIfMissing && !this->name_ids.count(nameID)) {
|
||||
bool added_unicode = false;
|
||||
bool added_macintosh = false;
|
||||
bool added_windows = false;
|
||||
const size_t names_size = this->names.size(); // original size
|
||||
for (size_t i = 0; i < names_size; ++i) switch (names[i].platform_id) {
|
||||
case 0:
|
||||
if (!added_unicode) {
|
||||
// If there is an existing NameRecord with platform_id == 0 (Unicode),
|
||||
// then add a NameRecord for the the specified nameID with arguments
|
||||
// 0 (Unicode), 0 (v1.0), 0 (unspecified language).
|
||||
this->names.emplace_back(0, 0, 0, nameID);
|
||||
this->names.back().text = "NoName";
|
||||
added_unicode = true;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (!added_macintosh) {
|
||||
// If there is an existing NameRecord with platform_id == 1 (Macintosh),
|
||||
// then add a NameRecord for the specified nameID with arguments
|
||||
// 1 (Macintosh), 0 (Roman), 0 (English).
|
||||
this->names.emplace_back(1, 0, 0, nameID);
|
||||
this->names.back().text = "NoName";
|
||||
added_macintosh = true;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!added_windows) {
|
||||
// If there is an existing NameRecord with platform_id == 3 (Windows),
|
||||
// then add a NameRecord for the specified nameID with arguments
|
||||
// 3 (Windows), 1 (UCS), 1033 (US English).
|
||||
this->names.emplace_back(3, 1, 1033, nameID);
|
||||
this->names.back().text = "NoName";
|
||||
added_windows = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (added_unicode || added_macintosh || added_windows) {
|
||||
std::sort(this->names.begin(), this->names.end());
|
||||
this->name_ids.insert(nameID);
|
||||
}
|
||||
}
|
||||
return this->name_ids.count(nameID);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
|
@ -50,10 +51,12 @@ class OpenTypeNAME : public Table {
|
|||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool IsValidNameId(uint16_t nameID, bool addIfMissing = false);
|
||||
|
||||
private:
|
||||
std::vector<NameRecord> names;
|
||||
std::vector<std::string> lang_tags;
|
||||
std::unordered_set<uint16_t> name_ids;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
|
|
@ -47,6 +47,16 @@
|
|||
#include "vmtx.h"
|
||||
#include "vorg.h"
|
||||
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
#include "feat.h"
|
||||
#include "glat.h"
|
||||
#include "gloc.h"
|
||||
#include "sile.h"
|
||||
#include "silf.h"
|
||||
#include "sill.h"
|
||||
#endif
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct Arena {
|
||||
|
@ -124,6 +134,15 @@ const struct {
|
|||
{ OTS_TAG_VHEA, false },
|
||||
{ OTS_TAG_VMTX, false },
|
||||
{ OTS_TAG_MATH, false },
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
{ OTS_TAG_GLOC, false },
|
||||
{ OTS_TAG_GLAT, false },
|
||||
{ OTS_TAG_FEAT, false },
|
||||
{ OTS_TAG_SILF, false },
|
||||
{ OTS_TAG_SILE, false },
|
||||
{ OTS_TAG_SILL, false },
|
||||
#endif
|
||||
{ 0, false },
|
||||
};
|
||||
|
||||
|
@ -869,6 +888,15 @@ bool Font::ParseTable(const TableEntry& table_entry, const uint8_t* data,
|
|||
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
|
||||
case OTS_TAG_VHEA: table = new OpenTypeVHEA(this, tag); break;
|
||||
case OTS_TAG_VMTX: table = new OpenTypeVMTX(this, tag); break;
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
case OTS_TAG_FEAT: table = new OpenTypeFEAT(this, tag); break;
|
||||
case OTS_TAG_GLAT: table = new OpenTypeGLAT(this, tag); break;
|
||||
case OTS_TAG_GLOC: table = new OpenTypeGLOC(this, tag); break;
|
||||
case OTS_TAG_SILE: table = new OpenTypeSILE(this, tag); break;
|
||||
case OTS_TAG_SILF: table = new OpenTypeSILF(this, tag); break;
|
||||
case OTS_TAG_SILL: table = new OpenTypeSILL(this, tag); break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
@ -910,6 +938,21 @@ Table* Font::GetTypedTable(uint32_t tag) const {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void Font::DropGraphite() {
|
||||
file->context->Message(0, "Dropping all Graphite tables");
|
||||
for (const std::pair<uint32_t, Table*> entry : m_tables) {
|
||||
if (entry.first == OTS_TAG_FEAT ||
|
||||
entry.first == OTS_TAG_GLAT ||
|
||||
entry.first == OTS_TAG_GLOC ||
|
||||
entry.first == OTS_TAG_SILE ||
|
||||
entry.first == OTS_TAG_SILF ||
|
||||
entry.first == OTS_TAG_SILL) {
|
||||
entry.second->Drop("Discarding Graphite table");
|
||||
}
|
||||
}
|
||||
dropped_graphite = true;
|
||||
}
|
||||
|
||||
bool Table::ShouldSerialize() {
|
||||
return m_shouldSerialize;
|
||||
}
|
||||
|
@ -950,6 +993,16 @@ bool Table::Drop(const char *format, ...) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Table::DropGraphite(const char *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
Message(0, format, va);
|
||||
va_end(va);
|
||||
|
||||
m_font->DropGraphite();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TablePassthru::Parse(const uint8_t *data, size_t length) {
|
||||
m_data = data;
|
||||
m_length = length;
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
#ifndef OTS_H_
|
||||
#define OTS_H_
|
||||
|
||||
// Not needed in the gecko build
|
||||
// #include "config.h"
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cstdarg>
|
||||
|
@ -188,9 +189,12 @@ bool IsValidVersionTag(uint32_t tag);
|
|||
#define OTS_TAG_CFF OTS_TAG('C','F','F',' ')
|
||||
#define OTS_TAG_CMAP OTS_TAG('c','m','a','p')
|
||||
#define OTS_TAG_CVT OTS_TAG('c','v','t',' ')
|
||||
#define OTS_TAG_FEAT OTS_TAG('F','e','a','t')
|
||||
#define OTS_TAG_FPGM OTS_TAG('f','p','g','m')
|
||||
#define OTS_TAG_GASP OTS_TAG('g','a','s','p')
|
||||
#define OTS_TAG_GDEF OTS_TAG('G','D','E','F')
|
||||
#define OTS_TAG_GLAT OTS_TAG('G','l','a','t')
|
||||
#define OTS_TAG_GLOC OTS_TAG('G','l','o','c')
|
||||
#define OTS_TAG_GLYF OTS_TAG('g','l','y','f')
|
||||
#define OTS_TAG_GPOS OTS_TAG('G','P','O','S')
|
||||
#define OTS_TAG_GSUB OTS_TAG('G','S','U','B')
|
||||
|
@ -207,6 +211,9 @@ bool IsValidVersionTag(uint32_t tag);
|
|||
#define OTS_TAG_OS2 OTS_TAG('O','S','/','2')
|
||||
#define OTS_TAG_POST OTS_TAG('p','o','s','t')
|
||||
#define OTS_TAG_PREP OTS_TAG('p','r','e','p')
|
||||
#define OTS_TAG_SILE OTS_TAG('S','i','l','e')
|
||||
#define OTS_TAG_SILF OTS_TAG('S','i','l','f')
|
||||
#define OTS_TAG_SILL OTS_TAG('S','i','l','l')
|
||||
#define OTS_TAG_VDMX OTS_TAG('V','D','M','X')
|
||||
#define OTS_TAG_VHEA OTS_TAG('v','h','e','a')
|
||||
#define OTS_TAG_VMTX OTS_TAG('v','m','t','x')
|
||||
|
@ -244,6 +251,7 @@ class Table {
|
|||
bool Error(const char *format, ...);
|
||||
bool Warning(const char *format, ...);
|
||||
bool Drop(const char *format, ...);
|
||||
bool DropGraphite(const char *format, ...);
|
||||
|
||||
private:
|
||||
void Message(int level, const char *format, va_list va);
|
||||
|
@ -277,7 +285,8 @@ struct Font {
|
|||
num_tables(0),
|
||||
search_range(0),
|
||||
entry_selector(0),
|
||||
range_shift(0) {
|
||||
range_shift(0),
|
||||
dropped_graphite(false) {
|
||||
}
|
||||
|
||||
bool ParseTable(const TableEntry& tableinfo, const uint8_t* data,
|
||||
|
@ -289,6 +298,9 @@ struct Font {
|
|||
// if not (i.e. if the table was treated as Passthru), it will return NULL.
|
||||
Table* GetTypedTable(uint32_t tag) const;
|
||||
|
||||
// Drop all Graphite tables and don't parse new ones.
|
||||
void DropGraphite();
|
||||
|
||||
FontFile *file;
|
||||
|
||||
uint32_t version;
|
||||
|
@ -296,6 +308,7 @@ struct Font {
|
|||
uint16_t search_range;
|
||||
uint16_t entry_selector;
|
||||
uint16_t range_shift;
|
||||
bool dropped_graphite;
|
||||
|
||||
private:
|
||||
std::map<uint32_t, Table*> m_tables;
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "sile.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeSILE::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
|
||||
return DropGraphite("Failed to read valid version");
|
||||
}
|
||||
if (!table.ReadU32(&this->checksum)) {
|
||||
return DropGraphite("Failed to read checksum");
|
||||
}
|
||||
if (!table.ReadU32(&this->createTime[0]) ||
|
||||
!table.ReadU32(&this->createTime[1])) {
|
||||
return DropGraphite("Failed to read createTime");
|
||||
}
|
||||
if (!table.ReadU32(&this->modifyTime[0]) ||
|
||||
!table.ReadU32(&this->modifyTime[1])) {
|
||||
return DropGraphite("Failed to read modifyTime");
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->fontNameLength)) {
|
||||
return DropGraphite("Failed to read fontNameLength");
|
||||
}
|
||||
//this->fontName.resize(this->fontNameLength);
|
||||
for (unsigned i = 0; i < this->fontNameLength; ++i) {
|
||||
this->fontName.emplace_back();
|
||||
if (!table.ReadU16(&this->fontName[i])) {
|
||||
return DropGraphite("Failed to read fontName[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->fontFileLength)) {
|
||||
return DropGraphite("Failed to read fontFileLength");
|
||||
}
|
||||
//this->baseFile.resize(this->fontFileLength);
|
||||
for (unsigned i = 0; i < this->fontFileLength; ++i) {
|
||||
this->baseFile.emplace_back();
|
||||
if (!table.ReadU16(&this->baseFile[i])) {
|
||||
return DropGraphite("Failed to read baseFile[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILE::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU32(this->checksum) ||
|
||||
!out->WriteU32(this->createTime[0]) ||
|
||||
!out->WriteU32(this->createTime[1]) ||
|
||||
!out->WriteU32(this->modifyTime[0]) ||
|
||||
!out->WriteU32(this->modifyTime[1]) ||
|
||||
!out->WriteU16(this->fontNameLength) ||
|
||||
!SerializeParts(this->fontName, out) ||
|
||||
!out->WriteU16(this->fontFileLength) ||
|
||||
!SerializeParts(this->baseFile, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_SILE_H_
|
||||
#define OTS_SILE_H_
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeSILE : public Table {
|
||||
public:
|
||||
explicit OpenTypeSILE(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
uint32_t version;
|
||||
uint32_t checksum;
|
||||
uint32_t createTime[2];
|
||||
uint32_t modifyTime[2];
|
||||
uint16_t fontNameLength;
|
||||
std::vector<uint16_t> fontName;
|
||||
uint16_t fontFileLength;
|
||||
std::vector<uint16_t> baseFile;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_SILE_H_
|
|
@ -0,0 +1,950 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "silf.h"
|
||||
|
||||
#include "name.h"
|
||||
#include "mozilla/Compression.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
|
||||
bool prevent_decompression) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!table.ReadU32(&this->version)) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
if (this->version >> 16 != 1 &&
|
||||
this->version >> 16 != 2 &&
|
||||
this->version >> 16 != 3 &&
|
||||
this->version >> 16 != 4 &&
|
||||
this->version >> 16 != 5) {
|
||||
return DropGraphite("Unsupported table version: %u", this->version >> 16);
|
||||
}
|
||||
if (this->version >> 16 >= 3 && !table.ReadU32(&this->compHead)) {
|
||||
return DropGraphite("Failed to read compHead");
|
||||
}
|
||||
if (this->version >> 16 >= 5) {
|
||||
switch ((this->compHead & SCHEME) >> 27) {
|
||||
case 0: // uncompressed
|
||||
break;
|
||||
case 1: { // lz4
|
||||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0);
|
||||
size_t outputSize = 0;
|
||||
if (!mozilla::Compression::LZ4::decompress(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
table.remaining(),
|
||||
reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(),
|
||||
&outputSize) ||
|
||||
outputSize != (this->compHead & FULL_SIZE)) {
|
||||
return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
default:
|
||||
return DropGraphite("Unknown compression scheme");
|
||||
}
|
||||
}
|
||||
if (!table.ReadU16(&this->numSub)) {
|
||||
return DropGraphite("Failed to read numSub");
|
||||
}
|
||||
if (this->version >> 16 >= 2 && !table.ReadU16(&this->reserved)) {
|
||||
return DropGraphite("Failed to read reserved");
|
||||
}
|
||||
if (this->version >> 16 >= 2 && this->reserved != 0) {
|
||||
Warning("Nonzero reserved");
|
||||
}
|
||||
|
||||
unsigned long last_offset = 0;
|
||||
//this->offset.resize(this->numSub);
|
||||
for (unsigned i = 0; i < this->numSub; ++i) {
|
||||
this->offset.emplace_back();
|
||||
if (!table.ReadU32(&this->offset[i]) || this->offset[i] < last_offset) {
|
||||
return DropGraphite("Failed to read offset[%u]", i);
|
||||
}
|
||||
last_offset = this->offset[i];
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < this->numSub; ++i) {
|
||||
if (table.offset() != this->offset[i]) {
|
||||
return DropGraphite("Offset check failed for tables[%lu]", i);
|
||||
}
|
||||
SILSub subtable(this);
|
||||
if (!subtable.ParsePart(table)) {
|
||||
return DropGraphite("Failed to read tables[%u]", i);
|
||||
}
|
||||
tables.push_back(subtable);
|
||||
}
|
||||
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
(this->version >> 16 >= 3 && !out->WriteU32(this->compHead)) ||
|
||||
!out->WriteU16(this->numSub) ||
|
||||
(this->version >> 16 >= 2 && !out->WriteU16(this->reserved)) ||
|
||||
!SerializeParts(this->offset, out) ||
|
||||
!SerializeParts(this->tables, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
|
||||
size_t init_offset = table.offset();
|
||||
if (parent->version >> 16 >= 3) {
|
||||
if (!table.ReadU32(&this->ruleVersion)) {
|
||||
return parent->Error("SILSub: Failed to read ruleVersion");
|
||||
}
|
||||
if (!table.ReadU16(&this->passOffset)) {
|
||||
return parent->Error("SILSub: Failed to read passOffset");
|
||||
}
|
||||
if (!table.ReadU16(&this->pseudosOffset)) {
|
||||
return parent->Error("SILSub: Failed to read pseudosOffset");
|
||||
}
|
||||
}
|
||||
if (!table.ReadU16(&this->maxGlyphID)) {
|
||||
return parent->Error("SILSub: Failed to read maxGlyphID");
|
||||
}
|
||||
if (!table.ReadS16(&this->extraAscent)) {
|
||||
return parent->Error("SILSub: Failed to read extraAscent");
|
||||
}
|
||||
if (!table.ReadS16(&this->extraDescent)) {
|
||||
return parent->Error("SILSub: Failed to read extraDescent");
|
||||
}
|
||||
if (!table.ReadU8(&this->numPasses)) {
|
||||
return parent->Error("SILSub: Failed to read numPasses");
|
||||
}
|
||||
if (!table.ReadU8(&this->iSubst) || this->iSubst > this->numPasses) {
|
||||
return parent->Error("SILSub: Failed to read valid iSubst");
|
||||
}
|
||||
if (!table.ReadU8(&this->iPos) || this->iPos > this->numPasses) {
|
||||
return parent->Error("SILSub: Failed to read valid iPos");
|
||||
}
|
||||
if (!table.ReadU8(&this->iJust) || this->iJust > this->numPasses) {
|
||||
return parent->Error("SILSub: Failed to read valid iJust");
|
||||
}
|
||||
if (!table.ReadU8(&this->iBidi) ||
|
||||
!(iBidi == 0xFF || this->iBidi <= this->iPos)) {
|
||||
return parent->Error("SILSub: Failed to read valid iBidi");
|
||||
}
|
||||
if (!table.ReadU8(&this->flags)) {
|
||||
return parent->Error("SILSub: Failed to read flags");
|
||||
// checks omitted
|
||||
}
|
||||
if (!table.ReadU8(&this->maxPreContext)) {
|
||||
return parent->Error("SILSub: Failed to read maxPreContext");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxPostContext)) {
|
||||
return parent->Error("SILSub: Failed to read maxPostContext");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrPseudo)) {
|
||||
return parent->Error("SILSub: Failed to read attrPseudo");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrBreakWeight)) {
|
||||
return parent->Error("SILSub: Failed to read attrBreakWeight");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrDirectionality)) {
|
||||
return parent->Error("SILSub: Failed to read attrDirectionality");
|
||||
}
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU8(&this->attrMirroring)) {
|
||||
return parent->Error("SILSub: Failed to read attrMirroring");
|
||||
}
|
||||
if (parent->version >> 16 < 4 && this->attrMirroring != 0) {
|
||||
parent->Warning("SILSub: Nonzero attrMirroring (reserved before v4)");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrSkipPasses)) {
|
||||
return parent->Error("SILSub: Failed to read attrSkipPasses");
|
||||
}
|
||||
if (parent->version >> 16 < 4 && this->attrSkipPasses != 0) {
|
||||
parent->Warning("SILSub: Nonzero attrSkipPasses (reserved2 before v4)");
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->numJLevels)) {
|
||||
return parent->Error("SILSub: Failed to read numJLevels");
|
||||
}
|
||||
//this->jLevels.resize(this->numJLevels, parent);
|
||||
for (unsigned i = 0; i < this->numJLevels; ++i) {
|
||||
this->jLevels.emplace_back(parent);
|
||||
if (!this->jLevels[i].ParsePart(table)) {
|
||||
return parent->Error("SILSub: Failed to read jLevels[%u]", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->numLigComp)) {
|
||||
return parent->Error("SILSub: Failed to read numLigComp");
|
||||
}
|
||||
if (!table.ReadU8(&this->numUserDefn)) {
|
||||
return parent->Error("SILSub: Failed to read numUserDefn");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxCompPerLig)) {
|
||||
return parent->Error("SILSub: Failed to read maxCompPerLig");
|
||||
}
|
||||
if (!table.ReadU8(&this->direction)) {
|
||||
return parent->Error("SILSub: Failed to read direction");
|
||||
}
|
||||
if (!table.ReadU8(&this->attCollisions)) {
|
||||
return parent->Error("SILSub: Failed to read attCollisions");
|
||||
}
|
||||
if (parent->version >> 16 < 5 && this->attCollisions != 0) {
|
||||
parent->Warning("SILSub: Nonzero attCollisions (reserved before v5)");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved4)) {
|
||||
return parent->Error("SILSub: Failed to read reserved4");
|
||||
}
|
||||
if (this->reserved4 != 0) {
|
||||
parent->Warning("SILSub: Nonzero reserved4");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved5)) {
|
||||
return parent->Error("SILSub: Failed to read reserved5");
|
||||
}
|
||||
if (this->reserved5 != 0) {
|
||||
parent->Warning("SILSub: Nonzero reserved5");
|
||||
}
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU8(&this->reserved6)) {
|
||||
return parent->Error("SILSub: Failed to read reserved6");
|
||||
}
|
||||
if (this->reserved6 != 0) {
|
||||
parent->Warning("SILSub: Nonzero reserved6");
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->numCritFeatures)) {
|
||||
return parent->Error("SILSub: Failed to read numCritFeatures");
|
||||
}
|
||||
//this->critFeatures.resize(this->numCritFeatures);
|
||||
for (unsigned i = 0; i < this->numCritFeatures; ++i) {
|
||||
this->critFeatures.emplace_back();
|
||||
if (!table.ReadU16(&this->critFeatures[i])) {
|
||||
return parent->Error("SILSub: Failed to read critFeatures[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->reserved7)) {
|
||||
return parent->Error("SILSub: Failed to read reserved7");
|
||||
}
|
||||
if (this->reserved7 != 0) {
|
||||
parent->Warning("SILSub: Nonzero reserved7");
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->numScriptTag)) {
|
||||
return parent->Error("SILSub: Failed to read numScriptTag");
|
||||
}
|
||||
//this->scriptTag.resize(this->numScriptTag);
|
||||
for (unsigned i = 0; i < this->numScriptTag; ++i) {
|
||||
this->scriptTag.emplace_back();
|
||||
if (!table.ReadU32(&this->scriptTag[i])) {
|
||||
return parent->Error("SILSub: Failed to read scriptTag[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->lbGID) || this->lbGID > this->maxGlyphID) {
|
||||
return parent->Error("SILSub: Failed to read valid lbGID");
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 3 &&
|
||||
table.offset() != init_offset + this->passOffset) {
|
||||
return parent->Error("SILSub: passOffset check failed");
|
||||
}
|
||||
unsigned long last_oPass = 0;
|
||||
//this->oPasses.resize(static_cast<unsigned>(this->numPasses) + 1);
|
||||
for (unsigned i = 0; i <= this->numPasses; ++i) {
|
||||
this->oPasses.emplace_back();
|
||||
if (!table.ReadU32(&this->oPasses[i]) || this->oPasses[i] < last_oPass) {
|
||||
return false;
|
||||
}
|
||||
last_oPass = this->oPasses[i];
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 3 &&
|
||||
table.offset() != init_offset + this->pseudosOffset) {
|
||||
return parent->Error("SILSub: pseudosOffset check failed");
|
||||
}
|
||||
if (!table.ReadU16(&this->numPseudo)) {
|
||||
return parent->Error("SILSub: Failed to read numPseudo");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchPseudo) || this->searchPseudo !=
|
||||
(this->numPseudo == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::pow(2, std::floor(std::log2(this->numPseudo))))) {
|
||||
return parent->Error("SILSub: Failed to read valid searchPseudo");
|
||||
}
|
||||
if (!table.ReadU16(&this->pseudoSelector) || this->pseudoSelector !=
|
||||
(this->numPseudo == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::floor(std::log2(this->numPseudo)))) {
|
||||
return parent->Error("SILSub: Failed to read valid pseudoSelector");
|
||||
}
|
||||
if (!table.ReadU16(&this->pseudoShift) ||
|
||||
this->pseudoShift != this->numPseudo - this->searchPseudo) {
|
||||
return parent->Error("SILSub: Failed to read valid pseudoShift");
|
||||
}
|
||||
|
||||
//this->pMaps.resize(this->numPseudo, parent);
|
||||
for (unsigned i = 0; i < numPseudo; i++) {
|
||||
this->pMaps.emplace_back(parent);
|
||||
if (!this->pMaps[i].ParsePart(table)) {
|
||||
return parent->Error("SILSub: Failed to read pMaps[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->classes.ParsePart(table)) {
|
||||
return parent->Error("SILSub: Failed to read classes");
|
||||
}
|
||||
|
||||
//this->passes.resize(this->numPasses, parent);
|
||||
for (unsigned i = 0; i < this->numPasses; ++i) {
|
||||
this->passes.emplace_back(parent);
|
||||
if (table.offset() != init_offset + this->oPasses[i]) {
|
||||
return parent->Error("SILSub: Offset check failed for passes[%u]", i);
|
||||
}
|
||||
if (!this->passes[i].ParsePart(table, init_offset, this->oPasses[i+1])) {
|
||||
return parent->Error("SILSub: Failed to read passes[%u]", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::SerializePart(OTSStream* out) const {
|
||||
if ((parent->version >> 16 >= 3 &&
|
||||
(!out->WriteU32(this->ruleVersion) ||
|
||||
!out->WriteU16(this->passOffset) ||
|
||||
!out->WriteU16(this->pseudosOffset))) ||
|
||||
!out->WriteU16(this->maxGlyphID) ||
|
||||
!out->WriteS16(this->extraAscent) ||
|
||||
!out->WriteS16(this->extraDescent) ||
|
||||
!out->WriteU8(this->numPasses) ||
|
||||
!out->WriteU8(this->iSubst) ||
|
||||
!out->WriteU8(this->iPos) ||
|
||||
!out->WriteU8(this->iJust) ||
|
||||
!out->WriteU8(this->iBidi) ||
|
||||
!out->WriteU8(this->flags) ||
|
||||
!out->WriteU8(this->maxPreContext) ||
|
||||
!out->WriteU8(this->maxPostContext) ||
|
||||
!out->WriteU8(this->attrPseudo) ||
|
||||
!out->WriteU8(this->attrBreakWeight) ||
|
||||
!out->WriteU8(this->attrDirectionality) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU8(this->attrMirroring) ||
|
||||
!out->WriteU8(this->attrSkipPasses) ||
|
||||
!out->WriteU8(this->numJLevels) ||
|
||||
!SerializeParts(this->jLevels, out))) ||
|
||||
!out->WriteU16(this->numLigComp) ||
|
||||
!out->WriteU8(this->numUserDefn) ||
|
||||
!out->WriteU8(this->maxCompPerLig) ||
|
||||
!out->WriteU8(this->direction) ||
|
||||
!out->WriteU8(this->attCollisions) ||
|
||||
!out->WriteU8(this->reserved4) ||
|
||||
!out->WriteU8(this->reserved5) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU8(this->reserved6) ||
|
||||
!out->WriteU8(this->numCritFeatures) ||
|
||||
!SerializeParts(this->critFeatures, out) ||
|
||||
!out->WriteU8(this->reserved7))) ||
|
||||
!out->WriteU8(this->numScriptTag) ||
|
||||
!SerializeParts(this->scriptTag, out) ||
|
||||
!out->WriteU16(this->lbGID) ||
|
||||
!SerializeParts(this->oPasses, out) ||
|
||||
!out->WriteU16(this->numPseudo) ||
|
||||
!out->WriteU16(this->searchPseudo) ||
|
||||
!out->WriteU16(this->pseudoSelector) ||
|
||||
!out->WriteU16(this->pseudoShift) ||
|
||||
!SerializeParts(this->pMaps, out) ||
|
||||
!this->classes.SerializePart(out) ||
|
||||
!SerializeParts(this->passes, out)) {
|
||||
return parent->Error("SILSub: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
JustificationLevel::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->attrStretch)) {
|
||||
return parent->Error("JustificationLevel: Failed to read attrStretch");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrShrink)) {
|
||||
return parent->Error("JustificationLevel: Failed to read attrShrink");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrStep)) {
|
||||
return parent->Error("JustificationLevel: Failed to read attrStep");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrWeight)) {
|
||||
return parent->Error("JustificationLevel: Failed to read attrWeight");
|
||||
}
|
||||
if (!table.ReadU8(&this->runto)) {
|
||||
return parent->Error("JustificationLevel: Failed to read runto");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved)) {
|
||||
return parent->Error("JustificationLevel: Failed to read reserved");
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
parent->Warning("JustificationLevel: Nonzero reserved");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved2)) {
|
||||
return parent->Error("JustificationLevel: Failed to read reserved2");
|
||||
}
|
||||
if (this->reserved2 != 0) {
|
||||
parent->Warning("JustificationLevel: Nonzero reserved2");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved3)) {
|
||||
return parent->Error("JustificationLevel: Failed to read reserved3");
|
||||
}
|
||||
if (this->reserved3 != 0) {
|
||||
parent->Warning("JustificationLevel: Nonzero reserved3");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
JustificationLevel::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->attrStretch) ||
|
||||
!out->WriteU8(this->attrShrink) ||
|
||||
!out->WriteU8(this->attrStep) ||
|
||||
!out->WriteU8(this->attrWeight) ||
|
||||
!out->WriteU8(this->runto) ||
|
||||
!out->WriteU8(this->reserved) ||
|
||||
!out->WriteU8(this->reserved2) ||
|
||||
!out->WriteU8(this->reserved3)) {
|
||||
return parent->Error("JustificationLevel: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
PseudoMap::ParsePart(Buffer& table) {
|
||||
if (parent->version >> 16 >= 2 && !table.ReadU32(&this->unicode)) {
|
||||
return parent->Error("PseudoMap: Failed to read unicode");
|
||||
}
|
||||
if (parent->version >> 16 == 1) {
|
||||
uint16_t unicode;
|
||||
if (!table.ReadU16(&unicode)) {
|
||||
return parent->Error("PseudoMap: Failed to read unicode");
|
||||
}
|
||||
this->unicode = unicode;
|
||||
}
|
||||
if (!table.ReadU16(&this->nPseudo)) {
|
||||
return parent->Error("PseudoMap: Failed to read nPseudo");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
PseudoMap::SerializePart(OTSStream* out) const {
|
||||
if ((parent->version >> 16 >= 2 && !out->WriteU32(this->unicode)) ||
|
||||
(parent->version >> 16 == 1 &&
|
||||
!out->WriteU16(static_cast<uint16_t>(this->unicode))) ||
|
||||
!out->WriteU16(this->nPseudo)) {
|
||||
return parent->Error("PseudoMap: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
ClassMap::ParsePart(Buffer& table) {
|
||||
size_t init_offset = table.offset();
|
||||
if (!table.ReadU16(&this->numClass)) {
|
||||
return parent->Error("ClassMap: Failed to read numClass");
|
||||
}
|
||||
if (!table.ReadU16(&this->numLinear) || this->numLinear > this->numClass) {
|
||||
return parent->Error("ClassMap: Failed to read valid numLinear");
|
||||
}
|
||||
|
||||
//this->oClass.resize(static_cast<unsigned long>(this->numClass) + 1);
|
||||
if (parent->version >> 16 >= 4) {
|
||||
unsigned long last_oClass = 0;
|
||||
for (unsigned long i = 0; i <= this->numClass; ++i) {
|
||||
this->oClass.emplace_back();
|
||||
if (!table.ReadU32(&this->oClass[i]) || this->oClass[i] < last_oClass) {
|
||||
return parent->Error("ClassMap: Failed to read oClass[%lu]", i);
|
||||
}
|
||||
last_oClass = this->oClass[i];
|
||||
}
|
||||
}
|
||||
if (parent->version >> 16 < 4) {
|
||||
unsigned last_oClass = 0;
|
||||
for (unsigned long i = 0; i <= this->numClass; ++i) {
|
||||
uint16_t offset;
|
||||
if (!table.ReadU16(&offset) || offset < last_oClass) {
|
||||
return parent->Error("ClassMap: Failed to read oClass[%lu]", i);
|
||||
}
|
||||
last_oClass = offset;
|
||||
this->oClass.push_back(static_cast<uint32_t>(offset));
|
||||
}
|
||||
}
|
||||
|
||||
if (table.offset() - init_offset > this->oClass[this->numLinear]) {
|
||||
return parent->Error("ClassMap: Failed to calculate length of glyphs");
|
||||
}
|
||||
unsigned long glyphs_len = (this->oClass[this->numLinear] -
|
||||
(table.offset() - init_offset))/2;
|
||||
//this->glyphs.resize(glyphs_len);
|
||||
for (unsigned long i = 0; i < glyphs_len; ++i) {
|
||||
this->glyphs.emplace_back();
|
||||
if (!table.ReadU16(&this->glyphs[i])) {
|
||||
return parent->Error("ClassMap: Failed to read glyphs[%lu]", i);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned lookups_len = this->numClass - this->numLinear;
|
||||
// this->numLinear <= this->numClass
|
||||
//this->lookups.resize(lookups_len, parent);
|
||||
for (unsigned i = 0; i < lookups_len; ++i) {
|
||||
this->lookups.emplace_back(parent);
|
||||
if (table.offset() != init_offset + oClass[this->numLinear + i]) {
|
||||
return parent->Error("ClassMap: Offset check failed for lookups[%u]", i);
|
||||
}
|
||||
if (!this->lookups[i].ParsePart(table)) {
|
||||
return parent->Error("ClassMap: Failed to read lookups[%u]", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
ClassMap::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->numClass) ||
|
||||
!out->WriteU16(this->numLinear) ||
|
||||
(parent->version >> 16 >= 4 && !SerializeParts(this->oClass, out)) ||
|
||||
(parent->version >> 16 < 4 &&
|
||||
![&] {
|
||||
for (uint32_t offset : this->oClass) {
|
||||
if (!out->WriteU16(static_cast<uint16_t>(offset))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}()) ||
|
||||
!SerializeParts(this->glyphs, out) ||
|
||||
!SerializeParts(this->lookups, out)) {
|
||||
return parent->Error("ClassMap: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ClassMap::
|
||||
LookupClass::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU16(&this->numIDs)) {
|
||||
return parent->Error("LookupClass: Failed to read numIDs");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchRange) || this->searchRange !=
|
||||
(this->numIDs == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::pow(2, std::floor(std::log2(this->numIDs))))) {
|
||||
return parent->Error("LookupClass: Failed to read valid searchRange");
|
||||
}
|
||||
if (!table.ReadU16(&this->entrySelector) || this->entrySelector !=
|
||||
(this->numIDs == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::floor(std::log2(this->numIDs)))) {
|
||||
return parent->Error("LookupClass: Failed to read valid entrySelector");
|
||||
}
|
||||
if (!table.ReadU16(&this->rangeShift) ||
|
||||
this->rangeShift != this->numIDs - this->searchRange) {
|
||||
return parent->Error("LookupClass: Failed to read valid rangeShift");
|
||||
}
|
||||
|
||||
//this->lookups.resize(this->numIDs, parent);
|
||||
for (unsigned i = 0; i < numIDs; ++i) {
|
||||
this->lookups.emplace_back(parent);
|
||||
if (!this->lookups[i].ParsePart(table)) {
|
||||
return parent->Error("LookupClass: Failed to read lookups[%u]", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ClassMap::
|
||||
LookupClass::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->numIDs) ||
|
||||
!out->WriteU16(this->searchRange) ||
|
||||
!out->WriteU16(this->entrySelector) ||
|
||||
!out->WriteU16(this->rangeShift) ||
|
||||
!SerializeParts(this->lookups, out)) {
|
||||
return parent->Error("LookupClass: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ClassMap::LookupClass::
|
||||
LookupPair::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU16(&this->glyphId)) {
|
||||
return parent->Error("LookupPair: Failed to read glyphId");
|
||||
}
|
||||
if (!table.ReadU16(&this->index)) {
|
||||
return parent->Error("LookupPair: Failed to read index");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ClassMap::LookupClass::
|
||||
LookupPair::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->glyphId) ||
|
||||
!out->WriteU16(this->index)) {
|
||||
return parent->Error("LookupPair: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
||||
const size_t next_pass_offset) {
|
||||
size_t init_offset = table.offset();
|
||||
if (!table.ReadU8(&this->flags)) {
|
||||
return parent->Error("SILPass: Failed to read flags");
|
||||
// checks omitted
|
||||
}
|
||||
if (!table.ReadU8(&this->maxRuleLoop)) {
|
||||
return parent->Error("SILPass: Failed to read valid maxRuleLoop");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxRuleContext)) {
|
||||
return parent->Error("SILPass: Failed to read maxRuleContext");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxBackup)) {
|
||||
return parent->Error("SILPass: Failed to read maxBackup");
|
||||
}
|
||||
if (!table.ReadU16(&this->numRules)) {
|
||||
return parent->Error("SILPass: Failed to read numRules");
|
||||
}
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU16(&this->fsmOffset)) {
|
||||
return parent->Error("SILPass: Failed to read fsmOffset");
|
||||
}
|
||||
if (parent->version >> 16 == 2 && this->fsmOffset != 0) {
|
||||
parent->Warning("SILPass: Nonzero fsmOffset (reserved in SILSub v2)");
|
||||
}
|
||||
if (!table.ReadU32(&this->pcCode) ||
|
||||
(parent->version >= 3 && this->pcCode < this->fsmOffset)) {
|
||||
return parent->Error("SILPass: Failed to read pcCode");
|
||||
}
|
||||
}
|
||||
if (!table.ReadU32(&this->rcCode) ||
|
||||
(parent->version >> 16 >= 2 && this->rcCode < this->pcCode)) {
|
||||
return parent->Error("SILPass: Failed to read valid rcCode");
|
||||
}
|
||||
if (!table.ReadU32(&this->aCode) || this->aCode < this->rcCode) {
|
||||
return parent->Error("SILPass: Failed to read valid aCode");
|
||||
}
|
||||
if (!table.ReadU32(&this->oDebug) ||
|
||||
(this->oDebug && this->oDebug < this->aCode)) {
|
||||
return parent->Error("SILPass: Failed to read valid oDebug");
|
||||
}
|
||||
if (parent->version >> 16 >= 3 &&
|
||||
table.offset() != init_offset + this->fsmOffset) {
|
||||
return parent->Error("SILPass: fsmOffset check failed");
|
||||
}
|
||||
if (!table.ReadU16(&this->numRows) ||
|
||||
(this->oDebug && this->numRows < this->numRules)) {
|
||||
return parent->Error("SILPass: Failed to read valid numRows");
|
||||
}
|
||||
if (!table.ReadU16(&this->numTransitional)) {
|
||||
return parent->Error("SILPass: Failed to read numTransitional");
|
||||
}
|
||||
if (!table.ReadU16(&this->numSuccess)) {
|
||||
return parent->Error("SILPass: Failed to read numSuccess");
|
||||
}
|
||||
if (!table.ReadU16(&this->numColumns)) {
|
||||
return parent->Error("SILPass: Failed to read numColumns");
|
||||
}
|
||||
if (!table.ReadU16(&this->numRange)) {
|
||||
return parent->Error("SILPass: Failed to read numRange");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchRange) || this->searchRange !=
|
||||
(this->numRange == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::pow(2, std::floor(std::log2(this->numRange))))) {
|
||||
return parent->Error("SILPass: Failed to read valid searchRange");
|
||||
}
|
||||
if (!table.ReadU16(&this->entrySelector) || this->entrySelector !=
|
||||
(this->numRange == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::floor(std::log2(this->numRange)))) {
|
||||
return parent->Error("SILPass: Failed to read valid entrySelector");
|
||||
}
|
||||
if (!table.ReadU16(&this->rangeShift) ||
|
||||
this->rangeShift != this->numRange - this->searchRange) {
|
||||
return parent->Error("SILPass: Failed to read valid rangeShift");
|
||||
}
|
||||
|
||||
//this->ranges.resize(this->numRange, parent);
|
||||
for (unsigned i = 0 ; i < this->numRange; ++i) {
|
||||
this->ranges.emplace_back(parent);
|
||||
if (!this->ranges[i].ParsePart(table)) {
|
||||
return parent->Error("SILPass: Failed to read ranges[%u]", i);
|
||||
}
|
||||
}
|
||||
unsigned ruleMap_len = 0; // maximum value in oRuleMap
|
||||
//this->oRuleMap.resize(static_cast<unsigned long>(this->numSuccess) + 1);
|
||||
for (unsigned long i = 0; i <= this->numSuccess; ++i) {
|
||||
this->oRuleMap.emplace_back();
|
||||
if (!table.ReadU16(&this->oRuleMap[i])) {
|
||||
return parent->Error("SILPass: Failed to read oRuleMap[%u]", i);
|
||||
}
|
||||
if (oRuleMap[i] > ruleMap_len) {
|
||||
ruleMap_len = oRuleMap[i];
|
||||
}
|
||||
}
|
||||
|
||||
//this->ruleMap.resize(ruleMap_len);
|
||||
for (unsigned i = 0; i < ruleMap_len; ++i) {
|
||||
this->ruleMap.emplace_back();
|
||||
if (!table.ReadU16(&this->ruleMap[i])) {
|
||||
return parent->Error("SILPass: Failed to read ruleMap[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->minRulePreContext)) {
|
||||
return parent->Error("SILPass: Failed to read minRulePreContext");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxRulePreContext) ||
|
||||
this->maxRulePreContext < this->minRulePreContext) {
|
||||
return parent->Error("SILPass: Failed to read valid maxRulePreContext");
|
||||
}
|
||||
|
||||
unsigned startStates_len = this->maxRulePreContext - this->minRulePreContext
|
||||
+ 1;
|
||||
// this->minRulePreContext <= this->maxRulePreContext
|
||||
//this->startStates.resize(startStates_len);
|
||||
for (unsigned i = 0; i < startStates_len; ++i) {
|
||||
this->startStates.emplace_back();
|
||||
if (!table.ReadS16(&this->startStates[i])) {
|
||||
return parent->Error("SILPass: Failed to read startStates[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
//this->ruleSortKeys.resize(this->numRules);
|
||||
for (unsigned i = 0; i < this->numRules; ++i) {
|
||||
this->ruleSortKeys.emplace_back();
|
||||
if (!table.ReadU16(&this->ruleSortKeys[i])) {
|
||||
return parent->Error("SILPass: Failed to read ruleSortKeys[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
//this->rulePreContext.resize(this->numRules);
|
||||
for (unsigned i = 0; i < this->numRules; ++i) {
|
||||
this->rulePreContext.emplace_back();
|
||||
if (!table.ReadU8(&this->rulePreContext[i])) {
|
||||
return parent->Error("SILPass: Failed to read rulePreContext[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU8(&this->collisionThreshold)) {
|
||||
return parent->Error("SILPass: Failed to read collisionThreshold");
|
||||
}
|
||||
if (parent->version >> 16 < 5 && this->collisionThreshold != 0) {
|
||||
parent->Warning("SILPass: Nonzero collisionThreshold"
|
||||
" (reserved before v5)");
|
||||
}
|
||||
if (!table.ReadU16(&this->pConstraint)) {
|
||||
return parent->Error("SILPass: Failed to read pConstraint");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long ruleConstraints_len = this->aCode - this->rcCode;
|
||||
// this->rcCode <= this->aCode
|
||||
//this->oConstraints.resize(static_cast<unsigned long>(this->numRules) + 1);
|
||||
for (unsigned long i = 0; i <= this->numRules; ++i) {
|
||||
this->oConstraints.emplace_back();
|
||||
if (!table.ReadU16(&this->oConstraints[i]) ||
|
||||
this->oConstraints[i] > ruleConstraints_len) {
|
||||
return parent->Error("SILPass: Failed to read valid oConstraints[%lu]",
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->oDebug && this->aCode > next_pass_offset) {
|
||||
return parent->Error("SILPass: Failed to calculate length of actions");
|
||||
}
|
||||
unsigned long actions_len = this->oDebug ? this->oDebug - this->aCode :
|
||||
next_pass_offset - this->aCode;
|
||||
// if this->oDebug, then this->aCode <= this->oDebug
|
||||
//this->oActions.resize(static_cast<unsigned long>(this->numRules) + 1);
|
||||
for (unsigned long i = 0; i <= this->numRules; ++i) {
|
||||
this->oActions.emplace_back();
|
||||
if (!table.ReadU16(&this->oActions[i]) ||
|
||||
(this->oActions[i] > actions_len)) {
|
||||
return parent->Error("SILPass: Failed to read valid oActions[%lu]", i);
|
||||
}
|
||||
}
|
||||
|
||||
//this->stateTrans.resize(this->numTransitional);
|
||||
for (unsigned i = 0; i < this->numTransitional; ++i) {
|
||||
this->stateTrans.emplace_back();
|
||||
//this->stateTrans[i].resize(this->numColumns);
|
||||
for (unsigned j = 0; j < this->numColumns; ++j) {
|
||||
this->stateTrans[i].emplace_back();
|
||||
if (!table.ReadU16(&stateTrans[i][j])) {
|
||||
return parent->Error("SILPass: Failed to read stateTrans[%u][%u]",
|
||||
i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU8(&this->reserved2)) {
|
||||
return parent->Error("SILPass: Failed to read reserved2");
|
||||
}
|
||||
if (this->reserved2 != 0) {
|
||||
parent->Warning("SILPass: Nonzero reserved2");
|
||||
}
|
||||
|
||||
if (table.offset() != SILSub_init_offset + this->pcCode) {
|
||||
return parent->Error("SILPass: pcCode check failed");
|
||||
}
|
||||
//this->passConstraints.resize(this->pConstraint);
|
||||
for (unsigned i = 0; i < this->pConstraint; ++i) {
|
||||
this->passConstraints.emplace_back();
|
||||
if (!table.ReadU8(&this->passConstraints[i])) {
|
||||
return parent->Error("SILPass: Failed to read passConstraints[%u]", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table.offset() != SILSub_init_offset + this->rcCode) {
|
||||
return parent->Error("SILPass: rcCode check failed");
|
||||
}
|
||||
//this->ruleConstraints.resize(ruleConstraints_len); // calculated above
|
||||
for (unsigned long i = 0; i < ruleConstraints_len; ++i) {
|
||||
this->ruleConstraints.emplace_back();
|
||||
if (!table.ReadU8(&this->ruleConstraints[i])) {
|
||||
return parent->Error("SILPass: Failed to read ruleConstraints[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (table.offset() != SILSub_init_offset + this->aCode) {
|
||||
return parent->Error("SILPass: aCode check failed");
|
||||
}
|
||||
//this->actions.resize(actions_len); // calculated above
|
||||
for (unsigned long i = 0; i < actions_len; ++i) {
|
||||
this->actions.emplace_back();
|
||||
if (!table.ReadU8(&this->actions[i])) {
|
||||
return parent->Error("SILPass: Failed to read actions[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->oDebug) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
parent->GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
if (!name) {
|
||||
return parent->Error("SILPass: Required name table is missing");
|
||||
}
|
||||
|
||||
if (table.offset() != SILSub_init_offset + this->oDebug) {
|
||||
return parent->Error("SILPass: oDebug check failed");
|
||||
}
|
||||
//this->dActions.resize(this->numRules);
|
||||
for (unsigned i = 0; i < this->numRules; ++i) {
|
||||
this->dActions.emplace_back();
|
||||
if (!table.ReadU16(&this->dActions[i]) ||
|
||||
!name->IsValidNameId(this->dActions[i])) {
|
||||
return parent->Error("SILPass: Failed to read valid dActions[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned dStates_len = this->numRows - this->numRules;
|
||||
// this->numRules <= this->numRows
|
||||
//this->dStates.resize(dStates_len);
|
||||
for (unsigned i = 0; i < dStates_len; ++i) {
|
||||
this->dStates.emplace_back();
|
||||
if (!table.ReadU16(&this->dStates[i]) ||
|
||||
!name->IsValidNameId(this->dStates[i])) {
|
||||
return parent->Error("SILPass: Failed to read valid dStates[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
//this->dCols.resize(this->numRules);
|
||||
for (unsigned i = 0; i < this->numRules; ++i) {
|
||||
this->dCols.emplace_back();
|
||||
if (!table.ReadU16(&this->dCols[i]) ||
|
||||
!name->IsValidNameId(this->dCols[i])) {
|
||||
return parent->Error("SILPass: Failed to read valid dCols[%u]");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
SILPass::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->flags) ||
|
||||
!out->WriteU8(this->maxRuleLoop) ||
|
||||
!out->WriteU8(this->maxRuleContext) ||
|
||||
!out->WriteU8(this->maxBackup) ||
|
||||
!out->WriteU16(this->numRules) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU16(this->fsmOffset) ||
|
||||
!out->WriteU32(this->pcCode))) ||
|
||||
!out->WriteU32(this->rcCode) ||
|
||||
!out->WriteU32(this->aCode) ||
|
||||
!out->WriteU32(this->oDebug) ||
|
||||
!out->WriteU16(this->numRows) ||
|
||||
!out->WriteU16(this->numTransitional) ||
|
||||
!out->WriteU16(this->numSuccess) ||
|
||||
!out->WriteU16(this->numColumns) ||
|
||||
!out->WriteU16(this->numRange) ||
|
||||
!out->WriteU16(this->searchRange) ||
|
||||
!out->WriteU16(this->entrySelector) ||
|
||||
!out->WriteU16(this->rangeShift) ||
|
||||
!SerializeParts(this->ranges, out) ||
|
||||
!SerializeParts(this->oRuleMap, out) ||
|
||||
!SerializeParts(this->ruleMap, out) ||
|
||||
!out->WriteU8(this->minRulePreContext) ||
|
||||
!out->WriteU8(this->maxRulePreContext) ||
|
||||
!SerializeParts(this->startStates, out) ||
|
||||
!SerializeParts(this->ruleSortKeys, out) ||
|
||||
!SerializeParts(this->rulePreContext, out) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU8(this->collisionThreshold) ||
|
||||
!out->WriteU16(this->pConstraint))) ||
|
||||
!SerializeParts(this->oConstraints, out) ||
|
||||
!SerializeParts(this->oActions, out) ||
|
||||
!SerializeParts(this->stateTrans, out) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU8(this->reserved2) ||
|
||||
!SerializeParts(this->passConstraints, out))) ||
|
||||
!SerializeParts(this->ruleConstraints, out) ||
|
||||
!SerializeParts(this->actions, out) ||
|
||||
!SerializeParts(this->dActions, out) ||
|
||||
!SerializeParts(this->dStates, out) ||
|
||||
!SerializeParts(this->dCols, out)) {
|
||||
return parent->Error("SILPass: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::SILPass::
|
||||
PassRange::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU16(&this->firstId)) {
|
||||
return parent->Error("PassRange: Failed to read firstId");
|
||||
}
|
||||
if (!table.ReadU16(&this->lastId)) {
|
||||
return parent->Error("PassRange: Failed to read lastId");
|
||||
}
|
||||
if (!table.ReadU16(&this->colId)) {
|
||||
return parent->Error("PassRange: Failed to read colId");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::SILPass::
|
||||
PassRange::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->firstId) ||
|
||||
!out->WriteU16(this->lastId) ||
|
||||
!out->WriteU16(this->colId)) {
|
||||
return parent->Error("PassRange: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_SILF_H_
|
||||
#define OTS_SILF_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeSILF : public Table {
|
||||
public:
|
||||
explicit OpenTypeSILF(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length) {
|
||||
return this->Parse(data, length, false);
|
||||
}
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
bool Parse(const uint8_t* data, size_t length, bool prevent_decompression);
|
||||
struct SILSub : public TablePart<OpenTypeSILF> {
|
||||
explicit SILSub(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent), classes(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct JustificationLevel : public TablePart<OpenTypeSILF> {
|
||||
explicit JustificationLevel(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint8_t attrStretch;
|
||||
uint8_t attrShrink;
|
||||
uint8_t attrStep;
|
||||
uint8_t attrWeight;
|
||||
uint8_t runto;
|
||||
uint8_t reserved;
|
||||
uint8_t reserved2;
|
||||
uint8_t reserved3;
|
||||
};
|
||||
struct PseudoMap : public TablePart<OpenTypeSILF> {
|
||||
explicit PseudoMap(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint32_t unicode;
|
||||
uint16_t nPseudo;
|
||||
};
|
||||
struct ClassMap : public TablePart<OpenTypeSILF> {
|
||||
explicit ClassMap(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct LookupClass : public TablePart<OpenTypeSILF> {
|
||||
explicit LookupClass(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct LookupPair : public TablePart<OpenTypeSILF> {
|
||||
explicit LookupPair(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint16_t glyphId;
|
||||
uint16_t index;
|
||||
};
|
||||
uint16_t numIDs;
|
||||
uint16_t searchRange;
|
||||
uint16_t entrySelector;
|
||||
uint16_t rangeShift;
|
||||
std::vector<LookupPair> lookups;
|
||||
};
|
||||
uint16_t numClass;
|
||||
uint16_t numLinear;
|
||||
std::vector<uint32_t> oClass; // uint16_t before v4
|
||||
std::vector<uint16_t> glyphs;
|
||||
std::vector<LookupClass> lookups;
|
||||
};
|
||||
struct SILPass : public TablePart<OpenTypeSILF> {
|
||||
explicit SILPass(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table) { return false; }
|
||||
bool ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
||||
const size_t next_pass_offset);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct PassRange : public TablePart<OpenTypeSILF> {
|
||||
explicit PassRange(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint16_t firstId;
|
||||
uint16_t lastId;
|
||||
uint16_t colId;
|
||||
};
|
||||
uint8_t flags;
|
||||
uint8_t maxRuleLoop;
|
||||
uint8_t maxRuleContext;
|
||||
uint8_t maxBackup;
|
||||
uint16_t numRules;
|
||||
uint16_t fsmOffset;
|
||||
uint32_t pcCode;
|
||||
uint32_t rcCode;
|
||||
uint32_t aCode;
|
||||
uint32_t oDebug;
|
||||
uint16_t numRows;
|
||||
uint16_t numTransitional;
|
||||
uint16_t numSuccess;
|
||||
uint16_t numColumns;
|
||||
uint16_t numRange;
|
||||
uint16_t searchRange;
|
||||
uint16_t entrySelector;
|
||||
uint16_t rangeShift;
|
||||
std::vector<PassRange> ranges;
|
||||
std::vector<uint16_t> oRuleMap;
|
||||
std::vector<uint16_t> ruleMap;
|
||||
uint8_t minRulePreContext;
|
||||
uint8_t maxRulePreContext;
|
||||
std::vector<int16_t> startStates;
|
||||
std::vector<uint16_t> ruleSortKeys;
|
||||
std::vector<uint8_t> rulePreContext;
|
||||
uint8_t collisionThreshold; // reserved before v5
|
||||
uint16_t pConstraint;
|
||||
std::vector<uint16_t> oConstraints;
|
||||
std::vector<uint16_t> oActions;
|
||||
std::vector<std::vector<uint16_t>> stateTrans;
|
||||
uint8_t reserved2;
|
||||
std::vector<uint8_t> passConstraints;
|
||||
std::vector<uint8_t> ruleConstraints;
|
||||
std::vector<uint8_t> actions;
|
||||
std::vector<uint16_t> dActions;
|
||||
std::vector<uint16_t> dStates;
|
||||
std::vector<uint16_t> dCols;
|
||||
};
|
||||
uint32_t ruleVersion;
|
||||
uint16_t passOffset;
|
||||
uint16_t pseudosOffset;
|
||||
uint16_t maxGlyphID;
|
||||
int16_t extraAscent;
|
||||
int16_t extraDescent;
|
||||
uint8_t numPasses;
|
||||
uint8_t iSubst;
|
||||
uint8_t iPos;
|
||||
uint8_t iJust;
|
||||
uint8_t iBidi;
|
||||
uint8_t flags;
|
||||
uint8_t maxPreContext;
|
||||
uint8_t maxPostContext;
|
||||
uint8_t attrPseudo;
|
||||
uint8_t attrBreakWeight;
|
||||
uint8_t attrDirectionality;
|
||||
uint8_t attrMirroring; // reserved before v4
|
||||
uint8_t attrSkipPasses; // reserved2 before v4
|
||||
uint8_t numJLevels;
|
||||
std::vector<JustificationLevel> jLevels;
|
||||
uint16_t numLigComp;
|
||||
uint8_t numUserDefn;
|
||||
uint8_t maxCompPerLig;
|
||||
uint8_t direction;
|
||||
uint8_t attCollisions; // reserved3 before v5
|
||||
uint8_t reserved4;
|
||||
uint8_t reserved5;
|
||||
uint8_t reserved6;
|
||||
uint8_t numCritFeatures;
|
||||
std::vector<uint16_t> critFeatures;
|
||||
uint8_t reserved7;
|
||||
uint8_t numScriptTag;
|
||||
std::vector<uint32_t> scriptTag;
|
||||
uint16_t lbGID;
|
||||
std::vector<uint32_t> oPasses;
|
||||
uint16_t numPseudo;
|
||||
uint16_t searchPseudo;
|
||||
uint16_t pseudoSelector;
|
||||
uint16_t pseudoShift;
|
||||
std::vector<PseudoMap> pMaps;
|
||||
ClassMap classes;
|
||||
std::vector<SILPass> passes;
|
||||
};
|
||||
uint32_t version;
|
||||
uint32_t compHead; // compression header
|
||||
static const uint32_t SCHEME = 0xF8000000;
|
||||
static const uint32_t FULL_SIZE = 0x07FFFFFF;
|
||||
static const uint32_t COMPILER_VERSION = 0x07FFFFFF;
|
||||
uint16_t numSub;
|
||||
uint16_t reserved;
|
||||
std::vector<uint32_t> offset;
|
||||
std::vector<SILSub> tables;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_SILF_H_
|
|
@ -0,0 +1,151 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "sill.h"
|
||||
|
||||
#include "feat.h"
|
||||
#include <cmath>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeSILL::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
|
||||
return Drop("Failed to read valid version");
|
||||
}
|
||||
if (!table.ReadU16(&this->numLangs)) {
|
||||
return Drop("Failed to read numLangs");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchRange) || this->searchRange !=
|
||||
(this->numLangs == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::pow(2, std::floor(std::log2(this->numLangs))))) {
|
||||
return Drop("Failed to read valid searchRange");
|
||||
}
|
||||
if (!table.ReadU16(&this->entrySelector) || this->entrySelector !=
|
||||
(this->numLangs == 0 ? 0 : // protect against log2(0)
|
||||
(unsigned)std::floor(std::log2(this->numLangs)))) {
|
||||
return Drop("Failed to read valid entrySelector");
|
||||
}
|
||||
if (!table.ReadU16(&this->rangeShift) ||
|
||||
this->rangeShift != this->numLangs - this->searchRange) {
|
||||
return Drop("Failed to read valid rangeShift");
|
||||
}
|
||||
|
||||
std::unordered_set<size_t> unverified;
|
||||
//this->entries.resize(static_cast<unsigned long>(this->numLangs) + 1, this);
|
||||
for (unsigned long i = 0; i <= this->numLangs; ++i) {
|
||||
this->entries.emplace_back(this);
|
||||
LanguageEntry& entry = this->entries[i];
|
||||
if (!entry.ParsePart(table)) {
|
||||
return Drop("Failed to read entries[%u]", i);
|
||||
}
|
||||
for (unsigned j = 0; j < entry.numSettings; ++j) {
|
||||
size_t offset = entry.offset + j * 8;
|
||||
if (offset < entry.offset || offset > length) {
|
||||
return DropGraphite("Invalid LangFeatureSetting offset %zu/%zu",
|
||||
offset, length);
|
||||
}
|
||||
unverified.insert(offset);
|
||||
// need to verify that this LanguageEntry points to valid
|
||||
// LangFeatureSetting
|
||||
}
|
||||
}
|
||||
|
||||
while (table.remaining()) {
|
||||
unverified.erase(table.offset());
|
||||
LangFeatureSetting setting(this);
|
||||
if (!setting.ParsePart(table)) {
|
||||
return Drop("Failed to read a LangFeatureSetting");
|
||||
}
|
||||
settings.push_back(setting);
|
||||
}
|
||||
|
||||
if (!unverified.empty()) {
|
||||
return Drop("%zu incorrect offsets into settings", unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU16(this->numLangs) ||
|
||||
!out->WriteU16(this->searchRange) ||
|
||||
!out->WriteU16(this->entrySelector) ||
|
||||
!out->WriteU16(this->rangeShift) ||
|
||||
!SerializeParts(this->entries, out) ||
|
||||
!SerializeParts(this->settings, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::LanguageEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->langcode[0]) ||
|
||||
!table.ReadU8(&this->langcode[1]) ||
|
||||
!table.ReadU8(&this->langcode[2]) ||
|
||||
!table.ReadU8(&this->langcode[3])) {
|
||||
return parent->Error("LanguageEntry: Failed to read langcode");
|
||||
}
|
||||
if (!table.ReadU16(&this->numSettings)) {
|
||||
return parent->Error("LanguageEntry: Failed to read numSettings");
|
||||
}
|
||||
if (!table.ReadU16(&this->offset)) {
|
||||
return parent->Error("LanguageEntry: Failed to read offset");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::LanguageEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->langcode[0]) ||
|
||||
!out->WriteU8(this->langcode[1]) ||
|
||||
!out->WriteU8(this->langcode[2]) ||
|
||||
!out->WriteU8(this->langcode[3]) ||
|
||||
!out->WriteU16(this->numSettings) ||
|
||||
!out->WriteU16(this->offset)) {
|
||||
return parent->Error("LanguageEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::LangFeatureSetting::ParsePart(Buffer& table) {
|
||||
OpenTypeFEAT* feat = static_cast<OpenTypeFEAT*>(
|
||||
parent->GetFont()->GetTypedTable(OTS_TAG_FEAT));
|
||||
if (!feat) {
|
||||
return parent->Error("FeatureDefn: Required Feat table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->featureId) ||
|
||||
!feat->IsValidFeatureId(this->featureId)) {
|
||||
return parent->Error("LangFeatureSetting: Failed to read valid featureId");
|
||||
}
|
||||
if (!table.ReadS16(&this->value)) {
|
||||
return parent->Error("LangFeatureSetting: Failed to read value");
|
||||
}
|
||||
if (!table.ReadU16(&this->reserved)) {
|
||||
return parent->Error("LangFeatureSetting: Failed to read reserved");
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
parent->Warning("LangFeatureSetting: Nonzero reserved");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::LangFeatureSetting::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU32(this->featureId) ||
|
||||
!out->WriteS16(this->value) ||
|
||||
!out->WriteU16(this->reserved)) {
|
||||
return parent->Error("LangFeatureSetting: Failed to read reserved");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_SILL_H_
|
||||
#define OTS_SILL_H_
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeSILL : public Table {
|
||||
public:
|
||||
explicit OpenTypeSILL(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
struct LanguageEntry : public TablePart<OpenTypeSILL> {
|
||||
explicit LanguageEntry(OpenTypeSILL* parent)
|
||||
: TablePart<OpenTypeSILL>(parent) { }
|
||||
bool ParsePart(Buffer &table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint8_t langcode[4];
|
||||
uint16_t numSettings;
|
||||
uint16_t offset;
|
||||
};
|
||||
struct LangFeatureSetting : public TablePart<OpenTypeSILL> {
|
||||
explicit LangFeatureSetting(OpenTypeSILL* parent)
|
||||
: TablePart<OpenTypeSILL>(parent) { }
|
||||
bool ParsePart(Buffer &table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint32_t featureId;
|
||||
int16_t value;
|
||||
uint16_t reserved;
|
||||
};
|
||||
uint32_t version;
|
||||
uint16_t numLangs;
|
||||
uint16_t searchRange;
|
||||
uint16_t entrySelector;
|
||||
uint16_t rangeShift;
|
||||
std::vector<LanguageEntry> entries;
|
||||
std::vector<LangFeatureSetting> settings;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_SILL_H_
|
|
@ -34,5 +34,5 @@ mv README.tmp README.mozilla
|
|||
echo "Applying ots-visibility.patch..."
|
||||
patch -p3 < ots-visibility.patch
|
||||
|
||||
echo "Applying ots-config.patch..."
|
||||
patch -p3 < ots-config.patch
|
||||
echo "Applying ots-lz4.patch..."
|
||||
patch -p3 < ots-lz4.patch
|
||||
|
|
|
@ -197,11 +197,6 @@ public:
|
|||
aTag == TRUETYPE_TAG('H', 'V', 'A', 'R') ||
|
||||
aTag == TRUETYPE_TAG('M', 'V', 'A', 'R') ||
|
||||
aTag == TRUETYPE_TAG('V', 'V', 'A', 'R'))) ||
|
||||
aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') ||
|
||||
aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') ||
|
||||
aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') ||
|
||||
aTag == TRUETYPE_TAG('G', 'l', 'a', 't') ||
|
||||
aTag == TRUETYPE_TAG('F', 'e', 'a', 't') ||
|
||||
aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
|
||||
aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
|
||||
aTag == TRUETYPE_TAG('C', 'P', 'A', 'L')) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче