#429: update OTS to 5.2.0 plus patches and M1396026

This commit is contained in:
Cameron Kaiser 2017-10-05 20:43:51 -07:00
parent 87b5609122
commit 1ce2a83ec7
87 changed files with 7678 additions and 3715 deletions

View File

@ -1,27 +1,27 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -2,10 +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: e8039a2b0f1a8ac1d2b21c088d1cbf0cd53cfe94
Current revision: f87b4556191e4132ef5c47365762eb88ace97fc3 (6.0.0)
Upstream files included: LICENSE, src/, include/
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-lz4.patch

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -44,13 +44,15 @@ typedef unsigned __int64 uint64_t;
#include <stdint.h>
#endif
#include <sys/types.h>
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstring>
#define OTS_TAG(c1,c2,c3,c4) ((uint32_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4))))
#define OTS_UNTAG(tag) ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
#define OTS_UNTAG(tag) ((char)((tag)>>24)), ((char)((tag)>>16)), ((char)((tag)>>8)), ((char)(tag))
namespace ots {
@ -174,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
};
@ -184,7 +186,7 @@ class OTS_API OTSContext {
OTSContext() {}
virtual ~OTSContext() {}
// Process a given OpenType file and write out a sanitised version
// 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.
@ -203,7 +205,7 @@ class OTS_API OTSContext {
// This function will be called when OTS needs to decide what to do for a
// font table.
// tag: table tag as a platform-native unsigned integer
// tag: table tag formed with OTS_TAG() macro
virtual TableAction GetTableAction(uint32_t tag) { return ots::TABLE_ACTION_DEFAULT; }
};

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.

66
gfx/ots/ots-lz4.patch Normal file
View File

@ -0,0 +1,66 @@
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,14 +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);
- int ret = LZ4_decompress_safe_partial(
+ size_t outputSize = 0;
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()),
- reinterpret_cast<char*>(decompressed.data()),
table.remaining(), // input buffer size (input size + padding)
+ reinterpret_cast<char*>(decompressed.data()),
decompressed.size(), // target output size
- decompressed.size()); // output buffer size
- if (ret != decompressed.size()) {
- return DropGraphite("Decompression failed with error code %d", ret);
+ &outputSize); // return output size
+ if (!ret || outputSize != decompressed.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,14 +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);
- int ret = LZ4_decompress_safe_partial(
+ size_t outputSize = 0;
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()),
- reinterpret_cast<char*>(decompressed.data()),
table.remaining(), // input buffer size (input size + padding)
+ reinterpret_cast<char*>(decompressed.data()),
decompressed.size(), // target output size
- decompressed.size()); // output buffer size
- if (ret != decompressed.size()) {
- return DropGraphite("Decompression failed with error code %d", ret);
+ &outputSize); // return output size
+ if (!ret || outputSize != decompressed.size()) {
+ return DropGraphite("Decompression failed");
}
return this->Parse(decompressed.data(), decompressed.size(), true);
}

View File

@ -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 sanitised 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.

View File

@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Copyright (c) 2012-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.
@ -904,14 +904,13 @@ bool ParseDictData(const uint8_t *data, size_t table_length,
namespace ots {
bool ots_cff_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeCFF::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
font->cff = new OpenTypeCFF;
font->cff->data = data;
font->cff->length = length;
font->cff->font_dict_length = 0;
font->cff->local_subrs = NULL;
Font *font = GetFont();
this->m_data = data;
this->m_length = length;
// parse "6. Header" in the Adobe Compact Font Format Specification
uint8_t major = 0;
@ -949,7 +948,7 @@ bool ots_cff_parse(Font *font, const uint8_t *data, size_t length) {
if (!ParseIndex(&table, &name_index)) {
return OTS_FAILURE();
}
if (!ParseNameData(&table, name_index, &(font->cff->name))) {
if (!ParseNameData(&table, name_index, &(this->name))) {
return OTS_FAILURE();
}
@ -973,14 +972,19 @@ bool ots_cff_parse(Font *font, const uint8_t *data, size_t length) {
return OTS_FAILURE();
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return Error("Required maxp table missing");
}
const uint16_t num_glyphs = maxp->num_glyphs;
const size_t sid_max = string_index.count + kNStdString;
// string_index.count == 0 is allowed.
// parse "9. Top DICT Data"
if (!ParseDictData(data, length, top_dict_index,
num_glyphs, sid_max,
DICT_DATA_TOPLEVEL, font->cff)) {
DICT_DATA_TOPLEVEL, this)) {
return OTS_FAILURE();
}
@ -993,58 +997,44 @@ bool ots_cff_parse(Font *font, const uint8_t *data, size_t length) {
// Check if all fd_index in FDSelect are valid.
std::map<uint16_t, uint8_t>::const_iterator iter;
std::map<uint16_t, uint8_t>::const_iterator end = font->cff->fd_select.end();
for (iter = font->cff->fd_select.begin(); iter != end; ++iter) {
if (iter->second >= font->cff->font_dict_length) {
std::map<uint16_t, uint8_t>::const_iterator end = this->fd_select.end();
for (iter = this->fd_select.begin(); iter != end; ++iter) {
if (iter->second >= this->font_dict_length) {
return OTS_FAILURE();
}
}
// Check if all charstrings (font hinting code for each glyph) are valid.
for (size_t i = 0; i < font->cff->char_strings_array.size(); ++i) {
for (size_t i = 0; i < this->char_strings_array.size(); ++i) {
if (!ValidateType2CharStringIndex(font,
*(font->cff->char_strings_array.at(i)),
*(this->char_strings_array.at(i)),
global_subrs_index,
font->cff->fd_select,
font->cff->local_subrs_per_font,
font->cff->local_subrs,
this->fd_select,
this->local_subrs_per_font,
this->local_subrs,
&table)) {
return OTS_FAILURE_MSG("Failed validating charstring set %d", (int) i);
return Error("Failed validating charstring set %d", (int) i);
}
}
return true;
}
bool ots_cff_should_serialise(Font *font) {
return font->cff != NULL;
}
bool ots_cff_serialise(OTSStream *out, Font *font) {
// TODO(yusukes): would be better to transcode the data,
// rather than simple memcpy.
if (!out->Write(font->cff->data, font->cff->length)) {
return OTS_FAILURE();
bool OpenTypeCFF::Serialize(OTSStream *out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write table");
}
return true;
}
void ots_cff_reuse(Font *font, Font *other) {
font->cff = other->cff;
font->cff_reused = true;
}
void ots_cff_free(Font *font) {
if (font->cff) {
for (size_t i = 0; i < font->cff->char_strings_array.size(); ++i) {
delete (font->cff->char_strings_array)[i];
}
for (size_t i = 0; i < font->cff->local_subrs_per_font.size(); ++i) {
delete (font->cff->local_subrs_per_font)[i];
}
delete font->cff->local_subrs;
delete font->cff;
OpenTypeCFF::~OpenTypeCFF() {
for (size_t i = 0; i < this->char_strings_array.size(); ++i) {
delete (this->char_strings_array)[i];
}
for (size_t i = 0; i < this->local_subrs_per_font.size(); ++i) {
delete (this->local_subrs_per_font)[i];
}
delete this->local_subrs;
}
} // namespace ots

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -22,9 +22,21 @@ struct CFFIndex {
uint32_t offset_to_next;
};
struct OpenTypeCFF {
const uint8_t *data;
size_t length;
class OpenTypeCFF : public Table {
public:
explicit OpenTypeCFF(Font *font, uint32_t tag)
: Table(font, tag, tag),
font_dict_length(0),
local_subrs(NULL),
m_data(NULL),
m_length(0) {
}
~OpenTypeCFF();
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
// Name INDEX. This name is used in name.cc as a postscript font name.
std::string name;
@ -39,6 +51,10 @@ struct OpenTypeCFF {
std::vector<CFFIndex *> local_subrs_per_font;
// A Local Subrs associated with Top DICT. Can be NULL.
CFFIndex *local_subrs;
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots

View File

@ -1,4 +1,4 @@
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Copyright (c) 2010-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.

View File

@ -1,4 +1,4 @@
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Copyright (c) 2010-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.

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -15,8 +15,6 @@
// cmap - Character To Glyph Index Mapping Table
// http://www.microsoft.com/typography/otspec/cmap.htm
#define TABLE_NAME "cmap"
namespace {
struct CMAPSubtableHeader {
@ -36,10 +34,6 @@ struct Subtable314Range {
uint32_t id_range_offset_offset;
};
// The maximum number of groups in format 12, 13 or 14 subtables.
// Note: 0xFFFF is the maximum number of glyphs in a single font file.
const unsigned kMaxCMAPGroups = 0xFFFF;
// Glyph array size for the Mac Roman (format 0) table.
const size_t kFormat0ArraySize = 256;
@ -60,8 +54,12 @@ const uint32_t kIVSStart = 0xE0100;
const uint32_t kIVSEnd = 0xE01EF;
const uint32_t kUVSUpperLimit = 0xFFFFFF;
} // namespace
namespace ots {
// Parses Format 4 tables
bool ParseFormat4(ots::Font *font, int platform, int encoding,
bool OpenTypeCMAP::ParseFormat4(int platform, int encoding,
const uint8_t *data, size_t length, uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
@ -69,20 +67,22 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
// whole thing and recompacting it, we validate it and include it verbatim
// in the output.
if (!font->os2) {
return OTS_FAILURE_MSG("Required OS/2 table missing");
OpenTypeOS2 *os2 = static_cast<OpenTypeOS2*>(
GetFont()->GetTypedTable(OTS_TAG_OS2));
if (!os2) {
return Error("Required OS/2 table missing");
}
if (!subtable.Skip(4)) {
return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable");
return Error("Can't read 4 bytes at start of cmap format 4 subtable");
}
uint16_t language = 0;
if (!subtable.ReadU16(&language)) {
return OTS_FAILURE_MSG("Can't read language");
return Error("Can't read language");
}
if (language) {
// Platform ID 3 (windows) subtables should have language '0'.
return OTS_FAILURE_MSG("Languages should be 0 (%d)", language);
return Error("Languages should be 0 (%d)", language);
}
uint16_t segcountx2, search_range, entry_selector, range_shift;
@ -91,16 +91,16 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
!subtable.ReadU16(&search_range) ||
!subtable.ReadU16(&entry_selector) ||
!subtable.ReadU16(&range_shift)) {
return OTS_FAILURE_MSG("Failed to read subcmap structure");
return Error("Failed to read subcmap structure");
}
if (segcountx2 & 1 || search_range & 1) {
return OTS_FAILURE_MSG("Bad subcmap structure");
return Error("Bad subcmap structure");
}
const uint16_t segcount = segcountx2 >> 1;
// There must be at least one segment according the spec.
if (segcount < 1) {
return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount);
return Error("Segcount < 1 (%d)", segcount);
}
// log2segcount is the maximal x s.t. 2^x < segcount
@ -111,48 +111,48 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
const uint16_t expected_search_range = 2 * 1u << log2segcount;
if (expected_search_range != search_range) {
return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range);
return Error("expected search range != search range (%d != %d)", expected_search_range, search_range);
}
if (entry_selector != log2segcount) {
return OTS_FAILURE_MSG("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
return Error("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
}
const uint16_t expected_range_shift = segcountx2 - search_range;
if (range_shift != expected_range_shift) {
return OTS_FAILURE_MSG("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
return Error("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
}
std::vector<Subtable314Range> ranges(segcount);
for (unsigned i = 0; i < segcount; ++i) {
if (!subtable.ReadU16(&ranges[i].end_range)) {
return OTS_FAILURE_MSG("Failed to read segment %d", i);
return Error("Failed to read segment %d", i);
}
}
uint16_t padding;
if (!subtable.ReadU16(&padding)) {
return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding");
return Error("Failed to read cmap subtable segment padding");
}
if (padding) {
return OTS_FAILURE_MSG("Non zero cmap subtable segment padding (%d)", padding);
return Error("Non zero cmap subtable segment padding (%d)", padding);
}
for (unsigned i = 0; i < segcount; ++i) {
if (!subtable.ReadU16(&ranges[i].start_range)) {
return OTS_FAILURE_MSG("Failed to read segment start range %d", i);
return Error("Failed to read segment start range %d", i);
}
}
for (unsigned i = 0; i < segcount; ++i) {
if (!subtable.ReadS16(&ranges[i].id_delta)) {
return OTS_FAILURE_MSG("Failed to read segment delta %d", i);
return Error("Failed to read segment delta %d", i);
}
}
for (unsigned i = 0; i < segcount; ++i) {
ranges[i].id_range_offset_offset = subtable.offset();
if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
return OTS_FAILURE_MSG("Failed to read segment range offset %d", i);
return Error("Failed to read segment range offset %d", i);
}
if (ranges[i].id_range_offset & 1) {
@ -160,12 +160,12 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
// for 0xFFFF-0xFFFF range.
// (e.g., many fonts in http://www.princexml.com/fonts/)
if (i == segcount - 1u) {
OTS_WARNING("bad id_range_offset");
Warning("bad id_range_offset");
ranges[i].id_range_offset = 0;
// The id_range_offset value in the transcoded font will not change
// since this table is not actually "transcoded" yet.
} else {
return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset);
return Error("Bad segment offset (%d)", ranges[i].id_range_offset);
}
}
}
@ -180,36 +180,36 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
(ranges[i].end_range == 0xffff)) {
// Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
// We'll accept them as an exception.
OTS_WARNING("multiple 0xffff terminators found");
Warning("multiple 0xffff terminators found");
continue;
}
// Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
// unsorted table...
if (ranges[i].end_range <= ranges[i - 1].end_range) {
return OTS_FAILURE_MSG("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
return Error("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
}
if (ranges[i].start_range <= ranges[i - 1].end_range) {
return OTS_FAILURE_MSG("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
return Error("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
}
// On many fonts, the value of {first, last}_char_index are incorrect.
// Fix them.
if (font->os2->first_char_index != 0xFFFF &&
if (os2->table.first_char_index != 0xFFFF &&
ranges[i].start_range != 0xFFFF &&
font->os2->first_char_index > ranges[i].start_range) {
font->os2->first_char_index = ranges[i].start_range;
os2->table.first_char_index > ranges[i].start_range) {
os2->table.first_char_index = ranges[i].start_range;
}
if (font->os2->last_char_index != 0xFFFF &&
if (os2->table.last_char_index != 0xFFFF &&
ranges[i].end_range != 0xFFFF &&
font->os2->last_char_index < ranges[i].end_range) {
font->os2->last_char_index = ranges[i].end_range;
os2->table.last_char_index < ranges[i].end_range) {
os2->table.last_char_index = ranges[i].end_range;
}
}
// The last range must end at 0xffff
if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
return OTS_FAILURE_MSG("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
return Error("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
}
@ -223,7 +223,7 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
// this is explictly allowed to overflow in the spec
const uint16_t glyph = code_point + ranges[i].id_delta;
if (glyph >= num_glyphs) {
return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
}
} else {
const uint16_t range_delta = code_point - ranges[i].start_range;
@ -234,13 +234,13 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
range_delta * 2;
// We need to be able to access a 16-bit value from this offset
if (glyph_id_offset + 1 >= length) {
return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
return Error("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
}
uint16_t glyph;
std::memcpy(&glyph, data + glyph_id_offset, 2);
glyph = ntohs(glyph);
if (glyph >= num_glyphs) {
return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
}
}
}
@ -249,100 +249,85 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
// We accept the table.
// TODO(yusukes): transcode the subtable.
if (platform == 3 && encoding == 0) {
font->cmap->subtable_3_0_4_data = data;
font->cmap->subtable_3_0_4_length = length;
this->subtable_3_0_4_data = data;
this->subtable_3_0_4_length = length;
} else if (platform == 3 && encoding == 1) {
font->cmap->subtable_3_1_4_data = data;
font->cmap->subtable_3_1_4_length = length;
this->subtable_3_1_4_data = data;
this->subtable_3_1_4_length = length;
} else if (platform == 0 && encoding == 3) {
font->cmap->subtable_0_3_4_data = data;
font->cmap->subtable_0_3_4_length = length;
this->subtable_0_3_4_data = data;
this->subtable_0_3_4_length = length;
} else {
return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
return Error("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
}
return true;
}
bool Parse31012(ots::Font *font,
const uint8_t *data, size_t length, uint16_t num_glyphs) {
bool OpenTypeCMAP::Parse31012(const uint8_t *data, size_t length,
uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
// Format 12 tables are simple. We parse these and fully serialise them
// later.
if (!subtable.Skip(8)) {
return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable");
return Error("failed to skip the first 8 bytes of format 12 subtable");
}
uint32_t language = 0;
if (!subtable.ReadU32(&language)) {
return OTS_FAILURE_MSG("can't read format 12 subtable language");
return Error("can't read format 12 subtable language");
}
if (language) {
return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language);
return Error("format 12 subtable language should be zero (%d)", language);
}
uint32_t num_groups = 0;
if (!subtable.ReadU32(&num_groups)) {
return OTS_FAILURE_MSG("can't read number of format 12 subtable groups");
return Error("can't read number of format 12 subtable groups");
}
if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
return OTS_FAILURE_MSG("Bad format 12 subtable group count %d", num_groups);
if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
return Error("Bad format 12 subtable group count %d", num_groups);
}
std::vector<ots::OpenTypeCMAPSubtableRange> &groups
= font->cmap->subtable_3_10_12;
= this->subtable_3_10_12;
groups.resize(num_groups);
for (unsigned i = 0; i < num_groups; ++i) {
if (!subtable.ReadU32(&groups[i].start_range) ||
!subtable.ReadU32(&groups[i].end_range) ||
!subtable.ReadU32(&groups[i].start_glyph_id)) {
return OTS_FAILURE_MSG("can't read format 12 subtable group");
return Error("can't read format 12 subtable group");
}
if (groups[i].start_range > kUnicodeUpperLimit ||
groups[i].end_range > kUnicodeUpperLimit ||
groups[i].start_glyph_id > 0xFFFF) {
return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
return Error("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
}
// [0xD800, 0xDFFF] are surrogate code points.
if (groups[i].start_range >= 0xD800 &&
groups[i].start_range <= 0xDFFF) {
return OTS_FAILURE_MSG("format 12 subtable out of range group startCharCode (0x%4X)", groups[i].start_range);
}
if (groups[i].end_range >= 0xD800 &&
groups[i].end_range <= 0xDFFF) {
return OTS_FAILURE_MSG("format 12 subtable out of range group endCharCode (0x%4X)", groups[i].end_range);
}
if (groups[i].start_range < 0xD800 &&
groups[i].end_range > 0xDFFF) {
return OTS_FAILURE_MSG("bad format 12 subtable group startCharCode (0x%4X) or endCharCode (0x%4X)",
groups[i].start_range, groups[i].end_range);
}
// We assert that the glyph value is within range. Because of the range
// limits, above, we don't need to worry about overflow.
if (groups[i].end_range < groups[i].start_range) {
return OTS_FAILURE_MSG("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
return Error("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
groups[i].end_range, groups[i].start_range);
}
if ((groups[i].end_range - groups[i].start_range) +
groups[i].start_glyph_id > num_glyphs) {
return OTS_FAILURE_MSG("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
return Error("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
}
}
// the groups must be sorted by start code and may not overlap
for (unsigned i = 1; i < num_groups; ++i) {
if (groups[i].start_range <= groups[i - 1].start_range) {
return OTS_FAILURE_MSG("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
return Error("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
groups[i].start_range, groups[i-1].start_range);
}
if (groups[i].start_range <= groups[i - 1].end_range) {
return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
return Error("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
groups[i].start_range, groups[i-1].end_range);
}
}
@ -350,44 +335,43 @@ bool Parse31012(ots::Font *font,
return true;
}
bool Parse31013(ots::Font *font,
const uint8_t *data, size_t length, uint16_t num_glyphs) {
bool OpenTypeCMAP::Parse31013(const uint8_t *data, size_t length,
uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
// Format 13 tables are simple. We parse these and fully serialise them
// later.
if (!subtable.Skip(8)) {
return OTS_FAILURE_MSG("Bad cmap subtable length");
return Error("Bad cmap subtable length");
}
uint32_t language = 0;
if (!subtable.ReadU32(&language)) {
return OTS_FAILURE_MSG("Can't read cmap subtable language");
return Error("Can't read cmap subtable language");
}
if (language) {
return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
return Error("Cmap subtable language should be zero but is %d", language);
}
uint32_t num_groups = 0;
if (!subtable.ReadU32(&num_groups)) {
return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable");
return Error("Can't read number of groups in a cmap subtable");
}
// We limit the number of groups in the same way as in 3.10.12 tables. See
// the comment there in
if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
return OTS_FAILURE_MSG("Bad format 13 subtable group count %d", num_groups);
if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
return Error("Bad format 13 subtable group count %d", num_groups);
}
std::vector<ots::OpenTypeCMAPSubtableRange> &groups
= font->cmap->subtable_3_10_13;
std::vector<ots::OpenTypeCMAPSubtableRange> &groups = this->subtable_3_10_13;
groups.resize(num_groups);
for (unsigned i = 0; i < num_groups; ++i) {
if (!subtable.ReadU32(&groups[i].start_range) ||
!subtable.ReadU32(&groups[i].end_range) ||
!subtable.ReadU32(&groups[i].start_glyph_id)) {
return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable");
return Error("Can't read subrange structure in a cmap subtable");
}
// We conservatively limit all of the values to protect some parsers from
@ -395,29 +379,29 @@ bool Parse31013(ots::Font *font,
if (groups[i].start_range > kUnicodeUpperLimit ||
groups[i].end_range > kUnicodeUpperLimit ||
groups[i].start_glyph_id > 0xFFFF) {
return OTS_FAILURE_MSG("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
return Error("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
}
if (groups[i].start_glyph_id >= num_glyphs) {
return OTS_FAILURE_MSG("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
return Error("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
}
}
// the groups must be sorted by start code and may not overlap
for (unsigned i = 1; i < num_groups; ++i) {
if (groups[i].start_range <= groups[i - 1].start_range) {
return OTS_FAILURE_MSG("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
return Error("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
}
if (groups[i].start_range <= groups[i - 1].end_range) {
return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
return Error("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
}
}
return true;
}
bool Parse0514(ots::Font *font,
const uint8_t *data, size_t length, uint16_t num_glyphs) {
bool OpenTypeCMAP::Parse0514(const uint8_t *data, size_t length,
uint16_t num_glyphs) {
// Unicode Variation Selector table
ots::Buffer subtable(data, length);
@ -426,26 +410,26 @@ bool Parse0514(ots::Font *font,
// Skip format (USHORT) and length (ULONG)
if (!subtable.Skip(6)) {
return OTS_FAILURE_MSG("Can't read start of cmap subtable");
return Error("Can't read start of cmap subtable");
}
uint32_t num_records = 0;
if (!subtable.ReadU32(&num_records)) {
return OTS_FAILURE_MSG("Can't read number of records in cmap subtable");
return Error("Can't read number of records in cmap subtable");
}
if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
return OTS_FAILURE_MSG("Bad format 14 subtable records count %d", num_records);
return Error("Bad format 14 subtable records count %d", num_records);
}
std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
= font->cmap->subtable_0_5_14;
= this->subtable_0_5_14;
records.resize(num_records);
for (unsigned i = 0; i < num_records; ++i) {
if (!subtable.ReadU24(&records[i].var_selector) ||
!subtable.ReadU32(&records[i].default_offset) ||
!subtable.ReadU32(&records[i].non_default_offset)) {
return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i);
return Error("Can't read record structure of record %d in cmap subtale", i);
}
// Checks the value of variation selector
if (!((records[i].var_selector >= kMongolianVSStart &&
@ -454,24 +438,24 @@ bool Parse0514(ots::Font *font,
records[i].var_selector <= kVSEnd) ||
(records[i].var_selector >= kIVSStart &&
records[i].var_selector <= kIVSEnd))) {
return OTS_FAILURE_MSG("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
return Error("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
}
if (i > 0 &&
records[i-1].var_selector >= records[i].var_selector) {
return OTS_FAILURE_MSG("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
return Error("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
}
// Checks offsets
if (!records[i].default_offset && !records[i].non_default_offset) {
return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i);
return Error("No default aoffset in variation selector record %d", i);
}
if (records[i].default_offset &&
records[i].default_offset >= length) {
return OTS_FAILURE_MSG("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
return Error("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
}
if (records[i].non_default_offset &&
records[i].non_default_offset >= length) {
return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
return Error("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
}
}
@ -481,10 +465,10 @@ bool Parse0514(ots::Font *font,
subtable.set_offset(records[i].default_offset);
uint32_t num_ranges = 0;
if (!subtable.ReadU32(&num_ranges)) {
return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i);
return Error("Can't read number of ranges in record %d", i);
}
if (num_ranges == 0 || num_ranges > kMaxCMAPGroups) {
return OTS_FAILURE_MSG("Bad number of ranges (%d) in record %d", num_ranges, i);
if (num_ranges == 0 || subtable.remaining() / 4 < num_ranges) {
return Error("Bad number of ranges (%d) in record %d", num_ranges, i);
}
uint32_t last_unicode_value = 0;
@ -495,7 +479,7 @@ bool Parse0514(ots::Font *font,
for (unsigned j = 0; j < num_ranges; ++j) {
if (!subtable.ReadU24(&ranges[j].unicode_value) ||
!subtable.ReadU8(&ranges[j].additional_count)) {
return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i);
return Error("Can't read range info in variation selector record %d", i);
}
const uint32_t check_value =
ranges[j].unicode_value + ranges[j].additional_count;
@ -504,7 +488,7 @@ bool Parse0514(ots::Font *font,
check_value > kUVSUpperLimit ||
(last_unicode_value &&
ranges[j].unicode_value <= last_unicode_value)) {
return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
return Error("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
}
last_unicode_value = check_value;
}
@ -515,10 +499,10 @@ bool Parse0514(ots::Font *font,
subtable.set_offset(records[i].non_default_offset);
uint32_t num_mappings = 0;
if (!subtable.ReadU32(&num_mappings)) {
return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i);
return Error("Can't read number of mappings in variation selector record %d", i);
}
if (num_mappings == 0) {
return OTS_FAILURE_MSG("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
if (num_mappings == 0 || subtable.remaining() / 5 < num_mappings) {
return Error("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
}
uint32_t last_unicode_value = 0;
@ -529,14 +513,14 @@ bool Parse0514(ots::Font *font,
for (unsigned j = 0; j < num_mappings; ++j) {
if (!subtable.ReadU24(&mappings[j].unicode_value) ||
!subtable.ReadU16(&mappings[j].glyph_id)) {
return OTS_FAILURE_MSG("Can't read mapping %d in variation selector record %d", j, i);
return Error("Can't read mapping %d in variation selector record %d", j, i);
}
if (mappings[j].glyph_id == 0 ||
mappings[j].unicode_value == 0 ||
mappings[j].unicode_value > kUnicodeUpperLimit ||
(last_unicode_value &&
mappings[j].unicode_value <= last_unicode_value)) {
return OTS_FAILURE_MSG("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
return Error("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
}
last_unicode_value = mappings[j].unicode_value;
}
@ -544,60 +528,55 @@ bool Parse0514(ots::Font *font,
}
if (subtable.offset() != length) {
return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
return Error("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
}
font->cmap->subtable_0_5_14_length = subtable.offset();
this->subtable_0_5_14_length = subtable.offset();
return true;
}
bool Parse100(ots::Font *font, const uint8_t *data, size_t length) {
bool OpenTypeCMAP::Parse100(const uint8_t *data, size_t length) {
// Mac Roman table
ots::Buffer subtable(data, length);
if (!subtable.Skip(4)) {
return OTS_FAILURE_MSG("Bad cmap subtable");
return Error("Bad cmap subtable");
}
uint16_t language = 0;
if (!subtable.ReadU16(&language)) {
return OTS_FAILURE_MSG("Can't read language in cmap subtable");
return Error("Can't read language in cmap subtable");
}
if (language) {
// simsun.ttf has non-zero language id.
OTS_WARNING("language id should be zero: %u", language);
Warning("language id should be zero: %u", language);
}
font->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
this->subtable_1_0_0.reserve(kFormat0ArraySize);
for (size_t i = 0; i < kFormat0ArraySize; ++i) {
uint8_t glyph_id = 0;
if (!subtable.ReadU8(&glyph_id)) {
return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i);
return Error("Can't read glyph id at array[%ld] in cmap subtable", i);
}
font->cmap->subtable_1_0_0.push_back(glyph_id);
this->subtable_1_0_0.push_back(glyph_id);
}
return true;
}
} // namespace
namespace ots {
bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeCMAP::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
font->cmap = new OpenTypeCMAP;
uint16_t version = 0;
uint16_t num_tables = 0;
if (!table.ReadU16(&version) ||
!table.ReadU16(&num_tables)) {
return OTS_FAILURE_MSG("Can't read structure of cmap");
return Error("Can't read structure of cmap");
}
if (version != 0) {
return OTS_FAILURE_MSG("Non zero cmap version (%d)", version);
return Error("Non zero cmap version (%d)", version);
}
if (!num_tables) {
return OTS_FAILURE_MSG("No subtables in cmap!");
return Error("No subtables in cmap!");
}
std::vector<CMAPSubtableHeader> subtable_headers;
@ -610,7 +589,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
if (!table.ReadU16(&subt.platform) ||
!table.ReadU16(&subt.encoding) ||
!table.ReadU32(&subt.offset)) {
return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i);
return Error("Can't read subtable information cmap subtable %d", i);
}
subtable_headers.push_back(subt);
@ -621,11 +600,11 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
// make sure that all the offsets are valid.
for (unsigned i = 0; i < num_tables; ++i) {
if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i);
return Error("Bad subtable offset in cmap subtable %d", i);
}
if (subtable_headers[i].offset < data_offset ||
subtable_headers[i].offset >= length) {
return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
return Error("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
}
}
@ -634,7 +613,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
for (unsigned i = 0; i < num_tables; ++i) {
table.set_offset(subtable_headers[i].offset);
if (!table.ReadU16(&subtable_headers[i].format)) {
return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i);
return Error("Can't read cmap subtable header format %d", i);
}
uint16_t len = 0;
@ -643,10 +622,10 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
case 0:
case 4:
if (!table.ReadU16(&len)) {
return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
return Error("Can't read cmap subtable %d length", i);
}
if (!table.ReadU16(&lang)) {
return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
return Error("Can't read cmap subtable %d language", i);
}
subtable_headers[i].length = len;
subtable_headers[i].language = lang;
@ -654,18 +633,18 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
case 12:
case 13:
if (!table.Skip(2)) {
return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i);
return Error("Bad cmap subtable %d structure", i);
}
if (!table.ReadU32(&subtable_headers[i].length)) {
return OTS_FAILURE_MSG("Can read cmap subtable %d length", i);
return Error("Can read cmap subtable %d length", i);
}
if (!table.ReadU32(&subtable_headers[i].language)) {
return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
return Error("Can't read cmap subtable %d language", i);
}
break;
case 14:
if (!table.ReadU32(&subtable_headers[i].length)) {
return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
return Error("Can't read cmap subtable %d length", i);
}
subtable_headers[i].language = 0;
break;
@ -677,34 +656,35 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
}
// check if the table is sorted first by platform ID, then by encoding ID.
uint32_t last_id = 0;
for (unsigned i = 0; i < num_tables; ++i) {
uint32_t current_id
= (subtable_headers[i].platform << 24)
+ (subtable_headers[i].encoding << 16)
+ subtable_headers[i].language;
if ((i != 0) && (last_id >= current_id)) {
OTS_WARNING("subtable %d with platform ID %d, encoding ID %d, language ID %d "
for (unsigned i = 1; i < num_tables; ++i) {
if (subtable_headers[i - 1].platform > subtable_headers[i].platform ||
(subtable_headers[i - 1].platform == subtable_headers[i].platform &&
(subtable_headers[i - 1].encoding > subtable_headers[i].encoding ||
(subtable_headers[i - 1].encoding == subtable_headers[i].encoding &&
subtable_headers[i - 1].language > subtable_headers[i].language))))
Warning("subtable %d with platform ID %d, encoding ID %d, language ID %d "
"following subtable with platform ID %d, encoding ID %d, language ID %d",
i,
(uint8_t)(current_id >> 24), (uint8_t)(current_id >> 16), (uint8_t)(current_id),
(uint8_t)(last_id >> 24), (uint8_t)(last_id >> 16), (uint8_t)(last_id));
}
last_id = current_id;
subtable_headers[i].platform,
subtable_headers[i].encoding,
subtable_headers[i].language,
subtable_headers[i - 1].platform,
subtable_headers[i - 1].encoding,
subtable_headers[i - 1].language);
}
// Now, verify that all the lengths are sane
for (unsigned i = 0; i < num_tables; ++i) {
if (!subtable_headers[i].length) continue;
if (subtable_headers[i].length > 1024 * 1024 * 1024) {
return OTS_FAILURE_MSG("Bad cmap subtable %d length", i);
return Error("Bad cmap subtable %d length", i);
}
// We know that both the offset and length are < 1GB, so the following
// addition doesn't overflow
const uint32_t end_byte
= subtable_headers[i].offset + subtable_headers[i].length;
if (end_byte > length) {
return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
return Error("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
}
}
@ -732,16 +712,18 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
for (unsigned i = 0; i < overlap_checker.size(); ++i) {
overlap_count += (overlap_checker[i].second ? 1 : -1);
if (overlap_count > 1) {
return OTS_FAILURE_MSG("Excessive overlap count %d", overlap_count);
return Error("Excessive overlap count %d", overlap_count);
}
}
// we grab the number of glyphs in the file from the maxp table to make sure
// that the character map isn't referencing anything beyound this range.
if (!font->maxp) {
return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap.");
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return Error("No maxp table in font! Needed by cmap.");
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
const uint16_t num_glyphs = maxp->num_glyphs;
// We only support a subset of the possible character map tables. Microsoft
// 'strongly recommends' that everyone supports the Unicode BMP table with
@ -778,29 +760,29 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
// table actually points to MS symbol data and thus should be parsed as
// 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
// recovered in ots_cmap_serialise().
if (!ParseFormat4(font, 3, 1, data + subtable_headers[i].offset,
if (!ParseFormat4(3, 1, data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
return Error("Failed to parse format 4 cmap subtable %d", i);
}
} else if ((subtable_headers[i].encoding == 3) &&
(subtable_headers[i].format == 4)) {
// parse and output the 0-3-4 table as 0-3-4 table.
if (!ParseFormat4(font, 0, 3, data + subtable_headers[i].offset,
if (!ParseFormat4(0, 3, data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
return Error("Failed to parse format 4 cmap subtable %d", i);
}
} else if ((subtable_headers[i].encoding == 3) &&
(subtable_headers[i].format == 12)) {
// parse and output the 0-3-12 table as 3-10-12 table.
if (!Parse31012(font, data + subtable_headers[i].offset,
if (!Parse31012(data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i);
return Error("Failed to parse format 12 cmap subtable %d", i);
}
} else if ((subtable_headers[i].encoding == 5) &&
(subtable_headers[i].format == 14)) {
if (!Parse0514(font, data + subtable_headers[i].offset,
if (!Parse0514(data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i);
return Error("Failed to parse format 14 cmap subtable %d", i);
}
}
} else if (subtable_headers[i].platform == 1) {
@ -809,7 +791,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
if ((subtable_headers[i].encoding == 0) &&
(subtable_headers[i].format == 0)) {
// parse and output the 1-0-0 table.
if (!Parse100(font, data + subtable_headers[i].offset,
if (!Parse100(data + subtable_headers[i].offset,
subtable_headers[i].length)) {
return OTS_FAILURE();
}
@ -822,7 +804,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
case 1:
if (subtable_headers[i].format == 4) {
// parse 3-0-4 or 3-1-4 table.
if (!ParseFormat4(font, subtable_headers[i].platform,
if (!ParseFormat4(subtable_headers[i].platform,
subtable_headers[i].encoding,
data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
@ -832,14 +814,14 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
break;
case 10:
if (subtable_headers[i].format == 12) {
font->cmap->subtable_3_10_12.clear();
if (!Parse31012(font, data + subtable_headers[i].offset,
this->subtable_3_10_12.clear();
if (!Parse31012(data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
return OTS_FAILURE();
}
} else if (subtable_headers[i].format == 13) {
font->cmap->subtable_3_10_13.clear();
if (!Parse31013(font, data + subtable_headers[i].offset,
this->subtable_3_10_13.clear();
if (!Parse31013(data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
return OTS_FAILURE();
}
@ -852,20 +834,16 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
return true;
}
bool ots_cmap_should_serialise(Font *font) {
return font->cmap != NULL;
}
bool ots_cmap_serialise(OTSStream *out, Font *font) {
const bool have_034 = font->cmap->subtable_0_3_4_data != NULL;
const bool have_0514 = font->cmap->subtable_0_5_14.size() != 0;
const bool have_100 = font->cmap->subtable_1_0_0.size() != 0;
const bool have_304 = font->cmap->subtable_3_0_4_data != NULL;
bool OpenTypeCMAP::Serialize(OTSStream *out) {
const bool have_034 = this->subtable_0_3_4_data != NULL;
const bool have_0514 = this->subtable_0_5_14.size() != 0;
const bool have_100 = this->subtable_1_0_0.size() != 0;
const bool have_304 = this->subtable_3_0_4_data != NULL;
// MS Symbol and MS Unicode tables should not co-exist.
// See the comment above in 0-0-4 parser.
const bool have_314 = (!have_304) && font->cmap->subtable_3_1_4_data;
const bool have_31012 = font->cmap->subtable_3_10_12.size() != 0;
const bool have_31013 = font->cmap->subtable_3_10_13.size() != 0;
const bool have_314 = (!have_304) && this->subtable_3_1_4_data;
const bool have_31012 = this->subtable_3_10_12.size() != 0;
const bool have_31013 = this->subtable_3_10_13.size() != 0;
const uint16_t num_subtables = static_cast<uint16_t>(have_034) +
static_cast<uint16_t>(have_0514) +
static_cast<uint16_t>(have_100) +
@ -878,7 +856,7 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
// Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
// (e.g., old fonts for Mac). We don't support them.
if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
return OTS_FAILURE_MSG("no supported subtables were found");
return Error("no supported subtables were found");
}
if (!out->WriteU16(0) ||
@ -893,8 +871,8 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
const off_t offset_034 = out->Tell();
if (have_034) {
if (!out->Write(font->cmap->subtable_0_3_4_data,
font->cmap->subtable_0_3_4_length)) {
if (!out->Write(this->subtable_0_3_4_data,
this->subtable_0_3_4_length)) {
return OTS_FAILURE();
}
}
@ -902,10 +880,10 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
const off_t offset_0514 = out->Tell();
if (have_0514) {
const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
= font->cmap->subtable_0_5_14;
= this->subtable_0_5_14;
const unsigned num_records = records.size();
if (!out->WriteU16(14) ||
!out->WriteU32(font->cmap->subtable_0_5_14_length) ||
!out->WriteU32(this->subtable_0_5_14_length) ||
!out->WriteU32(num_records)) {
return OTS_FAILURE();
}
@ -957,23 +935,23 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
!out->WriteU16(0)) { // language
return OTS_FAILURE();
}
if (!out->Write(&(font->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
if (!out->Write(&(this->subtable_1_0_0[0]), kFormat0ArraySize)) {
return OTS_FAILURE();
}
}
const off_t offset_304 = out->Tell();
if (have_304) {
if (!out->Write(font->cmap->subtable_3_0_4_data,
font->cmap->subtable_3_0_4_length)) {
if (!out->Write(this->subtable_3_0_4_data,
this->subtable_3_0_4_length)) {
return OTS_FAILURE();
}
}
const off_t offset_314 = out->Tell();
if (have_314) {
if (!out->Write(font->cmap->subtable_3_1_4_data,
font->cmap->subtable_3_1_4_length)) {
if (!out->Write(this->subtable_3_1_4_data,
this->subtable_3_1_4_length)) {
return OTS_FAILURE();
}
}
@ -981,7 +959,7 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
const off_t offset_31012 = out->Tell();
if (have_31012) {
std::vector<OpenTypeCMAPSubtableRange> &groups
= font->cmap->subtable_3_10_12;
= this->subtable_3_10_12;
const unsigned num_groups = groups.size();
if (!out->WriteU16(12) ||
!out->WriteU16(0) ||
@ -1003,7 +981,7 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
const off_t offset_31013 = out->Tell();
if (have_31013) {
std::vector<OpenTypeCMAPSubtableRange> &groups
= font->cmap->subtable_3_10_13;
= this->subtable_3_10_13;
const unsigned num_groups = groups.size();
if (!out->WriteU16(13) ||
!out->WriteU16(0) ||
@ -1092,15 +1070,4 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
return true;
}
void ots_cmap_reuse(Font *font, Font *other) {
font->cmap = other->cmap;
font->cmap_reused = true;
}
void ots_cmap_free(Font *font) {
delete font->cmap;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -35,9 +35,11 @@ struct OpenTypeCMAPSubtableVSRecord {
std::vector<OpenTypeCMAPSubtableVSMapping> mappings;
};
struct OpenTypeCMAP {
OpenTypeCMAP()
: subtable_0_3_4_data(NULL),
class OpenTypeCMAP : public Table {
public:
explicit OpenTypeCMAP(Font *font, uint32_t tag)
: Table(font, tag, tag),
subtable_0_3_4_data(NULL),
subtable_0_3_4_length(0),
subtable_0_5_14_length(0),
subtable_3_0_4_data(NULL),
@ -46,6 +48,10 @@ struct OpenTypeCMAP {
subtable_3_1_4_length(0) {
}
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
private:
// Platform 0, Encoding 3, Format 4, Unicode BMP table.
const uint8_t *subtable_0_3_4_data;
size_t subtable_0_3_4_length;
@ -67,6 +73,13 @@ struct OpenTypeCMAP {
std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
// Platform 1, Encoding 0, Format 0, Mac Roman table.
std::vector<uint8_t> subtable_1_0_0;
bool ParseFormat4(int platform, int encoding, const uint8_t *data,
size_t length, uint16_t num_glyphs);
bool Parse31012(const uint8_t *data, size_t length, uint16_t num_glyphs);
bool Parse31013(const uint8_t *data, size_t length, uint16_t num_glyphs);
bool Parse0514(const uint8_t *data, size_t length, uint16_t num_glyphs);
bool Parse100(const uint8_t *data, size_t length);
};
} // namespace ots

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -7,59 +7,40 @@
// cvt - Control Value Table
// http://www.microsoft.com/typography/otspec/cvt.htm
#define TABLE_NAME "cvt"
namespace ots {
bool ots_cvt_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeCVT::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeCVT *cvt = new OpenTypeCVT;
font->cvt = cvt;
if (length >= 128 * 1024u) {
return OTS_FAILURE_MSG("Length (%d) > 120K"); // almost all cvt tables are less than 4k bytes.
return Error("Length (%d) > 120K"); // almost all cvt tables are less than 4k bytes.
}
if (length % 2 != 0) {
return OTS_FAILURE_MSG("Uneven cvt length (%d)", length);
return Error("Uneven table length (%d)", length);
}
if (!table.Skip(length)) {
return OTS_FAILURE_MSG("Length too high");
return Error("Table length too high");
}
cvt->data = data;
cvt->length = length;
this->data = data;
this->length = length;
return true;
}
bool ots_cvt_should_serialise(Font *font) {
if (!font->glyf) {
return false; // this table is not for CFF fonts.
}
return font->cvt != NULL;
}
bool ots_cvt_serialise(OTSStream *out, Font *font) {
const OpenTypeCVT *cvt = font->cvt;
if (!out->Write(cvt->data, cvt->length)) {
return OTS_FAILURE_MSG("Failed to write CVT table");
bool OpenTypeCVT::Serialize(OTSStream *out) {
if (!out->Write(this->data, this->length)) {
return Error("Failed to write cvt table");
}
return true;
}
void ots_cvt_reuse(Font *font, Font *other) {
font->cvt = other->cvt;
font->cvt_reused = true;
}
void ots_cvt_free(Font *font) {
delete font->cvt;
bool OpenTypeCVT::ShouldSerialize() {
return Table::ShouldSerialize() &&
// this table is not for CFF fonts.
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,7 +9,16 @@
namespace ots {
struct OpenTypeCVT {
class OpenTypeCVT : public Table {
public:
explicit OpenTypeCVT(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
const uint8_t *data;
uint32_t length;
};

193
gfx/ots/src/feat.cc Normal file
View File

@ -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

61
gfx/ots/src/feat.h Normal file
View File

@ -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_

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -7,53 +7,36 @@
// fpgm - Font Program
// http://www.microsoft.com/typography/otspec/fpgm.htm
#define TABLE_NAME "fpgm"
namespace ots {
bool ots_fpgm_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeFPGM::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeFPGM *fpgm = new OpenTypeFPGM;
font->fpgm = fpgm;
if (length >= 128 * 1024u) {
return OTS_FAILURE_MSG("length (%ld) > 120", length); // almost all fpgm tables are less than 5k bytes.
return Error("length (%ld) > 120", length); // almost all fpgm tables are less than 5k bytes.
}
if (!table.Skip(length)) {
return OTS_FAILURE_MSG("Bad fpgm length");
return Error("Bad table length");
}
fpgm->data = data;
fpgm->length = length;
this->data = data;
this->length = length;
return true;
}
bool ots_fpgm_should_serialise(Font *font) {
if (!font->glyf) return false; // this table is not for CFF fonts.
return font->fpgm != NULL;
}
bool ots_fpgm_serialise(OTSStream *out, Font *font) {
const OpenTypeFPGM *fpgm = font->fpgm;
if (!out->Write(fpgm->data, fpgm->length)) {
return OTS_FAILURE_MSG("Failed to write fpgm");
bool OpenTypeFPGM::Serialize(OTSStream *out) {
if (!out->Write(this->data, this->length)) {
return Error("Failed to write fpgm table");
}
return true;
}
void ots_fpgm_reuse(Font *font, Font *other) {
font->fpgm = other->fpgm;
font->fpgm_reused = true;
}
void ots_fpgm_free(Font *font) {
delete font->fpgm;
bool OpenTypeFPGM::ShouldSerialize() {
return Table::ShouldSerialize() &&
// this table is not for CFF fonts.
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,7 +9,16 @@
namespace ots {
struct OpenTypeFPGM {
class OpenTypeFPGM : public Table {
public:
explicit OpenTypeFPGM(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
const uint8_t *data;
uint32_t length;
};

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -7,113 +7,78 @@
// gasp - Grid-fitting And Scan-conversion Procedure
// http://www.microsoft.com/typography/otspec/gasp.htm
#define TABLE_NAME "gasp"
#define DROP_THIS_TABLE(...) \
do { \
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
OTS_FAILURE_MSG("Table discarded"); \
delete font->gasp; \
font->gasp = 0; \
} while (0)
namespace ots {
bool ots_gasp_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeGASP::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeGASP *gasp = new OpenTypeGASP;
font->gasp = gasp;
uint16_t num_ranges = 0;
if (!table.ReadU16(&gasp->version) ||
if (!table.ReadU16(&this->version) ||
!table.ReadU16(&num_ranges)) {
return OTS_FAILURE_MSG("Failed to read table header");
return Error("Failed to read table header");
}
if (gasp->version > 1) {
if (this->version > 1) {
// Lots of Linux fonts have bad version numbers...
DROP_THIS_TABLE("bad version: %u", gasp->version);
return true;
return Drop("Unsupported version: %u", this->version);
}
if (num_ranges == 0) {
DROP_THIS_TABLE("num_ranges is zero");
return true;
return Drop("numRanges is zero");
}
gasp->gasp_ranges.reserve(num_ranges);
this->gasp_ranges.reserve(num_ranges);
for (unsigned i = 0; i < num_ranges; ++i) {
uint16_t max_ppem = 0;
uint16_t behavior = 0;
if (!table.ReadU16(&max_ppem) ||
!table.ReadU16(&behavior)) {
return OTS_FAILURE_MSG("Failed to read subrange %d", i);
return Error("Failed to read GASPRANGE %d", i);
}
if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
if ((i > 0) && (this->gasp_ranges[i - 1].first >= max_ppem)) {
// The records in the gaspRange[] array must be sorted in order of
// increasing rangeMaxPPEM value.
DROP_THIS_TABLE("ranges are not sorted");
return true;
return Drop("Ranges are not sorted");
}
if ((i == num_ranges - 1u) && // never underflow.
(max_ppem != 0xffffu)) {
DROP_THIS_TABLE("The last record should be 0xFFFF as a sentinel value "
return Drop("The last record should be 0xFFFF as a sentinel value "
"for rangeMaxPPEM");
return true;
}
if (behavior >> 8) {
OTS_WARNING("undefined bits are used: %x", behavior);
Warning("Undefined bits are used: %x", behavior);
// mask undefined bits.
behavior &= 0x000fu;
}
if (gasp->version == 0 && (behavior >> 2) != 0) {
OTS_WARNING("changed the version number to 1");
gasp->version = 1;
if (this->version == 0 && (behavior >> 2) != 0) {
Warning("Changed the version number to 1");
this->version = 1;
}
gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
this->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
}
return true;
}
bool ots_gasp_should_serialise(Font *font) {
return font->gasp != NULL;
}
bool ots_gasp_serialise(OTSStream *out, Font *font) {
const OpenTypeGASP *gasp = font->gasp;
const uint16_t num_ranges = static_cast<uint16_t>(gasp->gasp_ranges.size());
if (num_ranges != gasp->gasp_ranges.size() ||
!out->WriteU16(gasp->version) ||
bool OpenTypeGASP::Serialize(OTSStream *out) {
const uint16_t num_ranges = static_cast<uint16_t>(this->gasp_ranges.size());
if (num_ranges != this->gasp_ranges.size() ||
!out->WriteU16(this->version) ||
!out->WriteU16(num_ranges)) {
return OTS_FAILURE_MSG("failed to write gasp header");
return Error("Failed to write table header");
}
for (uint16_t i = 0; i < num_ranges; ++i) {
if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
!out->WriteU16(gasp->gasp_ranges[i].second)) {
return OTS_FAILURE_MSG("Failed to write gasp subtable %d", i);
if (!out->WriteU16(this->gasp_ranges[i].first) ||
!out->WriteU16(this->gasp_ranges[i].second)) {
return Error("Failed to write GASPRANGE %d", i);
}
}
return true;
}
void ots_gasp_reuse(Font *font, Font *other) {
font->gasp = other->gasp;
font->gasp_reused = true;
}
void ots_gasp_free(Font *font) {
delete font->gasp;
}
} // namespace ots
#undef TABLE_NAME
#undef DROP_THIS_TABLE

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -13,7 +13,15 @@
namespace ots {
struct OpenTypeGASP {
class OpenTypeGASP : public Table {
public:
explicit OpenTypeGASP(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
private:
uint16_t version;
// A array of (max PPEM, GASP behavior) pairs.
std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -15,8 +15,6 @@
// GDEF - The Glyph Definition Table
// http://www.microsoft.com/typography/otspec/gdef.htm
#define TABLE_NAME "GDEF"
namespace {
// The maximum class value in class definition tables.
@ -28,51 +26,48 @@ const uint16_t kMaxGlyphClassDefValue = 4;
// ParseLigCaretListTable() for the reason.
const uint16_t kMaxCaretValueFormat = 2;
bool ParseGlyphClassDefTable(ots::Font *font, const uint8_t *data,
size_t length, const uint16_t num_glyphs) {
return ots::ParseClassDefTable(font, data, length, num_glyphs,
kMaxGlyphClassDefValue);
}
} // namespace
bool ParseAttachListTable(ots::Font *font, const uint8_t *data,
size_t length, const uint16_t num_glyphs) {
namespace ots {
bool OpenTypeGDEF::ParseAttachListTable(const uint8_t *data, size_t length) {
ots::Buffer subtable(data, length);
uint16_t offset_coverage = 0;
uint16_t glyph_count = 0;
if (!subtable.ReadU16(&offset_coverage) ||
!subtable.ReadU16(&glyph_count)) {
return OTS_FAILURE_MSG("Failed to read gdef header");
return Error("Failed to read gdef header");
}
const unsigned attach_points_end =
2 * static_cast<unsigned>(glyph_count) + 4;
if (attach_points_end > std::numeric_limits<uint16_t>::max()) {
return OTS_FAILURE_MSG("Bad glyph count in gdef");
return Error("Bad glyph count in gdef");
}
if (offset_coverage == 0 || offset_coverage >= length ||
offset_coverage < attach_points_end) {
return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
return Error("Bad coverage offset %d", offset_coverage);
}
if (glyph_count > num_glyphs) {
return OTS_FAILURE_MSG("Bad glyph count %u", glyph_count);
if (glyph_count > this->m_num_glyphs) {
return Error("Bad glyph count %u", glyph_count);
}
std::vector<uint16_t> attach_points;
attach_points.resize(glyph_count);
for (unsigned i = 0; i < glyph_count; ++i) {
if (!subtable.ReadU16(&attach_points[i])) {
return OTS_FAILURE_MSG("Can't read attachment point %d", i);
return Error("Can't read attachment point %d", i);
}
if (attach_points[i] >= length ||
attach_points[i] < attach_points_end) {
return OTS_FAILURE_MSG("Bad attachment point %d of %d", i, attach_points[i]);
return Error("Bad attachment point %d of %d", i, attach_points[i]);
}
}
// Parse coverage table
if (!ots::ParseCoverageTable(font, data + offset_coverage,
length - offset_coverage, num_glyphs)) {
return OTS_FAILURE_MSG("Bad coverage table");
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
length - offset_coverage, this->m_num_glyphs)) {
return Error("Bad coverage table");
}
// Parse attach point table
@ -80,20 +75,20 @@ bool ParseAttachListTable(ots::Font *font, const uint8_t *data,
subtable.set_offset(attach_points[i]);
uint16_t point_count = 0;
if (!subtable.ReadU16(&point_count)) {
return OTS_FAILURE_MSG("Can't read point count %d", i);
return Error("Can't read point count %d", i);
}
if (point_count == 0) {
return OTS_FAILURE_MSG("zero point count %d", i);
return Error("zero point count %d", i);
}
uint16_t last_point_index = 0;
uint16_t point_index = 0;
for (unsigned j = 0; j < point_count; ++j) {
if (!subtable.ReadU16(&point_index)) {
return OTS_FAILURE_MSG("Can't read point index %d in point %d", j, i);
return Error("Can't read point index %d in point %d", j, i);
}
// Contour point indeces are in increasing numerical order
if (last_point_index != 0 && last_point_index >= point_index) {
return OTS_FAILURE_MSG("bad contour indeces: %u >= %u",
return Error("bad contour indeces: %u >= %u",
last_point_index, point_index);
}
last_point_index = point_index;
@ -102,43 +97,42 @@ bool ParseAttachListTable(ots::Font *font, const uint8_t *data,
return true;
}
bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
size_t length, const uint16_t num_glyphs) {
bool OpenTypeGDEF::ParseLigCaretListTable(const uint8_t *data, size_t length) {
ots::Buffer subtable(data, length);
uint16_t offset_coverage = 0;
uint16_t lig_glyph_count = 0;
if (!subtable.ReadU16(&offset_coverage) ||
!subtable.ReadU16(&lig_glyph_count)) {
return OTS_FAILURE_MSG("Can't read caret structure");
return Error("Can't read caret structure");
}
const unsigned lig_glyphs_end =
2 * static_cast<unsigned>(lig_glyph_count) + 4;
if (lig_glyphs_end > std::numeric_limits<uint16_t>::max()) {
return OTS_FAILURE_MSG("Bad caret structure");
return Error("Bad caret structure");
}
if (offset_coverage == 0 || offset_coverage >= length ||
offset_coverage < lig_glyphs_end) {
return OTS_FAILURE_MSG("Bad caret coverate offset %d", offset_coverage);
return Error("Bad caret coverate offset %d", offset_coverage);
}
if (lig_glyph_count > num_glyphs) {
return OTS_FAILURE_MSG("bad ligature glyph count: %u", lig_glyph_count);
if (lig_glyph_count > this->m_num_glyphs) {
return Error("bad ligature glyph count: %u", lig_glyph_count);
}
std::vector<uint16_t> lig_glyphs;
lig_glyphs.resize(lig_glyph_count);
for (unsigned i = 0; i < lig_glyph_count; ++i) {
if (!subtable.ReadU16(&lig_glyphs[i])) {
return OTS_FAILURE_MSG("Can't read ligature glyph location %d", i);
return Error("Can't read ligature glyph location %d", i);
}
if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
return OTS_FAILURE_MSG("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
return Error("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
}
}
// Parse coverage table
if (!ots::ParseCoverageTable(font, data + offset_coverage,
length - offset_coverage, num_glyphs)) {
return OTS_FAILURE_MSG("Can't parse caret coverage table");
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
length - offset_coverage, this->m_num_glyphs)) {
return Error("Can't parse caret coverage table");
}
// Parse ligature glyph table
@ -146,10 +140,10 @@ bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
subtable.set_offset(lig_glyphs[i]);
uint16_t caret_count = 0;
if (!subtable.ReadU16(&caret_count)) {
return OTS_FAILURE_MSG("Can't read caret count for glyph %d", i);
return Error("Can't read caret count for glyph %d", i);
}
if (caret_count == 0) {
return OTS_FAILURE_MSG("bad caret value count: %u", caret_count);
return Error("bad caret value count: %u", caret_count);
}
std::vector<uint16_t> caret_value_offsets;
@ -157,10 +151,10 @@ bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
unsigned caret_value_offsets_end = 2 * static_cast<unsigned>(caret_count) + 2;
for (unsigned j = 0; j < caret_count; ++j) {
if (!subtable.ReadU16(&caret_value_offsets[j])) {
return OTS_FAILURE_MSG("Can't read caret offset %d for glyph %d", j, i);
return Error("Can't read caret offset %d for glyph %d", j, i);
}
if (caret_value_offsets[j] >= length || caret_value_offsets[j] < caret_value_offsets_end) {
return OTS_FAILURE_MSG("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
return Error("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
}
}
@ -169,91 +163,81 @@ bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
subtable.set_offset(lig_glyphs[i] + caret_value_offsets[j]);
uint16_t caret_format = 0;
if (!subtable.ReadU16(&caret_format)) {
return OTS_FAILURE_MSG("Can't read caret values table %d in glyph %d", j, i);
return Error("Can't read caret values table %d in glyph %d", j, i);
}
// TODO(bashi): We only support caret value format 1 and 2 for now
// because there are no fonts which contain caret value format 3
// as far as we investigated.
if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
return OTS_FAILURE_MSG("bad caret value format: %u", caret_format);
return Error("bad caret value format: %u", caret_format);
}
// CaretValueFormats contain a 2-byte field which could be
// arbitrary value.
if (!subtable.Skip(2)) {
return OTS_FAILURE_MSG("Bad caret value table structure %d in glyph %d", j, i);
return Error("Bad caret value table structure %d in glyph %d", j, i);
}
}
}
return true;
}
bool ParseMarkAttachClassDefTable(ots::Font *font, const uint8_t *data,
size_t length, const uint16_t num_glyphs) {
return ots::ParseClassDefTable(font, data, length, num_glyphs, kMaxClassDefValue);
}
bool ParseMarkGlyphSetsDefTable(ots::Font *font, const uint8_t *data,
size_t length, const uint16_t num_glyphs) {
bool OpenTypeGDEF::ParseMarkGlyphSetsDefTable(const uint8_t *data, size_t length) {
ots::Buffer subtable(data, length);
uint16_t format = 0;
uint16_t mark_set_count = 0;
if (!subtable.ReadU16(&format) ||
!subtable.ReadU16(&mark_set_count)) {
return OTS_FAILURE_MSG("Can' read mark glyph table structure");
return Error("Can' read mark glyph table structure");
}
if (format != 1) {
return OTS_FAILURE_MSG("bad mark glyph set table format: %u", format);
return Error("bad mark glyph set table format: %u", format);
}
const unsigned mark_sets_end = 2 * static_cast<unsigned>(mark_set_count) + 4;
if (mark_sets_end > std::numeric_limits<uint16_t>::max()) {
return OTS_FAILURE_MSG("Bad mark_set %d", mark_sets_end);
return Error("Bad mark_set %d", mark_sets_end);
}
for (unsigned i = 0; i < mark_set_count; ++i) {
uint32_t offset_coverage = 0;
if (!subtable.ReadU32(&offset_coverage)) {
return OTS_FAILURE_MSG("Can't read covrage location for mark set %d", i);
return Error("Can't read covrage location for mark set %d", i);
}
if (offset_coverage >= length ||
offset_coverage < mark_sets_end) {
return OTS_FAILURE_MSG("Bad coverage location %d for mark set %d", offset_coverage, i);
return Error("Bad coverage location %d for mark set %d", offset_coverage, i);
}
if (!ots::ParseCoverageTable(font, data + offset_coverage,
length - offset_coverage, num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse coverage table for mark set %d", i);
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
length - offset_coverage, this->m_num_glyphs)) {
return Error("Failed to parse coverage table for mark set %d", i);
}
}
font->gdef->num_mark_glyph_sets = mark_set_count;
this->num_mark_glyph_sets = mark_set_count;
return true;
}
} // namespace
bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
namespace ots {
bool ots_gdef_parse(Font *font, const uint8_t *data, size_t length) {
// Grab the number of glyphs in the font from the maxp table to check
// GlyphIDs in GDEF table.
if (!font->maxp) {
return OTS_FAILURE_MSG("No maxp table in font, needed by GDEF");
if (!maxp) {
return Error("No maxp table in font, needed by GDEF");
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
this->m_num_glyphs = maxp->num_glyphs;
Buffer table(data, length);
OpenTypeGDEF *gdef = new OpenTypeGDEF;
font->gdef = gdef;
uint32_t version = 0;
if (!table.ReadU32(&version)) {
return OTS_FAILURE_MSG("Incomplete table");
return Error("Incomplete table");
}
if (version < 0x00010000 || version == 0x00010001) {
return OTS_FAILURE_MSG("Bad version");
return Error("Bad version");
}
if (version >= 0x00010002) {
gdef->version_2 = true;
this->version_2 = true;
}
uint16_t offset_glyph_class_def = 0;
@ -264,110 +248,91 @@ bool ots_gdef_parse(Font *font, const uint8_t *data, size_t length) {
!table.ReadU16(&offset_attach_list) ||
!table.ReadU16(&offset_lig_caret_list) ||
!table.ReadU16(&offset_mark_attach_class_def)) {
return OTS_FAILURE_MSG("Incomplete table");
return Error("Incomplete table");
}
uint16_t offset_mark_glyph_sets_def = 0;
if (gdef->version_2) {
if (this->version_2) {
if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
return OTS_FAILURE_MSG("Incomplete table");
return Error("Incomplete table");
}
}
unsigned gdef_header_end = 4 + 4 * 2;
if (gdef->version_2)
if (this->version_2)
gdef_header_end += 2;
// Parse subtables
if (offset_glyph_class_def) {
if (offset_glyph_class_def >= length ||
offset_glyph_class_def < gdef_header_end) {
return OTS_FAILURE_MSG("Invalid offset to glyph classes");
return Error("Invalid offset to glyph classes");
}
if (!ParseGlyphClassDefTable(font, data + offset_glyph_class_def,
if (!ots::ParseClassDefTable(GetFont(), data + offset_glyph_class_def,
length - offset_glyph_class_def,
num_glyphs)) {
return OTS_FAILURE_MSG("Invalid glyph classes");
this->m_num_glyphs, kMaxGlyphClassDefValue)) {
return Error("Invalid glyph classes");
}
gdef->has_glyph_class_def = true;
this->has_glyph_class_def = true;
}
if (offset_attach_list) {
if (offset_attach_list >= length ||
offset_attach_list < gdef_header_end) {
return OTS_FAILURE_MSG("Invalid offset to attachment list");
return Error("Invalid offset to attachment list");
}
if (!ParseAttachListTable(font, data + offset_attach_list,
length - offset_attach_list,
num_glyphs)) {
return OTS_FAILURE_MSG("Invalid attachment list");
if (!ParseAttachListTable(data + offset_attach_list,
length - offset_attach_list)) {
return Error("Invalid attachment list");
}
}
if (offset_lig_caret_list) {
if (offset_lig_caret_list >= length ||
offset_lig_caret_list < gdef_header_end) {
return OTS_FAILURE_MSG("Invalid offset to ligature caret list");
return Error("Invalid offset to ligature caret list");
}
if (!ParseLigCaretListTable(font, data + offset_lig_caret_list,
length - offset_lig_caret_list,
num_glyphs)) {
return OTS_FAILURE_MSG("Invalid ligature caret list");
if (!ParseLigCaretListTable(data + offset_lig_caret_list,
length - offset_lig_caret_list)) {
return Error("Invalid ligature caret list");
}
}
if (offset_mark_attach_class_def) {
if (offset_mark_attach_class_def >= length ||
offset_mark_attach_class_def < gdef_header_end) {
return OTS_FAILURE_MSG("Invalid offset to mark attachment list");
return Error("Invalid offset to mark attachment list");
}
if (!ParseMarkAttachClassDefTable(font,
data + offset_mark_attach_class_def,
length - offset_mark_attach_class_def,
num_glyphs)) {
return OTS_FAILURE_MSG("Invalid mark attachment list");
if (!ots::ParseClassDefTable(GetFont(),
data + offset_mark_attach_class_def,
length - offset_mark_attach_class_def,
this->m_num_glyphs, kMaxClassDefValue)) {
return Error("Invalid mark attachment list");
}
gdef->has_mark_attachment_class_def = true;
this->has_mark_attachment_class_def = true;
}
if (offset_mark_glyph_sets_def) {
if (offset_mark_glyph_sets_def >= length ||
offset_mark_glyph_sets_def < gdef_header_end) {
return OTS_FAILURE_MSG("invalid offset to mark glyph sets");
return Error("invalid offset to mark glyph sets");
}
if (!ParseMarkGlyphSetsDefTable(font,
data + offset_mark_glyph_sets_def,
length - offset_mark_glyph_sets_def,
num_glyphs)) {
return OTS_FAILURE_MSG("Invalid mark glyph sets");
if (!ParseMarkGlyphSetsDefTable(data + offset_mark_glyph_sets_def,
length - offset_mark_glyph_sets_def)) {
return Error("Invalid mark glyph sets");
}
gdef->has_mark_glyph_sets_def = true;
this->has_mark_glyph_sets_def = true;
}
gdef->data = data;
gdef->length = length;
this->m_data = data;
this->m_length = length;
return true;
}
bool ots_gdef_should_serialise(Font *font) {
return font->gdef != NULL && font->gdef->data != NULL;
}
bool ots_gdef_serialise(OTSStream *out, Font *font) {
if (!out->Write(font->gdef->data, font->gdef->length)) {
return OTS_FAILURE_MSG("Failed to write GDEF table");
bool OpenTypeGDEF::Serialize(OTSStream *out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write table");
}
return true;
}
void ots_gdef_reuse(Font *font, Font *other) {
font->gdef = other->gdef;
font->gdef_reused = true;
}
void ots_gdef_free(Font *font) {
delete font->gdef;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -9,25 +9,37 @@
namespace ots {
struct OpenTypeGDEF {
OpenTypeGDEF()
: version_2(false),
class OpenTypeGDEF : public Table {
public:
explicit OpenTypeGDEF(Font *font, uint32_t tag)
: Table(font, tag, tag),
version_2(false),
has_glyph_class_def(false),
has_mark_attachment_class_def(false),
has_mark_glyph_sets_def(false),
num_mark_glyph_sets(0),
data(NULL),
length(0) {
m_data(NULL),
m_length(0),
m_num_glyphs(0) {
}
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool version_2;
bool has_glyph_class_def;
bool has_mark_attachment_class_def;
bool has_mark_glyph_sets_def;
uint16_t num_mark_glyph_sets;
const uint8_t *data;
size_t length;
private:
bool ParseAttachListTable(const uint8_t *data, size_t length);
bool ParseLigCaretListTable(const uint8_t *data, size_t length);
bool ParseMarkGlyphSetsDefTable(const uint8_t *data, size_t length);
const uint8_t *m_data;
size_t m_length;
uint16_t m_num_glyphs;
};
} // namespace ots

447
gfx/ots/src/glat.cc Normal file
View File

@ -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 (int 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 (int 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);
size_t outputSize = 0;
bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()),
table.remaining(), // input buffer size (input size + padding)
reinterpret_cast<char*>(decompressed.data()),
decompressed.size(), // target output size
&outputSize); // return output size
if (!ret || outputSize != decompressed.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 (int 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

172
gfx/ots/src/glat.h Normal file
View File

@ -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_

108
gfx/ots/src/gloc.cc Normal file
View File

@ -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

36
gfx/ots/src/gloc.h Normal file
View File

@ -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_

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -14,20 +14,15 @@
// glyf - Glyph Data
// http://www.microsoft.com/typography/otspec/glyf.htm
#define TABLE_NAME "glyf"
namespace ots {
namespace {
bool ParseFlagsForSimpleGlyph(ots::Font *font,
ots::Buffer *table,
uint32_t gly_length,
uint32_t num_flags,
uint32_t *flags_count_logical,
uint32_t *flags_count_physical,
uint32_t *xy_coordinates_length) {
bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
uint32_t num_flags,
uint32_t *flag_index,
uint32_t *coordinates_length) {
uint8_t flag = 0;
if (!table->ReadU8(&flag)) {
return OTS_FAILURE_MSG("Can't read flag");
if (!glyph.ReadU8(&flag)) {
return Error("Can't read flag");
}
uint32_t delta = 0;
@ -44,139 +39,192 @@ bool ParseFlagsForSimpleGlyph(ots::Font *font,
}
if (flag & (1u << 3)) { // repeat
if (*flags_count_logical + 1 >= num_flags) {
return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags);
if (*flag_index + 1 >= num_flags) {
return Error("Count too high (%d + 1 >= %d)", *flag_index, num_flags);
}
uint8_t repeat = 0;
if (!table->ReadU8(&repeat)) {
return OTS_FAILURE_MSG("Can't read repeat value");
if (!glyph.ReadU8(&repeat)) {
return Error("Can't read repeat value");
}
if (repeat == 0) {
return OTS_FAILURE_MSG("Zero repeat");
return Error("Zero repeat");
}
delta += (delta * repeat);
*flags_count_logical += repeat;
if (*flags_count_logical >= num_flags) {
return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags);
*flag_index += repeat;
if (*flag_index >= num_flags) {
return Error("Count too high (%d >= %d)", *flag_index, num_flags);
}
++(*flags_count_physical);
}
if ((flag & (1u << 6)) || (flag & (1u << 7))) { // reserved flags
return OTS_FAILURE_MSG("Bad glyph flag value (%d), reserved flags must be set to zero", flag);
return Error("Bad glyph flag value (%d), reserved flags must be set to zero", flag);
}
*xy_coordinates_length += delta;
if (gly_length < *xy_coordinates_length) {
return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length);
*coordinates_length += delta;
if (glyph.length() < *coordinates_length) {
return Error("Glyph coordinates length bigger than glyph length (%d > %d)",
*coordinates_length, glyph.length());
}
return true;
}
bool ParseSimpleGlyph(ots::Font *font, const uint8_t *data,
ots::Buffer *table, int16_t num_contours,
uint32_t gly_offset, uint32_t gly_length,
uint32_t *new_size) {
ots::OpenTypeGLYF *glyf = font->glyf;
bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,
int16_t num_contours) {
// read the end-points array
uint16_t num_flags = 0;
for (int i = 0; i < num_contours; ++i) {
uint16_t tmp_index = 0;
if (!table->ReadU16(&tmp_index)) {
return OTS_FAILURE_MSG("Can't read contour index %d", i);
if (!glyph.ReadU16(&tmp_index)) {
return Error("Can't read contour index %d", i);
}
if (tmp_index == 0xffffu) {
return OTS_FAILURE_MSG("Bad contour index %d", i);
return Error("Bad contour index %d", i);
}
// check if the indices are monotonically increasing
if (i && (tmp_index + 1 <= num_flags)) {
return OTS_FAILURE_MSG("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
return Error("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
}
num_flags = tmp_index + 1;
}
uint16_t bytecode_length = 0;
if (!table->ReadU16(&bytecode_length)) {
return OTS_FAILURE_MSG("Can't read bytecode length");
}
if ((font->maxp->version_1) &&
(font->maxp->max_size_glyf_instructions < bytecode_length)) {
return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length);
if (!glyph.ReadU16(&bytecode_length)) {
return Error("Can't read bytecode length");
}
const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
if (gly_length < (gly_header_length + bytecode_length)) {
return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length);
if (this->maxp->version_1 &&
this->maxp->max_size_glyf_instructions < bytecode_length) {
return Error("Bytecode length is bigger than maxp.maxSizeOfInstructions "
"%d: %d", this->maxp->max_size_glyf_instructions, bytecode_length);
}
glyf->iov.push_back(std::make_pair(
data + gly_offset,
static_cast<size_t>(gly_header_length + bytecode_length)));
if (!table->Skip(bytecode_length)) {
return OTS_FAILURE_MSG("Can't skip bytecode of length %d", bytecode_length);
if (!glyph.Skip(bytecode_length)) {
return Error("Can't read bytecode of length %d", bytecode_length);
}
uint32_t flags_count_physical = 0; // on memory
uint32_t xy_coordinates_length = 0;
for (uint32_t flags_count_logical = 0;
flags_count_logical < num_flags;
++flags_count_logical, ++flags_count_physical) {
if (!ParseFlagsForSimpleGlyph(font,
table,
gly_length,
num_flags,
&flags_count_logical,
&flags_count_physical,
&xy_coordinates_length)) {
return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical);
uint32_t coordinates_length = 0;
for (uint32_t i = 0; i < num_flags; ++i) {
if (!ParseFlagsForSimpleGlyph(glyph, num_flags, &i, &coordinates_length)) {
return Error("Failed to parse glyph flags %d", i);
}
}
if (gly_length < (gly_header_length + bytecode_length +
flags_count_physical + xy_coordinates_length)) {
return OTS_FAILURE_MSG("Glyph too short %d", gly_length);
if (!glyph.Skip(coordinates_length)) {
return Error("Glyph too short %d", glyph.length());
}
if (gly_length - (gly_header_length + bytecode_length +
flags_count_physical + xy_coordinates_length) > 3) {
if (glyph.remaining() > 3) {
// We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
// zero-padded length.
return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length);
Warning("Extra bytes at end of the glyph: %d", glyph.remaining());
}
glyf->iov.push_back(std::make_pair(
data + gly_offset + gly_header_length + bytecode_length,
static_cast<size_t>(flags_count_physical + xy_coordinates_length)));
*new_size
= gly_header_length + flags_count_physical + xy_coordinates_length + bytecode_length;
this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
return true;
}
} // namespace
#define ARG_1_AND_2_ARE_WORDS (1u << 0)
#define WE_HAVE_A_SCALE (1u << 3)
#define MORE_COMPONENTS (1u << 5)
#define WE_HAVE_AN_X_AND_Y_SCALE (1u << 6)
#define WE_HAVE_A_TWO_BY_TWO (1u << 7)
#define WE_HAVE_INSTRUCTIONS (1u << 8)
namespace ots {
bool OpenTypeGLYF::ParseCompositeGlyph(Buffer &glyph) {
uint16_t flags = 0;
uint16_t gid = 0;
do {
if (!glyph.ReadU16(&flags) || !glyph.ReadU16(&gid)) {
return Error("Can't read composite glyph flags or glyphIndex");
}
bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
Buffer table(data, length);
if (gid >= this->maxp->num_glyphs) {
return Error("Invalid glyph id used in composite glyph: %d", gid);
}
if (!font->maxp || !font->loca || !font->head) {
return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table");
if (flags & ARG_1_AND_2_ARE_WORDS) {
int16_t argument1;
int16_t argument2;
if (!glyph.ReadS16(&argument1) || !glyph.ReadS16(&argument2)) {
return Error("Can't read argument1 or argument2");
}
} else {
uint8_t argument1;
uint8_t argument2;
if (!glyph.ReadU8(&argument1) || !glyph.ReadU8(&argument2)) {
return Error("Can't read argument1 or argument2");
}
}
if (flags & WE_HAVE_A_SCALE) {
int16_t scale;
if (!glyph.ReadS16(&scale)) {
return Error("Can't read scale");
}
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
int16_t xscale;
int16_t yscale;
if (!glyph.ReadS16(&xscale) || !glyph.ReadS16(&yscale)) {
return Error("Can't read xscale or yscale");
}
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
int16_t xscale;
int16_t scale01;
int16_t scale10;
int16_t yscale;
if (!glyph.ReadS16(&xscale) ||
!glyph.ReadS16(&scale01) ||
!glyph.ReadS16(&scale10) ||
!glyph.ReadS16(&yscale)) {
return Error("Can't read transform");
}
}
} while (flags & MORE_COMPONENTS);
if (flags & WE_HAVE_INSTRUCTIONS) {
uint16_t bytecode_length;
if (!glyph.ReadU16(&bytecode_length)) {
return Error("Can't read instructions size");
}
if (this->maxp->version_1 &&
this->maxp->max_size_glyf_instructions < bytecode_length) {
return Error("Bytecode length is bigger than maxp.maxSizeOfInstructions "
"%d: %d",
this->maxp->max_size_glyf_instructions, bytecode_length);
}
if (!glyph.Skip(bytecode_length)) {
return Error("Can't read bytecode of length %d", bytecode_length);
}
}
OpenTypeGLYF *glyf = new OpenTypeGLYF;
font->glyf = glyf;
this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
const unsigned num_glyphs = font->maxp->num_glyphs;
std::vector<uint32_t> &offsets = font->loca->offsets;
return true;
}
bool OpenTypeGLYF::Parse(const uint8_t *data, size_t length) {
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
OpenTypeLOCA *loca = static_cast<OpenTypeLOCA*>(
GetFont()->GetTypedTable(OTS_TAG_LOCA));
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
GetFont()->GetTypedTable(OTS_TAG_HEAD));
if (!maxp || !loca || !head) {
return Error("Missing maxp or loca or head table needed by glyf table");
}
this->maxp = maxp;
const unsigned num_glyphs = maxp->num_glyphs;
std::vector<uint32_t> &offsets = loca->offsets;
if (offsets.size() != num_glyphs + 1) {
return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
return Error("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
}
std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
@ -193,30 +241,31 @@ bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
}
if (gly_offset >= length) {
return OTS_FAILURE_MSG("Glyph %d offset %d too high %ld", i, gly_offset, length);
return Error("Glyph %d offset %d too high %ld", i, gly_offset, length);
}
// Since these are unsigned types, the compiler is not allowed to assume
// that they never overflow.
if (gly_offset + gly_length < gly_offset) {
return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length);
return Error("Glyph %d length (%d < 0)!", i, gly_length);
}
if (gly_offset + gly_length > length) {
return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length);
return Error("Glyph %d length %d too high", i, gly_length);
}
table.set_offset(gly_offset);
Buffer glyph(data + gly_offset, gly_length);
int16_t num_contours, xmin, ymin, xmax, ymax;
if (!table.ReadS16(&num_contours) ||
!table.ReadS16(&xmin) ||
!table.ReadS16(&ymin) ||
!table.ReadS16(&xmax) ||
!table.ReadS16(&ymax)) {
return OTS_FAILURE_MSG("Can't read glyph %d header", i);
if (!glyph.ReadS16(&num_contours) ||
!glyph.ReadS16(&xmin) ||
!glyph.ReadS16(&ymin) ||
!glyph.ReadS16(&xmax) ||
!glyph.ReadS16(&ymax)) {
return Error("Can't read glyph %d header", i);
}
if (num_contours <= -2) {
// -2, -3, -4, ... are reserved for future use.
return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i);
return Error("Bad number of contours %d in glyph %d", num_contours, i);
}
// workaround for fonts in http://www.princexml.com/fonts/
@ -224,35 +273,36 @@ bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
(xmax == -32767) &&
(ymin == 32767) &&
(ymax == -32767)) {
OTS_WARNING("bad xmin/xmax/ymin/ymax values");
Warning("bad xmin/xmax/ymin/ymax values");
xmin = xmax = ymin = ymax = 0;
}
if (xmin > xmax || ymin > ymax) {
return OTS_FAILURE_MSG("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
return Error("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
}
unsigned new_size = 0;
if (num_contours >= 0) {
// this is a simple glyph and might contain bytecode
if (!ParseSimpleGlyph(font, data, &table,
num_contours, gly_offset, gly_length, &new_size)) {
return OTS_FAILURE_MSG("Failed to parse glyph %d", i);
if (num_contours == 0) {
// This is an empty glyph and shouldnt have any glyph data, but if it
// does we will simply ignore it.
glyph.set_offset(0);
} else if (num_contours > 0) {
if (!ParseSimpleGlyph(glyph, num_contours)) {
return Error("Failed to parse glyph %d", i);
}
} else {
// it's a composite glyph without any bytecode. Enqueue the whole thing
glyf->iov.push_back(std::make_pair(data + gly_offset,
static_cast<size_t>(gly_length)));
new_size = gly_length;
if (!ParseCompositeGlyph(glyph)) {
return Error("Failed to parse glyph %d", i);
}
}
size_t new_size = glyph.offset();
resulting_offsets[i] = current_offset;
// glyphs must be four byte aligned
// TODO(yusukes): investigate whether this padding is really necessary.
// Which part of the spec requires this?
const unsigned padding = (4 - (new_size & 3)) % 4;
if (padding) {
glyf->iov.push_back(std::make_pair(
this->iov.push_back(std::make_pair(
reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"),
static_cast<size_t>(padding)));
new_size += padding;
@ -264,40 +314,32 @@ bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
const uint16_t max16 = std::numeric_limits<uint16_t>::max();
if ((*std::max_element(resulting_offsets.begin(),
resulting_offsets.end()) >= (max16 * 2u)) &&
(font->head->index_to_loc_format != 1)) {
OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
font->head->index_to_loc_format = 1;
(head->index_to_loc_format != 1)) {
head->index_to_loc_format = 1;
}
loca->offsets = resulting_offsets;
if (this->iov.empty()) {
// As a special case when all glyph in the font are empty, add a zero byte
// to the table, so that we dont reject it down the way, and to make the
// table work on Windows as well.
// See https://github.com/khaledhosny/ots/issues/52
static const uint8_t kZero = 0;
this->iov.push_back(std::make_pair(&kZero, 1));
}
font->loca->offsets = resulting_offsets;
return true;
}
bool ots_glyf_should_serialise(Font *font) {
return font->glyf != NULL;
}
bool ots_glyf_serialise(OTSStream *out, Font *font) {
const OpenTypeGLYF *glyf = font->glyf;
for (unsigned i = 0; i < glyf->iov.size(); ++i) {
if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
return OTS_FAILURE_MSG("Falied to write glyph %d", i);
bool OpenTypeGLYF::Serialize(OTSStream *out) {
for (unsigned i = 0; i < this->iov.size(); ++i) {
if (!out->Write(this->iov[i].first, this->iov[i].second)) {
return Error("Falied to write glyph %d", i);
}
}
return true;
}
void ots_glyf_reuse(Font *font, Font *other) {
font->glyf = other->glyf;
font->glyf_reused = true;
}
void ots_glyf_free(Font *font) {
delete font->glyf;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -12,8 +12,26 @@
#include "ots.h"
namespace ots {
class OpenTypeMAXP;
class OpenTypeGLYF : public Table {
public:
explicit OpenTypeGLYF(Font *font, uint32_t tag)
: Table(font, tag, tag), maxp(NULL) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
private:
bool ParseFlagsForSimpleGlyph(Buffer &glyph,
uint32_t num_flags,
uint32_t *flag_index,
uint32_t *coordinates_length);
bool ParseSimpleGlyph(Buffer &glyph, int16_t num_contours);
bool ParseCompositeGlyph(Buffer &glyph);
OpenTypeMAXP* maxp;
struct OpenTypeGLYF {
std::vector<std::pair<const uint8_t*, size_t> > iov;
};

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -76,9 +76,23 @@ const ots::LookupSubtableParser kGposLookupSubtableParser = {
// Shared Tables: ValueRecord, Anchor Table, and MarkArray
size_t CalcValueRecordSize(const uint16_t value_format) {
size_t size = 0;
for (unsigned i = 0; i < 8; ++i) {
if ((value_format >> i) & 0x1) {
size += 2;
}
}
return size;
}
bool ParseValueRecord(const ots::Font *font,
ots::Buffer* subtable, const uint8_t *data,
const size_t length, const uint16_t value_format) {
ots::Buffer* subtable,
const uint16_t value_format) {
const uint8_t *data = subtable->buffer();
const size_t length = subtable->length();
// Check existence of adjustment fields.
for (unsigned i = 0; i < 4; ++i) {
if ((value_format >> i) & 0x1) {
@ -207,6 +221,12 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
const size_t length) {
ots::Buffer subtable(data, length);
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
uint16_t format = 0;
uint16_t offset_coverage = 0;
uint16_t value_format = 0;
@ -218,7 +238,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
if (format == 1) {
// Format 1 exactly one value record.
if (!ParseValueRecord(font, &subtable, data, length, value_format)) {
if (!ParseValueRecord(font, &subtable, value_format)) {
return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table");
}
} else if (format == 2) {
@ -227,7 +247,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table");
}
for (unsigned i = 0; i < value_count; ++i) {
if (!ParseValueRecord(font, &subtable, data, length, value_format)) {
if (!ParseValueRecord(font, &subtable, value_format)) {
return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i);
}
}
@ -241,7 +261,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
if (!ots::ParseCoverageTable(font, data + offset_coverage,
length - offset_coverage,
font->maxp->num_glyphs)) {
maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table");
}
@ -268,10 +288,10 @@ bool ParsePairSetTable(const ots::Font *font,
if (glyph_id >= num_glyphs) {
return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs);
}
if (!ParseValueRecord(font, &subtable, data, length, value_format1)) {
if (!ParseValueRecord(font, &subtable, value_format1)) {
return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table");
}
if (!ParseValueRecord(font, &subtable, data, length, value_format2)) {
if (!ParseValueRecord(font, &subtable, value_format2)) {
return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table");
}
}
@ -341,26 +361,36 @@ bool ParsePairPosFormat2(const ots::Font *font,
return OTS_FAILURE_MSG("Failed to read pair pos format 2 data");
}
size_t value_record1_size = CalcValueRecordSize(value_format1);
size_t value_record2_size = CalcValueRecordSize(value_format2);
size_t value_records_size = size_t(class1_count) * size_t(class2_count) *
(value_record1_size + value_record2_size);
// Check the validity of class definition offsets.
if (offset_class_def1 < subtable.offset() + value_records_size ||
offset_class_def2 < subtable.offset() + value_records_size ||
offset_class_def1 >= length || offset_class_def2 >= length) {
return OTS_FAILURE_MSG("Bad ParsePairPosFormat2 class definition offsets %d or %d", offset_class_def1, offset_class_def2);
}
// Check class 1 records.
for (unsigned i = 0; i < class1_count; ++i) {
// Check class 2 records.
for (unsigned j = 0; j < class2_count; ++j) {
if (value_format1 && !ParseValueRecord(font, &subtable, data, length,
value_format1)) {
return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
}
if (value_format2 && !ParseValueRecord(font, &subtable, data, length,
value_format2)) {
return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
if (value_record1_size || value_record2_size) {
for (unsigned i = 0; i < class1_count; ++i) {
// Check class 2 records.
for (unsigned j = 0; j < class2_count; ++j) {
if (value_format1 && value_record2_size &&
!ParseValueRecord(font, &subtable, value_format1)) {
return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
}
if (value_format2 && value_record2_size &&
!ParseValueRecord(font, &subtable, value_format2)) {
return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
}
}
}
}
// Check class definition tables.
if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2);
}
if (!ots::ParseClassDefTable(font, data + offset_class_def1,
length - offset_class_def1,
num_glyphs, kMaxClassDefValue)) {
@ -381,6 +411,12 @@ bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
const size_t length) {
ots::Buffer subtable(data, length);
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
uint16_t format = 0;
uint16_t offset_coverage = 0;
uint16_t value_format1 = 0;
@ -394,12 +430,12 @@ bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
if (format == 1) {
if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2,
font->maxp->num_glyphs)) {
maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse pair pos format 1");
}
} else if (format == 2) {
if (!ParsePairPosFormat2(font, data, length, value_format1, value_format2,
font->maxp->num_glyphs)) {
maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse pair format 2");
}
} else {
@ -411,7 +447,7 @@ bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
}
if (!ots::ParseCoverageTable(font, data + offset_coverage,
length - offset_coverage,
font->maxp->num_glyphs)) {
maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse coverage table");
}
@ -424,6 +460,12 @@ bool ParseCursiveAttachment(const ots::Font *font, const uint8_t *data,
const size_t length) {
ots::Buffer subtable(data, length);
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
uint16_t format = 0;
uint16_t offset_coverage = 0;
uint16_t entry_exit_count = 0;
@ -478,7 +520,7 @@ bool ParseCursiveAttachment(const ots::Font *font, const uint8_t *data,
}
if (!ots::ParseCoverageTable(font, data + offset_coverage,
length - offset_coverage,
font->maxp->num_glyphs)) {
maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment");
}
@ -552,6 +594,12 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font,
const GPOS_TYPE type) {
ots::Buffer subtable(data, length);
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
uint16_t format = 0;
uint16_t offset_coverage1 = 0;
uint16_t offset_coverage2 = 0;
@ -580,7 +628,7 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font,
}
if (!ots::ParseCoverageTable(font, data + offset_coverage1,
length - offset_coverage1,
font->maxp->num_glyphs)) {
maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse converge 1 table");
}
if (offset_coverage2 < header_end || offset_coverage2 >= length) {
@ -588,7 +636,7 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font,
}
if (!ots::ParseCoverageTable(font, data + offset_coverage2,
length - offset_coverage2,
font->maxp->num_glyphs)) {
maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to parse coverage table 2");
}
@ -652,17 +700,37 @@ bool ParseMarkToMarkAttachment(const ots::Font *font,
// Contextual Positioning Subtables
bool ParseContextPositioning(const ots::Font *font,
const uint8_t *data, const size_t length) {
return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs,
font->gpos->num_lookups);
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
ots::OpenTypeGPOS *gpos = static_cast<ots::OpenTypeGPOS*>(
font->GetTypedTable(OTS_TAG_GPOS));
if (!gpos) {
return OTS_FAILURE_MSG("Internal error!");
}
return ots::ParseContextSubtable(font, data, length, maxp->num_glyphs,
gpos->num_lookups);
}
// Lookup Type 8:
// Chaining Contexual Positioning Subtable
bool ParseChainedContextPositioning(const ots::Font *font,
const uint8_t *data, const size_t length) {
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
ots::OpenTypeGPOS *gpos = static_cast<ots::OpenTypeGPOS*>(
font->GetTypedTable(OTS_TAG_GPOS));
if (!gpos) {
return OTS_FAILURE_MSG("Internal error!");
}
return ots::ParseChainingContextSubtable(font, data, length,
font->maxp->num_glyphs,
font->gpos->num_lookups);
maxp->num_glyphs,
gpos->num_lookups);
}
// Lookup Type 9:
@ -677,63 +745,10 @@ bool ParseExtensionPositioning(const ots::Font *font,
namespace ots {
// As far as I checked, following fonts contain invalid GPOS table and
// OTS will drop their GPOS table.
//
// # invalid delta format in device table
// samanata.ttf
//
// # bad size range in device table
// Sarai_07.ttf
//
// # bad offset to PairSetTable
// chandas1-2.ttf
//
// # bad offset to FeatureTable
// glrso12.ttf
// gllr12.ttf
// glbo12.ttf
// glb12.ttf
// glro12.ttf
// glbso12.ttf
// glrc12.ttf
// glrsc12.ttf
// glbs12.ttf
// glrs12.ttf
// glr12.ttf
//
// # ScriptRecords aren't sorted by tag
// Garogier_unhinted.otf
//
// # bad start coverage index in CoverageFormat2
// AndBasR.ttf
// CharisSILB.ttf
// CharisSILBI.ttf
// CharisSILI.ttf
// CharisSILR.ttf
// DoulosSILR.ttf
// GenBasBI.ttf
// GenBasI.ttf
// GenBkBasI.ttf
// GenBkBasB.ttf
// GenBkBasR.ttf
// Padauk-Bold.ttf
// Padauk.ttf
//
// # Contour point indexes aren't sorted
// Arial Unicode.ttf
bool ots_gpos_parse(Font *font, const uint8_t *data, size_t length) {
// Parsing GPOS table requires num_glyphs which is contained in maxp table.
if (!font->maxp) {
return OTS_FAILURE_MSG("missing maxp table needed in GPOS");
}
bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
Font *font = GetFont();
Buffer table(data, length);
OpenTypeGPOS *gpos = new OpenTypeGPOS;
font->gpos = gpos;
uint32_t version = 0;
uint16_t offset_script_list = 0;
uint16_t offset_feature_list = 0;
@ -742,76 +757,63 @@ bool ots_gpos_parse(Font *font, const uint8_t *data, size_t length) {
!table.ReadU16(&offset_script_list) ||
!table.ReadU16(&offset_feature_list) ||
!table.ReadU16(&offset_lookup_list)) {
return OTS_FAILURE_MSG("Incomplete table");
return Error("Incomplete table");
}
if (version != 0x00010000) {
return OTS_FAILURE_MSG("Bad version");
return Error("Bad version");
}
if (offset_lookup_list) {
if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
return OTS_FAILURE_MSG("Bad lookup list offset in table header");
return Error("Bad lookup list offset in table header");
}
if (!ParseLookupListTable(font, data + offset_lookup_list,
length - offset_lookup_list,
&kGposLookupSubtableParser,
&gpos->num_lookups)) {
return OTS_FAILURE_MSG("Failed to parse lookup list table");
&this->num_lookups)) {
return Error("Failed to parse lookup list table");
}
}
uint16_t num_features = 0;
if (offset_feature_list) {
if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
return OTS_FAILURE_MSG("Bad feature list offset in table header");
return Error("Bad feature list offset in table header");
}
if (!ParseFeatureListTable(font, data + offset_feature_list,
length - offset_feature_list, gpos->num_lookups,
length - offset_feature_list, this->num_lookups,
&num_features)) {
return OTS_FAILURE_MSG("Failed to parse feature list table");
return Error("Failed to parse feature list table");
}
}
if (offset_script_list) {
if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
return OTS_FAILURE_MSG("Bad script list offset in table header");
return Error("Bad script list offset in table header");
}
if (!ParseScriptListTable(font, data + offset_script_list,
length - offset_script_list, num_features)) {
return OTS_FAILURE_MSG("Failed to parse script list table");
return Error("Failed to parse script list table");
}
}
gpos->data = data;
gpos->length = length;
this->m_data = data;
this->m_length = length;
return true;
}
bool ots_gpos_should_serialise(Font *font) {
return font->gpos != NULL && font->gpos->data != NULL;
}
bool ots_gpos_serialise(OTSStream *out, Font *font) {
if (!out->Write(font->gpos->data, font->gpos->length)) {
return OTS_FAILURE_MSG("Failed to write GPOS table");
bool OpenTypeGPOS::Serialize(OTSStream *out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write GPOS table");
}
return true;
}
void ots_gpos_reuse(Font *font, Font *other) {
font->gpos = other->gpos;
font->gpos_reused = true;
}
void ots_gpos_free(Font *font) {
delete font->gpos;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -9,18 +9,24 @@
namespace ots {
struct OpenTypeGPOS {
OpenTypeGPOS()
: num_lookups(0),
data(NULL),
length(0) {
class OpenTypeGPOS : public Table {
public:
explicit OpenTypeGPOS(Font *font, uint32_t tag)
: Table(font, tag, tag),
num_lookups(0),
m_data(NULL),
m_length(0) {
}
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
// Number of lookups in GPOS table
uint16_t num_lookups;
const uint8_t *data;
size_t length;
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots

95
gfx/ots/src/graphite.h Normal file
View File

@ -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_

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -82,7 +82,12 @@ bool ParseSingleSubstitution(const ots::Font *font,
return OTS_FAILURE_MSG("Failed to read single subst table header");
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
const uint16_t num_glyphs = maxp->num_glyphs;
if (format == 1) {
// Parse SingleSubstFormat1
int16_t delta_glyph_id = 0;
@ -170,7 +175,12 @@ bool ParseMutipleSubstitution(const ots::Font *font,
return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
const uint16_t num_glyphs = maxp->num_glyphs;
const unsigned sequence_end = static_cast<unsigned>(6) +
sequence_count * 2;
if (sequence_end > std::numeric_limits<uint16_t>::max()) {
@ -245,7 +255,12 @@ bool ParseAlternateSubstitution(const ots::Font *font,
return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
const uint16_t num_glyphs = maxp->num_glyphs;
const unsigned alternate_set_end = static_cast<unsigned>(6) +
alternate_set_count * 2;
if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
@ -362,7 +377,12 @@ bool ParseLigatureSubstitution(const ots::Font *font,
return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
const uint16_t num_glyphs = maxp->num_glyphs;
const unsigned ligature_set_end = static_cast<unsigned>(6) +
lig_set_count * 2;
if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
@ -398,8 +418,18 @@ bool ParseLigatureSubstitution(const ots::Font *font,
// Contextual Substitution Subtable
bool ParseContextSubstitution(const ots::Font *font,
const uint8_t *data, const size_t length) {
return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs,
font->gsub->num_lookups);
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
ots::OpenTypeGSUB *gsub = static_cast<ots::OpenTypeGSUB*>(
font->GetTypedTable(OTS_TAG_GSUB));
if (!gsub) {
return OTS_FAILURE_MSG("Internal error!");
}
return ots::ParseContextSubtable(font, data, length, maxp->num_glyphs,
gsub->num_lookups);
}
// Lookup Type 6:
@ -407,9 +437,19 @@ bool ParseContextSubstitution(const ots::Font *font,
bool ParseChainingContextSubstitution(const ots::Font *font,
const uint8_t *data,
const size_t length) {
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
ots::OpenTypeGSUB *gsub = static_cast<ots::OpenTypeGSUB*>(
font->GetTypedTable(OTS_TAG_GSUB));
if (!gsub) {
return OTS_FAILURE_MSG("Internal error!");
}
return ots::ParseChainingContextSubtable(font, data, length,
font->maxp->num_glyphs,
font->gsub->num_lookups);
maxp->num_glyphs,
gsub->num_lookups);
}
// Lookup Type 7:
@ -434,7 +474,12 @@ bool ParseReverseChainingContextSingleSubstitution(
return OTS_FAILURE_MSG("Failed to read reverse chaining header");
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
font->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return OTS_FAILURE_MSG("Required maxp table missing");
}
const uint16_t num_glyphs = maxp->num_glyphs;
uint16_t backtrack_glyph_count = 0;
if (!subtable.ReadU16(&backtrack_glyph_count)) {
@ -530,67 +575,11 @@ bool ParseReverseChainingContextSingleSubstitution(
namespace ots {
// As far as I checked, following fonts contain invalid values in GSUB table.
// OTS will drop their GSUB table.
//
// # too large substitute (value is 0xFFFF)
// kaiu.ttf
// mingliub2.ttf
// mingliub1.ttf
// mingliub0.ttf
// GraublauWeb.otf
// GraublauWebBold.otf
//
// # too large alternate (value is 0xFFFF)
// ManchuFont.ttf
//
// # bad offset to lang sys table (NULL offset)
// DejaVuMonoSansBold.ttf
// DejaVuMonoSansBoldOblique.ttf
// DejaVuMonoSansOblique.ttf
// DejaVuSansMono-BoldOblique.ttf
// DejaVuSansMono-Oblique.ttf
// DejaVuSansMono-Bold.ttf
//
// # bad start coverage index
// GenBasBI.ttf
// GenBasI.ttf
// AndBasR.ttf
// GenBkBasI.ttf
// CharisSILR.ttf
// CharisSILBI.ttf
// CharisSILI.ttf
// CharisSILB.ttf
// DoulosSILR.ttf
// CharisSILBI.ttf
// GenBkBasB.ttf
// GenBkBasR.ttf
// GenBkBasBI.ttf
// GenBasB.ttf
// GenBasR.ttf
//
// # glyph range is overlapping
// KacstTitleL.ttf
// KacstDecorative.ttf
// KacstTitle.ttf
// KacstArt.ttf
// KacstPoster.ttf
// KacstQurn.ttf
// KacstDigital.ttf
// KacstBook.ttf
// KacstFarsi.ttf
bool ots_gsub_parse(Font *font, const uint8_t *data, size_t length) {
// Parsing gsub table requires |font->maxp->num_glyphs|
if (!font->maxp) {
return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
}
bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
// Parsing gsub table requires |maxp->num_glyphs|
Font *font = GetFont();
Buffer table(data, length);
OpenTypeGSUB *gsub = new OpenTypeGSUB;
font->gsub = gsub;
uint32_t version = 0;
uint16_t offset_script_list = 0;
uint16_t offset_feature_list = 0;
@ -599,76 +588,63 @@ bool ots_gsub_parse(Font *font, const uint8_t *data, size_t length) {
!table.ReadU16(&offset_script_list) ||
!table.ReadU16(&offset_feature_list) ||
!table.ReadU16(&offset_lookup_list)) {
return OTS_FAILURE_MSG("Incomplete table");
return Error("Incomplete table");
}
if (version != 0x00010000) {
return OTS_FAILURE_MSG("Bad version");
return Error("Bad version");
}
if (offset_lookup_list) {
if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
return OTS_FAILURE_MSG("Bad lookup list offset in table header");
return Error("Bad lookup list offset in table header");
}
if (!ParseLookupListTable(font, data + offset_lookup_list,
length - offset_lookup_list,
&kGsubLookupSubtableParser,
&gsub->num_lookups)) {
return OTS_FAILURE_MSG("Failed to parse lookup list table");
&this->num_lookups)) {
return Error("Failed to parse lookup list table");
}
}
uint16_t num_features = 0;
if (offset_feature_list) {
if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
return OTS_FAILURE_MSG("Bad feature list offset in table header");
return Error("Bad feature list offset in table header");
}
if (!ParseFeatureListTable(font, data + offset_feature_list,
length - offset_feature_list, gsub->num_lookups,
length - offset_feature_list, this->num_lookups,
&num_features)) {
return OTS_FAILURE_MSG("Failed to parse feature list table");
return Error("Failed to parse feature list table");
}
}
if (offset_script_list) {
if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
return OTS_FAILURE_MSG("Bad script list offset in table header");
return Error("Bad script list offset in table header");
}
if (!ParseScriptListTable(font, data + offset_script_list,
length - offset_script_list, num_features)) {
return OTS_FAILURE_MSG("Failed to parse script list table");
return Error("Failed to parse script list table");
}
}
gsub->data = data;
gsub->length = length;
this->m_data = data;
this->m_length = length;
return true;
}
bool ots_gsub_should_serialise(Font *font) {
return font->gsub != NULL && font->gsub->data != NULL;
}
bool ots_gsub_serialise(OTSStream *out, Font *font) {
if (!out->Write(font->gsub->data, font->gsub->length)) {
return OTS_FAILURE_MSG("Failed to write GSUB table");
bool OpenTypeGSUB::Serialize(OTSStream *out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write GSUB table");
}
return true;
}
void ots_gsub_reuse(Font *font, Font *other) {
font->gsub = other->gsub;
font->gsub_reused = true;
}
void ots_gsub_free(Font *font) {
delete font->gsub;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -9,18 +9,24 @@
namespace ots {
struct OpenTypeGSUB {
OpenTypeGSUB()
: num_lookups(0),
data(NULL),
length(0) {
class OpenTypeGSUB : public Table {
public:
explicit OpenTypeGSUB(Font *font, uint32_t tag)
: Table(font, tag, tag),
num_lookups(0),
m_data(NULL),
m_length(0) {
}
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
// Number of lookups in GPSUB table
uint16_t num_lookups;
const uint8_t *data;
size_t length;
//private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,139 +9,112 @@
// hdmx - Horizontal Device Metrics
// http://www.microsoft.com/typography/otspec/hdmx.htm
#define TABLE_NAME "hdmx"
#define DROP_THIS_TABLE(...) \
do { \
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
OTS_FAILURE_MSG("Table discarded"); \
delete font->hdmx; \
font->hdmx = 0; \
} while (0)
namespace ots {
bool ots_hdmx_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeHDMX::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
font->hdmx = new OpenTypeHDMX;
OpenTypeHDMX * const hdmx = font->hdmx;
if (!font->head || !font->maxp) {
return OTS_FAILURE_MSG("Missing maxp or head tables in font, needed by hdmx");
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
GetFont()->GetTypedTable(OTS_TAG_HEAD));
if (!head || !maxp) {
return Error("Missing maxp or head tables in font, needed by hdmx");
}
if ((font->head->flags & 0x14) == 0) {
// http://www.microsoft.com/typography/otspec/recom.htm
DROP_THIS_TABLE("the table should not be present when bit 2 and 4 of the "
"head->flags are not set");
return true;
if ((head->flags & 0x14) == 0) {
// http://www.microsoft.com/typography/otspec/recom.htm#hdmx
return Drop("the table should not be present when bit 2 and 4 of the "
"head->flags are not set");
}
int16_t num_recs;
if (!table.ReadU16(&hdmx->version) ||
if (!table.ReadU16(&this->version) ||
!table.ReadS16(&num_recs) ||
!table.ReadS32(&hdmx->size_device_record)) {
return OTS_FAILURE_MSG("Failed to read hdmx header");
!table.ReadS32(&this->size_device_record)) {
return Error("Failed to read table header");
}
if (hdmx->version != 0) {
DROP_THIS_TABLE("bad version: %u", hdmx->version);
return true;
if (this->version != 0) {
return Drop("Unsupported version: %u", this->version);
}
if (num_recs <= 0) {
DROP_THIS_TABLE("bad num_recs: %d", num_recs);
return true;
return Drop("Bad numRecords: %d", num_recs);
}
const int32_t actual_size_device_record = font->maxp->num_glyphs + 2;
if (hdmx->size_device_record < actual_size_device_record) {
DROP_THIS_TABLE("bad hdmx->size_device_record: %d", hdmx->size_device_record);
return true;
const int32_t actual_size_device_record = maxp->num_glyphs + 2;
if (this->size_device_record < actual_size_device_record) {
return Drop("Bad sizeDeviceRecord: %d", this->size_device_record);
}
hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
if (hdmx->pad_len > 3) {
return OTS_FAILURE_MSG("Bad padding %d", hdmx->pad_len);
this->pad_len = this->size_device_record - actual_size_device_record;
if (this->pad_len > 3) {
return Error("Bad DeviceRecord padding %d", this->pad_len);
}
uint8_t last_pixel_size = 0;
hdmx->records.reserve(num_recs);
this->records.reserve(num_recs);
for (int i = 0; i < num_recs; ++i) {
OpenTypeHDMXDeviceRecord rec;
if (!table.ReadU8(&rec.pixel_size) ||
!table.ReadU8(&rec.max_width)) {
return OTS_FAILURE_MSG("Failed to read hdmx record %d", i);
return Error("Failed to read DeviceRecord %d", i);
}
if ((i != 0) &&
(rec.pixel_size <= last_pixel_size)) {
DROP_THIS_TABLE("records are not sorted");
return true;
return Drop("DeviceRecord's are not sorted");
}
last_pixel_size = rec.pixel_size;
rec.widths.reserve(font->maxp->num_glyphs);
for (unsigned j = 0; j < font->maxp->num_glyphs; ++j) {
rec.widths.reserve(maxp->num_glyphs);
for (unsigned j = 0; j < maxp->num_glyphs; ++j) {
uint8_t width;
if (!table.ReadU8(&width)) {
return OTS_FAILURE_MSG("Failed to read glyph width %d in record %d", j, i);
return Error("Failed to read glyph width %d in DeviceRecord %d", j, i);
}
rec.widths.push_back(width);
}
if ((hdmx->pad_len > 0) &&
!table.Skip(hdmx->pad_len)) {
return OTS_FAILURE_MSG("Failed to skip padding %d", hdmx->pad_len);
if ((this->pad_len > 0) &&
!table.Skip(this->pad_len)) {
return Error("DeviceRecord %d should be padded by %d", i, this->pad_len);
}
hdmx->records.push_back(rec);
this->records.push_back(rec);
}
return true;
}
bool ots_hdmx_should_serialise(Font *font) {
if (!font->hdmx) return false;
if (!font->glyf) return false; // this table is not for CFF fonts.
return true;
bool OpenTypeHDMX::ShouldSerialize() {
return Table::ShouldSerialize() &&
// this table is not for CFF fonts.
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
}
bool ots_hdmx_serialise(OTSStream *out, Font *font) {
OpenTypeHDMX * const hdmx = font->hdmx;
const int16_t num_recs = static_cast<int16_t>(hdmx->records.size());
if (hdmx->records.size() >
bool OpenTypeHDMX::Serialize(OTSStream *out) {
const int16_t num_recs = static_cast<int16_t>(this->records.size());
if (this->records.size() >
static_cast<size_t>(std::numeric_limits<int16_t>::max()) ||
!out->WriteU16(hdmx->version) ||
!out->WriteU16(this->version) ||
!out->WriteS16(num_recs) ||
!out->WriteS32(hdmx->size_device_record)) {
return OTS_FAILURE_MSG("Failed to write hdmx header");
!out->WriteS32(this->size_device_record)) {
return Error("Failed to write table header");
}
for (int16_t i = 0; i < num_recs; ++i) {
const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
const OpenTypeHDMXDeviceRecord& rec = this->records[i];
if (!out->Write(&rec.pixel_size, 1) ||
!out->Write(&rec.max_width, 1) ||
!out->Write(&rec.widths[0], rec.widths.size())) {
return OTS_FAILURE_MSG("Failed to write hdmx record %d", i);
return Error("Failed to write DeviceRecord %d", i);
}
if ((hdmx->pad_len > 0) &&
!out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
return OTS_FAILURE_MSG("Failed to write hdmx padding of length %d", hdmx->pad_len);
if ((this->pad_len > 0) &&
!out->Write((const uint8_t *)"\x00\x00\x00", this->pad_len)) {
return Error("Failed to write padding of length %d", this->pad_len);
}
}
return true;
}
void ots_hdmx_reuse(Font *font, Font *other) {
font->hdmx = other->hdmx;
font->hdmx_reused = true;
}
void ots_hdmx_free(Font *font) {
delete font->hdmx;
}
} // namespace ots
#undef TABLE_NAME
#undef DROP_THIS_TABLE

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -17,7 +17,16 @@ struct OpenTypeHDMXDeviceRecord {
std::vector<uint8_t> widths;
};
struct OpenTypeHDMX {
class OpenTypeHDMX : public Table {
public:
explicit OpenTypeHDMX(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
uint16_t version;
int32_t size_device_record;
int32_t pad_len;

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,151 +9,124 @@
// head - Font Header
// http://www.microsoft.com/typography/otspec/head.htm
#define TABLE_NAME "head"
namespace ots {
bool ots_head_parse(Font* font, const uint8_t *data, size_t length) {
bool OpenTypeHEAD::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeHEAD *head = new OpenTypeHEAD;
font->head = head;
uint32_t version = 0;
if (!table.ReadU32(&version) ||
!table.ReadU32(&head->revision)) {
return OTS_FAILURE_MSG("Failed to read head header");
!table.ReadU32(&this->revision)) {
return Error("Failed to read table header");
}
if (version >> 16 != 1) {
return OTS_FAILURE_MSG("Bad head table version of %d", version);
return Error("Unsupported majorVersion: %d", version >> 16);
}
// Skip the checksum adjustment
if (!table.Skip(4)) {
return OTS_FAILURE_MSG("Failed to read checksum");
return Error("Failed to read checksum");
}
uint32_t magic;
if (!table.ReadU32(&magic) || magic != 0x5F0F3CF5) {
return OTS_FAILURE_MSG("Failed to read font magic number");
return Error("Failed to read or incorrect magicNumber");
}
if (!table.ReadU16(&head->flags)) {
return OTS_FAILURE_MSG("Failed to read head flags");
if (!table.ReadU16(&this->flags)) {
return Error("Failed to read flags");
}
// We allow bits 0..4, 11..13
head->flags &= 0x381f;
this->flags &= 0x381f;
if (!table.ReadU16(&head->ppem)) {
return OTS_FAILURE_MSG("Failed to read pixels per em");
if (!table.ReadU16(&this->upem)) {
return Error("Failed to read unitsPerEm");
}
// ppem must be in range
if (head->ppem < 16 ||
head->ppem > 16384) {
return OTS_FAILURE_MSG("Bad ppm of %d", head->ppem);
// upem must be in range
if (this->upem < 16 ||
this->upem > 16384) {
return Error("unitsPerEm on in the range [16, 16384]: %d", this->upem);
}
// ppem must be a power of two
#if 0
// We don't call ots_failure() for now since lots of TrueType fonts are
// not following this rule. Putting OTS_WARNING here is too noisy.
if ((head->ppem - 1) & head->ppem) {
return OTS_FAILURE_MSG("ppm not a power of two: %d", head->ppem);
}
#endif
if (!table.ReadR64(&head->created) ||
!table.ReadR64(&head->modified)) {
return OTS_FAILURE_MSG("Can't read font dates");
if (!table.ReadR64(&this->created) ||
!table.ReadR64(&this->modified)) {
return Error("Can't read font dates");
}
if (!table.ReadS16(&head->xmin) ||
!table.ReadS16(&head->ymin) ||
!table.ReadS16(&head->xmax) ||
!table.ReadS16(&head->ymax)) {
return OTS_FAILURE_MSG("Failed to read font bounding box");
if (!table.ReadS16(&this->xmin) ||
!table.ReadS16(&this->ymin) ||
!table.ReadS16(&this->xmax) ||
!table.ReadS16(&this->ymax)) {
return Error("Failed to read font bounding box");
}
if (head->xmin > head->xmax) {
return OTS_FAILURE_MSG("Bad x dimension in the font bounding box (%d, %d)", head->xmin, head->xmax);
if (this->xmin > this->xmax) {
return Error("Bad x dimension in the font bounding box (%d, %d)",
this->xmin, this->xmax);
}
if (head->ymin > head->ymax) {
return OTS_FAILURE_MSG("Bad y dimension in the font bounding box (%d, %d)", head->ymin, head->ymax);
if (this->ymin > this->ymax) {
return Error("Bad y dimension in the font bounding box (%d, %d)",
this->ymin, this->ymax);
}
if (!table.ReadU16(&head->mac_style)) {
return OTS_FAILURE_MSG("Failed to read font style");
if (!table.ReadU16(&this->mac_style)) {
return Error("Failed to read macStyle");
}
// We allow bits 0..6
head->mac_style &= 0x7f;
this->mac_style &= 0x7f;
if (!table.ReadU16(&head->min_ppem)) {
return OTS_FAILURE_MSG("Failed to read font minimum ppm");
if (!table.ReadU16(&this->min_ppem)) {
return Error("Failed to read lowestRecPPEM");
}
// We don't care about the font direction hint
if (!table.Skip(2)) {
return OTS_FAILURE_MSG("Failed to skip font direction hint");
return Error("Failed to read fontDirectionHint");
}
if (!table.ReadS16(&head->index_to_loc_format)) {
return OTS_FAILURE_MSG("Failed to read index to loc format");
if (!table.ReadS16(&this->index_to_loc_format)) {
return Error("Failed to read indexToLocFormat");
}
if (head->index_to_loc_format < 0 ||
head->index_to_loc_format > 1) {
return OTS_FAILURE_MSG("Bad index to loc format %d", head->index_to_loc_format);
if (this->index_to_loc_format < 0 ||
this->index_to_loc_format > 1) {
return Error("Bad indexToLocFormat %d", this->index_to_loc_format);
}
int16_t glyph_data_format;
if (!table.ReadS16(&glyph_data_format) ||
glyph_data_format) {
return OTS_FAILURE_MSG("Failed to read glyph data format");
return Error("Failed to read or bad glyphDataFormat");
}
return true;
}
bool ots_head_should_serialise(Font *font) {
return font->head != NULL;
}
bool ots_head_serialise(OTSStream *out, Font *font) {
const OpenTypeHEAD *head = font->head;
bool OpenTypeHEAD::Serialize(OTSStream *out) {
if (!out->WriteU32(0x00010000) ||
!out->WriteU32(head->revision) ||
!out->WriteU32(this->revision) ||
!out->WriteU32(0) || // check sum not filled in yet
!out->WriteU32(0x5F0F3CF5) ||
!out->WriteU16(head->flags) ||
!out->WriteU16(head->ppem) ||
!out->WriteR64(head->created) ||
!out->WriteR64(head->modified) ||
!out->WriteS16(head->xmin) ||
!out->WriteS16(head->ymin) ||
!out->WriteS16(head->xmax) ||
!out->WriteS16(head->ymax) ||
!out->WriteU16(head->mac_style) ||
!out->WriteU16(head->min_ppem) ||
!out->WriteU16(this->flags) ||
!out->WriteU16(this->upem) ||
!out->WriteR64(this->created) ||
!out->WriteR64(this->modified) ||
!out->WriteS16(this->xmin) ||
!out->WriteS16(this->ymin) ||
!out->WriteS16(this->xmax) ||
!out->WriteS16(this->ymax) ||
!out->WriteU16(this->mac_style) ||
!out->WriteU16(this->min_ppem) ||
!out->WriteS16(2) ||
!out->WriteS16(head->index_to_loc_format) ||
!out->WriteS16(this->index_to_loc_format) ||
!out->WriteS16(0)) {
return OTS_FAILURE_MSG("Failed to write head table");
return Error("Failed to write table");
}
return true;
}
void ots_head_reuse(Font *font, Font *other) {
font->head = other->head;
font->head_reused = true;
}
void ots_head_free(Font *font) {
delete font->head;
}
} // namespace
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,10 +9,17 @@
namespace ots {
struct OpenTypeHEAD {
class OpenTypeHEAD : public Table {
public:
explicit OpenTypeHEAD(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
uint32_t revision;
uint16_t flags;
uint16_t ppem;
uint16_t upem;
uint64_t created;
uint64_t modified;

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -10,49 +10,23 @@
// hhea - Horizontal Header
// http://www.microsoft.com/typography/otspec/hhea.htm
#define TABLE_NAME "hhea"
namespace ots {
bool ots_hhea_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeHHEA::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeHHEA *hhea = new OpenTypeHHEA;
font->hhea = hhea;
if (!table.ReadU32(&hhea->header.version)) {
return OTS_FAILURE_MSG("Failed to read hhea version");
if (!table.ReadU32(&this->version)) {
return Error("Failed to read table version");
}
if (hhea->header.version >> 16 != 1) {
return OTS_FAILURE_MSG("Bad hhea version of %d", hhea->header.version);
if (this->version >> 16 != 1) {
return Error("Unsupported majorVersion: %d", this->version >> 16);
}
if (!ParseMetricsHeader(font, &table, &hhea->header)) {
return OTS_FAILURE_MSG("Failed to parse horizontal metrics");
}
return true;
return OpenTypeMetricsHeader::Parse(data, length);
}
bool ots_hhea_should_serialise(Font *font) {
return font->hhea != NULL;
}
bool ots_hhea_serialise(OTSStream *out, Font *font) {
if (!SerialiseMetricsHeader(font, out, &font->hhea->header)) {
return OTS_FAILURE_MSG("Failed to serialise horizontal metrics");
}
return true;
}
void ots_hhea_reuse(Font *font, Font *other) {
font->hhea = other->hhea;
font->hhea_reused = true;
}
void ots_hhea_free(Font *font) {
delete font->hhea;
bool OpenTypeHHEA::Serialize(OTSStream *out) {
return OpenTypeMetricsHeader::Serialize(out);
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -10,8 +10,13 @@
namespace ots {
struct OpenTypeHHEA {
OpenTypeMetricsHeader header;
class OpenTypeHHEA : public OpenTypeMetricsHeader {
public:
explicit OpenTypeHHEA(Font *font, uint32_t tag)
: OpenTypeMetricsHeader(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
};
} // namespace ots

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -10,47 +10,14 @@
// hmtx - Horizontal Metrics
// http://www.microsoft.com/typography/otspec/hmtx.htm
#define TABLE_NAME "hmtx"
namespace ots {
bool ots_hmtx_parse(Font *font, const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeHMTX *hmtx = new OpenTypeHMTX;
font->hmtx = hmtx;
if (!font->hhea || !font->maxp) {
return OTS_FAILURE_MSG("Missing hhea or maxp tables in font, needed by hmtx");
}
if (!ParseMetricsTable(font, &table, font->maxp->num_glyphs,
&font->hhea->header, &hmtx->metrics)) {
return OTS_FAILURE_MSG("Failed to parse hmtx metrics");
}
return true;
bool OpenTypeHMTX::Parse(const uint8_t *data, size_t length) {
return OpenTypeMetricsTable::Parse(data, length);
}
bool ots_hmtx_should_serialise(Font *font) {
return font->hmtx != NULL;
}
bool ots_hmtx_serialise(OTSStream *out, Font *font) {
if (!SerialiseMetricsTable(font, out, &font->hmtx->metrics)) {
return OTS_FAILURE_MSG("Failed to serialise htmx metrics");
}
return true;
}
void ots_hmtx_reuse(Font *font, Font *other) {
font->hmtx = other->hmtx;
font->hmtx_reused = true;
}
void ots_hmtx_free(Font *font) {
delete font->hmtx;
bool OpenTypeHMTX::Serialize(OTSStream *out) {
return OpenTypeMetricsTable::Serialize(out);
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -6,12 +6,18 @@
#define OTS_HMTX_H_
#include "metrics.h"
#include "hhea.h"
#include "ots.h"
namespace ots {
struct OpenTypeHMTX {
OpenTypeMetricsTable metrics;
class OpenTypeHMTX : public OpenTypeMetricsTable {
public:
explicit OpenTypeHMTX(Font *font, uint32_t tag)
: OpenTypeMetricsTable(font, tag, tag, OTS_TAG_HHEA) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
};
} // namespace ots

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -7,75 +7,61 @@
// kern - Kerning
// http://www.microsoft.com/typography/otspec/kern.htm
#define TABLE_NAME "kern"
#define DROP_THIS_TABLE(msg_) \
do { \
OTS_FAILURE_MSG(msg_ ", table discarded"); \
delete font->kern; \
font->kern = 0; \
} while (0)
namespace ots {
bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeKERN::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeKERN *kern = new OpenTypeKERN;
font->kern = kern;
uint16_t num_tables = 0;
if (!table.ReadU16(&kern->version) ||
if (!table.ReadU16(&this->version) ||
!table.ReadU16(&num_tables)) {
return OTS_FAILURE_MSG("Failed to read kern header");
return Error("Failed to read table header");
}
if (kern->version > 0) {
DROP_THIS_TABLE("bad table version");
return true;
if (this->version > 0) {
return Drop("Unsupported table version: %d", this->version);
}
if (num_tables == 0) {
DROP_THIS_TABLE("num_tables is zero");
return true;
return Drop("nTables is zero");
}
kern->subtables.reserve(num_tables);
this->subtables.reserve(num_tables);
for (unsigned i = 0; i < num_tables; ++i) {
OpenTypeKERNFormat0 subtable;
uint16_t sub_length = 0;
if (!table.ReadU16(&subtable.version) ||
!table.ReadU16(&sub_length)) {
return OTS_FAILURE_MSG("Failed to read kern subtable %d header", i);
return Error("Failed to read subtable %d header", i);
}
if (subtable.version > 0) {
OTS_WARNING("Bad subtable version: %d", subtable.version);
Warning("Ignoring subtable %d with unsupported version: %d",
i, subtable.version);
continue;
}
const size_t current_offset = table.offset();
if (current_offset - 4 + sub_length > length) {
return OTS_FAILURE_MSG("Bad kern subtable %d offset %ld", i, current_offset);
return Error("Bad subtable %d offset %ld", i, current_offset);
}
if (!table.ReadU16(&subtable.coverage)) {
return OTS_FAILURE_MSG("Cailed to read kern subtable %d coverage", i);
return Error("Failed to read subtable %d coverage", i);
}
if (!(subtable.coverage & 0x1)) {
OTS_WARNING(
Warning(
"We don't support vertical data as the renderer doesn't support it.");
continue;
}
if (subtable.coverage & 0xF0) {
DROP_THIS_TABLE("Reserved fields should zero-filled");
return true;
return Drop("Reserved fields should be zero");
}
const uint32_t format = (subtable.coverage & 0xFF00) >> 8;
if (format != 0) {
OTS_WARNING("Format %d is not supported.", format);
Warning("Ignoring subtable %d with unsupported format: %d", i, format);
continue;
}
@ -85,12 +71,11 @@ bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
!table.ReadU16(&subtable.search_range) ||
!table.ReadU16(&subtable.entry_selector) ||
!table.ReadU16(&subtable.range_shift)) {
return OTS_FAILURE_MSG("Failed to read kern subtable %d format 0 fields", i);
return Error("Failed to read subtable %d format 0 fields", i);
}
if (!num_pairs) {
DROP_THIS_TABLE("Zero length subtable is found");
return true;
return Drop("Zero length subtable is found");
}
// Sanity checks for search_range, entry_selector, and range_shift. See the
@ -98,8 +83,7 @@ bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
const size_t kFormat0PairSize = 6; // left, right, and value. 2 bytes each.
if (num_pairs > (65536 / kFormat0PairSize)) {
// Some fonts (e.g. calibri.ttf, pykes_peak_zero.ttf) have pairs >= 10923.
DROP_THIS_TABLE("Too large subtable");
return true;
return Drop("Too large subtable");
}
unsigned max_pow2 = 0;
while (1u << (max_pow2 + 1) <= num_pairs) {
@ -107,16 +91,16 @@ bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
}
const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize;
if (subtable.search_range != expected_search_range) {
OTS_WARNING("bad search range");
Warning("bad search range");
subtable.search_range = expected_search_range;
}
if (subtable.entry_selector != max_pow2) {
return OTS_FAILURE_MSG("Bad subtable %d entry selector %d", i, subtable.entry_selector);
return Error("Bad subtable %d entry selector %d", i, subtable.entry_selector);
}
const uint16_t expected_range_shift =
kFormat0PairSize * num_pairs - subtable.search_range;
if (subtable.range_shift != expected_range_shift) {
OTS_WARNING("bad range shift");
Warning("bad range shift");
subtable.range_shift = expected_range_shift;
}
@ -128,64 +112,55 @@ bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
if (!table.ReadU16(&kerning_pair.left) ||
!table.ReadU16(&kerning_pair.right) ||
!table.ReadS16(&kerning_pair.value)) {
return OTS_FAILURE_MSG("Failed to read subtable %d kerning pair %d", i, j);
return Error("Failed to read subtable %d kerning pair %d", i, j);
}
const uint32_t current_pair
= (kerning_pair.left << 16) + kerning_pair.right;
if (j != 0 && current_pair <= last_pair) {
// Many free fonts don't follow this rule, so we don't call OTS_FAILURE
// in order to support these fonts.
DROP_THIS_TABLE("Kerning pairs are not sorted");
return true;
return Drop("Kerning pairs are not sorted");
}
last_pair = current_pair;
subtable.pairs.push_back(kerning_pair);
}
kern->subtables.push_back(subtable);
this->subtables.push_back(subtable);
}
if (!kern->subtables.size()) {
DROP_THIS_TABLE("All subtables are removed");
return true;
if (!this->subtables.size()) {
return Drop("All subtables were removed");
}
return true;
}
bool ots_kern_should_serialise(Font *font) {
if (!font->glyf) return false; // this table is not for CFF fonts.
return font->kern != NULL;
}
bool ots_kern_serialise(OTSStream *out, Font *font) {
const OpenTypeKERN *kern = font->kern;
const uint16_t num_subtables = static_cast<uint16_t>(kern->subtables.size());
if (num_subtables != kern->subtables.size() ||
!out->WriteU16(kern->version) ||
bool OpenTypeKERN::Serialize(OTSStream *out) {
const uint16_t num_subtables = static_cast<uint16_t>(this->subtables.size());
if (num_subtables != this->subtables.size() ||
!out->WriteU16(this->version) ||
!out->WriteU16(num_subtables)) {
return OTS_FAILURE_MSG("Can't write kern table header");
return Error("Failed to write kern table header");
}
for (uint16_t i = 0; i < num_subtables; ++i) {
const size_t length = 14 + (6 * kern->subtables[i].pairs.size());
const size_t length = 14 + (6 * this->subtables[i].pairs.size());
if (length > std::numeric_limits<uint16_t>::max() ||
!out->WriteU16(kern->subtables[i].version) ||
!out->WriteU16(this->subtables[i].version) ||
!out->WriteU16(static_cast<uint16_t>(length)) ||
!out->WriteU16(kern->subtables[i].coverage) ||
!out->WriteU16(this->subtables[i].coverage) ||
!out->WriteU16(
static_cast<uint16_t>(kern->subtables[i].pairs.size())) ||
!out->WriteU16(kern->subtables[i].search_range) ||
!out->WriteU16(kern->subtables[i].entry_selector) ||
!out->WriteU16(kern->subtables[i].range_shift)) {
return OTS_FAILURE_MSG("Failed to write kern subtable %d", i);
static_cast<uint16_t>(this->subtables[i].pairs.size())) ||
!out->WriteU16(this->subtables[i].search_range) ||
!out->WriteU16(this->subtables[i].entry_selector) ||
!out->WriteU16(this->subtables[i].range_shift)) {
return Error("Failed to write kern subtable %d", i);
}
for (unsigned j = 0; j < kern->subtables[i].pairs.size(); ++j) {
if (!out->WriteU16(kern->subtables[i].pairs[j].left) ||
!out->WriteU16(kern->subtables[i].pairs[j].right) ||
!out->WriteS16(kern->subtables[i].pairs[j].value)) {
return OTS_FAILURE_MSG("Failed to write kern pair %d for subtable %d", j, i);
for (unsigned j = 0; j < this->subtables[i].pairs.size(); ++j) {
if (!out->WriteU16(this->subtables[i].pairs[j].left) ||
!out->WriteU16(this->subtables[i].pairs[j].right) ||
!out->WriteS16(this->subtables[i].pairs[j].value)) {
return Error("Failed to write kern pair %d for subtable %d", j, i);
}
}
}
@ -193,16 +168,10 @@ bool ots_kern_serialise(OTSStream *out, Font *font) {
return true;
}
void ots_kern_reuse(Font *font, Font *other) {
font->kern = other->kern;
font->kern_reused = true;
}
void ots_kern_free(Font *font) {
delete font->kern;
bool OpenTypeKERN::ShouldSerialize() {
return Table::ShouldSerialize() &&
// this table is not for CFF fonts.
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
}
} // namespace ots
#undef TABLE_NAME
#undef DROP_THIS_TABLE

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -30,7 +30,16 @@ struct OpenTypeKERNFormat0 {
// WebFonts unlikely use it. I've checked thousands of proprietary fonts and
// free fonts, and found no font uses the format.
struct OpenTypeKERN {
class OpenTypeKERN : public Table {
public:
explicit OpenTypeKERN(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
uint16_t version;
std::vector<OpenTypeKERNFormat0> subtables;
};

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -94,10 +94,12 @@ bool ParseScriptTable(const ots::Font *font,
}
// The spec requires a script table for 'DFLT' tag must contain non-NULL
// |offset_default_lang_sys| and |lang_sys_count| == 0
if (tag == kScriptTableTagDflt &&
(offset_default_lang_sys == 0 || lang_sys_count != 0)) {
return OTS_FAILURE_MSG("DFLT table doesn't satisfy the spec. for script tag %c%c%c%c", OTS_UNTAG(tag));
// |offset_default_lang_sys|.
// https://www.microsoft.com/typography/otspec/chapter2.htm
if (tag == kScriptTableTagDflt) {
if (offset_default_lang_sys == 0) {
return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. DefaultLangSys is NULL");
}
}
const unsigned lang_sys_record_end =
@ -192,19 +194,27 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data,
return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
}
ots::OpenTypeGDEF *gdef = static_cast<ots::OpenTypeGDEF*>(
font->GetTypedTable(OTS_TAG_GDEF));
// Check lookup flags.
if ((lookup_flag & kGdefRequiredFlags) &&
(!font->gdef || !font->gdef->has_glyph_class_def)) {
return OTS_FAILURE_MSG("Bad lookup flags %d", lookup_flag);
(!gdef || !gdef->has_glyph_class_def)) {
return OTS_FAILURE_MSG("Lookup flags require GDEF table, "
"but none was found: %d", lookup_flag);
}
if ((lookup_flag & kMarkAttachmentTypeMask) &&
(!font->gdef || !font->gdef->has_mark_attachment_class_def)) {
return OTS_FAILURE_MSG("lookup flag asks for mark attachment that is bad %d", lookup_flag);
(!gdef || !gdef->has_mark_attachment_class_def)) {
return OTS_FAILURE_MSG("Lookup flags ask for mark attachment, "
"but there is no GDEF table or it has no "
"mark attachment classes: %d", lookup_flag);
}
bool use_mark_filtering_set = false;
if (lookup_flag & kUseMarkFilteringSetBit) {
if (!font->gdef || !font->gdef->has_mark_glyph_sets_def) {
return OTS_FAILURE_MSG("lookup flag asks for mark filtering that is bad %d", lookup_flag);
if (!gdef || !gdef->has_mark_glyph_sets_def) {
return OTS_FAILURE_MSG("Lookup flags ask for mark filtering, "
"but there is no GDEF table or it has no "
"mark filtering sets: %d", lookup_flag);
}
use_mark_filtering_set = true;
}
@ -238,8 +248,8 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data,
if (!subtable.ReadU16(&mark_filtering_set)) {
return OTS_FAILURE_MSG("Failed to read mark filtering set");
}
if (font->gdef->num_mark_glyph_sets == 0 ||
mark_filtering_set >= font->gdef->num_mark_glyph_sets) {
if (gdef->num_mark_glyph_sets == 0 ||
mark_filtering_set >= gdef->num_mark_glyph_sets) {
return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
}
}
@ -301,15 +311,15 @@ bool ParseClassDefFormat2(const ots::Font *font,
// Skip format field.
if (!subtable.Skip(2)) {
return OTS_FAILURE_MSG("Failed to skip format of class defintion header");
return OTS_FAILURE_MSG("Failed to read class definition format");
}
uint16_t range_count = 0;
if (!subtable.ReadU16(&range_count)) {
return OTS_FAILURE_MSG("Failed to read range count in class definition");
return OTS_FAILURE_MSG("Failed to read classRangeCount");
}
if (range_count > num_glyphs) {
return OTS_FAILURE_MSG("bad range count: %u", range_count);
return OTS_FAILURE_MSG("classRangeCount > glyph count: %u > %u", range_count, num_glyphs);
}
uint16_t last_end = 0;
@ -320,13 +330,16 @@ bool ParseClassDefFormat2(const ots::Font *font,
if (!subtable.ReadU16(&start) ||
!subtable.ReadU16(&end) ||
!subtable.ReadU16(&class_value)) {
return OTS_FAILURE_MSG("Failed to read class definition reange %d", i);
return OTS_FAILURE_MSG("Failed to read ClassRangeRecord %d", i);
}
if (start > end || (last_end && start <= last_end)) {
return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i);
if (start > end) {
return OTS_FAILURE_MSG("ClassRangeRecord %d, start > end: %u > %u", i, start, end);
}
if (last_end && start <= last_end) {
return OTS_FAILURE_MSG("ClassRangeRecord %d start overlaps with end of the previous one: %u <= %u", i, start, last_end);
}
if (class_value > num_classes) {
return OTS_FAILURE_MSG("bad class value: %u", class_value);
return OTS_FAILURE_MSG("ClassRangeRecord %d class > number of classes: %u > %u", i, class_value, num_classes);
}
last_end = end;
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -10,84 +10,79 @@
// loca - Index to Location
// http://www.microsoft.com/typography/otspec/loca.htm
#define TABLE_NAME "loca"
namespace ots {
bool ots_loca_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeLOCA::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
// We can't do anything useful in validating this data except to ensure that
// the values are monotonically increasing.
OpenTypeLOCA *loca = new OpenTypeLOCA;
font->loca = loca;
if (!font->maxp || !font->head) {
return OTS_FAILURE_MSG("maxp or head tables missing from font, needed by loca");
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
GetFont()->GetTypedTable(OTS_TAG_HEAD));
if (!maxp || !head) {
return Error("Required maxp or head tables are missing");
}
const unsigned num_glyphs = font->maxp->num_glyphs;
const unsigned num_glyphs = maxp->num_glyphs;
unsigned last_offset = 0;
loca->offsets.resize(num_glyphs + 1);
this->offsets.resize(num_glyphs + 1);
// maxp->num_glyphs is uint16_t, thus the addition never overflows.
if (font->head->index_to_loc_format == 0) {
if (head->index_to_loc_format == 0) {
// Note that the <= here (and below) is correct. There is one more offset
// than the number of glyphs in order to give the length of the final
// glyph.
for (unsigned i = 0; i <= num_glyphs; ++i) {
uint16_t offset = 0;
if (!table.ReadU16(&offset)) {
return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
return Error("Failed to read offset for glyph %d", i);
}
if (offset < last_offset) {
return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
return Error("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
}
last_offset = offset;
loca->offsets[i] = offset * 2;
this->offsets[i] = offset * 2;
}
} else {
for (unsigned i = 0; i <= num_glyphs; ++i) {
uint32_t offset = 0;
if (!table.ReadU32(&offset)) {
return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
return Error("Failed to read offset for glyph %d", i);
}
if (offset < last_offset) {
return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
return Error("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
}
last_offset = offset;
loca->offsets[i] = offset;
this->offsets[i] = offset;
}
}
return true;
}
bool ots_loca_should_serialise(Font *font) {
return font->loca != NULL;
}
bool ots_loca_serialise(OTSStream *out, Font *font) {
const OpenTypeLOCA *loca = font->loca;
const OpenTypeHEAD *head = font->head;
bool OpenTypeLOCA::Serialize(OTSStream *out) {
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
GetFont()->GetTypedTable(OTS_TAG_HEAD));
if (!head) {
return OTS_FAILURE_MSG("Missing head table in font needed by loca");
return Error("Required head table is missing");
}
if (head->index_to_loc_format == 0) {
for (unsigned i = 0; i < loca->offsets.size(); ++i) {
const uint16_t offset = static_cast<uint16_t>(loca->offsets[i] >> 1);
if ((offset != (loca->offsets[i] >> 1)) ||
for (unsigned i = 0; i < this->offsets.size(); ++i) {
const uint16_t offset = static_cast<uint16_t>(this->offsets[i] >> 1);
if ((offset != (this->offsets[i] >> 1)) ||
!out->WriteU16(offset)) {
return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
return Error("Failed to write glyph offset for glyph %d", i);
}
}
} else {
for (unsigned i = 0; i < loca->offsets.size(); ++i) {
if (!out->WriteU32(loca->offsets[i])) {
return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
for (unsigned i = 0; i < this->offsets.size(); ++i) {
if (!out->WriteU32(this->offsets[i])) {
return Error("Failed to write glyph offset for glyph %d", i);
}
}
}
@ -95,15 +90,4 @@ bool ots_loca_serialise(OTSStream *out, Font *font) {
return true;
}
void ots_loca_reuse(Font *font, Font *other) {
font->loca = other->loca;
font->loca_reused = true;
}
void ots_loca_free(Font *font) {
delete font->loca;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -11,7 +11,14 @@
namespace ots {
struct OpenTypeLOCA {
class OpenTypeLOCA : public Table {
public:
explicit OpenTypeLOCA(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
std::vector<uint32_t> offsets;
};

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,89 +9,63 @@
// LTSH - Linear Threshold
// http://www.microsoft.com/typography/otspec/ltsh.htm
#define TABLE_NAME "LTSH"
#define DROP_THIS_TABLE(...) \
do { \
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
OTS_FAILURE_MSG("Table discarded"); \
delete font->ltsh; \
font->ltsh = 0; \
} while (0)
namespace ots {
bool ots_ltsh_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeLTSH::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
if (!font->maxp) {
return OTS_FAILURE_MSG("Missing maxp table from font needed by ltsh");
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return Error("Required maxp table is missing");
}
OpenTypeLTSH *ltsh = new OpenTypeLTSH;
font->ltsh = ltsh;
uint16_t num_glyphs = 0;
if (!table.ReadU16(&ltsh->version) ||
if (!table.ReadU16(&this->version) ||
!table.ReadU16(&num_glyphs)) {
return OTS_FAILURE_MSG("Failed to read ltsh header");
return Error("Failed to read table header");
}
if (ltsh->version != 0) {
DROP_THIS_TABLE("bad version: %u", ltsh->version);
return true;
if (this->version != 0) {
return Drop("Unsupported version: %u", this->version);
}
if (num_glyphs != font->maxp->num_glyphs) {
DROP_THIS_TABLE("bad num_glyphs: %u", num_glyphs);
return true;
if (num_glyphs != maxp->num_glyphs) {
return Drop("Bad numGlyphs: %u", num_glyphs);
}
ltsh->ypels.reserve(num_glyphs);
this->ypels.reserve(num_glyphs);
for (unsigned i = 0; i < num_glyphs; ++i) {
uint8_t pel = 0;
if (!table.ReadU8(&pel)) {
return OTS_FAILURE_MSG("Failed to read pixels for glyph %d", i);
return Error("Failed to read pixels for glyph %d", i);
}
ltsh->ypels.push_back(pel);
this->ypels.push_back(pel);
}
return true;
}
bool ots_ltsh_should_serialise(Font *font) {
if (!font->glyf) return false; // this table is not for CFF fonts.
return font->ltsh != NULL;
}
bool ots_ltsh_serialise(OTSStream *out, Font *font) {
const OpenTypeLTSH *ltsh = font->ltsh;
const uint16_t num_ypels = static_cast<uint16_t>(ltsh->ypels.size());
if (num_ypels != ltsh->ypels.size() ||
!out->WriteU16(ltsh->version) ||
bool OpenTypeLTSH::Serialize(OTSStream *out) {
const uint16_t num_ypels = static_cast<uint16_t>(this->ypels.size());
if (num_ypels != this->ypels.size() ||
!out->WriteU16(this->version) ||
!out->WriteU16(num_ypels)) {
return OTS_FAILURE_MSG("Failed to write pels size");
return Error("Failed to write table header");
}
for (uint16_t i = 0; i < num_ypels; ++i) {
if (!out->Write(&(ltsh->ypels[i]), 1)) {
return OTS_FAILURE_MSG("Failed to write pixel size for glyph %d", i);
if (!out->Write(&(this->ypels[i]), 1)) {
return Error("Failed to write pixel size for glyph %d", i);
}
}
return true;
}
void ots_ltsh_reuse(Font *font, Font *other) {
font->ltsh = other->ltsh;
font->ltsh_reused = true;
}
void ots_ltsh_free(Font *font) {
delete font->ltsh;
bool OpenTypeLTSH::ShouldSerialize() {
return Table::ShouldSerialize() &&
// this table is not for CFF fonts.
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
}
} // namespace ots
#undef TABLE_NAME
#undef DROP_THIS_TABLE

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -11,7 +11,16 @@
namespace ots {
struct OpenTypeLTSH {
class OpenTypeLTSH : public Table {
public:
explicit OpenTypeLTSH(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
uint16_t version;
std::vector<uint8_t> ypels;
};

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Copyright (c) 2014-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.
@ -12,12 +12,7 @@
#include "maxp.h"
// MATH - The MATH Table
// The specification is not yet public but has been submitted to the MPEG group
// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font
// Format" Color Font Technology and MATH layout support'. Meanwhile, you can
// contact Microsoft's engineer Murray Sargent to obtain a copy.
#define TABLE_NAME "MATH"
// http://www.microsoft.com/typography/otspec/math.htm
namespace {
@ -48,11 +43,15 @@ const unsigned kMathValueRecordSize = 2 * 2;
// PartFlags
const unsigned kGlyphPartRecordSize = 5 * 2;
} // namespace
namespace ots {
// Shared Table: MathValueRecord
bool ParseMathValueRecord(const ots::Font *font,
ots::Buffer* subtable, const uint8_t *data,
const size_t length) {
bool OpenTypeMATH::ParseMathValueRecord(ots::Buffer* subtable,
const uint8_t *data,
const size_t length) {
// Check the Value field.
if (!subtable->Skip(2)) {
return OTS_FAILURE();
@ -67,7 +66,7 @@ bool ParseMathValueRecord(const ots::Font *font,
if (offset >= length) {
return OTS_FAILURE();
}
if (!ots::ParseDeviceTable(font, data + offset, length - offset)) {
if (!ots::ParseDeviceTable(GetFont(), data + offset, length - offset)) {
return OTS_FAILURE();
}
}
@ -75,8 +74,8 @@ bool ParseMathValueRecord(const ots::Font *font,
return true;
}
bool ParseMathConstantsTable(const ots::Font *font,
const uint8_t *data, size_t length) {
bool OpenTypeMATH::ParseMathConstantsTable(const uint8_t *data,
size_t length) {
ots::Buffer subtable(data, length);
// Part 1: int16 or uint16 constants.
@ -146,7 +145,7 @@ bool ParseMathConstantsTable(const ots::Font *font,
//
// RadicalKernAfterDegree
for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
if (!ParseMathValueRecord(font, &subtable, data, length)) {
if (!ParseMathValueRecord(&subtable, data, length)) {
return OTS_FAILURE();
}
}
@ -160,11 +159,10 @@ bool ParseMathConstantsTable(const ots::Font *font,
return true;
}
bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
ots::Buffer* subtable,
const uint8_t *data,
const size_t length,
const uint16_t num_glyphs) {
bool OpenTypeMATH::ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
const uint8_t *data,
const size_t length,
const uint16_t num_glyphs) {
// Check the header.
uint16_t offset_coverage = 0;
uint16_t sequence_count = 0;
@ -183,7 +181,7 @@ bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
if (offset_coverage < sequence_end || offset_coverage >= length) {
return OTS_FAILURE();
}
if (!ots::ParseCoverageTable(font, data + offset_coverage,
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
length - offset_coverage,
num_glyphs, sequence_count)) {
return OTS_FAILURE();
@ -191,7 +189,7 @@ bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
// Check sequence.
for (unsigned i = 0; i < sequence_count; ++i) {
if (!ParseMathValueRecord(font, subtable, data, length)) {
if (!ParseMathValueRecord(subtable, data, length)) {
return OTS_FAILURE();
}
}
@ -199,26 +197,23 @@ bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
return true;
}
bool ParseMathItalicsCorrectionInfoTable(const ots::Font *font,
const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
bool OpenTypeMATH::ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
return ParseMathValueRecordSequenceForGlyphs(font, &subtable, data, length,
return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
num_glyphs);
}
bool ParseMathTopAccentAttachmentTable(const ots::Font *font,
const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
bool OpenTypeMATH::ParseMathTopAccentAttachmentTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
return ParseMathValueRecordSequenceForGlyphs(font, &subtable, data, length,
return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
num_glyphs);
}
bool ParseMathKernTable(const ots::Font *font,
const uint8_t *data, size_t length) {
bool OpenTypeMATH::ParseMathKernTable(const uint8_t *data, size_t length) {
ots::Buffer subtable(data, length);
// Check the Height count.
@ -229,14 +224,14 @@ bool ParseMathKernTable(const ots::Font *font,
// Check the Correction Heights.
for (unsigned i = 0; i < height_count; ++i) {
if (!ParseMathValueRecord(font, &subtable, data, length)) {
if (!ParseMathValueRecord(&subtable, data, length)) {
return OTS_FAILURE();
}
}
// Check the Kern Values.
for (unsigned i = 0; i <= height_count; ++i) {
if (!ParseMathValueRecord(font, &subtable, data, length)) {
if (!ParseMathValueRecord(&subtable, data, length)) {
return OTS_FAILURE();
}
}
@ -244,9 +239,9 @@ bool ParseMathKernTable(const ots::Font *font,
return true;
}
bool ParseMathKernInfoTable(const ots::Font *font,
const uint8_t *data, size_t length,
const uint16_t num_glyphs) {
bool OpenTypeMATH::ParseMathKernInfoTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
// Check the header.
@ -267,7 +262,7 @@ bool ParseMathKernInfoTable(const ots::Font *font,
if (offset_coverage < sequence_end || offset_coverage >= length) {
return OTS_FAILURE();
}
if (!ots::ParseCoverageTable(font, data + offset_coverage, length - offset_coverage,
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage, length - offset_coverage,
num_glyphs, sequence_count)) {
return OTS_FAILURE();
}
@ -282,7 +277,7 @@ bool ParseMathKernInfoTable(const ots::Font *font,
}
if (offset_math_kern) {
if (offset_math_kern < sequence_end || offset_math_kern >= length ||
!ParseMathKernTable(font, data + offset_math_kern,
!ParseMathKernTable(data + offset_math_kern,
length - offset_math_kern)) {
return OTS_FAILURE();
}
@ -293,9 +288,9 @@ bool ParseMathKernInfoTable(const ots::Font *font,
return true;
}
bool ParseMathGlyphInfoTable(const ots::Font *font,
const uint8_t *data, size_t length,
const uint16_t num_glyphs) {
bool OpenTypeMATH::ParseMathGlyphInfoTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
// Check Header.
@ -318,7 +313,7 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
if (offset_math_italics_correction_info >= length ||
offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
!ParseMathItalicsCorrectionInfoTable(
font, data + offset_math_italics_correction_info,
data + offset_math_italics_correction_info,
length - offset_math_italics_correction_info,
num_glyphs)) {
return OTS_FAILURE();
@ -327,7 +322,7 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
if (offset_math_top_accent_attachment) {
if (offset_math_top_accent_attachment >= length ||
offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
!ParseMathTopAccentAttachmentTable(font, data +
!ParseMathTopAccentAttachmentTable(data +
offset_math_top_accent_attachment,
length -
offset_math_top_accent_attachment,
@ -338,7 +333,7 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
if (offset_extended_shaped_coverage) {
if (offset_extended_shaped_coverage >= length ||
offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
!ots::ParseCoverageTable(font, data + offset_extended_shaped_coverage,
!ots::ParseCoverageTable(GetFont(), data + offset_extended_shaped_coverage,
length - offset_extended_shaped_coverage,
num_glyphs)) {
return OTS_FAILURE();
@ -347,7 +342,7 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
if (offset_math_kern_info) {
if (offset_math_kern_info >= length ||
offset_math_kern_info < kMathGlyphInfoHeaderSize ||
!ParseMathKernInfoTable(font, data + offset_math_kern_info,
!ParseMathKernInfoTable(data + offset_math_kern_info,
length - offset_math_kern_info, num_glyphs)) {
return OTS_FAILURE();
}
@ -356,14 +351,14 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
return true;
}
bool ParseGlyphAssemblyTable(const ots::Font *font,
const uint8_t *data,
size_t length, const uint16_t num_glyphs) {
bool OpenTypeMATH::ParseGlyphAssemblyTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
// Check the header.
uint16_t part_count = 0;
if (!ParseMathValueRecord(font, &subtable, data, length) ||
if (!ParseMathValueRecord(&subtable, data, length) ||
!subtable.ReadU16(&part_count)) {
return OTS_FAILURE();
}
@ -384,19 +379,19 @@ bool ParseGlyphAssemblyTable(const ots::Font *font,
return OTS_FAILURE();
}
if (glyph >= num_glyphs) {
return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
return Error("bad glyph ID: %u", glyph);
}
if (part_flags & ~0x00000001) {
return OTS_FAILURE_MSG("unknown part flag: %u", part_flags);
return Error("unknown part flag: %u", part_flags);
}
}
return true;
}
bool ParseMathGlyphConstructionTable(const ots::Font *font,
const uint8_t *data,
size_t length, const uint16_t num_glyphs) {
bool OpenTypeMATH::ParseMathGlyphConstructionTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
// Check the header.
@ -419,7 +414,7 @@ bool ParseMathGlyphConstructionTable(const ots::Font *font,
offset_glyph_assembly < sequence_end) {
return OTS_FAILURE();
}
if (!ParseGlyphAssemblyTable(font, data + offset_glyph_assembly,
if (!ParseGlyphAssemblyTable(data + offset_glyph_assembly,
length - offset_glyph_assembly, num_glyphs)) {
return OTS_FAILURE();
}
@ -433,26 +428,30 @@ bool ParseMathGlyphConstructionTable(const ots::Font *font,
return OTS_FAILURE();
}
if (glyph >= num_glyphs) {
return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
return Error("bad glyph ID: %u", glyph);
}
}
return true;
}
bool ParseMathGlyphConstructionSequence(const ots::Font *font,
ots::Buffer* subtable,
const uint8_t *data,
size_t length,
const uint16_t num_glyphs,
uint16_t offset_coverage,
uint16_t glyph_count,
const unsigned sequence_end) {
bool OpenTypeMATH::ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
const uint8_t *data,
size_t length,
const uint16_t num_glyphs,
uint16_t offset_coverage,
uint16_t glyph_count,
const unsigned sequence_end) {
// Zero glyph count, nothing to parse.
if (!glyph_count) {
return true;
}
// Check coverage table.
if (offset_coverage < sequence_end || offset_coverage >= length) {
return OTS_FAILURE();
}
if (!ots::ParseCoverageTable(font, data + offset_coverage,
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
length - offset_coverage,
num_glyphs, glyph_count)) {
return OTS_FAILURE();
@ -466,7 +465,7 @@ bool ParseMathGlyphConstructionSequence(const ots::Font *font,
}
if (offset_glyph_construction < sequence_end ||
offset_glyph_construction >= length ||
!ParseMathGlyphConstructionTable(font, data + offset_glyph_construction,
!ParseMathGlyphConstructionTable(data + offset_glyph_construction,
length - offset_glyph_construction,
num_glyphs)) {
return OTS_FAILURE();
@ -476,9 +475,9 @@ bool ParseMathGlyphConstructionSequence(const ots::Font *font,
return true;
}
bool ParseMathVariantsTable(const ots::Font *font,
const uint8_t *data,
size_t length, const uint16_t num_glyphs) {
bool OpenTypeMATH::ParseMathVariantsTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs) {
ots::Buffer subtable(data, length);
// Check the header.
@ -500,11 +499,11 @@ bool ParseMathVariantsTable(const ots::Font *font,
return OTS_FAILURE();
}
if (!ParseMathGlyphConstructionSequence(font, &subtable, data, length, num_glyphs,
if (!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
offset_vert_glyph_coverage,
vert_glyph_count,
sequence_end) ||
!ParseMathGlyphConstructionSequence(font, &subtable, data, length, num_glyphs,
!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
offset_horiz_glyph_coverage,
horiz_glyph_count,
sequence_end)) {
@ -514,37 +513,24 @@ bool ParseMathVariantsTable(const ots::Font *font,
return true;
}
} // namespace
#define DROP_THIS_TABLE(msg_) \
do { \
OTS_FAILURE_MSG(msg_ ", table discarded"); \
font->math->data = 0; \
font->math->length = 0; \
} while (0)
namespace ots {
bool ots_math_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeMATH::Parse(const uint8_t *data, size_t length) {
// Grab the number of glyphs in the font from the maxp table to check
// GlyphIDs in MATH table.
if (!font->maxp) {
return OTS_FAILURE();
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return Error("Required maxp table missing");
}
const uint16_t num_glyphs = font->maxp->num_glyphs;
const uint16_t num_glyphs = maxp->num_glyphs;
Buffer table(data, length);
OpenTypeMATH* math = new OpenTypeMATH;
font->math = math;
uint32_t version = 0;
if (!table.ReadU32(&version)) {
return OTS_FAILURE();
}
if (version != 0x00010000) {
DROP_THIS_TABLE("bad MATH version");
return true;
return Drop("bad MATH version");
}
uint16_t offset_math_constants = 0;
@ -562,53 +548,37 @@ bool ots_math_parse(Font *font, const uint8_t *data, size_t length) {
offset_math_glyph_info < kMathHeaderSize ||
offset_math_variants >= length ||
offset_math_variants < kMathHeaderSize) {
DROP_THIS_TABLE("bad offset in MATH header");
return true;
return Drop("bad offset in MATH header");
}
if (!ParseMathConstantsTable(font, data + offset_math_constants,
if (!ParseMathConstantsTable(data + offset_math_constants,
length - offset_math_constants)) {
DROP_THIS_TABLE("failed to parse MathConstants table");
return true;
return Drop("failed to parse MathConstants table");
}
if (!ParseMathGlyphInfoTable(font, data + offset_math_glyph_info,
if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info,
length - offset_math_glyph_info, num_glyphs)) {
DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
return true;
return Drop("failed to parse MathGlyphInfo table");
}
if (!ParseMathVariantsTable(font, data + offset_math_variants,
if (!ParseMathVariantsTable(data + offset_math_variants,
length - offset_math_variants, num_glyphs)) {
DROP_THIS_TABLE("failed to parse MathVariants table");
return true;
return Drop("failed to parse MathVariants table");
}
math->data = data;
math->length = length;
this->m_data = data;
this->m_length = length;
return true;
}
bool ots_math_should_serialise(Font *font) {
return font->math != NULL && font->math->data != NULL;
}
bool ots_math_serialise(OTSStream *out, Font *font) {
if (!out->Write(font->math->data, font->math->length)) {
bool OpenTypeMATH::Serialize(OTSStream *out) {
if (!out->Write(this->m_data, this->m_length)) {
return OTS_FAILURE();
}
return true;
}
void ots_math_reuse(Font *font, Font *other) {
font->math = other->math;
font->math_reused = true;
}
void ots_math_free(Font *font) {
delete font->math;
bool OpenTypeMATH::ShouldSerialize() {
return Table::ShouldSerialize() && this->m_data != NULL;
}
} // namespace ots
#undef TABLE_NAME
#undef DROP_THIS_TABLE

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Copyright (c) 2014-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.
@ -9,14 +9,58 @@
namespace ots {
struct OpenTypeMATH {
OpenTypeMATH()
: data(NULL),
length(0) {
}
class OpenTypeMATH : public Table {
public:
explicit OpenTypeMATH(Font *font, uint32_t tag)
: Table(font, tag, tag),
m_data(NULL),
m_length(0) { }
const uint8_t *data;
size_t length;
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
bool ParseMathValueRecord(ots::Buffer* subtable,
const uint8_t *data,
const size_t length);
bool ParseMathConstantsTable(const uint8_t *data, size_t length);
bool ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
const uint8_t *data,
const size_t length,
const uint16_t num_glyphs);
bool ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs);
bool ParseMathTopAccentAttachmentTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs);
bool ParseMathKernTable(const uint8_t *data, size_t length);
bool ParseMathKernInfoTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs);
bool ParseMathGlyphInfoTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs);
bool ParseGlyphAssemblyTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs);
bool ParseMathGlyphConstructionTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs);
bool ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
const uint8_t *data,
size_t length,
const uint16_t num_glyphs,
uint16_t offset_coverage,
uint16_t glyph_count,
const unsigned sequence_end);
bool ParseMathVariantsTable(const uint8_t *data,
size_t length,
const uint16_t num_glyphs);
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -7,119 +7,97 @@
// maxp - Maximum Profile
// http://www.microsoft.com/typography/otspec/maxp.htm
#define TABLE_NAME "maxp"
namespace ots {
bool ots_maxp_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeMAXP::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeMAXP *maxp = new OpenTypeMAXP;
font->maxp = maxp;
uint32_t version = 0;
if (!table.ReadU32(&version)) {
return OTS_FAILURE_MSG("Failed to read version of maxp table");
return Error("Failed to read table version");
}
if (version >> 16 > 1) {
return OTS_FAILURE_MSG("Bad maxp version %d", version);
return Error("Unsupported table version 0x%x", version);
}
if (!table.ReadU16(&maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to read number of glyphs from maxp table");
if (!table.ReadU16(&this->num_glyphs)) {
return Error("Failed to read numGlyphs");
}
if (!maxp->num_glyphs) {
return OTS_FAILURE_MSG("Bad number of glyphs 0 in maxp table");
if (!this->num_glyphs) {
return Error("numGlyphs is 0");
}
if (version >> 16 == 1) {
maxp->version_1 = true;
if (!table.ReadU16(&maxp->max_points) ||
!table.ReadU16(&maxp->max_contours) ||
!table.ReadU16(&maxp->max_c_points) ||
!table.ReadU16(&maxp->max_c_contours) ||
!table.ReadU16(&maxp->max_zones) ||
!table.ReadU16(&maxp->max_t_points) ||
!table.ReadU16(&maxp->max_storage) ||
!table.ReadU16(&maxp->max_fdefs) ||
!table.ReadU16(&maxp->max_idefs) ||
!table.ReadU16(&maxp->max_stack) ||
!table.ReadU16(&maxp->max_size_glyf_instructions) ||
!table.ReadU16(&maxp->max_c_components) ||
!table.ReadU16(&maxp->max_c_depth)) {
return OTS_FAILURE_MSG("Failed to read maxp table");
this->version_1 = true;
if (!table.ReadU16(&this->max_points) ||
!table.ReadU16(&this->max_contours) ||
!table.ReadU16(&this->max_c_points) ||
!table.ReadU16(&this->max_c_contours) ||
!table.ReadU16(&this->max_zones) ||
!table.ReadU16(&this->max_t_points) ||
!table.ReadU16(&this->max_storage) ||
!table.ReadU16(&this->max_fdefs) ||
!table.ReadU16(&this->max_idefs) ||
!table.ReadU16(&this->max_stack) ||
!table.ReadU16(&this->max_size_glyf_instructions) ||
!table.ReadU16(&this->max_c_components) ||
!table.ReadU16(&this->max_c_depth)) {
return Error("Failed to read version 1 table data");
}
if (maxp->max_zones == 0) {
if (this->max_zones == 0) {
// workaround for ipa*.ttf Japanese fonts.
OTS_WARNING("bad max_zones: %u", maxp->max_zones);
maxp->max_zones = 1;
} else if (maxp->max_zones == 3) {
Warning("Bad maxZones: %u", this->max_zones);
this->max_zones = 1;
} else if (this->max_zones == 3) {
// workaround for Ecolier-*.ttf fonts.
OTS_WARNING("bad max_zones: %u", maxp->max_zones);
maxp->max_zones = 2;
Warning("Bad maxZones: %u", this->max_zones);
this->max_zones = 2;
}
if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) {
return OTS_FAILURE_MSG("Bad max zones %d in maxp", maxp->max_zones);
if ((this->max_zones != 1) && (this->max_zones != 2)) {
return Error("Bad maxZones: %u", this->max_zones);
}
} else {
maxp->version_1 = false;
this->version_1 = false;
}
return true;
}
bool ots_maxp_should_serialise(Font *font) {
return font->maxp != NULL;
}
bool ots_maxp_serialise(OTSStream *out, Font *font) {
const OpenTypeMAXP *maxp = font->maxp;
if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
!out->WriteU16(maxp->num_glyphs)) {
return OTS_FAILURE_MSG("Failed to write maxp version or number of glyphs");
bool OpenTypeMAXP::Serialize(OTSStream *out) {
if (!out->WriteU32(this->version_1 ? 0x00010000 : 0x00005000) ||
!out->WriteU16(this->num_glyphs)) {
return Error("Failed to write version or numGlyphs");
}
if (!maxp->version_1) return true;
if (!this->version_1) return true;
if (!out->WriteU16(maxp->max_points) ||
!out->WriteU16(maxp->max_contours) ||
!out->WriteU16(maxp->max_c_points) ||
!out->WriteU16(maxp->max_c_contours)) {
return OTS_FAILURE_MSG("Failed to write maxp");
if (!out->WriteU16(this->max_points) ||
!out->WriteU16(this->max_contours) ||
!out->WriteU16(this->max_c_points) ||
!out->WriteU16(this->max_c_contours)) {
return Error("Failed to write maxp");
}
if (!out->WriteU16(maxp->max_zones) ||
!out->WriteU16(maxp->max_t_points) ||
!out->WriteU16(maxp->max_storage) ||
!out->WriteU16(maxp->max_fdefs) ||
!out->WriteU16(maxp->max_idefs) ||
!out->WriteU16(maxp->max_stack) ||
!out->WriteU16(maxp->max_size_glyf_instructions)) {
return OTS_FAILURE_MSG("Failed to write more maxp");
if (!out->WriteU16(this->max_zones) ||
!out->WriteU16(this->max_t_points) ||
!out->WriteU16(this->max_storage) ||
!out->WriteU16(this->max_fdefs) ||
!out->WriteU16(this->max_idefs) ||
!out->WriteU16(this->max_stack) ||
!out->WriteU16(this->max_size_glyf_instructions)) {
return Error("Failed to write more maxp");
}
if (!out->WriteU16(maxp->max_c_components) ||
!out->WriteU16(maxp->max_c_depth)) {
return OTS_FAILURE_MSG("Failed to write yet more maxp");
if (!out->WriteU16(this->max_c_components) ||
!out->WriteU16(this->max_c_depth)) {
return Error("Failed to write yet more maxp");
}
return true;
}
void ots_maxp_reuse(Font *font, Font *other) {
font->maxp = other->maxp;
font->maxp_reused = true;
}
void ots_maxp_free(Font *font) {
delete font->maxp;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,7 +9,14 @@
namespace ots {
struct OpenTypeMAXP {
class OpenTypeMAXP : public Table {
public:
explicit OpenTypeMAXP(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
uint16_t num_glyphs;
bool version_1;

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -11,177 +11,163 @@
// http://www.microsoft.com/typography/otspec/hhea.htm
// http://www.microsoft.com/typography/otspec/vhea.htm
#define TABLE_NAME "metrics" // XXX: use individual table names
namespace ots {
bool ParseMetricsHeader(Font *font, Buffer *table,
OpenTypeMetricsHeader *header) {
if (!table->ReadS16(&header->ascent) ||
!table->ReadS16(&header->descent) ||
!table->ReadS16(&header->linegap) ||
!table->ReadU16(&header->adv_width_max) ||
!table->ReadS16(&header->min_sb1) ||
!table->ReadS16(&header->min_sb2) ||
!table->ReadS16(&header->max_extent) ||
!table->ReadS16(&header->caret_slope_rise) ||
!table->ReadS16(&header->caret_slope_run) ||
!table->ReadS16(&header->caret_offset)) {
return OTS_FAILURE_MSG("Failed to read metrics header");
bool OpenTypeMetricsHeader::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
// Skip already read version.
if (!table.Skip(4)) {
return false;
}
if (header->ascent < 0) {
OTS_WARNING("bad ascent: %d", header->ascent);
header->ascent = 0;
}
if (header->linegap < 0) {
OTS_WARNING("bad linegap: %d", header->linegap);
header->linegap = 0;
if (!table.ReadS16(&this->ascent) ||
!table.ReadS16(&this->descent) ||
!table.ReadS16(&this->linegap) ||
!table.ReadU16(&this->adv_width_max) ||
!table.ReadS16(&this->min_sb1) ||
!table.ReadS16(&this->min_sb2) ||
!table.ReadS16(&this->max_extent) ||
!table.ReadS16(&this->caret_slope_rise) ||
!table.ReadS16(&this->caret_slope_run) ||
!table.ReadS16(&this->caret_offset)) {
return Error("Failed to read table");
}
if (!font->head) {
return OTS_FAILURE_MSG("Missing head font table");
if (this->ascent < 0) {
Warning("bad ascent: %d", this->ascent);
this->ascent = 0;
}
if (this->linegap < 0) {
Warning("bad linegap: %d", this->linegap);
this->linegap = 0;
}
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
GetFont()->GetTypedTable(OTS_TAG_HEAD));
if (!head) {
return Error("Missing head font table");
}
// if the font is non-slanted, caret_offset should be zero.
if (!(font->head->mac_style & 2) &&
(header->caret_offset != 0)) {
OTS_WARNING("bad caret offset: %d", header->caret_offset);
header->caret_offset = 0;
if (!(head->mac_style & 2) &&
(this->caret_offset != 0)) {
Warning("bad caret offset: %d", this->caret_offset);
this->caret_offset = 0;
}
// skip the reserved bytes
if (!table->Skip(8)) {
return OTS_FAILURE_MSG("Failed to skip reserverd bytes");
if (!table.Skip(8)) {
return Error("Failed to read reserverd bytes");
}
int16_t data_format;
if (!table->ReadS16(&data_format)) {
return OTS_FAILURE_MSG("Failed to read data format");
if (!table.ReadS16(&data_format)) {
return Error("Failed to read metricDataFormat");
}
if (data_format) {
return OTS_FAILURE_MSG("Bad data format %d", data_format);
return Error("Bad metricDataFormat: %d", data_format);
}
if (!table->ReadU16(&header->num_metrics)) {
return OTS_FAILURE_MSG("Failed to read number of metrics");
if (!table.ReadU16(&this->num_metrics)) {
return Error("Failed to read number of metrics");
}
if (!font->maxp) {
return OTS_FAILURE_MSG("Missing maxp font table");
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return Error("Missing maxp font table");
}
if (header->num_metrics > font->maxp->num_glyphs) {
return OTS_FAILURE_MSG("Bad number of metrics %d", header->num_metrics);
if (this->num_metrics > maxp->num_glyphs) {
return Error("Bad number of metrics %d", this->num_metrics);
}
return true;
}
bool SerialiseMetricsHeader(const ots::Font *font,
OTSStream *out,
const OpenTypeMetricsHeader *header) {
if (!out->WriteU32(header->version) ||
!out->WriteS16(header->ascent) ||
!out->WriteS16(header->descent) ||
!out->WriteS16(header->linegap) ||
!out->WriteU16(header->adv_width_max) ||
!out->WriteS16(header->min_sb1) ||
!out->WriteS16(header->min_sb2) ||
!out->WriteS16(header->max_extent) ||
!out->WriteS16(header->caret_slope_rise) ||
!out->WriteS16(header->caret_slope_run) ||
!out->WriteS16(header->caret_offset) ||
bool OpenTypeMetricsHeader::Serialize(OTSStream *out) {
if (!out->WriteU32(this->version) ||
!out->WriteS16(this->ascent) ||
!out->WriteS16(this->descent) ||
!out->WriteS16(this->linegap) ||
!out->WriteU16(this->adv_width_max) ||
!out->WriteS16(this->min_sb1) ||
!out->WriteS16(this->min_sb2) ||
!out->WriteS16(this->max_extent) ||
!out->WriteS16(this->caret_slope_rise) ||
!out->WriteS16(this->caret_slope_run) ||
!out->WriteS16(this->caret_offset) ||
!out->WriteR64(0) || // reserved
!out->WriteS16(0) || // metric data format
!out->WriteU16(header->num_metrics)) {
return OTS_FAILURE_MSG("Failed to write metrics");
!out->WriteU16(this->num_metrics)) {
return Error("Failed to write metrics");
}
return true;
}
bool ParseMetricsTable(const ots::Font *font,
Buffer *table,
const uint16_t num_glyphs,
const OpenTypeMetricsHeader *header,
OpenTypeMetricsTable *metrics) {
bool OpenTypeMetricsTable::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
// OpenTypeMetricsHeader is a superclass of both 'hhea' and 'vhea',
// so the cast here is OK, whichever m_header_tag we have.
OpenTypeMetricsHeader *header = static_cast<OpenTypeMetricsHeader*>(
GetFont()->GetTypedTable(m_header_tag));
if (!header) {
return Error("Required %c%c%c%c table missing", OTS_UNTAG(m_header_tag));
}
// |num_metrics| is a uint16_t, so it's bounded < 65536. This limits that
// amount of memory that we'll allocate for this to a sane amount.
const unsigned num_metrics = header->num_metrics;
if (num_metrics > num_glyphs) {
return OTS_FAILURE_MSG("Bad number of metrics %d", num_metrics);
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return Error("Required maxp table missing");
}
if (num_metrics > maxp->num_glyphs) {
return Error("Bad number of metrics %d", num_metrics);
}
if (!num_metrics) {
return OTS_FAILURE_MSG("No metrics!");
return Error("No metrics!");
}
const unsigned num_sbs = num_glyphs - num_metrics;
const unsigned num_sbs = maxp->num_glyphs - num_metrics;
metrics->entries.reserve(num_metrics);
this->entries.reserve(num_metrics);
for (unsigned i = 0; i < num_metrics; ++i) {
uint16_t adv = 0;
int16_t sb = 0;
if (!table->ReadU16(&adv) || !table->ReadS16(&sb)) {
return OTS_FAILURE_MSG("Failed to read metric %d", i);
if (!table.ReadU16(&adv) || !table.ReadS16(&sb)) {
return Error("Failed to read metric %d", i);
}
// This check is bogus, see https://github.com/khaledhosny/ots/issues/36
#if 0
// Since so many fonts don't have proper value on |adv| and |sb|,
// we should not call ots_failure() here. For example, about 20% of fonts
// in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
if (adv > header->adv_width_max) {
OTS_WARNING("bad adv: %u > %u", adv, header->adv_width_max);
adv = header->adv_width_max;
}
if (sb < header->min_sb1) {
OTS_WARNING("bad sb: %d < %d", sb, header->min_sb1);
sb = header->min_sb1;
}
#endif
metrics->entries.push_back(std::make_pair(adv, sb));
this->entries.push_back(std::make_pair(adv, sb));
}
metrics->sbs.reserve(num_sbs);
this->sbs.reserve(num_sbs);
for (unsigned i = 0; i < num_sbs; ++i) {
int16_t sb;
if (!table->ReadS16(&sb)) {
if (!table.ReadS16(&sb)) {
// Some Japanese fonts (e.g., mona.ttf) fail this test.
return OTS_FAILURE_MSG("Failed to read side bearing %d", i + num_metrics);
return Error("Failed to read side bearing %d", i + num_metrics);
}
// This check is bogus, see https://github.com/khaledhosny/ots/issues/36
#if 0
if (sb < header->min_sb1) {
// The same as above. Three fonts in http://www.fontsquirrel.com/fontface
// (e.g., Notice2Std.otf) have weird lsb values.
OTS_WARNING("bad lsb: %d < %d", sb, header->min_sb1);
sb = header->min_sb1;
}
#endif
metrics->sbs.push_back(sb);
this->sbs.push_back(sb);
}
return true;
}
bool SerialiseMetricsTable(const ots::Font *font,
OTSStream *out,
const OpenTypeMetricsTable *metrics) {
for (unsigned i = 0; i < metrics->entries.size(); ++i) {
if (!out->WriteU16(metrics->entries[i].first) ||
!out->WriteS16(metrics->entries[i].second)) {
return OTS_FAILURE_MSG("Failed to write metric %d", i);
bool OpenTypeMetricsTable::Serialize(OTSStream *out) {
for (unsigned i = 0; i < this->entries.size(); ++i) {
if (!out->WriteU16(this->entries[i].first) ||
!out->WriteS16(this->entries[i].second)) {
return Error("Failed to write metric %d", i);
}
}
for (unsigned i = 0; i < metrics->sbs.size(); ++i) {
if (!out->WriteS16(metrics->sbs[i])) {
return OTS_FAILURE_MSG("Failed to write side bearing %ld", i + metrics->entries.size());
for (unsigned i = 0; i < this->sbs.size(); ++i) {
if (!out->WriteS16(this->sbs[i])) {
return Error("Failed to write side bearing %ld", i + this->entries.size());
}
}
@ -189,5 +175,3 @@ bool SerialiseMetricsTable(const ots::Font *font,
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -13,7 +13,14 @@
namespace ots {
struct OpenTypeMetricsHeader {
class OpenTypeMetricsHeader : public Table {
public:
explicit OpenTypeMetricsHeader(Font *font, uint32_t tag, uint32_t type)
: Table(font, tag, type) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
uint32_t version;
int16_t ascent;
int16_t descent;
@ -28,27 +35,22 @@ struct OpenTypeMetricsHeader {
uint16_t num_metrics;
};
struct OpenTypeMetricsTable {
struct OpenTypeMetricsTable : public Table {
public:
explicit OpenTypeMetricsTable(Font *font, uint32_t tag, uint32_t type,
uint32_t header_tag)
: Table(font, tag, type), m_header_tag(header_tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
private:
uint32_t m_header_tag;
std::vector<std::pair<uint16_t, int16_t> > entries;
std::vector<int16_t> sbs;
};
bool ParseMetricsHeader(Font *font, Buffer *table,
OpenTypeMetricsHeader *header);
bool SerialiseMetricsHeader(const ots::Font *font,
OTSStream *out,
const OpenTypeMetricsHeader *header);
bool ParseMetricsTable(const ots::Font *font,
Buffer *table,
const uint16_t num_glyphs,
const OpenTypeMetricsHeader *header,
OpenTypeMetricsTable *metrics);
bool SerialiseMetricsTable(const ots::Font *font,
OTSStream *out,
const OpenTypeMetricsTable *metrics);
} // namespace ots
#endif // OTS_METRICS_H_

View File

@ -1,4 +1,4 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
@ -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,9 +43,13 @@ UNIFIED_SOURCES += [
'ots.cc',
'post.cc',
'prep.cc',
'sile.cc',
'silf.cc',
'sill.cc',
'vdmx.cc',
'vhea.cc',
'vmtx.cc',
'woff2.cc',
'vorg.cc',
]
# We allow warnings for third-party code that can be updated from upstream.
@ -54,7 +59,13 @@ FINAL_LIBRARY = 'gkmedias'
DEFINES['PACKAGE_VERSION'] = '"moz"'
DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
DEFINES['OTS_GRAPHITE'] = 1
USE_LIBS += [
'brotli',
'woff2',
]
LOCAL_INCLUDES += [
'/modules/woff2/src',
]

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -10,8 +10,6 @@
// name - Naming Table
// http://www.microsoft.com/typography/otspec/name.htm
#define TABLE_NAME "name"
namespace {
bool ValidInPsName(char c) {
@ -56,25 +54,22 @@ void AssignToUtf16BeFromAscii(std::string* target,
namespace ots {
bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
bool OpenTypeNAME::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
OpenTypeNAME* name = new OpenTypeNAME;
font->name = name;
uint16_t format = 0;
if (!table.ReadU16(&format) || format > 1) {
return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
return Error("Failed to read table format or bad format %d", format);
}
uint16_t count = 0;
if (!table.ReadU16(&count)) {
return OTS_FAILURE_MSG("Failed to read name count");
return Error("Failed to read name count");
}
uint16_t string_offset = 0;
if (!table.ReadU16(&string_offset) || string_offset > length) {
return OTS_FAILURE_MSG("Failed to read strings offset");
return Error("Failed to read or bad stringOffset");
}
const char* string_base = reinterpret_cast<const char*>(data) +
string_offset;
@ -94,7 +89,7 @@ bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
!table.ReadU16(&rec.name_id) ||
!table.ReadU16(&name_length) ||
!table.ReadU16(&name_offset)) {
return OTS_FAILURE_MSG("Failed to read name entry %d", i);
return Error("Failed to read name entry %d", i);
}
// check platform & encoding, discard names with unknown values
switch (rec.platform_id) {
@ -148,40 +143,41 @@ bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
}
}
if (!name->names.empty() && !(name->names.back() < rec)) {
OTS_WARNING("name records are not sorted.");
if (!this->names.empty() && !(this->names.back() < rec)) {
Warning("name records are not sorted.");
sort_required = true;
}
name->names.push_back(rec);
this->names.push_back(rec);
this->name_ids.insert(rec.name_id);
}
if (format == 1) {
// extended name table format with language tags
uint16_t lang_tag_count;
if (!table.ReadU16(&lang_tag_count)) {
return OTS_FAILURE_MSG("Failed to read language tag count");
return Error("Failed to read langTagCount");
}
for (unsigned i = 0; i < lang_tag_count; ++i) {
uint16_t tag_length = 0;
uint16_t tag_offset = 0;
if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
return OTS_FAILURE_MSG("Faile to read tag length or offset");
return Error("Faile to read length or offset for langTagRecord %d", i);
}
const unsigned tag_end = static_cast<unsigned>(string_offset) +
tag_offset + tag_length;
if (tag_end > length) {
return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
return Error("bad end of tag %d > %ld for langTagRecord %d", tag_end, length, i);
}
std::string tag(string_base + tag_offset, tag_length);
name->lang_tags.push_back(tag);
this->lang_tags.push_back(tag);
}
}
if (table.offset() > string_offset) {
// the string storage apparently overlapped the name/tag records;
// consider this font to be badly broken
return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
return Error("Bad table offset %ld > %d", table.offset(), string_offset);
}
// check existence of required name strings (synthesize if necessary)
@ -207,8 +203,8 @@ bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
// if not, we'll add our fixed versions here
bool mac_name[kStdNameCount] = { 0 };
bool win_name[kStdNameCount] = { 0 };
for (std::vector<NameRecord>::iterator name_iter = name->names.begin();
name_iter != name->names.end(); ++name_iter) {
for (std::vector<NameRecord>::iterator name_iter = this->names.begin();
name_iter != this->names.end(); ++name_iter) {
const uint16_t id = name_iter->name_id;
if (id >= kStdNameCount || kStdNames[id] == NULL) {
continue;
@ -236,48 +232,42 @@ bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
1033 /* language_id */ , i /* name_id */);
AssignToUtf16BeFromAscii(&win_rec.text, std::string(kStdNames[i]));
name->names.push_back(mac_rec);
name->names.push_back(win_rec);
this->names.push_back(mac_rec);
this->names.push_back(win_rec);
sort_required = true;
}
}
if (sort_required) {
std::sort(name->names.begin(), name->names.end());
std::sort(this->names.begin(), this->names.end());
}
return true;
}
bool ots_name_should_serialise(Font *font) {
return font->name != NULL;
}
bool ots_name_serialise(OTSStream* out, Font *font) {
const OpenTypeNAME* name = font->name;
uint16_t name_count = static_cast<uint16_t>(name->names.size());
uint16_t lang_tag_count = static_cast<uint16_t>(name->lang_tags.size());
bool OpenTypeNAME::Serialize(OTSStream* out) {
uint16_t name_count = static_cast<uint16_t>(this->names.size());
uint16_t lang_tag_count = static_cast<uint16_t>(this->lang_tags.size());
uint16_t format = 0;
size_t string_offset = 6 + name_count * 12;
if (name->lang_tags.size() > 0) {
if (this->lang_tags.size() > 0) {
// lang tags require a format-1 name table
format = 1;
string_offset += 2 + lang_tag_count * 4;
}
if (string_offset > 0xffff) {
return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
return Error("Bad stringOffset: %ld", string_offset);
}
if (!out->WriteU16(format) ||
!out->WriteU16(name_count) ||
!out->WriteU16(static_cast<uint16_t>(string_offset))) {
return OTS_FAILURE_MSG("Failed to write name header");
return Error("Failed to write name header");
}
std::string string_data;
for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
name_iter != name->names.end(); ++name_iter) {
for (std::vector<NameRecord>::const_iterator name_iter = this->names.begin();
name_iter != this->names.end(); ++name_iter) {
const NameRecord& rec = *name_iter;
if (string_data.size() + rec.text.size() >
std::numeric_limits<uint16_t>::max() ||
@ -287,44 +277,79 @@ bool ots_name_serialise(OTSStream* out, Font *font) {
!out->WriteU16(rec.name_id) ||
!out->WriteU16(static_cast<uint16_t>(rec.text.size())) ||
!out->WriteU16(static_cast<uint16_t>(string_data.size())) ) {
return OTS_FAILURE_MSG("Faile to write name entry");
return Error("Faile to write nameRecord");
}
string_data.append(rec.text);
}
if (format == 1) {
if (!out->WriteU16(lang_tag_count)) {
return OTS_FAILURE_MSG("Faile to write language tag count");
return Error("Faile to write langTagCount");
}
for (std::vector<std::string>::const_iterator tag_iter =
name->lang_tags.begin();
tag_iter != name->lang_tags.end(); ++tag_iter) {
this->lang_tags.begin();
tag_iter != this->lang_tags.end(); ++tag_iter) {
if (string_data.size() + tag_iter->size() >
std::numeric_limits<uint16_t>::max() ||
!out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
!out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
return OTS_FAILURE_MSG("Failed to write string");
return Error("Failed to write langTagRecord");
}
string_data.append(*tag_iter);
}
}
if (!out->Write(string_data.data(), string_data.size())) {
return OTS_FAILURE_MSG("Faile to write string data");
return Error("Faile to write string data");
}
return true;
}
void ots_name_reuse(Font *font, Font *other) {
font->name = other->name;
font->name_reused = true;
}
void ots_name_free(Font *font) {
delete font->name;
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
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -9,6 +9,7 @@
#include <string>
#include <utility>
#include <vector>
#include <unordered_set>
#include "ots.h"
@ -43,9 +44,19 @@ struct NameRecord {
}
};
struct OpenTypeNAME {
class OpenTypeNAME : public Table {
public:
explicit OpenTypeNAME(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
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

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -10,77 +10,76 @@
// OS/2 - OS/2 and Windows Metrics
// http://www.microsoft.com/typography/otspec/os2.htm
#define TABLE_NAME "OS/2"
namespace ots {
bool ots_os2_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeOS2 *os2 = new OpenTypeOS2;
font->os2 = os2;
if (!table.ReadU16(&os2->version) ||
!table.ReadS16(&os2->avg_char_width) ||
!table.ReadU16(&os2->weight_class) ||
!table.ReadU16(&os2->width_class) ||
!table.ReadU16(&os2->type) ||
!table.ReadS16(&os2->subscript_x_size) ||
!table.ReadS16(&os2->subscript_y_size) ||
!table.ReadS16(&os2->subscript_x_offset) ||
!table.ReadS16(&os2->subscript_y_offset) ||
!table.ReadS16(&os2->superscript_x_size) ||
!table.ReadS16(&os2->superscript_y_size) ||
!table.ReadS16(&os2->superscript_x_offset) ||
!table.ReadS16(&os2->superscript_y_offset) ||
!table.ReadS16(&os2->strikeout_size) ||
!table.ReadS16(&os2->strikeout_position) ||
!table.ReadS16(&os2->family_class)) {
return OTS_FAILURE_MSG("Error reading basic table elements");
if (!table.ReadU16(&this->table.version) ||
!table.ReadS16(&this->table.avg_char_width) ||
!table.ReadU16(&this->table.weight_class) ||
!table.ReadU16(&this->table.width_class) ||
!table.ReadU16(&this->table.type) ||
!table.ReadS16(&this->table.subscript_x_size) ||
!table.ReadS16(&this->table.subscript_y_size) ||
!table.ReadS16(&this->table.subscript_x_offset) ||
!table.ReadS16(&this->table.subscript_y_offset) ||
!table.ReadS16(&this->table.superscript_x_size) ||
!table.ReadS16(&this->table.superscript_y_size) ||
!table.ReadS16(&this->table.superscript_x_offset) ||
!table.ReadS16(&this->table.superscript_y_offset) ||
!table.ReadS16(&this->table.strikeout_size) ||
!table.ReadS16(&this->table.strikeout_position) ||
!table.ReadS16(&this->table.family_class)) {
return Error("Error reading basic table elements");
}
if (os2->version > 5) {
return OTS_FAILURE_MSG("Unsupported table version: %u", os2->version);
if (this->table.version > 5) {
return Error("Unsupported table version: %u", this->table.version);
}
// Follow WPF Font Selection Model's advice.
if (1 <= os2->weight_class && os2->weight_class <= 9) {
OTS_WARNING("Bad usWeightClass: %u, changing it to: %u", os2->weight_class, os2->weight_class * 100);
os2->weight_class *= 100;
if (1 <= this->table.weight_class && this->table.weight_class <= 9) {
Warning("Bad usWeightClass: %u, changing it to %u",
this->table.weight_class, this->table.weight_class * 100);
this->table.weight_class *= 100;
}
// Ditto.
if (os2->weight_class > 999) {
OTS_WARNING("Bad usWeightClass: %u, changing it to: %d", os2->weight_class, 999);
os2->weight_class = 999;
if (this->table.weight_class > 999) {
Warning("Bad usWeightClass: %u, changing it to %d",
this->table.weight_class, 999);
this->table.weight_class = 999;
}
if (os2->width_class < 1) {
OTS_WARNING("Bad usWidthClass: %u, changing it to: %d", os2->width_class, 1);
os2->width_class = 1;
} else if (os2->width_class > 9) {
OTS_WARNING("Bad usWidthClass: %u, changing it to: %d", os2->width_class, 9);
os2->width_class = 9;
if (this->table.width_class < 1) {
Warning("Bad usWidthClass: %u, changing it to %d",
this->table.width_class, 1);
this->table.width_class = 1;
} else if (this->table.width_class > 9) {
Warning("Bad usWidthClass: %u, changing it to %d",
this->table.width_class, 9);
this->table.width_class = 9;
}
// lowest 3 bits of fsType are exclusive.
if (os2->type & 0x2) {
if (this->table.type & 0x2) {
// mask bits 2 & 3.
os2->type &= 0xfff3u;
} else if (os2->type & 0x4) {
this->table.type &= 0xfff3u;
} else if (this->table.type & 0x4) {
// mask bits 1 & 3.
os2->type &= 0xfff4u;
} else if (os2->type & 0x8) {
this->table.type &= 0xfff4u;
} else if (this->table.type & 0x8) {
// mask bits 1 & 2.
os2->type &= 0xfff9u;
this->table.type &= 0xfff9u;
}
// mask reserved bits. use only 0..3, 8, 9 bits.
os2->type &= 0x30f;
this->table.type &= 0x30f;
#define SET_TO_ZERO(a, b) \
if (os2->b < 0) { \
OTS_WARNING("Bad " a ": %d, setting it to zero", os2->b); \
os2->b = 0; \
#define SET_TO_ZERO(a, b) \
if (this->table.b < 0) { \
Warning("Bad " a ": %d, setting it to zero", this->table.b); \
this->table.b = 0; \
}
SET_TO_ZERO("ySubscriptXSize", subscript_x_size);
@ -103,234 +102,221 @@ bool ots_os2_parse(Font *font, const uint8_t *data, size_t length) {
"bXHeight",
};
for (unsigned i = 0; i < 10; ++i) {
if (!table.ReadU8(&os2->panose[i])) {
return OTS_FAILURE_MSG("Error reading PANOSE %s", panose_strings[i].c_str());
if (!table.ReadU8(&this->table.panose[i])) {
return Error("Failed to read PANOSE %s", panose_strings[i].c_str());
}
}
if (!table.ReadU32(&os2->unicode_range_1) ||
!table.ReadU32(&os2->unicode_range_2) ||
!table.ReadU32(&os2->unicode_range_3) ||
!table.ReadU32(&os2->unicode_range_4) ||
!table.ReadU32(&os2->vendor_id) ||
!table.ReadU16(&os2->selection) ||
!table.ReadU16(&os2->first_char_index) ||
!table.ReadU16(&os2->last_char_index) ||
!table.ReadS16(&os2->typo_ascender) ||
!table.ReadS16(&os2->typo_descender) ||
!table.ReadS16(&os2->typo_linegap) ||
!table.ReadU16(&os2->win_ascent) ||
!table.ReadU16(&os2->win_descent)) {
return OTS_FAILURE_MSG("Error reading more basic table fields");
if (!table.ReadU32(&this->table.unicode_range_1) ||
!table.ReadU32(&this->table.unicode_range_2) ||
!table.ReadU32(&this->table.unicode_range_3) ||
!table.ReadU32(&this->table.unicode_range_4) ||
!table.ReadU32(&this->table.vendor_id) ||
!table.ReadU16(&this->table.selection) ||
!table.ReadU16(&this->table.first_char_index) ||
!table.ReadU16(&this->table.last_char_index) ||
!table.ReadS16(&this->table.typo_ascender) ||
!table.ReadS16(&this->table.typo_descender) ||
!table.ReadS16(&this->table.typo_linegap) ||
!table.ReadU16(&this->table.win_ascent) ||
!table.ReadU16(&this->table.win_descent)) {
return Error("Error reading more basic table fields");
}
// If bit 6 is set, then bits 0 and 5 must be clear.
if (os2->selection & 0x40) {
os2->selection &= 0xffdeu;
if (this->table.selection & 0x40) {
this->table.selection &= 0xffdeu;
}
// the settings of bits 0 and 1 must be reflected in the macStyle bits
// in the 'head' table.
if (!font->head) {
return OTS_FAILURE_MSG("Needed head table is missing from the font");
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
GetFont()->GetTypedTable(OTS_TAG_HEAD));
if ((this->table.selection & 0x1) &&
head && !(head->mac_style & 0x2)) {
Warning("Adjusting head.macStyle (italic) to match fsSelection");
head->mac_style |= 0x2;
}
if ((os2->selection & 0x1) &&
!(font->head->mac_style & 0x2)) {
OTS_WARNING("adjusting Mac style (italic)");
font->head->mac_style |= 0x2;
}
if ((os2->selection & 0x2) &&
!(font->head->mac_style & 0x4)) {
OTS_WARNING("adjusting Mac style (underscore)");
font->head->mac_style |= 0x4;
if ((this->table.selection & 0x2) &&
head && !(head->mac_style & 0x4)) {
Warning("Adjusting head.macStyle (underscore) to match fsSelection");
head->mac_style |= 0x4;
}
// While bit 6 on implies that bits 0 and 1 of macStyle are clear,
// the reverse is not true.
if ((os2->selection & 0x40) &&
(font->head->mac_style & 0x3)) {
OTS_WARNING("adjusting Mac style (regular)");
font->head->mac_style &= 0xfffcu;
if ((this->table.selection & 0x40) &&
head && (head->mac_style & 0x3)) {
Warning("Adjusting head.macStyle (regular) to match fsSelection");
head->mac_style &= 0xfffcu;
}
if ((os2->version < 4) &&
(os2->selection & 0x300)) {
if ((this->table.version < 4) &&
(this->table.selection & 0x300)) {
// bit 8 and 9 must be unset in OS/2 table versions less than 4.
return OTS_FAILURE_MSG("Version %d incompatible with selection %d", os2->version, os2->selection);
return Error("fSelection bits 8 and 9 must be unset for table version %d",
this->table.version);
}
// mask reserved bits. use only 0..9 bits.
os2->selection &= 0x3ff;
this->table.selection &= 0x3ff;
if (os2->first_char_index > os2->last_char_index) {
return OTS_FAILURE_MSG("first char index %d > last char index %d in os2", os2->first_char_index, os2->last_char_index);
if (this->table.first_char_index > this->table.last_char_index) {
return Error("usFirstCharIndex %d > usLastCharIndex %d",
this->table.first_char_index, this->table.last_char_index);
}
if (os2->typo_linegap < 0) {
OTS_WARNING("bad linegap: %d", os2->typo_linegap);
os2->typo_linegap = 0;
if (this->table.typo_linegap < 0) {
Warning("Bad sTypoLineGap, setting it to 0: %d", this->table.typo_linegap);
this->table.typo_linegap = 0;
}
if (os2->version < 1) {
if (this->table.version < 1) {
// http://www.microsoft.com/typography/otspec/os2ver0.htm
return true;
}
if (length < offsetof(OpenTypeOS2, code_page_range_2)) {
OTS_WARNING("bad version number: %u", os2->version);
if (length < offsetof(OS2Data, code_page_range_2)) {
Warning("Bad version number, setting it to 0: %u", this->table.version);
// Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version
// numbers. Fix them.
os2->version = 0;
this->table.version = 0;
return true;
}
if (!table.ReadU32(&os2->code_page_range_1) ||
!table.ReadU32(&os2->code_page_range_2)) {
return OTS_FAILURE_MSG("Failed to read codepage ranges");
if (!table.ReadU32(&this->table.code_page_range_1) ||
!table.ReadU32(&this->table.code_page_range_2)) {
return Error("Failed to read ulCodePageRange1 or ulCodePageRange2");
}
if (os2->version < 2) {
if (this->table.version < 2) {
// http://www.microsoft.com/typography/otspec/os2ver1.htm
return true;
}
if (length < offsetof(OpenTypeOS2, max_context)) {
OTS_WARNING("bad version number: %u", os2->version);
if (length < offsetof(OS2Data, max_context)) {
Warning("Bad version number, setting it to 1: %u", this->table.version);
// some Japanese fonts (e.g., mona.ttf) have weird version number.
// fix them.
os2->version = 1;
this->table.version = 1;
return true;
}
if (!table.ReadS16(&os2->x_height) ||
!table.ReadS16(&os2->cap_height) ||
!table.ReadU16(&os2->default_char) ||
!table.ReadU16(&os2->break_char) ||
!table.ReadU16(&os2->max_context)) {
return OTS_FAILURE_MSG("Failed to read version 2-specific fields");
if (!table.ReadS16(&this->table.x_height) ||
!table.ReadS16(&this->table.cap_height) ||
!table.ReadU16(&this->table.default_char) ||
!table.ReadU16(&this->table.break_char) ||
!table.ReadU16(&this->table.max_context)) {
return Error("Failed to read version 2-specific fields");
}
if (os2->x_height < 0) {
OTS_WARNING("bad x_height: %d", os2->x_height);
os2->x_height = 0;
if (this->table.x_height < 0) {
Warning("Bad sxHeight settig it to 0: %d", this->table.x_height);
this->table.x_height = 0;
}
if (os2->cap_height < 0) {
OTS_WARNING("bad cap_height: %d", os2->cap_height);
os2->cap_height = 0;
if (this->table.cap_height < 0) {
Warning("Bad sCapHeight setting it to 0: %d", this->table.cap_height);
this->table.cap_height = 0;
}
if (os2->version < 5) {
if (this->table.version < 5) {
// http://www.microsoft.com/typography/otspec/os2ver4.htm
return true;
}
if (!table.ReadU16(&os2->lower_optical_pointsize) ||
!table.ReadU16(&os2->upper_optical_pointsize)) {
return OTS_FAILURE_MSG("Failed to read version 5-specific fields");
if (!table.ReadU16(&this->table.lower_optical_pointsize) ||
!table.ReadU16(&this->table.upper_optical_pointsize)) {
return Error("Failed to read version 5-specific fields");
}
if (os2->lower_optical_pointsize > 0xFFFE) {
OTS_WARNING("'usLowerOpticalPointSize' is bigger than 0xFFFE: %d", os2->lower_optical_pointsize);
os2->lower_optical_pointsize = 0xFFFE;
if (this->table.lower_optical_pointsize > 0xFFFE) {
Warning("usLowerOpticalPointSize is bigger than 0xFFFE: %d",
this->table.lower_optical_pointsize);
this->table.lower_optical_pointsize = 0xFFFE;
}
if (os2->upper_optical_pointsize < 2) {
OTS_WARNING("'usUpperOpticalPointSize' is lower than 2: %d", os2->upper_optical_pointsize);
os2->upper_optical_pointsize = 2;
if (this->table.upper_optical_pointsize < 2) {
Warning("usUpperOpticalPointSize is lower than 2: %d",
this->table.upper_optical_pointsize);
this->table.upper_optical_pointsize = 2;
}
return true;
}
bool ots_os2_should_serialise(Font *font) {
return font->os2 != NULL;
}
bool ots_os2_serialise(OTSStream *out, Font *font) {
const OpenTypeOS2 *os2 = font->os2;
if (!out->WriteU16(os2->version) ||
!out->WriteS16(os2->avg_char_width) ||
!out->WriteU16(os2->weight_class) ||
!out->WriteU16(os2->width_class) ||
!out->WriteU16(os2->type) ||
!out->WriteS16(os2->subscript_x_size) ||
!out->WriteS16(os2->subscript_y_size) ||
!out->WriteS16(os2->subscript_x_offset) ||
!out->WriteS16(os2->subscript_y_offset) ||
!out->WriteS16(os2->superscript_x_size) ||
!out->WriteS16(os2->superscript_y_size) ||
!out->WriteS16(os2->superscript_x_offset) ||
!out->WriteS16(os2->superscript_y_offset) ||
!out->WriteS16(os2->strikeout_size) ||
!out->WriteS16(os2->strikeout_position) ||
!out->WriteS16(os2->family_class)) {
return OTS_FAILURE_MSG("Failed to write basic OS2 information");
bool OpenTypeOS2::Serialize(OTSStream *out) {
if (!out->WriteU16(this->table.version) ||
!out->WriteS16(this->table.avg_char_width) ||
!out->WriteU16(this->table.weight_class) ||
!out->WriteU16(this->table.width_class) ||
!out->WriteU16(this->table.type) ||
!out->WriteS16(this->table.subscript_x_size) ||
!out->WriteS16(this->table.subscript_y_size) ||
!out->WriteS16(this->table.subscript_x_offset) ||
!out->WriteS16(this->table.subscript_y_offset) ||
!out->WriteS16(this->table.superscript_x_size) ||
!out->WriteS16(this->table.superscript_y_size) ||
!out->WriteS16(this->table.superscript_x_offset) ||
!out->WriteS16(this->table.superscript_y_offset) ||
!out->WriteS16(this->table.strikeout_size) ||
!out->WriteS16(this->table.strikeout_position) ||
!out->WriteS16(this->table.family_class)) {
return Error("Failed to write basic table data");
}
for (unsigned i = 0; i < 10; ++i) {
if (!out->Write(&os2->panose[i], 1)) {
return OTS_FAILURE_MSG("Failed to write os2 panose information");
if (!out->Write(&this->table.panose[i], 1)) {
return Error("Failed to write PANOSE data");
}
}
if (!out->WriteU32(os2->unicode_range_1) ||
!out->WriteU32(os2->unicode_range_2) ||
!out->WriteU32(os2->unicode_range_3) ||
!out->WriteU32(os2->unicode_range_4) ||
!out->WriteU32(os2->vendor_id) ||
!out->WriteU16(os2->selection) ||
!out->WriteU16(os2->first_char_index) ||
!out->WriteU16(os2->last_char_index) ||
!out->WriteS16(os2->typo_ascender) ||
!out->WriteS16(os2->typo_descender) ||
!out->WriteS16(os2->typo_linegap) ||
!out->WriteU16(os2->win_ascent) ||
!out->WriteU16(os2->win_descent)) {
return OTS_FAILURE_MSG("Failed to write version 1-specific fields");
if (!out->WriteU32(this->table.unicode_range_1) ||
!out->WriteU32(this->table.unicode_range_2) ||
!out->WriteU32(this->table.unicode_range_3) ||
!out->WriteU32(this->table.unicode_range_4) ||
!out->WriteU32(this->table.vendor_id) ||
!out->WriteU16(this->table.selection) ||
!out->WriteU16(this->table.first_char_index) ||
!out->WriteU16(this->table.last_char_index) ||
!out->WriteS16(this->table.typo_ascender) ||
!out->WriteS16(this->table.typo_descender) ||
!out->WriteS16(this->table.typo_linegap) ||
!out->WriteU16(this->table.win_ascent) ||
!out->WriteU16(this->table.win_descent)) {
return Error("Failed to write version 1-specific fields");
}
if (os2->version < 1) {
if (this->table.version < 1) {
return true;
}
if (!out->WriteU32(os2->code_page_range_1) ||
!out->WriteU32(os2->code_page_range_2)) {
return OTS_FAILURE_MSG("Failed to write codepage ranges");
if (!out->WriteU32(this->table.code_page_range_1) ||
!out->WriteU32(this->table.code_page_range_2)) {
return Error("Failed to write codepage ranges");
}
if (os2->version < 2) {
if (this->table.version < 2) {
return true;
}
if (!out->WriteS16(os2->x_height) ||
!out->WriteS16(os2->cap_height) ||
!out->WriteU16(os2->default_char) ||
!out->WriteU16(os2->break_char) ||
!out->WriteU16(os2->max_context)) {
return OTS_FAILURE_MSG("Failed to write version 2-specific fields");
if (!out->WriteS16(this->table.x_height) ||
!out->WriteS16(this->table.cap_height) ||
!out->WriteU16(this->table.default_char) ||
!out->WriteU16(this->table.break_char) ||
!out->WriteU16(this->table.max_context)) {
return Error("Failed to write version 2-specific fields");
}
if (os2->version < 5) {
if (this->table.version < 5) {
return true;
}
if (!out->WriteU16(os2->lower_optical_pointsize) ||
!out->WriteU16(os2->upper_optical_pointsize)) {
return OTS_FAILURE_MSG("Failed to write version 5-specific fields");
if (!out->WriteU16(this->table.lower_optical_pointsize) ||
!out->WriteU16(this->table.upper_optical_pointsize)) {
return Error("Failed to write version 5-specific fields");
}
return true;
}
void ots_os2_reuse(Font *font, Font *other) {
font->os2 = other->os2;
font->os2_reused = true;
}
void ots_os2_free(Font *font) {
delete font->os2;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,7 +9,7 @@
namespace ots {
struct OpenTypeOS2 {
struct OS2Data {
uint16_t version;
int16_t avg_char_width;
uint16_t weight_class;
@ -51,6 +51,17 @@ struct OpenTypeOS2 {
uint16_t upper_optical_pointsize;
};
class OpenTypeOS2 : public Table {
public:
explicit OpenTypeOS2(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
OS2Data table;
};
} // namespace ots
#endif // OTS_OS2_H_

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -14,37 +14,50 @@
#include <map>
#include <vector>
#include "woff2.h"
#include "woff2_dec.h"
// The OpenType Font File
// http://www.microsoft.com/typography/otspec/cmap.htm
namespace {
#include "cff.h"
#include "cmap.h"
#include "cvt.h"
#include "fpgm.h"
#include "gasp.h"
#include "gdef.h"
#include "glyf.h"
#include "gpos.h"
#include "gsub.h"
#include "hdmx.h"
#include "head.h"
#include "hhea.h"
#include "hmtx.h"
#include "kern.h"
#include "loca.h"
#include "ltsh.h"
#include "math_.h"
#include "maxp.h"
#include "name.h"
#include "os2.h"
#include "ots.h"
#include "post.h"
#include "prep.h"
#include "vdmx.h"
#include "vhea.h"
#include "vmtx.h"
#include "vorg.h"
// Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
#define OTS_FAILURE_MSG_HDR(msg_) OTS_FAILURE_MSG_(header, msg_)
#define OTS_WARNING_MSG_HDR(msg_) OTS_WARNING_MSG_(header, msg_)
// Graphite tables
#ifdef OTS_GRAPHITE
#include "feat.h"
#include "glat.h"
#include "gloc.h"
#include "sile.h"
#include "silf.h"
#include "sill.h"
#endif
struct OpenTypeTable {
uint32_t tag;
uint32_t chksum;
uint32_t offset;
uint32_t length;
uint32_t uncompressed_length;
};
bool CheckTag(uint32_t tag_value) {
for (unsigned i = 0; i < 4; ++i) {
const uint32_t check = tag_value & 0xff;
if (check < 32 || check > 126) {
return false; // non-ASCII character found.
}
tag_value >>= 8;
}
return true;
}
namespace ots {
struct Arena {
public:
@ -65,80 +78,83 @@ struct Arena {
std::vector<uint8_t*> hunks_;
};
}; // namespace ots
namespace {
#define OTS_MSG_TAG_(level,otf_,msg_,tag_) \
(OTS_MESSAGE_(level,otf_,"%c%c%c%c: %s", OTS_UNTAG(tag_), msg_), false)
// Generate a message with or without a table tag, when 'header' is the FontFile pointer
#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_MSG_TAG_(0, header, msg_, tag_)
#define OTS_FAILURE_MSG_HDR(...) OTS_FAILURE_MSG_(header, __VA_ARGS__)
#define OTS_WARNING_MSG_HDR(...) OTS_WARNING_MSG_(header, __VA_ARGS__)
bool CheckTag(uint32_t tag_value) {
for (unsigned i = 0; i < 4; ++i) {
const uint32_t check = tag_value & 0xff;
if (check < 32 || check > 126) {
return false; // non-ASCII character found.
}
tag_value >>= 8;
}
return true;
}
const struct {
uint32_t tag;
bool (*parse)(ots::Font *font, const uint8_t *data, size_t length);
bool (*serialise)(ots::OTSStream *out, ots::Font *font);
bool (*should_serialise)(ots::Font *font);
void (*reuse)(ots::Font *font, ots::Font *other);
bool required;
} table_parsers[] = {
{ OTS_TAG('m','a','x','p'), ots::ots_maxp_parse, ots::ots_maxp_serialise,
ots::ots_maxp_should_serialise, ots::ots_maxp_reuse, true },
{ OTS_TAG('h','e','a','d'), ots::ots_head_parse, ots::ots_head_serialise,
ots::ots_head_should_serialise, ots::ots_head_reuse, true },
{ OTS_TAG('O','S','/','2'), ots::ots_os2_parse, ots::ots_os2_serialise,
ots::ots_os2_should_serialise, ots::ots_os2_reuse, true },
{ OTS_TAG('c','m','a','p'), ots::ots_cmap_parse, ots::ots_cmap_serialise,
ots::ots_cmap_should_serialise, ots::ots_cmap_reuse, true },
{ OTS_TAG('h','h','e','a'), ots::ots_hhea_parse, ots::ots_hhea_serialise,
ots::ots_hhea_should_serialise, ots::ots_hhea_reuse, true },
{ OTS_TAG('h','m','t','x'), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
ots::ots_hmtx_should_serialise, ots::ots_hmtx_reuse, true },
{ OTS_TAG('n','a','m','e'), ots::ots_name_parse, ots::ots_name_serialise,
ots::ots_name_should_serialise, ots::ots_name_reuse, true },
{ OTS_TAG('p','o','s','t'), ots::ots_post_parse, ots::ots_post_serialise,
ots::ots_post_should_serialise, ots::ots_post_reuse, true },
{ OTS_TAG('l','o','c','a'), ots::ots_loca_parse, ots::ots_loca_serialise,
ots::ots_loca_should_serialise, ots::ots_loca_reuse, false },
{ OTS_TAG('g','l','y','f'), ots::ots_glyf_parse, ots::ots_glyf_serialise,
ots::ots_glyf_should_serialise, ots::ots_glyf_reuse, false },
{ OTS_TAG('C','F','F',' '), ots::ots_cff_parse, ots::ots_cff_serialise,
ots::ots_cff_should_serialise, ots::ots_cff_reuse, false },
{ OTS_TAG('V','D','M','X'), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
ots::ots_vdmx_should_serialise, ots::ots_vdmx_reuse, false },
{ OTS_TAG('h','d','m','x'), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
ots::ots_hdmx_should_serialise, ots::ots_hdmx_reuse, false },
{ OTS_TAG('g','a','s','p'), ots::ots_gasp_parse, ots::ots_gasp_serialise,
ots::ots_gasp_should_serialise, ots::ots_gasp_reuse, false },
{ OTS_TAG('c','v','t',' '), ots::ots_cvt_parse, ots::ots_cvt_serialise,
ots::ots_cvt_should_serialise, ots::ots_cvt_reuse, false },
{ OTS_TAG('f','p','g','m'), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
ots::ots_fpgm_should_serialise, ots::ots_fpgm_reuse, false },
{ OTS_TAG('p','r','e','p'), ots::ots_prep_parse, ots::ots_prep_serialise,
ots::ots_prep_should_serialise, ots::ots_prep_reuse, false },
{ OTS_TAG('L','T','S','H'), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
ots::ots_ltsh_should_serialise, ots::ots_ltsh_reuse, false },
{ OTS_TAG('V','O','R','G'), ots::ots_vorg_parse, ots::ots_vorg_serialise,
ots::ots_vorg_should_serialise, ots::ots_vorg_reuse, false },
{ OTS_TAG('k','e','r','n'), ots::ots_kern_parse, ots::ots_kern_serialise,
ots::ots_kern_should_serialise, ots::ots_kern_reuse, false },
} supported_tables[] = {
{ OTS_TAG_MAXP, true },
{ OTS_TAG_HEAD, true },
{ OTS_TAG_OS2, true },
{ OTS_TAG_CMAP, true },
{ OTS_TAG_HHEA, true },
{ OTS_TAG_HMTX, true },
{ OTS_TAG_NAME, true },
{ OTS_TAG_POST, true },
{ OTS_TAG_LOCA, false },
{ OTS_TAG_GLYF, false },
{ OTS_TAG_CFF, false },
{ OTS_TAG_VDMX, false },
{ OTS_TAG_HDMX, false },
{ OTS_TAG_GASP, false },
{ OTS_TAG_CVT, false },
{ OTS_TAG_FPGM, false },
{ OTS_TAG_PREP, false },
{ OTS_TAG_LTSH, false },
{ OTS_TAG_VORG, false },
{ OTS_TAG_KERN, false },
// We need to parse GDEF table in advance of parsing GSUB/GPOS tables
// because they could refer GDEF table.
{ OTS_TAG('G','D','E','F'), ots::ots_gdef_parse, ots::ots_gdef_serialise,
ots::ots_gdef_should_serialise, ots::ots_gdef_reuse, false },
{ OTS_TAG('G','P','O','S'), ots::ots_gpos_parse, ots::ots_gpos_serialise,
ots::ots_gpos_should_serialise, ots::ots_gpos_reuse, false },
{ OTS_TAG('G','S','U','B'), ots::ots_gsub_parse, ots::ots_gsub_serialise,
ots::ots_gsub_should_serialise, ots::ots_gsub_reuse, false },
{ OTS_TAG('v','h','e','a'), ots::ots_vhea_parse, ots::ots_vhea_serialise,
ots::ots_vhea_should_serialise, ots::ots_vhea_reuse, false },
{ OTS_TAG('v','m','t','x'), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
ots::ots_vmtx_should_serialise, ots::ots_vmtx_reuse, false },
{ OTS_TAG('M','A','T','H'), ots::ots_math_parse, ots::ots_math_serialise,
ots::ots_math_should_serialise, ots::ots_math_reuse, false },
{ 0, NULL, NULL, NULL, NULL, false },
{ OTS_TAG_GDEF, false },
{ OTS_TAG_GPOS, false },
{ OTS_TAG_GSUB, false },
{ 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 },
};
bool ProcessGeneric(ots::OpenTypeFile *header,
bool ProcessGeneric(ots::FontFile *header,
ots::Font *font,
uint32_t signature,
ots::OTSStream *output,
const uint8_t *data, size_t length,
const std::vector<OpenTypeTable>& tables,
const std::vector<ots::TableEntry>& tables,
ots::Buffer& file);
bool ProcessTTF(ots::OpenTypeFile *header,
bool ProcessTTF(ots::FontFile *header,
ots::Font *font,
ots::OTSStream *output, const uint8_t *data, size_t length,
uint32_t offset = 0) {
@ -202,10 +218,10 @@ bool ProcessTTF(ots::OpenTypeFile *header,
}
// Next up is the list of tables.
std::vector<OpenTypeTable> tables;
std::vector<ots::TableEntry> tables;
for (unsigned i = 0; i < font->num_tables; ++i) {
OpenTypeTable table;
ots::TableEntry table;
if (!file.ReadU32(&table.tag) ||
!file.ReadU32(&table.chksum) ||
!file.ReadU32(&table.offset) ||
@ -221,7 +237,7 @@ bool ProcessTTF(ots::OpenTypeFile *header,
tables, file);
}
bool ProcessTTC(ots::OpenTypeFile *header,
bool ProcessTTC(ots::FontFile *header,
ots::OTSStream *output,
const uint8_t *data,
size_t length,
@ -308,7 +324,7 @@ bool ProcessTTC(ots::OpenTypeFile *header,
}
}
bool ProcessWOFF(ots::OpenTypeFile *header,
bool ProcessWOFF(ots::FontFile *header,
ots::Font *font,
ots::OTSStream *output, const uint8_t *data, size_t length) {
ots::Buffer file(data, length);
@ -388,14 +404,14 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
}
// Next up is the list of tables.
std::vector<OpenTypeTable> tables;
std::vector<ots::TableEntry> tables;
uint32_t first_index = 0;
uint32_t last_index = 0;
// Size of sfnt header plus size of table records.
uint64_t total_sfnt_size = 12 + 16 * font->num_tables;
for (unsigned i = 0; i < font->num_tables; ++i) {
OpenTypeTable table;
ots::TableEntry table;
if (!file.ReadU32(&table.tag) ||
!file.ReadU32(&table.offset) ||
!file.ReadU32(&table.length) ||
@ -463,10 +479,16 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
return ProcessGeneric(header, font, woff_tag, output, data, length, tables, file);
}
bool ProcessWOFF2(ots::OpenTypeFile *header,
ots::Font *font,
ots::OTSStream *output, const uint8_t *data, size_t length) {
size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length);
bool ProcessWOFF2(ots::FontFile *header,
ots::OTSStream *output,
const uint8_t *data,
size_t length,
uint32_t index) {
size_t decompressed_size = woff2::ComputeWOFF2FinalSize(data, length);
if (decompressed_size < length) {
return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is less than compressed size");
}
if (decompressed_size == 0) {
return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0");
@ -476,24 +498,31 @@ bool ProcessWOFF2(ots::OpenTypeFile *header,
return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 font exceeds 30MB");
}
std::vector<uint8_t> decompressed_buffer(decompressed_size);
if (!ots::ConvertWOFF2ToSFNT(font, &decompressed_buffer[0], decompressed_size,
data, length)) {
std::string buf(decompressed_size, 0);
woff2::WOFF2StringOut out(&buf);
if (!woff2::ConvertWOFF2ToTTF(data, length, &out)) {
return OTS_FAILURE_MSG_HDR("Failed to convert WOFF 2.0 font to SFNT");
}
return ProcessTTF(header, font, output, &decompressed_buffer[0], decompressed_size);
const uint8_t *decompressed = reinterpret_cast<const uint8_t*>(buf.data());
if (data[4] == 't' && data[5] == 't' && data[6] == 'c' && data[7] == 'f') {
return ProcessTTC(header, output, decompressed, out.Size(), index);
} else {
ots::Font font(header);
return ProcessTTF(header, &font, output, decompressed, out.Size());
}
}
ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
ots::TableAction GetTableAction(const ots::FontFile *header, uint32_t tag) {
ots::TableAction action = header->context->GetTableAction(tag);
if (action == ots::TABLE_ACTION_DEFAULT) {
action = ots::TABLE_ACTION_DROP;
for (unsigned i = 0; ; ++i) {
if (table_parsers[i].parse == NULL) break;
if (supported_tables[i].tag == 0) break;
if (table_parsers[i].tag == tag) {
if (supported_tables[i].tag == tag) {
action = ots::TABLE_ACTION_SANITIZE;
break;
}
@ -505,14 +534,14 @@ ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
}
bool GetTableData(const uint8_t *data,
const OpenTypeTable& table,
Arena *arena,
const ots::TableEntry& table,
ots::Arena &arena,
size_t *table_length,
const uint8_t **table_data) {
if (table.uncompressed_length != table.length) {
// Compressed table. Need to uncompress into memory first.
*table_length = table.uncompressed_length;
*table_data = (*arena).Allocate(*table_length);
*table_data = arena.Allocate(*table_length);
uLongf dest_len = *table_length;
int r = uncompress((Bytef*) *table_data, &dest_len,
data + table.offset, table.length);
@ -528,12 +557,12 @@ bool GetTableData(const uint8_t *data,
return true;
}
bool ProcessGeneric(ots::OpenTypeFile *header,
bool ProcessGeneric(ots::FontFile *header,
ots::Font *font,
uint32_t signature,
ots::OTSStream *output,
const uint8_t *data, size_t length,
const std::vector<OpenTypeTable>& tables,
const std::vector<ots::TableEntry>& tables,
ots::Buffer& file) {
const size_t data_offset = file.offset();
@ -552,7 +581,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
// all tag names must be built from printable ASCII characters
if (!CheckTag(tables[i].tag)) {
return OTS_FAILURE_MSG_TAG("invalid table tag", tables[i].tag);
OTS_WARNING_MSG_HDR("Invalid table tag: 0x%X", tables[i].tag);
}
// tables must be 4-byte aligned
@ -607,11 +636,6 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
}
std::map<uint32_t, OpenTypeTable> table_map;
for (unsigned i = 0; i < font->num_tables; ++i) {
table_map[tables[i].tag] = tables[i];
}
// check that the tables are not overlapping.
std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
for (unsigned i = 0; i < font->num_tables; ++i) {
@ -630,80 +654,66 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
}
}
Arena arena;
std::map<uint32_t, ots::TableEntry> table_map;
for (unsigned i = 0; i < font->num_tables; ++i) {
table_map[tables[i].tag] = tables[i];
}
ots::Arena arena;
// Parse known tables first as we need to parse them in specific order.
for (unsigned i = 0; ; ++i) {
if (table_parsers[i].parse == NULL) break;
if (supported_tables[i].tag == 0) break;
uint32_t tag = table_parsers[i].tag;
const std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.find(tag);
ots::TableAction action = GetTableAction(header, tag);
if (it == table_map.end()) {
if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
uint32_t tag = supported_tables[i].tag;
const auto &it = table_map.find(tag);
if (it == table_map.cend()) {
if (supported_tables[i].required) {
return OTS_FAILURE_MSG_TAG("missing required table", tag);
}
continue;
}
uint32_t input_offset = it->second.offset;
const ots::TableMap::const_iterator ot = header->tables.find(input_offset);
if (ot == header->tables.end()) {
const uint8_t* table_data;
size_t table_length;
if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag);
} else {
if (!font->ParseTable(it->second, data, arena)) {
return OTS_FAILURE_MSG_TAG("Failed to parse table", tag);
}
if (action == ots::TABLE_ACTION_SANITIZE &&
!table_parsers[i].parse(font, table_data, table_length)) {
return OTS_FAILURE();
}
} else if (action == ots::TABLE_ACTION_SANITIZE) {
table_parsers[i].reuse(font, ot->second.first);
}
}
if (font->cff) {
// Then parse any tables left.
for (const auto &table_entry : tables) {
if (!font->GetTable(table_entry.tag)) {
if (!font->ParseTable(table_entry, data, arena)) {
return OTS_FAILURE_MSG_TAG("Failed to parse table", table_entry.tag);
}
}
}
if (font->GetTable(OTS_TAG_CFF)) {
// font with PostScript glyph
if (font->version != OTS_TAG('O','T','T','O')) {
return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
}
if (font->glyf || font->loca) {
if (font->GetTable(OTS_TAG_GLYF) || font->GetTable(OTS_TAG_LOCA)) {
// mixing outline formats is not recommended
return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
}
} else {
if (!font->glyf || !font->loca) {
if (!font->GetTable(OTS_TAG_GLYF) || !font->GetTable(OTS_TAG_LOCA)) {
// No TrueType glyph found.
#define PASSTHRU_TABLE(tag_) (table_map.find(tag_) != table_map.end() && \
GetTableAction(header, tag_) == ots::TABLE_ACTION_PASSTHRU)
// We don't sanitise bitmap table, but don't reject bitmap-only fonts if
// we keep the tables.
if (!PASSTHRU_TABLE(OTS_TAG('C','B','D','T')) ||
!PASSTHRU_TABLE(OTS_TAG('C','B','L','C'))) {
//
// We don't sanitize bitmap tables, but dont reject bitmap-only fonts if
// we are asked to pass them thru.
// Also dont reject if we are asked to pass glyf/loca thru.
if (!font->GetTable(OTS_TAG('C','B','D','T')) &&
!font->GetTable(OTS_TAG('C','B','L','C'))) {
return OTS_FAILURE_MSG_HDR("no supported glyph shapes table(s) present");
}
#undef PASSTHRU_TABLE
}
}
uint16_t num_output_tables = 0;
for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
it != table_map.end(); ++it) {
ots::TableAction action = GetTableAction(header, it->first);
if (action == ots::TABLE_ACTION_PASSTHRU) {
for (const auto &it : table_map) {
ots::Table *table = font->GetTable(it.first);
if (table != NULL && table->ShouldSerialize())
num_output_tables++;
} else {
for (unsigned i = 0; table_parsers[i].parse != NULL; ++i) {
if (table_parsers[i].tag == it->first &&
table_parsers[i].should_serialise(font)) {
num_output_tables++;
break;
}
}
}
}
uint16_t max_pow2 = 0;
@ -729,85 +739,49 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
return OTS_FAILURE_MSG_HDR("error writing output");
}
std::vector<ots::OutputTable> out_tables;
std::vector<ots::TableEntry> out_tables;
size_t head_table_offset = 0;
for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
it != table_map.end(); ++it) {
uint32_t input_offset = it->second.offset;
const ots::TableMap::const_iterator ot = header->tables.find(input_offset);
if (ot != header->tables.end()) {
ots::OutputTable out = ot->second.second;
for (const auto &it : table_map) {
uint32_t input_offset = it.second.offset;
const auto &ot = header->table_entries.find(input_offset);
if (ot != header->table_entries.end()) {
ots::TableEntry out = ot->second;
if (out.tag == OTS_TAG('h','e','a','d')) {
head_table_offset = out.offset;
}
out_tables.push_back(out);
} else {
ots::OutputTable out;
out.tag = it->first;
ots::TableEntry out;
out.tag = it.first;
out.offset = output->Tell();
if (out.tag == OTS_TAG('h','e','a','d')) {
head_table_offset = out.offset;
}
ots::TableAction action = GetTableAction(header, it->first);
if (action == ots::TABLE_ACTION_PASSTHRU) {
ots::Table *table = font->GetTable(out.tag);
if (table != NULL && table->ShouldSerialize()) {
output->ResetChecksum();
const uint8_t* table_data;
size_t table_length;
if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
return OTS_FAILURE_MSG_HDR("Failed to uncompress table");
}
if (!output->Write(table_data, table_length)) {
return OTS_FAILURE_MSG_HDR("Failed to serialize table");
if (!table->Serialize(output)) {
return OTS_FAILURE_MSG_TAG("Failed to serialize table", out.tag);
}
const size_t end_offset = output->Tell();
if (end_offset <= out.offset) {
// paranoid check. |end_offset| is supposed to be greater than the offset,
// as long as the Tell() interface is implemented correctly.
return OTS_FAILURE_MSG_HDR("error writing output");
return OTS_FAILURE_MSG_TAG("Table is empty or have -ve size", out.tag);
}
out.length = end_offset - out.offset;
// align tables to four bytes
if (!output->Pad((4 - (end_offset & 3)) % 4)) {
return OTS_FAILURE_MSG_HDR("error writing output");
return OTS_FAILURE_MSG_TAG("Failed to pad table to 4 bytes", out.tag);
}
out.chksum = output->chksum();
out_tables.push_back(out);
header->tables[input_offset] = std::make_pair(font, out);
} else {
for (unsigned i = 0; table_parsers[i].parse != NULL; ++i) {
if (table_parsers[i].tag == it->first &&
table_parsers[i].should_serialise(font)) {
output->ResetChecksum();
if (!table_parsers[i].serialise(output, font)) {
return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag);
}
const size_t end_offset = output->Tell();
if (end_offset <= out.offset) {
// paranoid check. |end_offset| is supposed to be greater than the offset,
// as long as the Tell() interface is implemented correctly.
return OTS_FAILURE_MSG_HDR("error writing output");
}
out.length = end_offset - out.offset;
// align tables to four bytes
if (!output->Pad((4 - (end_offset & 3)) % 4)) {
return OTS_FAILURE_MSG_HDR("error writing output");
}
out.chksum = output->chksum();
out_tables.push_back(out);
header->tables[input_offset] = std::make_pair(font, out);
break;
}
}
header->table_entries[input_offset] = out;
}
}
}
@ -860,6 +834,189 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
namespace ots {
FontFile::~FontFile() {
for (const auto& it : tables) {
delete it.second;
}
tables.clear();
}
bool Font::ParseTable(const TableEntry& table_entry, const uint8_t* data,
Arena &arena) {
uint32_t tag = table_entry.tag;
TableAction action = GetTableAction(file, tag);
if (action == TABLE_ACTION_DROP) {
return true;
}
const auto &it = file->tables.find(table_entry);
if (it != file->tables.end()) {
m_tables[tag] = it->second;
return true;
}
Table *table = NULL;
bool ret = false;
if (action == TABLE_ACTION_PASSTHRU) {
table = new TablePassthru(this, tag);
} else {
switch (tag) {
case OTS_TAG_CFF: table = new OpenTypeCFF(this, tag); break;
case OTS_TAG_CMAP: table = new OpenTypeCMAP(this, tag); break;
case OTS_TAG_CVT: table = new OpenTypeCVT(this, tag); break;
case OTS_TAG_FPGM: table = new OpenTypeFPGM(this, tag); break;
case OTS_TAG_GASP: table = new OpenTypeGASP(this, tag); break;
case OTS_TAG_GDEF: table = new OpenTypeGDEF(this, tag); break;
case OTS_TAG_GLYF: table = new OpenTypeGLYF(this, tag); break;
case OTS_TAG_GPOS: table = new OpenTypeGPOS(this, tag); break;
case OTS_TAG_GSUB: table = new OpenTypeGSUB(this, tag); break;
case OTS_TAG_HDMX: table = new OpenTypeHDMX(this, tag); break;
case OTS_TAG_HEAD: table = new OpenTypeHEAD(this, tag); break;
case OTS_TAG_HHEA: table = new OpenTypeHHEA(this, tag); break;
case OTS_TAG_HMTX: table = new OpenTypeHMTX(this, tag); break;
case OTS_TAG_KERN: table = new OpenTypeKERN(this, tag); break;
case OTS_TAG_LOCA: table = new OpenTypeLOCA(this, tag); break;
case OTS_TAG_LTSH: table = new OpenTypeLTSH(this, tag); break;
case OTS_TAG_MATH: table = new OpenTypeMATH(this, tag); break;
case OTS_TAG_MAXP: table = new OpenTypeMAXP(this, tag); break;
case OTS_TAG_NAME: table = new OpenTypeNAME(this, tag); break;
case OTS_TAG_OS2: table = new OpenTypeOS2(this, tag); break;
case OTS_TAG_POST: table = new OpenTypePOST(this, tag); break;
case OTS_TAG_PREP: table = new OpenTypePREP(this, tag); break;
case OTS_TAG_VDMX: table = new OpenTypeVDMX(this, tag); break;
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
case OTS_TAG_VHEA: table = new OpenTypeVHEA(this, tag); break;
case OTS_TAG_VMTX: table = new OpenTypeVMTX(this, tag); break;
// 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;
}
}
if (table) {
const uint8_t* table_data;
size_t table_length;
ret = GetTableData(data, table_entry, arena, &table_length, &table_data);
if (ret) {
// FIXME: Parsing some tables will fail if the table is not added to
// m_tables first.
m_tables[tag] = table;
ret = table->Parse(table_data, table_length);
if (ret)
file->tables[table_entry] = table;
else
m_tables.erase(tag);
}
}
if (!ret)
delete table;
return ret;
}
Table* Font::GetTable(uint32_t tag) const {
const auto &it = m_tables.find(tag);
if (it != m_tables.end())
return it->second;
return NULL;
}
Table* Font::GetTypedTable(uint32_t tag) const {
Table* t = GetTable(tag);
if (t && t->Type() == tag)
return t;
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;
}
void Table::Message(int level, const char *format, va_list va) {
char msg[206] = { OTS_UNTAG(m_tag), ':', ' ' };
std::vsnprintf(msg + 6, 200, format, va);
m_font->file->context->Message(level, msg);
}
bool Table::Error(const char *format, ...) {
va_list va;
va_start(va, format);
Message(0, format, va);
va_end(va);
return false;
}
bool Table::Warning(const char *format, ...) {
va_list va;
va_start(va, format);
Message(1, format, va);
va_end(va);
return true;
}
bool Table::Drop(const char *format, ...) {
m_shouldSerialize = false;
va_list va;
va_start(va, format);
Message(0, format, va);
m_font->file->context->Message(0, "Table discarded");
va_end(va);
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;
return true;
}
bool TablePassthru::Serialize(OTSStream *out) {
if (!out->Write(m_data, m_length)) {
return Error("Failed to write table");
}
return true;
}
bool IsValidVersionTag(uint32_t tag) {
return tag == 0x000010000 ||
// OpenType fonts with CFF data have 'OTTO' tag.
@ -873,7 +1030,7 @@ bool OTSContext::Process(OTSStream *output,
const uint8_t *data,
size_t length,
uint32_t index) {
OpenTypeFile header;
FontFile header;
Font font(&header);
header.context = this;
@ -885,7 +1042,7 @@ bool OTSContext::Process(OTSStream *output,
if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
result = ProcessWOFF(&header, &font, output, data, length);
} else if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') {
result = ProcessWOFF2(&header, &font, output, data, length);
result = ProcessWOFF2(&header, output, data, length, index);
} else if (data[0] == 't' && data[1] == 't' && data[2] == 'c' && data[3] == 'f') {
result = ProcessTTC(&header, output, data, length, index);
} else {

View File

@ -1,10 +1,14 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stddef.h>
#include <cstdarg>
#include <cstddef>
@ -54,13 +58,9 @@ namespace ots {
#define OTS_WARNING_MSG_(otf_,...) \
OTS_MESSAGE_(1,otf_,__VA_ARGS__)
// Generate a message with an associated table tag
#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
(OTS_MESSAGE_(0,otf_,"%c%c%c%c: %s", OTS_UNTAG(tag_), msg_), false)
// Convenience macros for use in files that only handle a single table tag,
// defined as TABLE_NAME at the top of the file; the 'file' variable is
// expected to be the current OpenTypeFile pointer.
// expected to be the current FontFile pointer.
#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)
#define OTS_WARNING(...) OTS_WARNING_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)
@ -158,6 +158,7 @@ class Buffer {
const uint8_t *buffer() const { return buffer_; }
size_t offset() const { return offset_; }
size_t length() const { return length_; }
size_t remaining() const { return length_ - offset_; }
void set_offset(size_t newoffset) { offset_ = newoffset; }
@ -185,109 +186,154 @@ template<typename T> T Round2(T value) {
bool IsValidVersionTag(uint32_t tag);
#define FOR_EACH_TABLE_TYPE \
F(cff, CFF) \
F(cmap, CMAP) \
F(cvt, CVT) \
F(fpgm, FPGM) \
F(gasp, GASP) \
F(gdef, GDEF) \
F(glyf, GLYF) \
F(gpos, GPOS) \
F(gsub, GSUB) \
F(hdmx, HDMX) \
F(head, HEAD) \
F(hhea, HHEA) \
F(hmtx, HMTX) \
F(kern, KERN) \
F(loca, LOCA) \
F(ltsh, LTSH) \
F(math, MATH) \
F(maxp, MAXP) \
F(name, NAME) \
F(os2, OS2) \
F(post, POST) \
F(prep, PREP) \
F(vdmx, VDMX) \
F(vorg, VORG) \
F(vhea, VHEA) \
F(vmtx, VMTX)
#define F(name, capname) struct OpenType##capname;
FOR_EACH_TABLE_TYPE
#undef F
#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')
#define OTS_TAG_HDMX OTS_TAG('h','d','m','x')
#define OTS_TAG_HEAD OTS_TAG('h','e','a','d')
#define OTS_TAG_HHEA OTS_TAG('h','h','e','a')
#define OTS_TAG_HMTX OTS_TAG('h','m','t','x')
#define OTS_TAG_KERN OTS_TAG('k','e','r','n')
#define OTS_TAG_LOCA OTS_TAG('l','o','c','a')
#define OTS_TAG_LTSH OTS_TAG('L','T','S','H')
#define OTS_TAG_MATH OTS_TAG('M','A','T','H')
#define OTS_TAG_MAXP OTS_TAG('m','a','x','p')
#define OTS_TAG_NAME OTS_TAG('n','a','m','e')
#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')
#define OTS_TAG_VORG OTS_TAG('V','O','R','G')
struct Font;
struct OpenTypeFile;
struct FontFile;
struct TableEntry;
struct Arena;
#define F(name, capname) \
bool ots_##name##_parse(Font *f, const uint8_t *d, size_t l); \
bool ots_##name##_should_serialise(Font *f); \
bool ots_##name##_serialise(OTSStream *s, Font *f); \
void ots_##name##_reuse(Font *f, Font *o);\
void ots_##name##_free(Font *f);
FOR_EACH_TABLE_TYPE
#undef F
class Table {
public:
explicit Table(Font *font, uint32_t tag, uint32_t type)
: m_tag(tag),
m_type(type),
m_font(font),
m_shouldSerialize(true) {
}
virtual ~Table() { }
virtual bool Parse(const uint8_t *data, size_t length) = 0;
virtual bool Serialize(OTSStream *out) = 0;
virtual bool ShouldSerialize();
// Return the tag (table type) this Table was parsed as, to support
// "poor man's RTTI" so that we know if we can safely down-cast to
// a specific Table subclass. The m_type field is initialized to the
// appropriate tag when a subclass is constructed, or to zero for
// TablePassthru (indicating unparsed data).
uint32_t Type() { return m_type; }
Font* GetFont() { return m_font; }
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);
uint32_t m_tag;
uint32_t m_type;
Font *m_font;
bool m_shouldSerialize;
};
class TablePassthru : public Table {
public:
explicit TablePassthru(Font *font, uint32_t tag)
: Table(font, tag, 0),
m_data(NULL),
m_length(0) {
}
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
private:
const uint8_t *m_data;
size_t m_length;
};
struct Font {
explicit Font(const OpenTypeFile *f)
explicit Font(FontFile *f)
: file(f),
version(0),
num_tables(0),
search_range(0),
entry_selector(0),
range_shift(0) {
#define F(name, capname) \
name = NULL; \
name##_reused = false;
FOR_EACH_TABLE_TYPE
#undef F
range_shift(0),
dropped_graphite(false) {
}
~Font() {
#define F(name, capname) \
if (!name##_reused) {\
ots_##name##_free(this); \
}
FOR_EACH_TABLE_TYPE
#undef F
}
bool ParseTable(const TableEntry& tableinfo, const uint8_t* data,
Arena &arena);
Table* GetTable(uint32_t tag) const;
const OpenTypeFile *file;
// This checks that the returned Table is actually of the correct subclass
// for |tag|, so it can safely be downcast to the corresponding OpenTypeXXXX;
// 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;
uint16_t num_tables;
uint16_t search_range;
uint16_t entry_selector;
uint16_t range_shift;
bool dropped_graphite;
#define F(name, capname) \
OpenType##capname *name; \
bool name##_reused;
FOR_EACH_TABLE_TYPE
#undef F
private:
std::map<uint32_t, Table*> m_tables;
};
struct OutputTable {
struct TableEntry {
uint32_t tag;
size_t offset;
size_t length;
uint32_t offset;
uint32_t length;
uint32_t uncompressed_length;
uint32_t chksum;
bool operator<(const OutputTable& other) const {
bool operator<(const TableEntry& other) const {
return tag < other.tag;
}
};
typedef std::map<uint32_t, std::pair<Font*, OutputTable> > TableMap;
struct FontFile {
~FontFile();
struct OpenTypeFile {
OTSContext *context;
TableMap tables;
std::map<TableEntry, Table*> tables;
std::map<uint32_t, TableEntry> table_entries;
};
} // namespace ots
#undef FOR_EACH_TABLE_TYPE
#endif // OTS_H_

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -6,80 +6,76 @@
#include "maxp.h"
#include "mozilla-config.h"
#include "plvmx.h"
// post - PostScript
// http://www.microsoft.com/typography/otspec/post.htm
#define TABLE_NAME "post"
namespace ots {
bool ots_post_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypePOST::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypePOST *post = new OpenTypePOST;
font->post = post;
if (!table.ReadU32(&post->version) ||
!table.ReadU32(&post->italic_angle) ||
!table.ReadS16(&post->underline) ||
!table.ReadS16(&post->underline_thickness) ||
!table.ReadU32(&post->is_fixed_pitch)) {
return OTS_FAILURE_MSG("Failed to read post header");
if (!table.ReadU32(&this->version)) {
return Error("Failed to read table version");
}
if (post->underline_thickness < 0) {
post->underline_thickness = 1;
}
if (post->version == 0x00010000) {
return true;
} else if (post->version == 0x00030000) {
return true;
} else if (post->version != 0x00020000) {
if (this->version != 0x00010000 &&
this->version != 0x00020000 &&
this->version != 0x00030000) {
// 0x00025000 is deprecated. We don't accept it.
return OTS_FAILURE_MSG("Bad post version %x", post->version);
return Error("Unsupported table version 0x%x", this->version);
}
if (!table.ReadU32(&this->italic_angle) ||
!table.ReadS16(&this->underline) ||
!table.ReadS16(&this->underline_thickness) ||
!table.ReadU32(&this->is_fixed_pitch) ||
// We don't care about the memory usage fields. We'll set all these to
// zero when serialising
!table.Skip(16)) {
return Error("Failed to read table header");
}
if (this->underline_thickness < 0) {
this->underline_thickness = 1;
}
if (this->version == 0x00010000 || this->version == 0x00030000) {
return true;
}
// We have a version 2 table with a list of Pascal strings at the end
// We don't care about the memory usage fields. We'll set all these to zero
// when serialising
if (!table.Skip(16)) {
return OTS_FAILURE_MSG("Failed to skip memory usage in post table");
}
uint16_t num_glyphs = 0;
if (!table.ReadU16(&num_glyphs)) {
return OTS_FAILURE_MSG("Failed to read number of glyphs");
return Error("Failed to read numberOfGlyphs");
}
if (!font->maxp) {
return OTS_FAILURE_MSG("No maxp table required by post table");
OpenTypeMAXP* maxp = static_cast<OpenTypeMAXP*>
(GetFont()->GetTable(OTS_TAG_MAXP));
if (!maxp) {
return Error("Missing required maxp table");
}
if (num_glyphs == 0) {
if (font->maxp->num_glyphs > 258) {
return OTS_FAILURE_MSG("Can't have no glyphs in the post table if there are more than 256 glyphs in the font");
if (maxp->num_glyphs > 258) {
return Error("Can't have no glyphs in the post table if there are more "
"than 258 glyphs in the font");
}
OTS_WARNING("table version is 1, but no glyf names are found");
// workaround for fonts in http://www.fontsquirrel.com/fontface
// (e.g., yataghan.ttf).
post->version = 0x00010000;
return true;
this->version = 0x00010000;
return Warning("Table version is 1, but no glyph names are found");
}
if (num_glyphs != font->maxp->num_glyphs) {
if (num_glyphs != maxp->num_glyphs) {
// Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
return OTS_FAILURE_MSG("Bad number of glyphs in post table %d", num_glyphs);
return Error("Bad number of glyphs: %d", num_glyphs);
}
post->glyph_name_index.resize(num_glyphs);
this->glyph_name_index.resize(num_glyphs);
for (unsigned i = 0; i < num_glyphs; ++i) {
if (!table.ReadU16(&post->glyph_name_index[i])) {
return OTS_FAILURE_MSG("Failed to read post information for glyph %d", i);
if (!table.ReadU16(&this->glyph_name_index[i])) {
return Error("Failed to read glyph name %d", i);
}
// Note: A strict interpretation of the specification requires name indexes
// are less than 32768. This, however, excludes fonts like unifont.ttf
@ -96,101 +92,85 @@ bool ots_post_parse(Font *font, const uint8_t *data, size_t length) {
if (strings == strings_end) break;
const unsigned string_length = *strings;
if (strings + 1 + string_length > strings_end) {
return OTS_FAILURE_MSG("Bad string length %d", string_length);
return Error("Bad string length %d", string_length);
}
if (VMX_HASCHR(strings + 1, '\0', string_length)) {
return OTS_FAILURE_MSG("Bad string of length %d", string_length);
if (std::memchr(strings + 1, '\0', string_length)) {
return Error("Bad string of length %d", string_length);
}
post->names.push_back(
this->names.push_back(
std::string(reinterpret_cast<const char*>(strings + 1), string_length));
strings += 1 + string_length;
}
const unsigned num_strings = post->names.size();
const unsigned num_strings = this->names.size();
// check that all the references are within bounds
for (unsigned i = 0; i < num_glyphs; ++i) {
unsigned offset = post->glyph_name_index[i];
unsigned offset = this->glyph_name_index[i];
if (offset < 258) {
continue;
}
offset -= 258;
if (offset >= num_strings) {
return OTS_FAILURE_MSG("Bad string index %d", offset);
return Error("Bad string index %d", offset);
}
}
return true;
}
bool ots_post_should_serialise(Font *font) {
return font->post != NULL;
}
bool ots_post_serialise(OTSStream *out, Font *font) {
const OpenTypePOST *post = font->post;
bool OpenTypePOST::Serialize(OTSStream *out) {
// OpenType with CFF glyphs must have v3 post table.
if (font->post && font->cff && font->post->version != 0x00030000) {
return OTS_FAILURE_MSG("Bad post version %x", post->version);
if (GetFont()->GetTable(OTS_TAG_CFF) && this->version != 0x00030000) {
return Error("Only version supported for fonts with CFF table is 0x00030000"
" not 0x%x", this->version);
}
if (!out->WriteU32(post->version) ||
!out->WriteU32(post->italic_angle) ||
!out->WriteS16(post->underline) ||
!out->WriteS16(post->underline_thickness) ||
!out->WriteU32(post->is_fixed_pitch) ||
if (!out->WriteU32(this->version) ||
!out->WriteU32(this->italic_angle) ||
!out->WriteS16(this->underline) ||
!out->WriteS16(this->underline_thickness) ||
!out->WriteU32(this->is_fixed_pitch) ||
!out->WriteU32(0) ||
!out->WriteU32(0) ||
!out->WriteU32(0) ||
!out->WriteU32(0)) {
return OTS_FAILURE_MSG("Failed to write post header");
return Error("Failed to write post header");
}
if (post->version != 0x00020000) {
if (this->version != 0x00020000) {
return true; // v1.0 and v3.0 does not have glyph names.
}
const uint16_t num_indexes =
static_cast<uint16_t>(post->glyph_name_index.size());
if (num_indexes != post->glyph_name_index.size() ||
static_cast<uint16_t>(this->glyph_name_index.size());
if (num_indexes != this->glyph_name_index.size() ||
!out->WriteU16(num_indexes)) {
return OTS_FAILURE_MSG("Failed to write number of indices");
return Error("Failed to write number of indices");
}
for (uint16_t i = 0; i < num_indexes; ++i) {
if (!out->WriteU16(post->glyph_name_index[i])) {
return OTS_FAILURE_MSG("Failed to write name index %d", i);
if (!out->WriteU16(this->glyph_name_index[i])) {
return Error("Failed to write name index %d", i);
}
}
// Now we just have to write out the strings in the correct order
for (unsigned i = 0; i < post->names.size(); ++i) {
const std::string& s = post->names[i];
for (unsigned i = 0; i < this->names.size(); ++i) {
const std::string& s = this->names[i];
const uint8_t string_length = static_cast<uint8_t>(s.size());
if (string_length != s.size() ||
!out->Write(&string_length, 1)) {
return OTS_FAILURE_MSG("Failed to write string %d", i);
return Error("Failed to write string %d", i);
}
// Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
// We allow them.
if (string_length > 0 && !out->Write(s.data(), string_length)) {
return OTS_FAILURE_MSG("Failed to write string length for string %d", i);
return Error("Failed to write string length for string %d", i);
}
}
return true;
}
void ots_post_reuse(Font *font, Font *other) {
font->post = other->post;
font->post_reused = true;
}
void ots_post_free(Font *font) {
delete font->post;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -13,7 +13,15 @@
namespace ots {
struct OpenTypePOST {
class OpenTypePOST : public Table {
public:
explicit OpenTypePOST(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 italic_angle;
int16_t underline;

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -7,53 +7,37 @@
// prep - Control Value Program
// http://www.microsoft.com/typography/otspec/prep.htm
#define TABLE_NAME "prep"
namespace ots {
bool ots_prep_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypePREP::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypePREP *prep = new OpenTypePREP;
font->prep = prep;
if (length >= 128 * 1024u) {
return OTS_FAILURE_MSG("table length %ld > 120K", length); // almost all prep tables are less than 9k bytes.
// almost all prep tables are less than 9k bytes.
return Error("Table length %ld > 120K", length);
}
if (!table.Skip(length)) {
return OTS_FAILURE_MSG("Failed to read table of length %ld", length);
return Error("Failed to read table of length %ld", length);
}
prep->data = data;
prep->length = length;
this->m_data = data;
this->m_length = length;
return true;
}
bool ots_prep_should_serialise(Font *font) {
if (!font->glyf) return false; // this table is not for CFF fonts.
return font->prep != NULL;
}
bool ots_prep_serialise(OTSStream *out, Font *font) {
const OpenTypePREP *prep = font->prep;
if (!out->Write(prep->data, prep->length)) {
return OTS_FAILURE_MSG("Failed to write table length");
bool OpenTypePREP::Serialize(OTSStream *out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write table length");
}
return true;
}
void ots_prep_reuse(Font *font, Font *other) {
font->prep = other->prep;
font->prep_reused = true;
}
void ots_prep_free(Font *font) {
delete font->prep;
bool OpenTypePREP::ShouldSerialize() {
return Table::ShouldSerialize() &&
// this table is not for CFF fonts.
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,9 +9,18 @@
namespace ots {
struct OpenTypePREP {
const uint8_t *data;
uint32_t length;
class OpenTypePREP : public Table {
public:
explicit OpenTypePREP(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
const uint8_t *m_data;
uint32_t m_length;
};
} // namespace ots

74
gfx/ots/src/sile.cc Normal file
View File

@ -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

36
gfx/ots/src/sile.h Normal file
View File

@ -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_

978
gfx/ots/src/silf.cc Normal file
View File

@ -0,0 +1,978 @@
// 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);
size_t outputSize = 0;
bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()),
table.remaining(), // input buffer size (input size + padding)
reinterpret_cast<char*>(decompressed.data()),
decompressed.size(), // target output size
&outputSize); // return output size
if (!ret || outputSize != decompressed.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)) {
return parent->Error("SILSub: Failed to read lbGID");
}
if (this->lbGID > this->maxGlyphID) {
parent->Warning("SILSub: lbGID %u outside range 0..%u, replaced with 0",
this->lbGID, this->maxGlyphID);
this->lbGID = 0;
}
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");
}
// The following three fields are deprecated and ignored. We fix them up here
// just for internal consistency, but the Graphite engine doesn't care.
if (!table.ReadU16(&this->searchPseudo) ||
!table.ReadU16(&this->pseudoSelector) ||
!table.ReadU16(&this->pseudoShift)) {
return parent->Error("SILSub: Failed to read searchPseudo..pseudoShift");
}
if (this->numPseudo == 0) {
if (this->searchPseudo != 0 || this->pseudoSelector != 0 || this->pseudoShift != 0) {
this->searchPseudo = this->pseudoSelector = this->pseudoShift = 0;
}
} else {
unsigned floorLog2 = std::floor(std::log2(this->numPseudo));
if (this->searchPseudo != 6 * (unsigned)std::pow(2, floorLog2) ||
this->pseudoSelector != floorLog2 ||
this->pseudoShift != 6 * this->numPseudo - this->searchPseudo) {
this->searchPseudo = 6 * (unsigned)std::pow(2, floorLog2);
this->pseudoSelector = floorLog2;
this->pseudoShift = 6 * this->numPseudo - this->searchPseudo;
}
}
//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) ||
!table.ReadU16(&this->entrySelector) ||
!table.ReadU16(&this->rangeShift)) {
return parent->Error("LookupClass: Failed to read searchRange..rangeShift");
}
if (this->numIDs == 0) {
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
parent->Warning("LookupClass: Correcting binary-search header for zero-length LookupPair list");
this->searchRange = this->entrySelector = this->rangeShift = 0;
}
} else {
unsigned floorLog2 = std::floor(std::log2(this->numIDs));
if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
this->entrySelector != floorLog2 ||
this->rangeShift != this->numIDs - this->searchRange) {
parent->Warning("LookupClass: Correcting binary-search header for LookupPair list");
this->searchRange = (unsigned)std::pow(2, floorLog2);
this->entrySelector = floorLog2;
this->rangeShift = this->numIDs - this->searchRange;
}
}
//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");
}
// The following three fields are deprecated and ignored. We fix them up here
// just for internal consistency, but the Graphite engine doesn't care.
if (!table.ReadU16(&this->searchRange) ||
!table.ReadU16(&this->entrySelector) ||
!table.ReadU16(&this->rangeShift)) {
return parent->Error("SILPass: Failed to read searchRange..rangeShift");
}
if (this->numRange == 0) {
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
this->searchRange = this->entrySelector = this->rangeShift = 0;
}
} else {
unsigned floorLog2 = std::floor(std::log2(this->numRange));
if (this->searchRange != 6 * (unsigned)std::pow(2, floorLog2) ||
this->entrySelector != floorLog2 ||
this->rangeShift != 6 * this->numRange - this->searchRange) {
this->searchRange = 6 * (unsigned)std::pow(2, floorLog2);
this->entrySelector = floorLog2;
this->rangeShift = 6 * this->numRange - this->searchRange;
}
}
//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

196
gfx/ots/src/silf.h Normal file
View File

@ -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_

159
gfx/ots/src/sill.cc Normal file
View File

@ -0,0 +1,159 @@
// 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");
}
// The following three fields are deprecated and ignored. We fix them up here
// just for internal consistency, but the Graphite engine doesn't care.
if (!table.ReadU16(&this->searchRange) ||
!table.ReadU16(&this->entrySelector) ||
!table.ReadU16(&this->rangeShift)) {
return Drop("Failed to read searchRange..rangeShift");
}
if (this->numLangs == 0) {
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
this->searchRange = this->entrySelector = this->rangeShift = 0;
}
} else {
unsigned floorLog2 = std::floor(std::log2(this->numLangs));
if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
this->entrySelector != floorLog2 ||
this->rangeShift != this->numLangs - this->searchRange) {
this->searchRange = (unsigned)std::pow(2, floorLog2);
this->entrySelector = floorLog2;
this->rangeShift = this->numLangs - this->searchRange;
}
}
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

53
gfx/ots/src/sill.h Normal file
View File

@ -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_

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -7,91 +7,75 @@
// VDMX - Vertical Device Metrics
// http://www.microsoft.com/typography/otspec/vdmx.htm
#define TABLE_NAME "VDMX"
#define DROP_THIS_TABLE(...) \
do { \
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
OTS_FAILURE_MSG("Table discarded"); \
delete font->vdmx; \
font->vdmx = 0; \
} while (0)
namespace ots {
bool ots_vdmx_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeVDMX::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
font->vdmx = new OpenTypeVDMX;
OpenTypeVDMX * const vdmx = font->vdmx;
if (!table.ReadU16(&vdmx->version) ||
!table.ReadU16(&vdmx->num_recs) ||
!table.ReadU16(&vdmx->num_ratios)) {
return OTS_FAILURE_MSG("Failed to read table header");
if (!table.ReadU16(&this->version) ||
!table.ReadU16(&this->num_recs) ||
!table.ReadU16(&this->num_ratios)) {
return Error("Failed to read table header");
}
if (vdmx->version > 1) {
DROP_THIS_TABLE("bad version: %u", vdmx->version);
return true; // continue transcoding
if (this->version > 1) {
return Drop("Unsupported table version: %u", this->version);
}
vdmx->rat_ranges.reserve(vdmx->num_ratios);
for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
this->rat_ranges.reserve(this->num_ratios);
for (unsigned i = 0; i < this->num_ratios; ++i) {
OpenTypeVDMXRatioRecord rec;
if (!table.ReadU8(&rec.charset) ||
!table.ReadU8(&rec.x_ratio) ||
!table.ReadU8(&rec.y_start_ratio) ||
!table.ReadU8(&rec.y_end_ratio)) {
return OTS_FAILURE_MSG("Failed to read ratio header %d", i);
return Error("Failed to read RatioRange record %d", i);
}
if (rec.charset > 1) {
DROP_THIS_TABLE("bad charset: %u", rec.charset);
return true;
return Drop("Unsupported character set: %u", rec.charset);
}
if (rec.y_start_ratio > rec.y_end_ratio) {
DROP_THIS_TABLE("bad y ratio");
return true;
return Drop("Bad y ratio");
}
// All values set to zero signal the default grouping to use;
// if present, this must be the last Ratio group in the table.
if ((i < vdmx->num_ratios - 1u) &&
if ((i < this->num_ratios - 1u) &&
(rec.x_ratio == 0) &&
(rec.y_start_ratio == 0) &&
(rec.y_end_ratio == 0)) {
// workaround for fonts which have 2 or more {0, 0, 0} terminators.
DROP_THIS_TABLE("superfluous terminator found");
return true;
return Drop("Superfluous terminator found");
}
vdmx->rat_ranges.push_back(rec);
this->rat_ranges.push_back(rec);
}
vdmx->offsets.reserve(vdmx->num_ratios);
this->offsets.reserve(this->num_ratios);
const size_t current_offset = table.offset();
// current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
for (unsigned i = 0; i < this->num_ratios; ++i) {
uint16_t offset;
if (!table.ReadU16(&offset)) {
return OTS_FAILURE_MSG("Failed to read ratio offset %d", i);
return Error("Failed to read ratio offset %d", i);
}
if (current_offset + offset >= length) { // thus doesn't overflow.
return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset, i);
return Error("Bad ratio offset %d for ration %d", offset, i);
}
vdmx->offsets.push_back(offset);
this->offsets.push_back(offset);
}
vdmx->groups.reserve(vdmx->num_recs);
for (unsigned i = 0; i < vdmx->num_recs; ++i) {
this->groups.reserve(this->num_recs);
for (unsigned i = 0; i < this->num_recs; ++i) {
OpenTypeVDMXGroup group;
if (!table.ReadU16(&group.recs) ||
!table.ReadU8(&group.startsz) ||
!table.ReadU8(&group.endsz)) {
return OTS_FAILURE_MSG("Failed to read record header %d", i);
return Error("Failed to read record header %d", i);
}
group.entries.reserve(group.recs);
for (unsigned j = 0; j < group.recs; ++j) {
@ -99,71 +83,68 @@ bool ots_vdmx_parse(Font *font, const uint8_t *data, size_t length) {
if (!table.ReadU16(&vt.y_pel_height) ||
!table.ReadS16(&vt.y_max) ||
!table.ReadS16(&vt.y_min)) {
return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i, j);
return Error("Failed to read reacord %d group %d", i, j);
}
if (vt.y_max < vt.y_min) {
DROP_THIS_TABLE("bad y min/max");
return true;
return Drop("bad y min/max");
}
// This table must appear in sorted order (sorted by yPelHeight),
// but need not be continuous.
if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
DROP_THIS_TABLE("the table is not sorted");
return true;
return Drop("The table is not sorted");
}
group.entries.push_back(vt);
}
vdmx->groups.push_back(group);
this->groups.push_back(group);
}
return true;
}
bool ots_vdmx_should_serialise(Font *font) {
if (!font->glyf) return false; // this table is not for CFF fonts.
return font->vdmx != NULL;
bool OpenTypeVDMX::ShouldSerialize() {
return Table::ShouldSerialize() &&
// this table is not for CFF fonts.
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
}
bool ots_vdmx_serialise(OTSStream *out, Font *font) {
OpenTypeVDMX * const vdmx = font->vdmx;
if (!out->WriteU16(vdmx->version) ||
!out->WriteU16(vdmx->num_recs) ||
!out->WriteU16(vdmx->num_ratios)) {
return OTS_FAILURE_MSG("Failed to write table header");
bool OpenTypeVDMX::Serialize(OTSStream *out) {
if (!out->WriteU16(this->version) ||
!out->WriteU16(this->num_recs) ||
!out->WriteU16(this->num_ratios)) {
return Error("Failed to write table header");
}
for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
for (unsigned i = 0; i < this->rat_ranges.size(); ++i) {
const OpenTypeVDMXRatioRecord& rec = this->rat_ranges[i];
if (!out->Write(&rec.charset, 1) ||
!out->Write(&rec.x_ratio, 1) ||
!out->Write(&rec.y_start_ratio, 1) ||
!out->Write(&rec.y_end_ratio, 1)) {
return OTS_FAILURE_MSG("Failed to write ratio %d", i);
return Error("Failed to write RatioRange record %d", i);
}
}
for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
if (!out->WriteU16(vdmx->offsets[i])) {
return OTS_FAILURE_MSG("Failed to write ratio offset %d", i);
for (unsigned i = 0; i < this->offsets.size(); ++i) {
if (!out->WriteU16(this->offsets[i])) {
return Error("Failed to write ratio offset %d", i);
}
}
for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
const OpenTypeVDMXGroup& group = vdmx->groups[i];
for (unsigned i = 0; i < this->groups.size(); ++i) {
const OpenTypeVDMXGroup& group = this->groups[i];
if (!out->WriteU16(group.recs) ||
!out->Write(&group.startsz, 1) ||
!out->Write(&group.endsz, 1)) {
return OTS_FAILURE_MSG("Failed to write group %d", i);
return Error("Failed to write group %d", i);
}
for (unsigned j = 0; j < group.entries.size(); ++j) {
const OpenTypeVDMXVTable& vt = group.entries[j];
if (!out->WriteU16(vt.y_pel_height) ||
!out->WriteS16(vt.y_max) ||
!out->WriteS16(vt.y_min)) {
return OTS_FAILURE_MSG("Failed to write group %d entry %d", i, j);
return Error("Failed to write group %d entry %d", i, j);
}
}
}
@ -171,16 +152,4 @@ bool ots_vdmx_serialise(OTSStream *out, Font *font) {
return true;
}
void ots_vdmx_reuse(Font *font, Font *other) {
font->vdmx = other->vdmx;
font->vdmx_reused = true;
}
void ots_vdmx_free(Font *font) {
delete font->vdmx;
}
} // namespace ots
#undef TABLE_NAME
#undef DROP_THIS_TABLE

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -31,7 +31,16 @@ struct OpenTypeVDMXGroup {
std::vector<OpenTypeVDMXVTable> entries;
};
struct OpenTypeVDMX {
class OpenTypeVDMX : public Table {
public:
explicit OpenTypeVDMX(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
uint16_t version;
uint16_t num_recs;
uint16_t num_ratios;

View File

@ -1,64 +1,39 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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 "vhea.h"
#include "gsub.h"
#include "head.h"
#include "maxp.h"
// vhea - Vertical Header Table
// http://www.microsoft.com/typography/otspec/vhea.htm
#define TABLE_NAME "vhea"
namespace ots {
bool ots_vhea_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeVHEA::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeVHEA *vhea = new OpenTypeVHEA;
font->vhea = vhea;
if (!table.ReadU32(&vhea->header.version)) {
return OTS_FAILURE_MSG("Failed to read version");
if (!table.ReadU32(&this->version)) {
return Error("Failed to read version");
}
if (vhea->header.version != 0x00010000 &&
vhea->header.version != 0x00011000) {
return OTS_FAILURE_MSG("Bad vhea version %x", vhea->header.version);
if (this->version != 0x00010000 &&
this->version != 0x00011000) {
return Error("Unsupported table version: 0x%x", this->version);
}
if (!ParseMetricsHeader(font, &table, &vhea->header)) {
return OTS_FAILURE_MSG("Failed to parse metrics in vhea");
}
return true;
return OpenTypeMetricsHeader::Parse(data, length);
}
bool ots_vhea_should_serialise(Font *font) {
// vhea should'nt serialise when vmtx doesn't exist.
// Firefox developer pointed out that vhea/vmtx should serialise iff GSUB is
// preserved. See http://crbug.com/77386
return font->vhea != NULL && font->vmtx != NULL &&
ots_gsub_should_serialise(font);
bool OpenTypeVHEA::Serialize(OTSStream *out) {
return OpenTypeMetricsHeader::Serialize(out);
}
bool ots_vhea_serialise(OTSStream *out, Font *font) {
if (!SerialiseMetricsHeader(font, out, &font->vhea->header)) {
return OTS_FAILURE_MSG("Failed to write vhea metrics");
}
return true;
}
void ots_vhea_reuse(Font *font, Font *other) {
font->vhea = other->vhea;
font->vhea_reused = true;
}
void ots_vhea_free(Font *font) {
delete font->vhea;
bool OpenTypeVHEA::ShouldSerialize() {
return OpenTypeMetricsHeader::ShouldSerialize() &&
// vhea shouldn't serialise when vmtx doesn't exist.
GetFont()->GetTable(OTS_TAG_VMTX) != NULL;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -10,8 +10,14 @@
namespace ots {
struct OpenTypeVHEA {
OpenTypeMetricsHeader header;
class OpenTypeVHEA : public OpenTypeMetricsHeader {
public:
explicit OpenTypeVHEA(Font *font, uint32_t tag)
: OpenTypeMetricsHeader(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
};
} // namespace ots

View File

@ -1,60 +1,29 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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 "vmtx.h"
#include "gsub.h"
#include "maxp.h"
#include "vhea.h"
// vmtx - Vertical Metrics Table
// http://www.microsoft.com/typography/otspec/vmtx.htm
#define TABLE_NAME "vmtx"
namespace ots {
bool ots_vmtx_parse(Font *font, const uint8_t *data, size_t length) {
Buffer table(data, length);
OpenTypeVMTX *vmtx = new OpenTypeVMTX;
font->vmtx = vmtx;
if (!font->vhea || !font->maxp) {
return OTS_FAILURE_MSG("vhea or maxp table missing as needed by vmtx");
}
if (!ParseMetricsTable(font, &table, font->maxp->num_glyphs,
&font->vhea->header, &vmtx->metrics)) {
return OTS_FAILURE_MSG("Failed to parse vmtx metrics");
}
return true;
bool OpenTypeVMTX::Parse(const uint8_t *data, size_t length) {
return OpenTypeMetricsTable::Parse(data, length);
}
bool ots_vmtx_should_serialise(Font *font) {
// vmtx should serialise when vhea and GSUB are preserved.
// See the comment in ots_vhea_should_serialise().
return font->vmtx != NULL && font->vhea != NULL &&
ots_gsub_should_serialise(font);
bool OpenTypeVMTX::Serialize(OTSStream *out) {
return OpenTypeMetricsTable::Serialize(out);
}
bool ots_vmtx_serialise(OTSStream *out, Font *font) {
if (!SerialiseMetricsTable(font, out, &font->vmtx->metrics)) {
return OTS_FAILURE_MSG("Failed to write vmtx metrics");
}
return true;
}
void ots_vmtx_reuse(Font *font, Font *other) {
font->vmtx = other->vmtx;
font->vmtx_reused = true;
}
void ots_vmtx_free(Font *font) {
delete font->vmtx;
bool OpenTypeVMTX::ShouldSerialize() {
return OpenTypeMetricsTable::ShouldSerialize() &&
// vmtx should serialise when vhea is preserved.
GetFont()->GetTable(OTS_TAG_VHEA) != NULL;
}
} // namespace ots
#undef TABLE_NAME

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2011-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.
@ -6,12 +6,19 @@
#define OTS_VMTX_H_
#include "metrics.h"
#include "vhea.h"
#include "ots.h"
namespace ots {
struct OpenTypeVMTX {
OpenTypeMetricsTable metrics;
struct OpenTypeVMTX : public OpenTypeMetricsTable {
public:
explicit OpenTypeVMTX(Font *font, uint32_t tag)
: OpenTypeMetricsTable(font, tag, tag, OTS_TAG_VHEA) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
};
} // namespace ots

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -9,37 +9,23 @@
// VORG - Vertical Origin Table
// http://www.microsoft.com/typography/otspec/vorg.htm
#define TABLE_NAME "VORG"
#define DROP_THIS_TABLE(...) \
do { \
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
OTS_FAILURE_MSG("Table discarded"); \
delete font->vorg; \
font->vorg = 0; \
} while (0)
namespace ots {
bool ots_vorg_parse(Font *font, const uint8_t *data, size_t length) {
bool OpenTypeVORG::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
font->vorg = new OpenTypeVORG;
OpenTypeVORG * const vorg = font->vorg;
uint16_t num_recs;
if (!table.ReadU16(&vorg->major_version) ||
!table.ReadU16(&vorg->minor_version) ||
!table.ReadS16(&vorg->default_vert_origin_y) ||
if (!table.ReadU16(&this->major_version) ||
!table.ReadU16(&this->minor_version) ||
!table.ReadS16(&this->default_vert_origin_y) ||
!table.ReadU16(&num_recs)) {
return OTS_FAILURE_MSG("Failed to read header");
return Error("Failed to read header");
}
if (vorg->major_version != 1) {
DROP_THIS_TABLE("bad major version: %u", vorg->major_version);
return true;
if (this->major_version != 1) {
return Drop("Unsupported majorVersion: %u", this->major_version);
}
if (vorg->minor_version != 0) {
DROP_THIS_TABLE("bad minor version: %u", vorg->minor_version);
return true;
if (this->minor_version != 0) {
return Drop("Unsupported minorVersion: %u", this->minor_version);
}
// num_recs might be zero (e.g., DFHSMinchoPro5-W3-Demo.otf).
@ -48,64 +34,50 @@ bool ots_vorg_parse(Font *font, const uint8_t *data, size_t length) {
}
uint16_t last_glyph_index = 0;
vorg->metrics.reserve(num_recs);
this->metrics.reserve(num_recs);
for (unsigned i = 0; i < num_recs; ++i) {
OpenTypeVORGMetrics rec;
if (!table.ReadU16(&rec.glyph_index) ||
!table.ReadS16(&rec.vert_origin_y)) {
return OTS_FAILURE_MSG("Failed to read record %d", i);
return Error("Failed to read record %d", i);
}
if ((i != 0) && (rec.glyph_index <= last_glyph_index)) {
DROP_THIS_TABLE("the table is not sorted");
return true;
return Drop("The table is not sorted");
}
last_glyph_index = rec.glyph_index;
vorg->metrics.push_back(rec);
this->metrics.push_back(rec);
}
return true;
}
bool ots_vorg_should_serialise(Font *font) {
if (!font->cff) return false; // this table is not for fonts with TT glyphs.
return font->vorg != NULL;
}
bool ots_vorg_serialise(OTSStream *out, Font *font) {
OpenTypeVORG * const vorg = font->vorg;
const uint16_t num_metrics = static_cast<uint16_t>(vorg->metrics.size());
if (num_metrics != vorg->metrics.size() ||
!out->WriteU16(vorg->major_version) ||
!out->WriteU16(vorg->minor_version) ||
!out->WriteS16(vorg->default_vert_origin_y) ||
bool OpenTypeVORG::Serialize(OTSStream *out) {
const uint16_t num_metrics = static_cast<uint16_t>(this->metrics.size());
if (num_metrics != this->metrics.size() ||
!out->WriteU16(this->major_version) ||
!out->WriteU16(this->minor_version) ||
!out->WriteS16(this->default_vert_origin_y) ||
!out->WriteU16(num_metrics)) {
return OTS_FAILURE_MSG("Failed to write table header");
return Error("Failed to write table header");
}
for (uint16_t i = 0; i < num_metrics; ++i) {
const OpenTypeVORGMetrics& rec = vorg->metrics[i];
const OpenTypeVORGMetrics& rec = this->metrics[i];
if (!out->WriteU16(rec.glyph_index) ||
!out->WriteS16(rec.vert_origin_y)) {
return OTS_FAILURE_MSG("Failed to write record %d", i);
return Error("Failed to write record %d", i);
}
}
return true;
}
void ots_vorg_reuse(Font *font, Font *other) {
font->vorg = other->vorg;
font->vorg_reused = true;
}
void ots_vorg_free(Font *font) {
delete font->vorg;
bool OpenTypeVORG::ShouldSerialize() {
return Table::ShouldSerialize() &&
// this table is not for fonts with TT glyphs.
GetFont()->GetTable(OTS_TAG_CFF) != NULL;
}
} // namespace ots
#undef TABLE_NAME
#undef DROP_THIS_TABLE

View File

@ -1,4 +1,4 @@
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// 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.
@ -16,7 +16,16 @@ struct OpenTypeVORGMetrics {
int16_t vert_origin_y;
};
struct OpenTypeVORG {
class OpenTypeVORG : public Table {
public:
explicit OpenTypeVORG(Font *font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool ShouldSerialize();
private:
uint16_t major_version;
uint16_t minor_version;
int16_t default_vert_origin_y;

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_WOFF2_H_
#define OTS_WOFF2_H_
namespace ots {
// Compute the size of the final uncompressed font, or 0 on error.
size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length);
// Decompresses the font into the target buffer. The result_length should
// be the same as determined by ComputeFinalSize(). Returns true on successful
// decompression.
bool ConvertWOFF2ToSFNT(Font *font, uint8_t *result, size_t result_length,
const uint8_t *data, size_t length);
}
#endif // OTS_WOFF2_H_

View File

@ -20,10 +20,19 @@ echo "Updating include..."
rm -rf include/
cp -r $1/include .
echo "Updating tests..."
rm -rf tests/*
mkdir -p tests
cp -r $1/tests/*.cc tests
echo "Updating README.mozilla..."
REVISION=`cd $1; git log | head -1 | sed "s/commit //"`
sed -e "s/\(Current revision: \).*/\1$REVISION/" README.mozilla > README.tmp
VERSION=`cd $1; git describe | cut -d '-' -f 1 | sed 's/v//'`
sed -e "s/\(Current revision: \).*/\1$REVISION \($VERSION\)/" README.mozilla > README.tmp
mv README.tmp README.mozilla
echo "Applying ots-visibility.patch..."
patch -p3 < ots-visibility.patch
echo "Applying ots-lz4.patch..."
patch -p3 < ots-lz4.patch

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,770 @@
// Copyright (c) 2011-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 <cmath>
#include <vector>
#include <gtest/gtest.h>
#include "layout.h"
#include "ots-memory-stream.h"
namespace {
const uint32_t kFakeTag = 0x00000000;
const size_t kScriptRecordSize = 6;
const size_t kLangSysRecordSize = 6;
bool BuildFakeScriptListTable(ots::OTSStream *out, const uint16_t script_count,
const uint16_t langsys_count,
const uint16_t feature_count) {
if (!out->WriteU16(script_count)) {
return false;
}
const off_t script_record_end = out->Tell() +
kScriptRecordSize * script_count;
const size_t script_table_size = 4 + kLangSysRecordSize * langsys_count;
for (unsigned i = 0; i < script_count; ++i) {
if (!out->WriteU32(kFakeTag) ||
!out->WriteU16(script_record_end + i * script_table_size)) {
return false;
}
}
// Offsets to LangSys tables are measured from the beginning of each
// script table.
const off_t langsys_record_end = 4 + kLangSysRecordSize * langsys_count;
const size_t langsys_table_size = 6 + 2 * feature_count;
// Write Fake Script tables.
for (unsigned i = 0; i < script_count; ++i) {
if (!out->WriteU16(0x0000) ||
!out->WriteU16(langsys_count)) {
return false;
}
for (unsigned j = 0; j < langsys_count; ++j) {
if (!out->WriteU32(kFakeTag) ||
!out->WriteU16(langsys_record_end + j * langsys_table_size)) {
return false;
}
}
}
// Write Fake LangSys tables.
for (unsigned i = 0; i < langsys_count; ++i) {
if (!out->WriteU16(0x0000) ||
!out->WriteU16(0xFFFF) ||
!out->WriteU16(feature_count)) {
return false;
}
for (unsigned j = 0; j < feature_count; ++j) {
if (!out->WriteU16(j)) {
return false;
}
}
}
return true;
}
const size_t kFeatureRecordSize = 6;
bool BuildFakeFeatureListTable(ots::OTSStream *out,
const uint16_t feature_count,
const uint16_t lookup_count) {
if (!out->WriteU16(feature_count)) {
return false;
}
const off_t feature_record_end = out->Tell() +
kFeatureRecordSize * feature_count;
const size_t feature_table_size = 4 + 2 * lookup_count;
for (unsigned i = 0; i < feature_count; ++i) {
if (!out->WriteU32(kFakeTag) ||
!out->WriteU16(feature_record_end + i * feature_table_size)) {
return false;
}
}
// Write FeatureTable
for (unsigned i = 0; i < feature_count; ++i) {
if (!out->WriteU16(0x0000) ||
!out->WriteU16(lookup_count)) {
return false;
}
for (uint16_t j = 0; j < lookup_count; ++j) {
if (!out->WriteU16(j)) {
return false;
}
}
}
return true;
}
bool BuildFakeLookupListTable(ots::OTSStream *out, const uint16_t lookup_count,
const uint16_t subtable_count) {
if (!out->WriteU16(lookup_count)) {
return false;
}
const off_t base_offset_lookup = out->Tell();
if (!out->Pad(2 * lookup_count)) {
return false;
}
std::vector<off_t> offsets_lookup(lookup_count, 0);
for (uint16_t i = 0; i < lookup_count; ++i) {
offsets_lookup[i] = out->Tell();
if (!out->WriteU16(i + 1) ||
!out->WriteU16(0) ||
!out->WriteU16(subtable_count) ||
!out->Pad(2 * subtable_count) ||
!out->WriteU16(0)) {
return false;
}
}
const off_t offset_lookup_table_end = out->Tell();
// Allocate 256 bytes for each subtable.
if (!out->Pad(256 * lookup_count * subtable_count)) {
return false;
}
if (!out->Seek(base_offset_lookup)) {
return false;
}
for (unsigned i = 0; i < lookup_count; ++i) {
if (!out->WriteU16(offsets_lookup[i])) {
return false;
}
}
for (unsigned i = 0; i < lookup_count; ++i) {
if (!out->Seek(offsets_lookup[i] + 6)) {
return false;
}
for (unsigned j = 0; j < subtable_count; ++j) {
if (!out->WriteU16(offset_lookup_table_end +
256*i*subtable_count + 256*j)) {
return false;
}
}
}
return true;
}
bool BuildFakeCoverageFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
if (!out->WriteU16(1) || !out->WriteU16(glyph_count)) {
return false;
}
for (uint16_t glyph_id = 1; glyph_id <= glyph_count; ++glyph_id) {
if (!out->WriteU16(glyph_id)) {
return false;
}
}
return true;
}
bool BuildFakeCoverageFormat2(ots::OTSStream *out, const uint16_t range_count) {
if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
return false;
}
uint16_t glyph_id = 1;
uint16_t start_coverage_index = 0;
for (unsigned i = 0; i < range_count; ++i) {
// Write consecutive ranges in which each range consists of two glyph id.
if (!out->WriteU16(glyph_id) ||
!out->WriteU16(glyph_id + 1) ||
!out->WriteU16(start_coverage_index)) {
return false;
}
glyph_id += 2;
start_coverage_index += 2;
}
return true;
}
bool BuildFakeClassDefFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
if (!out->WriteU16(1) ||
!out->WriteU16(1) ||
!out->WriteU16(glyph_count)) {
return false;
}
for (uint16_t class_value = 1; class_value <= glyph_count; ++class_value) {
if (!out->WriteU16(class_value)) {
return false;
}
}
return true;
}
bool BuildFakeClassDefFormat2(ots::OTSStream *out, const uint16_t range_count) {
if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
return false;
}
uint16_t glyph_id = 1;
for (uint16_t class_value = 1; class_value <= range_count; ++class_value) {
// Write consecutive ranges in which each range consists of one glyph id.
if (!out->WriteU16(glyph_id) ||
!out->WriteU16(glyph_id + 1) ||
!out->WriteU16(class_value)) {
return false;
}
glyph_id += 2;
}
return true;
}
bool BuildFakeDeviceTable(ots::OTSStream *out, const uint16_t start_size,
const uint16_t end_size, const uint16_t format) {
if (!out->WriteU16(start_size) ||
!out->WriteU16(end_size) ||
!out->WriteU16(format)) {
return false;
}
const unsigned num_values = std::abs(end_size - start_size) + 1;
const unsigned num_bits = (1 << format) * num_values;
const unsigned num_units = (num_bits - 1) / 16 + 1;
if (!out->Pad(num_units * 2)) {
return false;
}
return true;
}
class TestStream : public ots::MemoryStream {
public:
TestStream()
: ots::MemoryStream(data_, sizeof(data_)), size_(0) {
std::memset(reinterpret_cast<char*>(data_), 0, sizeof(data_));
}
uint8_t* data() { return data_; }
size_t size() const { return size_; }
virtual bool WriteRaw(const void *d, size_t length) {
if (Tell() + length > size_) {
size_ = Tell() + length;
}
return ots::MemoryStream::WriteRaw(d, length);
}
private:
size_t size_;
uint8_t data_[4096];
};
class TableTest : public ::testing::Test {
protected:
virtual void SetUp() {
ots::FontFile *file = new ots::FontFile();
file->context = new ots::OTSContext();
font = new ots::Font(file);
}
virtual void TearDown() {
delete font->file->context;
delete font->file;
delete font;
}
TestStream out;
ots::Font *font;
};
class ScriptListTableTest : public TableTest { };
class DeviceTableTest : public TableTest { };
class CoverageTableTest : public TableTest { };
class CoverageFormat1Test : public TableTest { };
class CoverageFormat2Test : public TableTest { };
class ClassDefTableTest : public TableTest { };
class ClassDefFormat1Test : public TableTest { };
class ClassDefFormat2Test : public TableTest { };
class LookupSubtableParserTest : public TableTest { };
class FeatureListTableTest : public TableTest {
protected:
virtual void SetUp() {
TableTest::SetUp();
num_features = 0;
}
uint16_t num_features;
};
bool fakeTypeParserReturnsTrue(const ots::Font*, const uint8_t *,
const size_t) {
return true;
}
bool fakeTypeParserReturnsFalse(const ots::Font*, const uint8_t *,
const size_t) {
return false;
}
const ots::LookupSubtableParser::TypeParser TypeParsersReturnTrue[] = {
{1, fakeTypeParserReturnsTrue},
{2, fakeTypeParserReturnsTrue},
{3, fakeTypeParserReturnsTrue},
{4, fakeTypeParserReturnsTrue},
{5, fakeTypeParserReturnsTrue}
};
// Fake lookup subtable parser which always returns true.
const ots::LookupSubtableParser FakeLookupParserReturnsTrue = {
5, 5, TypeParsersReturnTrue,
};
const ots::LookupSubtableParser::TypeParser TypeParsersReturnFalse[] = {
{1, fakeTypeParserReturnsFalse}
};
// Fake lookup subtable parser which always returns false.
const ots::LookupSubtableParser FakeLookupParserReturnsFalse = {
1, 1, TypeParsersReturnFalse
};
class LookupListTableTest : public TableTest {
protected:
virtual void SetUp() {
TableTest::SetUp();
num_lookups = 0;
}
bool Parse() {
return ots::ParseLookupListTable(font, out.data(), out.size(),
&FakeLookupParserReturnsTrue,
&num_lookups);
}
uint16_t num_lookups;
};
} // namespace
TEST_F(ScriptListTableTest, TestSuccess) {
BuildFakeScriptListTable(&out, 1, 1, 1);
EXPECT_TRUE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestBadScriptCount) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set too large script count.
out.Seek(0);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestScriptRecordOffsetUnderflow) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set bad offset to ScriptRecord[0].
out.Seek(6);
out.WriteU16(0);
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestScriptRecordOffsetOverflow) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set bad offset to ScriptRecord[0].
out.Seek(6);
out.WriteU16(out.size());
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestBadLangSysCount) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set too large langsys count.
out.Seek(10);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestLangSysRecordOffsetUnderflow) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set bad offset to LangSysRecord[0].
out.Seek(16);
out.WriteU16(0);
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestLangSysRecordOffsetOverflow) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set bad offset to LangSysRecord[0].
out.Seek(16);
out.WriteU16(out.size());
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestBadReqFeatureIndex) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set too large feature index to ReqFeatureIndex of LangSysTable[0].
out.Seek(20);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestBadFeatureCount) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set too large feature count to LangSysTable[0].
out.Seek(22);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(ScriptListTableTest, TestBadFeatureIndex) {
BuildFakeScriptListTable(&out, 1, 1, 1);
// Set too large feature index to ReatureIndex[0] of LangSysTable[0].
out.Seek(24);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
}
TEST_F(FeatureListTableTest, TestSuccess) {
BuildFakeFeatureListTable(&out, 1, 1);
EXPECT_TRUE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
&num_features));
EXPECT_EQ(num_features, 1);
}
TEST_F(FeatureListTableTest, TestSuccess2) {
BuildFakeFeatureListTable(&out, 5, 1);
EXPECT_TRUE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
&num_features));
EXPECT_EQ(num_features, 5);
}
TEST_F(FeatureListTableTest, TestBadFeatureCount) {
BuildFakeFeatureListTable(&out, 1, 1);
// Set too large feature count.
out.Seek(0);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
&num_features));
}
TEST_F(FeatureListTableTest, TestOffsetFeatureUnderflow) {
BuildFakeFeatureListTable(&out, 1, 1);
// Set bad offset to FeatureRecord[0].
out.Seek(6);
out.WriteU16(0);
EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
&num_features));
}
TEST_F(FeatureListTableTest, TestOffsetFeatureOverflow) {
BuildFakeFeatureListTable(&out, 1, 1);
// Set bad offset to FeatureRecord[0].
out.Seek(6);
out.WriteU16(out.size());
EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
&num_features));
}
TEST_F(FeatureListTableTest, TestBadLookupCount) {
BuildFakeFeatureListTable(&out, 1, 1);
// Set too large lookup count to FeatureTable[0].
out.Seek(10);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
&num_features));
}
TEST_F(LookupListTableTest, TestSuccess) {
BuildFakeLookupListTable(&out, 1, 1);
EXPECT_TRUE(Parse());
EXPECT_EQ(num_lookups, 1);
}
TEST_F(LookupListTableTest, TestSuccess2) {
BuildFakeLookupListTable(&out, 5, 1);
EXPECT_TRUE(Parse());
EXPECT_EQ(num_lookups, 5);
}
TEST_F(LookupListTableTest, TestOffsetLookupTableUnderflow) {
BuildFakeLookupListTable(&out, 1, 1);
// Set bad offset to Lookup[0].
out.Seek(2);
out.WriteU16(0);
EXPECT_FALSE(Parse());
}
TEST_F(LookupListTableTest, TestOffsetLookupTableOverflow) {
BuildFakeLookupListTable(&out, 1, 1);
// Set bad offset to Lookup[0].
out.Seek(2);
out.WriteU16(out.size());
EXPECT_FALSE(Parse());
}
TEST_F(LookupListTableTest, TestOffsetSubtableUnderflow) {
BuildFakeLookupListTable(&out, 1, 1);
// Set bad offset to SubTable[0] of LookupTable[0].
out.Seek(10);
out.WriteU16(0);
EXPECT_FALSE(Parse());
}
TEST_F(LookupListTableTest, TestOffsetSubtableOverflow) {
BuildFakeLookupListTable(&out, 1, 1);
// Set bad offset to SubTable[0] of LookupTable[0].
out.Seek(10);
out.WriteU16(out.size());
EXPECT_FALSE(Parse());
}
TEST_F(LookupListTableTest, TesBadLookupCount) {
BuildFakeLookupListTable(&out, 1, 1);
// Set too large lookup count of LookupTable[0].
out.Seek(0);
out.WriteU16(2);
EXPECT_FALSE(Parse());
}
TEST_F(LookupListTableTest, TesBadLookupType) {
BuildFakeLookupListTable(&out, 1, 1);
// Set too large lookup type of LookupTable[0].
out.Seek(4);
out.WriteU16(6);
EXPECT_FALSE(Parse());
}
TEST_F(LookupListTableTest, TesBadLookupFlag) {
BuildFakeLookupListTable(&out, 1, 1);
// Set IgnoreBaseGlyphs(0x0002) to the lookup flag of LookupTable[0].
out.Seek(6);
out.WriteU16(0x0002);
EXPECT_FALSE(Parse());
}
TEST_F(LookupListTableTest, TesBadSubtableCount) {
BuildFakeLookupListTable(&out, 1, 1);
// Set too large sutable count of LookupTable[0].
out.Seek(8);
out.WriteU16(2);
EXPECT_FALSE(Parse());
}
TEST_F(CoverageTableTest, TestSuccessFormat1) {
BuildFakeCoverageFormat1(&out, 1);
EXPECT_TRUE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
}
TEST_F(CoverageTableTest, TestSuccessFormat2) {
BuildFakeCoverageFormat2(&out, 1);
EXPECT_TRUE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
}
TEST_F(CoverageTableTest, TestBadFormat) {
BuildFakeCoverageFormat1(&out, 1);
// Set bad format.
out.Seek(0);
out.WriteU16(3);
EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
}
TEST_F(CoverageFormat1Test, TestBadGlyphCount) {
BuildFakeCoverageFormat1(&out, 1);
// Set too large glyph count.
out.Seek(2);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
}
TEST_F(CoverageFormat1Test, TestBadGlyphId) {
BuildFakeCoverageFormat1(&out, 1);
// Set too large glyph id.
out.Seek(4);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
}
TEST_F(CoverageFormat2Test, TestBadRangeCount) {
BuildFakeCoverageFormat2(&out, 1);
// Set too large range count.
out.Seek(2);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
}
TEST_F(CoverageFormat2Test, TestBadRange) {
BuildFakeCoverageFormat2(&out, 1);
// Set reverse order glyph id to start/end fields.
out.Seek(4);
out.WriteU16(2);
out.WriteU16(1);
EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
}
TEST_F(CoverageFormat2Test, TestRangeOverlap) {
BuildFakeCoverageFormat2(&out, 2);
// Set overlapping glyph id to an end field.
out.Seek(12);
out.WriteU16(1);
EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 2));
}
TEST_F(CoverageFormat2Test, TestRangeOverlap2) {
BuildFakeCoverageFormat2(&out, 2);
// Set overlapping range.
out.Seek(10);
out.WriteU16(1);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 2));
}
TEST_F(ClassDefTableTest, TestSuccessFormat1) {
BuildFakeClassDefFormat1(&out, 1);
EXPECT_TRUE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(ClassDefTableTest, TestSuccessFormat2) {
BuildFakeClassDefFormat2(&out, 1);
EXPECT_TRUE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(ClassDefTableTest, TestBadFormat) {
BuildFakeClassDefFormat1(&out, 1);
// Set bad format.
out.Seek(0);
out.WriteU16(3);
EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(ClassDefFormat1Test, TestBadStartGlyph) {
BuildFakeClassDefFormat1(&out, 1);
// Set too large start glyph id.
out.Seek(2);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(ClassDefFormat1Test, TestBadGlyphCount) {
BuildFakeClassDefFormat1(&out, 1);
// Set too large glyph count.
out.Seek(4);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(ClassDefFormat1Test, TestBadClassValue) {
BuildFakeClassDefFormat1(&out, 1);
// Set too large class value.
out.Seek(6);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(ClassDefFormat2Test, TestBadRangeCount) {
BuildFakeClassDefFormat2(&out, 1);
// Set too large range count.
out.Seek(2);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(ClassDefFormat2Test, TestRangeOverlap) {
BuildFakeClassDefFormat2(&out, 2);
// Set overlapping glyph id to an end field.
out.Seek(12);
out.WriteU16(1);
EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(ClassDefFormat2Test, TestRangeOverlap2) {
BuildFakeClassDefFormat2(&out, 2);
// Set overlapping range.
out.Seek(10);
out.WriteU16(1);
out.WriteU16(2);
EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
}
TEST_F(DeviceTableTest, TestDeltaFormat1Success) {
BuildFakeDeviceTable(&out, 1, 8, 1);
EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
}
TEST_F(DeviceTableTest, TestDeltaFormat1Success2) {
BuildFakeDeviceTable(&out, 1, 9, 1);
EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
}
TEST_F(DeviceTableTest, TestDeltaFormat1Fail) {
// Pass shorter length than expected.
BuildFakeDeviceTable(&out, 1, 8, 1);
EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
}
TEST_F(DeviceTableTest, TestDeltaFormat1Fail2) {
// Pass shorter length than expected.
BuildFakeDeviceTable(&out, 1, 9, 1);
EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
}
TEST_F(DeviceTableTest, TestDeltaFormat2Success) {
BuildFakeDeviceTable(&out, 1, 1, 2);
EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
}
TEST_F(DeviceTableTest, TestDeltaFormat2Success2) {
BuildFakeDeviceTable(&out, 1, 8, 2);
EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
}
TEST_F(DeviceTableTest, TestDeltaFormat2Fail) {
// Pass shorter length than expected.
BuildFakeDeviceTable(&out, 1, 8, 2);
EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
}
TEST_F(DeviceTableTest, TestDeltaFormat2Fail2) {
// Pass shorter length than expected.
BuildFakeDeviceTable(&out, 1, 9, 2);
EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
}
TEST_F(DeviceTableTest, TestDeltaFormat3Success) {
BuildFakeDeviceTable(&out, 1, 1, 3);
EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
}
TEST_F(DeviceTableTest, TestDeltaFormat3Success2) {
BuildFakeDeviceTable(&out, 1, 8, 3);
EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
}
TEST_F(DeviceTableTest, TestDeltaFormat3Fail) {
// Pass shorter length than expected.
BuildFakeDeviceTable(&out, 1, 8, 3);
EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
}
TEST_F(DeviceTableTest, TestDeltaFormat3Fail2) {
// Pass shorter length than expected.
BuildFakeDeviceTable(&out, 1, 9, 3);
EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
}
TEST_F(LookupSubtableParserTest, TestSuccess) {
{
EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 1));
}
{
EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 5));
}
}
TEST_F(LookupSubtableParserTest, TestFail) {
{
// Pass bad lookup type which less than the smallest type.
EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 0));
}
{
// Pass bad lookup type which greater than the maximum type.
EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 6));
}
{
// Check the type parser failure.
EXPECT_FALSE(FakeLookupParserReturnsFalse.Parse(font, 0, 0, 1));
}
}

View File

@ -71,3 +71,24 @@ LZ4::decompress(const char* aSource, size_t aInputSize, char* aDest,
return false;
}
bool
LZ4::decompressPartial(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize, size_t* aOutputSize)
{
CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
MOZ_ASSERT(maxOutputSizeChecked.isValid());
CheckedInt<int> inputSizeChecked = aInputSize;
MOZ_ASSERT(inputSizeChecked.isValid());
int ret = LZ4_decompress_safe_partial(aSource, aDest,
inputSizeChecked.value(),
maxOutputSizeChecked.value(),
maxOutputSizeChecked.value());
if (ret >= 0) {
*aOutputSize = ret;
return true;
}
*aOutputSize = 0;
return false;
}

View File

@ -96,6 +96,29 @@ public:
decompress(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize, size_t* aOutputSize);
/**
* If the source stream is malformed, the function will stop decoding
* and return false.
*
* This function never writes beyond aDest + aMaxOutputSize, and is
* therefore protected against malicious data packets. It also ignores
* unconsumed input upon reaching aMaxOutputSize and can therefore be used
* for partial decompression.
*
* Note: Destination buffer must be already allocated. This version is
* slightly slower than the decompress without the aMaxOutputSize.
*
* @param aInputSize is the length of the input compressed data
* @param aMaxOutputSize is the size of the destination buffer (which must be
* already allocated)
* @param aOutputSize the actual number of bytes decoded in the destination
* buffer (necessarily <= aMaxOutputSize)
* @return true on success, false on failure
*/
static MFBT_API MOZ_MUST_USE bool
decompressPartial(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize, size_t* aOutputSize);
/*
* Provides the maximum size that LZ4 may output in a "worst case"
* scenario (input data not compressible) primarily useful for memory