Update in-tree Brotli, Woff2 and OTS components.

This commit is contained in:
Fedor 2019-12-25 15:45:57 +03:00
parent adaffdd732
commit 41d0dc16ca
256 changed files with 44541 additions and 16491 deletions

View File

@ -1201,7 +1201,7 @@ BrowserGlue.prototype = {
},
_migrateUI: function BG__migrateUI() {
const UI_VERSION = 19;
const UI_VERSION = 20;
const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
let currentUIVersion = 0;
try {
@ -1436,6 +1436,11 @@ BrowserGlue.prototype = {
}
#endif
if (currentUIVersion < 20) {
// HPKP change of UI preference; reset enforcement level
Services.prefs.clearUserPref("security.cert_pinning.enforcement_level");
}
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
},

View File

@ -18,7 +18,6 @@ var gSecurityPane = {
{
this._pane = document.getElementById("paneSecurity");
this._initMasterPasswordUI();
this._initHPKPUI();
},
// ADD-ONS
@ -233,31 +232,5 @@ var gSecurityPane = {
document.documentElement.openWindow("Toolkit:PasswordManager",
"chrome://passwordmgr/content/passwordManager.xul",
"", null);
},
_initHPKPUI: function() {
let checkbox = document.getElementById("enableHPKP");
let HPKPpref = document.getElementById("security.cert_pinning.enforcement_level");
if (HPKPpref.value == 0) {
checkbox.checked = false;
} else {
checkbox.checked = true;
}
},
/**
* Updates the HPKP enforcement level to the proper value depending on checkbox
* state.
*/
updateHPKPPref: function() {
let checkbox = document.getElementById("enableHPKP");
let HPKPpref = document.getElementById("security.cert_pinning.enforcement_level");
if (checkbox.checked) {
HPKPpref.value = 2;
} else {
HPKPpref.value = 0;
}
}
};

View File

@ -46,9 +46,9 @@
<preference id="network.stricttransportsecurity.enabled"
name="network.stricttransportsecurity.enabled"
type="bool"/>
<preference id="security.cert_pinning.enforcement_level"
name="security.cert_pinning.enforcement_level"
type="int"/>
<preference id="security.cert_pinning.hpkp.enabled"
name="security.cert_pinning.hpkp.enabled"
type="bool"/>
<!-- Opportunistic Encryption -->
@ -150,7 +150,7 @@
<checkbox id="enableHPKP"
label="&enableHPKP.label;"
accesskey="&enableHPKP.accesskey;"
oncommand="gSecurityPane.updateHPKPPref();"/>
preference="security.cert_pinning.hpkp.enabled"/>
</vbox>
</groupbox>

View File

@ -14,14 +14,18 @@ const SJS_URL = "https://example.com/browser/devtools/client/webconsole/" +
"test/test_hpkp-invalid-headers.sjs";
const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" +
"Public_Key_Pinning" + DOCS_GA_PARAMS;
const HPKP_ENABLED_PREF = "security.cert_pinning.hpkp.enabled";
const NON_BUILTIN_ROOT_PREF = "security.cert_pinning.process_headers_from_" +
"non_builtin_roots";
add_task(function* () {
registerCleanupFunction(() => {
Services.prefs.clearUserPref(HPKP_ENABLED_PREF);
Services.prefs.clearUserPref(NON_BUILTIN_ROOT_PREF);
});
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, true);
yield loadTab(TEST_URI);
let hud = yield openConsole();

View File

@ -17,7 +17,8 @@
SimpleTest.waitForExplicitFinish();
let gCurrentTestCase = -1;
const HPKP_PREF = "security.cert_pinning.process_headers_from_non_builtin_roots";
const HPKP_ENABLED_PREF = "security.cert_pinning.hpkp.enabled";
const PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF = "security.cert_pinning.process_headers_from_non_builtin_roots";
// Static pins tested by unit/test_security-info-static-hpkp.js.
const TEST_CASES = [
@ -41,11 +42,11 @@ const TEST_CASES = [
function startTest()
{
// Need to enable this pref or pinning headers are rejected due test
// certificate.
Services.prefs.setBoolPref(HPKP_PREF, true);
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, true);
Services.prefs.setBoolPref(PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF, true);
SimpleTest.registerCleanupFunction(() => {
Services.prefs.setBoolPref(HPKP_PREF, false);
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, false);
Services.prefs.setBoolPref(PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF, false);
// Reset pinning state.
let gSSService = Cc["@mozilla.org/ssservice;1"]

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: ba8417620956a920ed1f05a2f666fb6317fb10cb
Current revision: 8bba749d9d5401726a7d7609ab914fdb5e92bfbe (8.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.
@ -35,13 +35,17 @@ typedef int int32_t;
typedef unsigned int uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#define ntohl(x) _byteswap_ulong (x)
#define ntohs(x) _byteswap_ushort (x)
#define htonl(x) _byteswap_ulong (x)
#define htons(x) _byteswap_ushort (x)
#define ots_ntohl(x) _byteswap_ulong (x)
#define ots_ntohs(x) _byteswap_ushort (x)
#define ots_htonl(x) _byteswap_ulong (x)
#define ots_htons(x) _byteswap_ushort (x)
#else
#include <arpa/inet.h>
#include <stdint.h>
#define ots_ntohl(x) ntohl (x)
#define ots_ntohs(x) ntohs (x)
#define ots_htonl(x) htonl (x)
#define ots_htons(x) htons (x)
#endif
#include <sys/types.h>
@ -52,7 +56,7 @@ typedef unsigned __int64 uint64_t;
#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 {
@ -80,7 +84,7 @@ class OTSStream {
const size_t l = std::min(length, static_cast<size_t>(4) - chksum_offset);
uint32_t tmp = 0;
std::memcpy(reinterpret_cast<uint8_t *>(&tmp) + chksum_offset, data, l);
chksum_ += ntohl(tmp);
chksum_ += ots_ntohl(tmp);
length -= l;
offset += l;
}
@ -89,7 +93,7 @@ class OTSStream {
uint32_t tmp;
std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
sizeof(uint32_t));
chksum_ += ntohl(tmp);
chksum_ += ots_ntohl(tmp);
length -= 4;
offset += 4;
}
@ -99,7 +103,7 @@ class OTSStream {
uint32_t tmp = 0;
std::memcpy(&tmp,
reinterpret_cast<const uint8_t*>(data) + offset, length);
chksum_ += ntohl(tmp);
chksum_ += ots_ntohl(tmp);
}
return WriteRaw(data, orig_length);
@ -127,27 +131,27 @@ class OTSStream {
}
bool WriteU16(uint16_t v) {
v = htons(v);
v = ots_htons(v);
return Write(&v, sizeof(v));
}
bool WriteS16(int16_t v) {
v = htons(v);
v = ots_htons(v);
return Write(&v, sizeof(v));
}
bool WriteU24(uint32_t v) {
v = htonl(v);
v = ots_htonl(v);
return Write(reinterpret_cast<uint8_t*>(&v)+1, 3);
}
bool WriteU32(uint32_t v) {
v = htonl(v);
v = ots_htonl(v);
return Write(&v, sizeof(v));
}
bool WriteS32(int32_t v) {
v = htonl(v);
v = ots_htonl(v);
return Write(&v, sizeof(v));
}
@ -176,7 +180,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
};
@ -186,7 +190,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.

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.

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

@ -0,0 +1,74 @@
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
@@ -4,9 +4,9 @@
#include "glat.h"
#include "gloc.h"
-#include "lz4.h"
+#include "mozilla/Compression.h"
#include <list>
namespace ots {
@@ -212,16 +212,17 @@ bool OpenTypeGLAT_v3::Parse(const uint8_
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
decompressed_size / (1024.0 * 1024.0));
}
std::vector<uint8_t> decompressed(decompressed_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 < 0 || unsigned(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);
}
default:
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
@@ -4,9 +4,9 @@
#include "silf.h"
#include "name.h"
-#include "lz4.h"
+#include "mozilla/Compression.h"
#include <cmath>
namespace ots {
@@ -50,16 +50,17 @@ bool OpenTypeSILF::Parse(const uint8_t*
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
decompressed_size / (1024.0 * 1024.0));
}
std::vector<uint8_t> decompressed(decompressed_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 < 0 || unsigned(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);
}
default:

View File

@ -1,10 +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.
@@ -4,8 +4,28 @@
#ifndef OPENTYPE_SANITISER_H_
#define OPENTYPE_SANITISER_H_
@ -33,15 +30,7 @@ diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-san
#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
@@ -164,9 +184,9 @@ enum TableAction {
TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
TABLE_ACTION_DROP // Drop the table
};
@ -52,7 +41,3 @@ diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-san
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.

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

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

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

@ -0,0 +1,42 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_AVAR_H_
#define OTS_AVAR_H_
#include "ots.h"
#include <vector>
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeAVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeAVAR : public Table {
public:
explicit OpenTypeAVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t reserved;
uint16_t axisCount;
struct AxisValueMap {
int16_t fromCoordinate;
int16_t toCoordinate;
};
std::vector<std::vector<AxisValueMap>> axisSegmentMaps;
};
} // namespace ots
#endif // OTS_AVAR_H_

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -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 {
@ -56,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);
@ -65,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;
@ -87,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
@ -107,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) {
@ -156,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);
}
}
}
@ -176,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);
}
@ -219,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;
@ -230,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);
glyph = ots_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);
}
}
}
@ -245,85 +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 || subtable.remaining() / 12 < num_groups) {
return OTS_FAILURE_MSG("Bad format 12 subtable group count %d", 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);
}
// 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);
}
}
@ -331,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 || subtable.remaining() / 12 < num_groups) {
return OTS_FAILURE_MSG("Bad format 13 subtable group count %d", 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
@ -376,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);
@ -407,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 &&
@ -435,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);
}
}
@ -462,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 || subtable.remaining() / 4 < num_ranges) {
return OTS_FAILURE_MSG("Bad number of ranges (%d) in record %d", num_ranges, i);
return Error("Bad number of ranges (%d) in record %d", num_ranges, i);
}
uint32_t last_unicode_value = 0;
@ -476,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;
@ -485,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;
}
@ -496,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 || subtable.remaining() / 5 < num_mappings) {
return OTS_FAILURE_MSG("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
return Error("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
}
uint32_t last_unicode_value = 0;
@ -510,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;
}
@ -525,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;
@ -591,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);
@ -602,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);
}
}
@ -615,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;
@ -624,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;
@ -635,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;
@ -664,7 +662,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
(subtable_headers[i - 1].encoding > subtable_headers[i].encoding ||
(subtable_headers[i - 1].encoding == subtable_headers[i].encoding &&
subtable_headers[i - 1].language > subtable_headers[i].language))))
OTS_WARNING("subtable %d with platform ID %d, encoding ID %d, language ID %d "
Warning("subtable %d with platform ID %d, encoding ID %d, language ID %d "
"following subtable with platform ID %d, encoding ID %d, language ID %d",
i,
subtable_headers[i].platform,
@ -679,14 +677,14 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
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);
}
}
@ -714,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
@ -760,29 +760,30 @@ 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) &&
} else if ((subtable_headers[i].encoding == 3 ||
subtable_headers[i].encoding == 4) &&
(subtable_headers[i].format == 12)) {
// parse and output the 0-3-12 table as 3-10-12 table.
if (!Parse31012(font, data + subtable_headers[i].offset,
// parse and output the 0-3-12 or 0-4-12 tables as 3-10-12 table.
if (!Parse31012(data + subtable_headers[i].offset,
subtable_headers[i].length, num_glyphs)) {
return 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) {
@ -791,7 +792,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();
}
@ -804,7 +805,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)) {
@ -814,14 +815,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();
}
@ -834,20 +835,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) +
@ -860,7 +857,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) ||
@ -875,8 +872,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();
}
}
@ -884,10 +881,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();
}
@ -939,23 +936,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();
}
}
@ -963,7 +960,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) ||
@ -985,7 +982,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) ||
@ -1074,15 +1071,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

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

@ -0,0 +1,56 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cvar.h"
#include "fvar.h"
#include "variations.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeCVAR
// -----------------------------------------------------------------------------
bool OpenTypeCVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
uint16_t majorVersion;
uint16_t minorVersion;
if (!table.ReadU16(&majorVersion) ||
!table.ReadU16(&minorVersion)) {
return Drop("Failed to read table header");
}
if (majorVersion != 1) {
return Drop("Unknown table version");
}
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
GetFont()->GetTypedTable(OTS_TAG_FVAR));
if (!fvar) {
return DropVariations("Required fvar table is missing");
}
if (!ParseVariationData(GetFont(), data + table.offset(), length - table.offset(),
fvar->AxisCount(), 0)) {
return Drop("Failed to parse variation data");
}
this->m_data = data;
this->m_length = length;
return true;
}
bool OpenTypeCVAR::Serialize(OTSStream* out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write cvar table");
}
return true;
}
} // namespace ots

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

@ -0,0 +1,31 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_CVAR_H_
#define OTS_CVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeCVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeCVAR : public Table {
public:
explicit OpenTypeCVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_CVAR_H_

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;
};

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

@ -0,0 +1,164 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "fvar.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeFVAR
// -----------------------------------------------------------------------------
bool OpenTypeFVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
if (!table.ReadU16(&this->majorVersion) ||
!table.ReadU16(&this->minorVersion) ||
!table.ReadU16(&this->axesArrayOffset) ||
!table.ReadU16(&this->reserved) ||
!table.ReadU16(&this->axisCount) ||
!table.ReadU16(&this->axisSize) ||
!table.ReadU16(&this->instanceCount) ||
!table.ReadU16(&this->instanceSize)) {
return DropVariations("Failed to read table header");
}
if (this->majorVersion != 1) {
return DropVariations("Unknown table version");
}
if (this->minorVersion > 0) {
Warning("Downgrading minor version to 0");
this->minorVersion = 0;
}
if (this->axesArrayOffset > length || this->axesArrayOffset < table.offset()) {
return DropVariations("Bad axesArrayOffset");
}
if (this->reserved != 2) {
Warning("Expected reserved=2");
this->reserved = 2;
}
if (this->axisCount == 0) {
return DropVariations("No variation axes");
}
if (this->axisSize != 20) {
return DropVariations("Invalid axisSize");
}
// instanceCount is not validated
if (this->instanceSize == this->axisCount * sizeof(Fixed) + 6) {
this->instancesHavePostScriptNameID = true;
} else if (this->instanceSize == this->axisCount * sizeof(Fixed) + 4) {
this->instancesHavePostScriptNameID = false;
} else {
return DropVariations("Invalid instanceSize");
}
// When we serialize, the axes array will go here, even if it was
// originally at a different offset. So we update the axesArrayOffset
// field for the header.
uint32_t origAxesArrayOffset = this->axesArrayOffset;
this->axesArrayOffset = table.offset();
table.set_offset(origAxesArrayOffset);
for (unsigned i = 0; i < this->axisCount; i++) {
this->axes.emplace_back();
auto& axis = this->axes[i];
if (!table.ReadU32(&axis.axisTag) ||
!table.ReadS32(&axis.minValue) ||
!table.ReadS32(&axis.defaultValue) ||
!table.ReadS32(&axis.maxValue) ||
!table.ReadU16(&axis.flags) ||
!table.ReadU16(&axis.axisNameID)) {
return DropVariations("Failed to read axis record");
}
if (!CheckTag(axis.axisTag)) {
return DropVariations("Bad axis tag");
}
if (!(axis.minValue <= axis.defaultValue && axis.defaultValue <= axis.maxValue)) {
return DropVariations("Bad axis value range");
}
if ((axis.flags & 0xFFFEu) != 0) {
Warning("Discarding unknown axis flags");
axis.flags &= ~0xFFFEu;
}
if (axis.axisNameID <= 255 || axis.axisNameID >= 32768) {
Warning("Axis nameID out of range");
// We don't check that the name actually exists -- assume the client can handle
// a missing name when it tries to read the table.
}
}
for (unsigned i = 0; i < this->instanceCount; i++) {
this->instances.emplace_back();
auto& inst = this->instances[i];
if (!table.ReadU16(&inst.subfamilyNameID) ||
!table.ReadU16(&inst.flags)) {
return DropVariations("Failed to read instance record");
}
inst.coordinates.reserve(this->axisCount);
for (unsigned j = 0; j < this->axisCount; j++) {
inst.coordinates.emplace_back();
auto& coord = inst.coordinates[j];
if (!table.ReadS32(&coord)) {
return DropVariations("Failed to read instance coordinates");
}
}
if (this->instancesHavePostScriptNameID) {
if (!table.ReadU16(&inst.postScriptNameID)) {
return DropVariations("Failed to read instance psname ID");
}
}
}
if (table.remaining()) {
return Warning("%zu bytes unparsed", table.remaining());
}
return true;
}
bool OpenTypeFVAR::Serialize(OTSStream* out) {
if (!out->WriteU16(this->majorVersion) ||
!out->WriteU16(this->minorVersion) ||
!out->WriteU16(this->axesArrayOffset) ||
!out->WriteU16(this->reserved) ||
!out->WriteU16(this->axisCount) ||
!out->WriteU16(this->axisSize) ||
!out->WriteU16(this->instanceCount) ||
!out->WriteU16(this->instanceSize)) {
return Error("Failed to write table");
}
for (unsigned i = 0; i < this->axisCount; i++) {
const auto& axis = this->axes[i];
if (!out->WriteU32(axis.axisTag) ||
!out->WriteS32(axis.minValue) ||
!out->WriteS32(axis.defaultValue) ||
!out->WriteS32(axis.maxValue) ||
!out->WriteU16(axis.flags) ||
!out->WriteU16(axis.axisNameID)) {
return Error("Failed to write table");
}
}
for (unsigned i = 0; i < this->instanceCount; i++) {
const auto& inst = this->instances[i];
if (!out->WriteU16(inst.subfamilyNameID) ||
!out->WriteU16(inst.flags)) {
return Error("Failed to write table");
}
for (unsigned j = 0; j < this->axisCount; j++) {
const auto& coord = inst.coordinates[j];
if (!out->WriteS32(coord)) {
return Error("Failed to write table");
}
}
if (this->instancesHavePostScriptNameID) {
if (!out->WriteU16(inst.postScriptNameID)) {
return Error("Failed to write table");
}
}
}
return true;
}
} // namespace ots

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

@ -0,0 +1,63 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_FVAR_H_
#define OTS_FVAR_H_
#include <vector>
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeFVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeFVAR : public Table {
public:
explicit OpenTypeFVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
uint16_t AxisCount() const { return axisCount; }
private:
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t axesArrayOffset;
uint16_t reserved;
uint16_t axisCount;
uint16_t axisSize;
uint16_t instanceCount;
uint16_t instanceSize;
typedef int32_t Fixed; /* 16.16 fixed-point value */
struct VariationAxisRecord {
uint32_t axisTag;
Fixed minValue;
Fixed defaultValue;
Fixed maxValue;
uint16_t flags;
uint16_t axisNameID;
};
std::vector<VariationAxisRecord> axes;
struct InstanceRecord {
uint16_t subfamilyNameID;
uint16_t flags;
std::vector<Fixed> coordinates;
uint16_t postScriptNameID; // optional
};
std::vector<InstanceRecord> instances;
bool instancesHavePostScriptNameID;
};
} // namespace ots
#endif // OTS_FVAR_H_

View File

@ -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.
@ -11,68 +11,60 @@
#include "gsub.h"
#include "layout.h"
#include "maxp.h"
#include "variations.h"
// GDEF - The Glyph Definition Table
// http://www.microsoft.com/typography/otspec/gdef.htm
#define TABLE_NAME "GDEF"
namespace {
// The maximum class value in class definition tables.
const uint16_t kMaxClassDefValue = 0xFFFF;
// The maximum class value in the glyph class definision table.
const uint16_t kMaxGlyphClassDefValue = 4;
// The maximum format number of caret value tables.
// We don't support format 3 for now. See the comment in
// ParseLigCaretListTable() for the reason.
const uint16_t kMaxCaretValueFormat = 2;
const uint16_t kMaxCaretValueFormat = 3;
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 +72,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 +94,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 +137,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 +148,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 +160,93 @@ 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);
}
if (caret_format == 3) {
uint16_t offset_device = 0;
if (!subtable.ReadU16(&offset_device)) {
return Error("Can't read device offset for caret value %d "
"in glyph %d", j, i);
}
uint16_t absolute_offset = lig_glyphs[i] + caret_value_offsets[j]
+ offset_device;
if (offset_device == 0 || absolute_offset >= length) {
return Error("Bad device offset for caret value %d in glyph %d: %d",
j, i, offset_device);
}
if (!ots::ParseDeviceTable(GetFont(), data + absolute_offset,
length - absolute_offset)) {
return Error("Bad device table for caret value %d in glyph %d",
j, i, offset_device);
}
}
}
}
return true;
}
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");
uint16_t version_major = 0, version_minor = 0;
if (!table.ReadU16(&version_major) ||
!table.ReadU16(&version_minor)) {
return Error("Incomplete table");
}
if (version < 0x00010000 || version == 0x00010001) {
return OTS_FAILURE_MSG("Bad version");
}
if (version >= 0x00010002) {
gdef->version_2 = true;
if (version_major != 1 || version_minor == 1) { // there is no v1.1
return Error("Bad version");
}
uint16_t offset_glyph_class_def = 0;
@ -264,110 +257,108 @@ 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 (version_minor >= 2) {
if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
return OTS_FAILURE_MSG("Incomplete table");
return Error("Incomplete table");
}
}
uint32_t item_var_store_offset = 0;
if (version_minor >= 3) {
if (!table.ReadU32(&item_var_store_offset)) {
return Error("Incomplete table");
}
}
unsigned gdef_header_end = 4 + 4 * 2;
if (gdef->version_2)
if (version_minor >= 2)
gdef_header_end += 2;
if (version_minor >= 3)
gdef_header_end += 4;
// Parse subtables
if (offset_glyph_class_def) {
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;
}
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;
}
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;
}
gdef->data = data;
gdef->length = length;
if (item_var_store_offset) {
if (item_var_store_offset >= length ||
item_var_store_offset < gdef_header_end) {
return Error("invalid offset to item variation store");
}
if (!ParseItemVariationStore(GetFont(), data + item_var_store_offset,
length - item_var_store_offset)) {
return Error("Invalid item variation store");
}
}
this->m_data = data;
this->m_length = length;
return true;
}
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,29 @@
namespace ots {
struct OpenTypeGDEF {
OpenTypeGDEF()
: version_2(false),
has_glyph_class_def(false),
has_mark_attachment_class_def(false),
has_mark_glyph_sets_def(false),
class OpenTypeGDEF : public Table {
public:
explicit OpenTypeGDEF(Font *font, uint32_t tag)
: Table(font, tag, tag),
num_mark_glyph_sets(0),
data(NULL),
length(0) {
m_data(NULL),
m_length(0),
m_num_glyphs(0) {
}
bool version_2;
bool has_glyph_class_def;
bool has_mark_attachment_class_def;
bool has_mark_glyph_sets_def;
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
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

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

@ -0,0 +1,459 @@
// 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");
}
size_t decompressed_size = this->compHead & FULL_SIZE;
if (decompressed_size < length) {
return DropGraphite("Decompressed size is less than compressed size");
}
if (decompressed_size == 0) {
return DropGraphite("Decompressed size is set to 0");
}
// decompressed table must be <= 30MB
if (decompressed_size > 30 * 1024 * 1024) {
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
decompressed_size / (1024.0 * 1024.0));
}
std::vector<uint8_t> decompressed(decompressed_size);
size_t outputSize = 0;
bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()),
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;
@ -43,140 +38,205 @@ bool ParseFlagsForSimpleGlyph(ots::Font *font,
delta += 2;
}
/* MS and Apple specs say this bit is reserved and must be set to zero, but
* Apple spec then contradicts itself and says it should be set on the first
* contour flag for simple glyphs with overlapping contours:
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html
* (Overlapping contours section) */
if (flag & (1u << 6) && *flag_index != 0) {
return Error("Bad glyph flag (%d), "
"bit 6 must be set to zero for flag %d", flag, *flag_index);
}
if (flag & (1u << 3)) { // repeat
if (*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);
if (flag & (1u << 7)) { // reserved flag
return Error("Bad glyph flag (%d), reserved bit 7 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) {
this->maxp->max_size_glyf_instructions = bytecode_length;
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions %d: %d",
this->maxp->max_size_glyf_instructions, bytecode_length);
}
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) {
this->maxp->max_size_glyf_instructions = bytecode_length;
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions "
"%d: %d",
this->maxp->max_size_glyf_instructions, bytecode_length);
}
if (!glyph.Skip(bytecode_length)) {
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 +253,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 +285,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 +326,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.
@ -30,12 +30,12 @@ enum GPOS_TYPE {
GPOS_TYPE_RESERVED = 10
};
// The size of gpos header.
const unsigned kGposHeaderSize = 10;
// The size of gpos header, version 1.0.
const unsigned kGposHeaderSize_1_0 = 10;
// The size of gpos header, version 1.1.
const unsigned kGposHeaderSize_1_1 = 14;
// The maximum format number for anchor tables.
const uint16_t kMaxAnchorFormat = 3;
// The maximum number of class value.
const uint16_t kMaxClassDefValue = 0xFFFF;
// Lookup type parsers.
bool ParseSingleAdjustment(const ots::Font *font,
@ -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,34 +361,44 @@ 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)) {
num_glyphs, ots::kMaxClassDefValue)) {
return OTS_FAILURE_MSG("Failed to parse class definition table 1");
}
if (!ots::ParseClassDefTable(font, data + offset_class_def2,
length - offset_class_def2,
num_glyphs, kMaxClassDefValue)) {
num_glyphs, ots::kMaxClassDefValue)) {
return OTS_FAILURE_MSG("Failed to parse class definition table 2");
}
@ -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,141 +745,98 @@ 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 version_major = 0, version_minor = 0;
uint16_t offset_script_list = 0;
uint16_t offset_feature_list = 0;
uint16_t offset_lookup_list = 0;
if (!table.ReadU32(&version) ||
uint32_t offset_feature_variations = 0;
if (!table.ReadU16(&version_major) ||
!table.ReadU16(&version_minor) ||
!table.ReadU16(&offset_script_list) ||
!table.ReadU16(&offset_feature_list) ||
!table.ReadU16(&offset_lookup_list)) {
return OTS_FAILURE_MSG("Incomplete table");
return Error("Incomplete table");
}
if (version != 0x00010000) {
return OTS_FAILURE_MSG("Bad version");
if (version_major != 1 || version_minor > 1) {
return Error("Bad version");
}
if (version_minor > 0) {
if (!table.ReadU32(&offset_feature_variations)) {
return Error("Incomplete table");
}
}
const size_t header_size =
(version_minor == 0) ? kGposHeaderSize_1_0 : kGposHeaderSize_1_1;
if (offset_lookup_list) {
if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
return OTS_FAILURE_MSG("Bad lookup list offset in table header");
if (offset_lookup_list < header_size || offset_lookup_list >= length) {
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");
if (offset_feature_list < header_size || offset_feature_list >= length) {
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");
if (offset_script_list < header_size || offset_script_list >= length) {
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;
if (offset_feature_variations) {
if (offset_feature_variations < header_size || offset_feature_variations >= length) {
return Error("Bad feature variations offset in table header");
}
if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
length - offset_feature_variations,
this->num_lookups)) {
return Error("Failed to parse feature variations table");
}
}
this->m_data = data;
this->m_length = length;
return true;
}
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

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

@ -0,0 +1,96 @@
// 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 ~TablePart() { }
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.
@ -17,8 +17,10 @@
namespace {
// The GSUB header size
const size_t kGsubHeaderSize = 4 + 3 * 2;
// The GSUB header size for table version 1.0
const size_t kGsubHeaderSize_1_0 = 4 + 3 * 2;
// GSUB header size v1.1
const size_t kGsubHeaderSize_1_1 = 4 + 3 * 2 + 4;
enum GSUB_TYPE {
GSUB_TYPE_SINGLE = 1,
@ -82,7 +84,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 +177,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 +257,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 +379,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 +420,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 +439,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 +476,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,145 +577,99 @@ 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 version_major = 0, version_minor = 0;
uint16_t offset_script_list = 0;
uint16_t offset_feature_list = 0;
uint16_t offset_lookup_list = 0;
if (!table.ReadU32(&version) ||
uint32_t offset_feature_variations = 0;
if (!table.ReadU16(&version_major) ||
!table.ReadU16(&version_minor) ||
!table.ReadU16(&offset_script_list) ||
!table.ReadU16(&offset_feature_list) ||
!table.ReadU16(&offset_lookup_list)) {
return OTS_FAILURE_MSG("Incomplete table");
return Error("Incomplete table");
}
if (version != 0x00010000) {
return OTS_FAILURE_MSG("Bad version");
if (version_major != 1 || version_minor > 1) {
return Error("Bad version");
}
if (version_minor > 0) {
if (!table.ReadU32(&offset_feature_variations)) {
return Error("Incomplete table");
}
}
const size_t header_size =
(version_minor == 0) ? kGsubHeaderSize_1_0 : kGsubHeaderSize_1_1;
if (offset_lookup_list) {
if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
return OTS_FAILURE_MSG("Bad lookup list offset in table header");
if (offset_lookup_list < header_size || offset_lookup_list >= length) {
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");
if (offset_feature_list < header_size || offset_feature_list >= length) {
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");
if (offset_script_list < header_size || offset_script_list >= length) {
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;
if (offset_feature_variations) {
if (offset_feature_variations < header_size || offset_feature_variations >= length) {
return Error("Bad feature variations offset in table header");
}
if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
length - offset_feature_variations,
this->num_lookups)) {
return Error("Failed to parse feature variations table");
}
}
this->m_data = data;
this->m_length = length;
return true;
}
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

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

@ -0,0 +1,158 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gvar.h"
#include "fvar.h"
#include "maxp.h"
#include "variations.h"
#define TABLE_NAME "gvar"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeGVAR
// -----------------------------------------------------------------------------
static bool ParseSharedTuples(const Font* font, const uint8_t* data, size_t length,
size_t sharedTupleCount, size_t axisCount) {
Buffer subtable(data, length);
for (unsigned i = 0; i < sharedTupleCount; i++) {
for (unsigned j = 0; j < axisCount; j++) {
int16_t coordinate;
if (!subtable.ReadS16(&coordinate)) {
return OTS_FAILURE_MSG("Failed to read shared tuple coordinate");
}
}
}
return true;
}
static bool ParseGlyphVariationDataArray(const Font* font, const uint8_t* data, size_t length,
uint16_t flags, size_t glyphCount, size_t axisCount,
size_t sharedTupleCount,
const uint8_t* glyphVariationData,
size_t glyphVariationDataLength) {
Buffer subtable(data, length);
bool glyphVariationDataOffsetsAreLong = (flags & 0x0001u);
uint32_t prevOffset = 0;
for (size_t i = 0; i < glyphCount + 1; i++) {
uint32_t offset;
if (glyphVariationDataOffsetsAreLong) {
if (!subtable.ReadU32(&offset)) {
return OTS_FAILURE_MSG("Failed to read GlyphVariationData offset");
}
} else {
uint16_t halfOffset;
if (!subtable.ReadU16(&halfOffset)) {
return OTS_FAILURE_MSG("Failed to read GlyphVariationData offset");
}
offset = halfOffset * 2;
}
if (i > 0 && offset > prevOffset) {
if (prevOffset > glyphVariationDataLength) {
return OTS_FAILURE_MSG("Invalid GlyphVariationData offset");
}
if (!ParseVariationData(font, glyphVariationData + prevOffset,
glyphVariationDataLength - prevOffset,
axisCount, sharedTupleCount)) {
return OTS_FAILURE_MSG("Failed to parse GlyphVariationData");
}
}
prevOffset = offset;
}
return true;
}
bool OpenTypeGVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t axisCount;
uint16_t sharedTupleCount;
uint32_t sharedTuplesOffset;
uint16_t glyphCount;
uint16_t flags;
uint32_t glyphVariationDataArrayOffset;
if (!table.ReadU16(&majorVersion) ||
!table.ReadU16(&minorVersion) ||
!table.ReadU16(&axisCount) ||
!table.ReadU16(&sharedTupleCount) ||
!table.ReadU32(&sharedTuplesOffset) ||
!table.ReadU16(&glyphCount) ||
!table.ReadU16(&flags) ||
!table.ReadU32(&glyphVariationDataArrayOffset)) {
return DropVariations("Failed to read table header");
}
if (majorVersion != 1) {
return DropVariations("Unknown table version");
}
// check axisCount == fvar->axisCount
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
GetFont()->GetTypedTable(OTS_TAG_FVAR));
if (!fvar) {
return DropVariations("Required fvar table is missing");
}
if (axisCount != fvar->AxisCount()) {
return DropVariations("Axis count mismatch");
}
// check glyphCount == maxp->num_glyphs
OpenTypeMAXP* maxp = static_cast<OpenTypeMAXP*>(
GetFont()->GetTypedTable(OTS_TAG_MAXP));
if (!maxp) {
return DropVariations("Required maxp table is missing");
}
if (glyphCount != maxp->num_glyphs) {
return DropVariations("Glyph count mismatch");
}
if (sharedTupleCount > 0) {
if (sharedTuplesOffset < table.offset() || sharedTuplesOffset > length) {
return DropVariations("Invalid sharedTuplesOffset");
}
if (!ParseSharedTuples(GetFont(),
data + sharedTuplesOffset, length - sharedTuplesOffset,
sharedTupleCount, axisCount)) {
return DropVariations("Failed to parse shared tuples");
}
}
if (glyphVariationDataArrayOffset) {
if (glyphVariationDataArrayOffset > length) {
return DropVariations("Invalid glyphVariationDataArrayOffset");
}
if (!ParseGlyphVariationDataArray(GetFont(),
data + table.offset(), length - table.offset(),
flags, glyphCount, axisCount, sharedTupleCount,
data + glyphVariationDataArrayOffset,
length - glyphVariationDataArrayOffset)) {
return DropVariations("Failed to read glyph variation data array");
}
}
this->m_data = data;
this->m_length = length;
return true;
}
bool OpenTypeGVAR::Serialize(OTSStream* out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write gvar table");
}
return true;
}
} // namespace ots
#undef TABLE_NAME

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

@ -0,0 +1,31 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_GVAR_H_
#define OTS_GVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeGVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeGVAR : public Table {
public:
explicit OpenTypeGVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_GVAR_H_

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

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

@ -0,0 +1,85 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "hvar.h"
#include "variations.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeHVAR
// -----------------------------------------------------------------------------
bool OpenTypeHVAR::Parse(const uint8_t* data, size_t length) {
Buffer table(data, length);
uint16_t majorVersion;
uint16_t minorVersion;
uint32_t itemVariationStoreOffset;
uint32_t advanceWidthMappingOffset;
uint32_t lsbMappingOffset;
uint32_t rsbMappingOffset;
if (!table.ReadU16(&majorVersion) ||
!table.ReadU16(&minorVersion) ||
!table.ReadU32(&itemVariationStoreOffset) ||
!table.ReadU32(&advanceWidthMappingOffset) ||
!table.ReadU32(&lsbMappingOffset) ||
!table.ReadU32(&rsbMappingOffset)) {
return DropVariations("Failed to read table header");
}
if (majorVersion != 1) {
return DropVariations("Unknown table version");
}
if (itemVariationStoreOffset > length ||
advanceWidthMappingOffset > length ||
lsbMappingOffset > length ||
rsbMappingOffset > length) {
return DropVariations("Invalid subtable offset");
}
if (!ParseItemVariationStore(GetFont(), data + itemVariationStoreOffset,
length - itemVariationStoreOffset)) {
return DropVariations("Failed to parse item variation store");
}
if (advanceWidthMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + advanceWidthMappingOffset,
length - advanceWidthMappingOffset)) {
return DropVariations("Failed to parse advance width mappings");
}
}
if (lsbMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + lsbMappingOffset,
length - lsbMappingOffset)) {
return DropVariations("Failed to parse LSB mappings");
}
}
if (rsbMappingOffset) {
if (!ParseDeltaSetIndexMap(GetFont(), data + rsbMappingOffset,
length - rsbMappingOffset)) {
return DropVariations("Failed to parse RSB mappings");
}
}
this->m_data = data;
this->m_length = length;
return true;
}
bool OpenTypeHVAR::Serialize(OTSStream* out) {
if (!out->Write(this->m_data, this->m_length)) {
return Error("Failed to write HVAR table");
}
return true;
}
} // namespace ots

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

@ -0,0 +1,31 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_HVAR_H_
#define OTS_HVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeHVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeHVAR : public Table {
public:
explicit OpenTypeHVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_HVAR_H_

View File

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

View File

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

View File

@ -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,21 +428,20 @@ 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;
@ -457,7 +451,7 @@ bool ParseMathGlyphConstructionSequence(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, glyph_count)) {
return OTS_FAILURE();
@ -471,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();
@ -481,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.
@ -505,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)) {
@ -519,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;
@ -567,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,149 +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);
}
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);
}
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());
}
}
@ -161,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

@ -9,41 +9,52 @@ EXPORTS += [
'../include/ots-memory-stream.h',
]
SOURCES += [
# don't unify sources that use a (file-specific) DROP_THIS_TABLE macro
UNIFIED_SOURCES += [
'avar.cc',
'cff.cc',
'cff_charstring.cc',
'cmap.cc',
'cvar.cc',
'cvt.cc',
'feat.cc',
'fpgm.cc',
'fvar.cc',
'gasp.cc',
'gdef.cc',
'glat.cc',
'gloc.cc',
'glyf.cc',
'gpos.cc',
'gsub.cc',
'gvar.cc',
'hdmx.cc',
'kern.cc',
'ltsh.cc',
'math.cc',
'vdmx.cc',
'vorg.cc',
]
UNIFIED_SOURCES += [
'cff.cc',
'cff_type2_charstring.cc',
'cmap.cc',
'cvt.cc',
'fpgm.cc',
'glyf.cc',
'head.cc',
'hhea.cc',
'hmtx.cc',
'hvar.cc',
'kern.cc',
'layout.cc',
'loca.cc',
'ltsh.cc',
'math.cc',
'maxp.cc',
'metrics.cc',
'mvar.cc',
'name.cc',
'os2.cc',
'ots.cc',
'post.cc',
'prep.cc',
'sile.cc',
'silf.cc',
'sill.cc',
'stat.cc',
'variations.cc',
'vdmx.cc',
'vhea.cc',
'vmtx.cc',
'vorg.cc',
'vvar.cc',
]
# We allow warnings for third-party code that can be updated from upstream.
@ -53,8 +64,14 @@ FINAL_LIBRARY = 'gkmedias'
DEFINES['PACKAGE_VERSION'] = '"moz"'
DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
DEFINES['OTS_GRAPHITE'] = 1
DEFINES['OTS_VARIATIONS'] = 1
USE_LIBS += [
'brotli',
'woff2',
]
LOCAL_INCLUDES += [
'/modules/woff2/src',
]

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

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

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

@ -0,0 +1,31 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_MVAR_H_
#define OTS_MVAR_H_
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeMVAR Interface
// -----------------------------------------------------------------------------
class OpenTypeMVAR : public Table {
public:
explicit OpenTypeMVAR(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
const uint8_t *m_data;
size_t m_length;
};
} // namespace ots
#endif // OTS_MVAR_H_

View File

@ -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,49 @@ 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);
}
// Lang tag is BCP 47 tag per the spec, the recommonded BCP 47 max tag
// length is 35:
// https://tools.ietf.org/html/bcp47#section-4.4.1
// We are being too generous and allowing for 100 (multiplied by 2 since
// this is UTF-16 string).
if (tag_length > 100 * 2) {
return Error("Too long language tag for LangTagRecord %d: %d", i, tag_length);
}
std::string tag(string_base + tag_offset, tag_length);
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,17 +211,16 @@ 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) {
const uint16_t id = name_iter->name_id;
for (const auto& name : this->names) {
const uint16_t id = name.name_id;
if (id >= kStdNameCount || kStdNames[id] == NULL) {
continue;
}
if (name_iter->platform_id == 1) {
if (name.platform_id == 1) {
mac_name[id] = true;
continue;
}
if (name_iter->platform_id == 3) {
if (name.platform_id == 3) {
win_name[id] = true;
continue;
}
@ -236,49 +239,41 @@ 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) {
const NameRecord& rec = *name_iter;
for (const auto& rec : this->names) {
if (string_data.size() + rec.text.size() >
std::numeric_limits<uint16_t>::max() ||
!out->WriteU16(rec.platform_id) ||
@ -287,44 +282,77 @@ 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) {
if (string_data.size() + tag_iter->size() >
for (const auto& tag : this->lang_tags) {
if (string_data.size() + tag.size() >
std::numeric_limits<uint16_t>::max() ||
!out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
!out->WriteU16(static_cast<uint16_t>(tag.size())) ||
!out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
return OTS_FAILURE_MSG("Failed to write string");
return Error("Failed to write langTagRecord");
}
string_data.append(*tag_iter);
string_data.append(tag);
}
}
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,73 @@
// 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;
}
// 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 < 1) {
Warning("Bad usWeightClass: %u, changing it to %d",
this->table.weight_class, 1);
this->table.weight_class = 1;
} else if (this->table.weight_class > 1000) {
Warning("Bad usWeightClass: %u, changing it to %d",
this->table.weight_class, 1000);
this->table.weight_class = 1000;
}
if (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);
@ -90,7 +86,7 @@ bool ots_os2_parse(Font *font, const uint8_t *data, size_t length) {
SET_TO_ZERO("yStrikeoutSize", strikeout_size);
#undef SET_TO_ZERO
static std::string panose_strings[10] = {
static const char* panose_strings[10] = {
"bFamilyType",
"bSerifStyle",
"bWeight",
@ -103,234 +99,222 @@ 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]);
}
}
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);
Warning("fsSelection 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) {
Warning("usFirstCharIndex %d > usLastCharIndex %d",
this->table.first_char_index, this->table.last_char_index);
this->table.first_char_index = this->table.last_char_index;
}
if (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,44 +14,64 @@
#include <map>
#include <vector>
#include "woff2_dec.h"
#include <woff2/decode.h>
// The OpenType Font File
// http://www.microsoft.com/typography/otspec/cmap.htm
// http://www.microsoft.com/typography/otspec/otff.htm
namespace {
#include "avar.h"
#include "cff.h"
#include "cmap.h"
#include "cvar.h"
#include "cvt.h"
#include "fpgm.h"
#include "fvar.h"
#include "gasp.h"
#include "gdef.h"
#include "glyf.h"
#include "gpos.h"
#include "gsub.h"
#include "gvar.h"
#include "hdmx.h"
#include "head.h"
#include "hhea.h"
#include "hmtx.h"
#include "hvar.h"
#include "kern.h"
#include "loca.h"
#include "ltsh.h"
#include "math_.h"
#include "maxp.h"
#include "mvar.h"
#include "name.h"
#include "os2.h"
#include "ots.h"
#include "post.h"
#include "prep.h"
#include "stat.h"
#include "vdmx.h"
#include "vhea.h"
#include "vmtx.h"
#include "vorg.h"
#include "vvar.h"
// 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:
~Arena() {
for (std::vector<uint8_t*>::iterator
i = hunks_.begin(); i != hunks_.end(); ++i) {
delete[] *i;
for (auto& hunk : hunks_) {
delete[] hunk;
}
}
@ -65,80 +85,94 @@ struct Arena {
std::vector<uint8_t*> hunks_;
};
bool CheckTag(uint32_t tag_value) {
for (unsigned i = 0; i < 4; ++i) {
const uint32_t check = tag_value & 0xff;
if (check < 32 || check > 126) {
return false; // non-ASCII character found.
}
tag_value >>= 8;
}
return true;
}
}; // namespace ots
namespace {
#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__)
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 fvar table before other tables that may need to know
// the number of variation axes (if any)
{ OTS_TAG_FVAR, false },
{ OTS_TAG_AVAR, false },
{ OTS_TAG_CVAR, false },
{ OTS_TAG_GVAR, false },
{ OTS_TAG_HVAR, false },
{ OTS_TAG_MVAR, false },
{ OTS_TAG_STAT, false },
{ OTS_TAG_VVAR, false },
{ OTS_TAG_CFF2, false },
// We need to parse GDEF table in advance of parsing GSUB/GPOS tables
// because they could refer GDEF table.
{ OTS_TAG('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 +236,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 +255,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 +342,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 +422,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,13 +497,17 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
return ProcessGeneric(header, font, woff_tag, output, data, length, tables, file);
}
bool ProcessWOFF2(ots::OpenTypeFile *header,
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");
}
@ -493,16 +531,16 @@ bool ProcessWOFF2(ots::OpenTypeFile *header,
}
}
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;
}
@ -514,14 +552,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);
@ -537,12 +575,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();
@ -560,8 +598,8 @@ 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);
if (!ots::CheckTag(tables[i].tag)) {
OTS_WARNING_MSG_HDR("Invalid table tag: 0x%X", tables[i].tag);
}
// tables must be 4-byte aligned
@ -616,11 +654,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) {
@ -639,80 +672,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->GetTable(OTS_TAG_CFF2)) {
// font with PostScript glyph
if (font->version != OTS_TAG('O','T','T','O')) {
return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
}
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;
@ -738,85 +757,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;
}
}
}
@ -869,6 +852,225 @@ 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_AVAR: table = new OpenTypeAVAR(this, tag); break;
case OTS_TAG_CFF: table = new OpenTypeCFF(this, tag); break;
case OTS_TAG_CFF2: table = new OpenTypeCFF2(this, tag); break;
case OTS_TAG_CMAP: table = new OpenTypeCMAP(this, tag); break;
case OTS_TAG_CVAR: table = new OpenTypeCVAR(this, tag); break;
case OTS_TAG_CVT: table = new OpenTypeCVT(this, tag); break;
case OTS_TAG_FPGM: table = new OpenTypeFPGM(this, tag); break;
case OTS_TAG_FVAR: table = new OpenTypeFVAR(this, tag); break;
case OTS_TAG_GASP: table = new OpenTypeGASP(this, tag); break;
case OTS_TAG_GDEF: table = new OpenTypeGDEF(this, tag); break;
case OTS_TAG_GLYF: table = new OpenTypeGLYF(this, tag); break;
case OTS_TAG_GPOS: table = new OpenTypeGPOS(this, tag); break;
case OTS_TAG_GSUB: table = new OpenTypeGSUB(this, tag); break;
case OTS_TAG_GVAR: table = new OpenTypeGVAR(this, tag); break;
case OTS_TAG_HDMX: table = new OpenTypeHDMX(this, tag); break;
case OTS_TAG_HEAD: table = new OpenTypeHEAD(this, tag); break;
case OTS_TAG_HHEA: table = new OpenTypeHHEA(this, tag); break;
case OTS_TAG_HMTX: table = new OpenTypeHMTX(this, tag); break;
case OTS_TAG_HVAR: table = new OpenTypeHVAR(this, tag); break;
case OTS_TAG_KERN: table = new OpenTypeKERN(this, tag); break;
case OTS_TAG_LOCA: table = new OpenTypeLOCA(this, tag); break;
case OTS_TAG_LTSH: table = new OpenTypeLTSH(this, tag); break;
case OTS_TAG_MATH: table = new OpenTypeMATH(this, tag); break;
case OTS_TAG_MAXP: table = new OpenTypeMAXP(this, tag); break;
case OTS_TAG_MVAR: table = new OpenTypeMVAR(this, tag); break;
case OTS_TAG_NAME: table = new OpenTypeNAME(this, tag); break;
case OTS_TAG_OS2: table = new OpenTypeOS2(this, tag); break;
case OTS_TAG_POST: table = new OpenTypePOST(this, tag); break;
case OTS_TAG_PREP: table = new OpenTypePREP(this, tag); break;
case OTS_TAG_STAT: table = new OpenTypeSTAT(this, tag); break;
case OTS_TAG_VDMX: table = new OpenTypeVDMX(this, tag); break;
case OTS_TAG_VHEA: table = new OpenTypeVHEA(this, tag); break;
case OTS_TAG_VMTX: table = new OpenTypeVMTX(this, tag); break;
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
case OTS_TAG_VVAR: table = new OpenTypeVVAR(this, tag); break;
// Graphite tables
#ifdef OTS_GRAPHITE
case OTS_TAG_FEAT: table = new OpenTypeFEAT(this, tag); break;
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;
}
void Font::DropVariations() {
file->context->Message(0, "Dropping all Variation tables");
for (const std::pair<uint32_t, Table*> entry : m_tables) {
if (entry.first == OTS_TAG_AVAR ||
entry.first == OTS_TAG_CVAR ||
entry.first == OTS_TAG_FVAR ||
entry.first == OTS_TAG_GVAR ||
entry.first == OTS_TAG_HVAR ||
entry.first == OTS_TAG_MVAR ||
entry.first == OTS_TAG_STAT ||
entry.first == OTS_TAG_VVAR) {
entry.second->Drop("Discarding Variations table");
}
}
dropped_variations = true;
}
bool Table::ShouldSerialize() {
return m_shouldSerialize;
}
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 Table::DropVariations(const char *format, ...) {
va_list va;
va_start(va, format);
Message(0, format, va);
va_end(va);
m_font->DropVariations();
return true;
}
bool TablePassthru::Parse(const uint8_t *data, size_t length) {
m_data = data;
m_length = length;
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.
@ -882,7 +1084,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;

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__)
@ -112,7 +112,7 @@ class Buffer {
return OTS_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
*value = ntohs(*value);
*value = ots_ntohs(*value);
offset_ += 2;
return true;
}
@ -137,7 +137,7 @@ class Buffer {
return OTS_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
*value = ntohl(*value);
*value = ots_ntohl(*value);
offset_ += 4;
return true;
}
@ -184,111 +184,175 @@ template<typename T> T Round2(T value) {
return (value + 1) & ~1;
}
// Check that a tag consists entirely of printable ASCII characters
bool CheckTag(uint32_t tag_value);
bool IsValidVersionTag(uint32_t tag);
#define 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 OTS_TAG_CFF OTS_TAG('C','F','F',' ')
#define OTS_TAG_CFF2 OTS_TAG('C','F','F','2')
#define OTS_TAG_CMAP OTS_TAG('c','m','a','p')
#define OTS_TAG_CVT OTS_TAG('c','v','t',' ')
#define OTS_TAG_FEAT OTS_TAG('F','e','a','t')
#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')
#define F(name, capname) struct OpenType##capname;
FOR_EACH_TABLE_TYPE
#undef F
#define OTS_TAG_AVAR OTS_TAG('a','v','a','r')
#define OTS_TAG_CVAR OTS_TAG('c','v','a','r')
#define OTS_TAG_FVAR OTS_TAG('f','v','a','r')
#define OTS_TAG_GVAR OTS_TAG('g','v','a','r')
#define OTS_TAG_HVAR OTS_TAG('H','V','A','R')
#define OTS_TAG_MVAR OTS_TAG('M','V','A','R')
#define OTS_TAG_VVAR OTS_TAG('V','V','A','R')
#define OTS_TAG_STAT OTS_TAG('S','T','A','T')
struct Font;
struct 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, ...);
bool DropVariations(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),
dropped_variations(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();
// Drop all Variations tables and don't parse new ones.
void DropVariations();
FontFile *file;
uint32_t version;
uint16_t num_tables;
uint16_t search_range;
uint16_t entry_selector;
uint16_t range_shift;
bool dropped_graphite;
bool dropped_variations;
#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.
@ -9,74 +9,73 @@
// 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
@ -93,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 (std::memchr(strings + 1, '\0', string_length)) {
return OTS_FAILURE_MSG("Bad string of length %d", 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 (post && font->cff && 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_

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

@ -0,0 +1,977 @@
// 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");
}
size_t decompressed_size = this->compHead & FULL_SIZE;
if (decompressed_size < length) {
return DropGraphite("Decompressed size is less than compressed size");
}
if (decompressed_size == 0) {
return DropGraphite("Decompressed size is set to 0");
}
// decompressed table must be <= 30MB
if (decompressed_size > 30 * 1024 * 1024) {
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
decompressed_size / (1024.0 * 1024.0));
}
std::vector<uint8_t> decompressed(decompressed_size);
size_t outputSize = 0;
bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()),
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 (!table.ReadU8(&this->attrSkipPasses)) {
return parent->Error("SILSub: Failed to read attrSkipPasses");
}
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 (!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 (!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_

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

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

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

@ -0,0 +1,155 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_STAT_H_
#define OTS_STAT_H_
#include <vector>
#include "ots.h"
namespace ots {
// -----------------------------------------------------------------------------
// OpenTypeSTAT Interface
// -----------------------------------------------------------------------------
class OpenTypeSTAT : public Table {
public:
explicit OpenTypeSTAT(Font* font, uint32_t tag)
: Table(font, tag, tag) { }
bool Parse(const uint8_t* data, size_t length);
bool Serialize(OTSStream* out);
private:
bool ValidateNameId(uint16_t nameid, bool allowPredefined = true);
uint16_t majorVersion;
uint16_t minorVersion;
uint16_t designAxisSize;
uint16_t designAxisCount;
uint32_t designAxesOffset;
uint16_t axisValueCount;
uint32_t offsetToAxisValueOffsets;
uint16_t elidedFallbackNameID;
struct AxisRecord {
uint32_t axisTag;
uint16_t axisNameID;
uint16_t axisOrdering;
};
std::vector<AxisRecord> designAxes;
typedef int32_t Fixed; /* 16.16 fixed-point value */
struct AxisValueFormat1 {
uint16_t axisIndex;
uint16_t flags;
uint16_t valueNameID;
Fixed value;
static size_t Length() {
return 3 * sizeof(uint16_t) + sizeof(Fixed);
}
};
struct AxisValueFormat2 {
uint16_t axisIndex;
uint16_t flags;
uint16_t valueNameID;
Fixed nominalValue;
Fixed rangeMinValue;
Fixed rangeMaxValue;
static size_t Length() {
return 3 * sizeof(uint16_t) + 3 * sizeof(Fixed);
}
};
struct AxisValueFormat3 {
uint16_t axisIndex;
uint16_t flags;
uint16_t valueNameID;
Fixed value;
Fixed linkedValue;
static size_t Length() {
return 3 * sizeof(uint16_t) + 2 * sizeof(Fixed);
}
};
struct AxisValueFormat4 {
uint16_t axisCount;
uint16_t flags;
uint16_t valueNameID;
struct AxisValue {
uint16_t axisIndex;
Fixed value;
};
std::vector<AxisValue> axisValues;
size_t Length() const {
return 3 * sizeof(uint16_t) + axisValues.size() * (sizeof(uint16_t) + sizeof(Fixed));
}
};
struct AxisValueRecord {
uint16_t format;
union {
AxisValueFormat1 format1;
AxisValueFormat2 format2;
AxisValueFormat3 format3;
AxisValueFormat4 format4;
};
explicit AxisValueRecord(uint16_t format_)
: format(format_)
{
if (format == 4) {
new (&this->format4) AxisValueFormat4();
}
}
AxisValueRecord(const AxisValueRecord& other_)
: format(other_.format)
{
switch (format) {
case 1:
format1 = other_.format1;
break;
case 2:
format2 = other_.format2;
break;
case 3:
format3 = other_.format3;
break;
case 4:
new (&this->format4) AxisValueFormat4();
format4 = other_.format4;
break;
}
}
~AxisValueRecord() {
if (format == 4) {
this->format4.~AxisValueFormat4();
}
}
uint32_t Length() const {
switch (format) {
case 1:
return sizeof(uint16_t) + format1.Length();
case 2:
return sizeof(uint16_t) + format2.Length();
case 3:
return sizeof(uint16_t) + format3.Length();
case 4:
return sizeof(uint16_t) + format4.Length();
default:
// can't happen
return 0;
}
}
};
std::vector<AxisValueRecord> axisValues;
};
} // namespace ots
#endif // OTS_STAT_H_

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

@ -0,0 +1,261 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "layout.h"
#include "fvar.h"
// OpenType Variations Common Table Formats
#define TABLE_NAME "Variations" // XXX: use individual table names
namespace {
bool ParseVariationRegionList(const ots::Font* font, const uint8_t* data, const size_t length,
uint16_t* regionCount) {
ots::Buffer subtable(data, length);
uint16_t axisCount;
if (!subtable.ReadU16(&axisCount) ||
!subtable.ReadU16(regionCount)) {
return OTS_FAILURE_MSG("Failed to read variation region list header");
}
if (*regionCount == 0) {
return true;
}
const ots::OpenTypeFVAR* fvar =
static_cast<ots::OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
if (!fvar) {
return OTS_FAILURE_MSG("Required fvar table is missing");
}
if (axisCount != fvar->AxisCount()) {
return OTS_FAILURE_MSG("Axis count mismatch");
}
for (unsigned i = 0; i < *regionCount; i++) {
for (unsigned j = 0; j < axisCount; j++) {
int16_t startCoord, peakCoord, endCoord;
if (!subtable.ReadS16(&startCoord) ||
!subtable.ReadS16(&peakCoord) ||
!subtable.ReadS16(&endCoord)) {
return OTS_FAILURE_MSG("Failed to read region axis coordinates");
}
if (startCoord > peakCoord || peakCoord > endCoord) {
return OTS_FAILURE_MSG("Region axis coordinates out of order");
}
if (startCoord < -0x4000 || endCoord > 0x4000) {
return OTS_FAILURE_MSG("Region axis coordinate out of range");
}
if ((peakCoord < 0 && endCoord > 0) ||
(peakCoord > 0 && startCoord < 0)) {
return OTS_FAILURE_MSG("Invalid region axis coordinates");
}
}
}
return true;
}
bool
ParseVariationDataSubtable(const ots::Font* font, const uint8_t* data, const size_t length,
const uint16_t regionCount,
uint16_t* regionIndexCount) {
ots::Buffer subtable(data, length);
uint16_t itemCount;
uint16_t shortDeltaCount;
if (!subtable.ReadU16(&itemCount) ||
!subtable.ReadU16(&shortDeltaCount) ||
!subtable.ReadU16(regionIndexCount)) {
return OTS_FAILURE_MSG("Failed to read variation data subtable header");
}
for (unsigned i = 0; i < *regionIndexCount; i++) {
uint16_t regionIndex;
if (!subtable.ReadU16(&regionIndex) || regionIndex >= regionCount) {
return OTS_FAILURE_MSG("Bad region index");
}
}
if (!subtable.Skip(size_t(itemCount) * (size_t(shortDeltaCount) + size_t(*regionIndexCount)))) {
return OTS_FAILURE_MSG("Failed to read delta data");
}
return true;
}
} // namespace
namespace ots {
bool
ParseItemVariationStore(const Font* font,
const uint8_t* data, const size_t length,
std::vector<uint16_t>* regionIndexCounts) {
Buffer subtable(data, length);
uint16_t format;
uint32_t variationRegionListOffset;
uint16_t itemVariationDataCount;
if (!subtable.ReadU16(&format) ||
!subtable.ReadU32(&variationRegionListOffset) ||
!subtable.ReadU16(&itemVariationDataCount)) {
return OTS_FAILURE_MSG("Failed to read item variation store header");
}
if (format != 1) {
return OTS_FAILURE_MSG("Unknown item variation store format");
}
if (variationRegionListOffset < subtable.offset() + 4 * itemVariationDataCount ||
variationRegionListOffset > length) {
return OTS_FAILURE_MSG("Invalid variation region list offset");
}
uint16_t regionCount;
if (!ParseVariationRegionList(font,
data + variationRegionListOffset,
length - variationRegionListOffset,
&regionCount)) {
return OTS_FAILURE_MSG("Failed to parse variation region list");
}
for (unsigned i = 0; i < itemVariationDataCount; i++) {
uint32_t offset;
if (!subtable.ReadU32(&offset)) {
return OTS_FAILURE_MSG("Failed to read variation data subtable offset");
}
if (offset >= length) {
return OTS_FAILURE_MSG("Bad offset to variation data subtable");
}
uint16_t regionIndexCount = 0;
if (!ParseVariationDataSubtable(font, data + offset, length - offset,
regionCount,
&regionIndexCount)) {
return OTS_FAILURE_MSG("Failed to parse variation data subtable");
}
if (regionIndexCounts) {
regionIndexCounts->push_back(regionIndexCount);
}
}
return true;
}
bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length) {
Buffer subtable(data, length);
uint16_t entryFormat;
uint16_t mapCount;
if (!subtable.ReadU16(&entryFormat) ||
!subtable.ReadU16(&mapCount)) {
return OTS_FAILURE_MSG("Failed to read delta set index map header");
}
const uint16_t MAP_ENTRY_SIZE_MASK = 0x0030;
const uint16_t entrySize = (((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1);
if (!subtable.Skip(entrySize * mapCount)) {
return OTS_FAILURE_MSG("Failed to read delta set index map data");
}
return true;
}
bool ParseVariationData(const Font* font, const uint8_t* data, size_t length,
size_t axisCount, size_t sharedTupleCount) {
Buffer subtable(data, length);
uint16_t tupleVariationCount;
uint16_t dataOffset;
if (!subtable.ReadU16(&tupleVariationCount) ||
!subtable.ReadU16(&dataOffset)) {
return OTS_FAILURE_MSG("Failed to read variation data header");
}
if (dataOffset > length) {
return OTS_FAILURE_MSG("Invalid serialized data offset");
}
tupleVariationCount &= 0x0FFF; // mask off flags
const uint16_t EMBEDDED_PEAK_TUPLE = 0x8000;
const uint16_t INTERMEDIATE_REGION = 0x4000;
const uint16_t TUPLE_INDEX_MASK = 0x0FFF;
for (unsigned i = 0; i < tupleVariationCount; i++) {
uint16_t variationDataSize;
uint16_t tupleIndex;
if (!subtable.ReadU16(&variationDataSize) ||
!subtable.ReadU16(&tupleIndex)) {
return OTS_FAILURE_MSG("Failed to read tuple variation header");
}
if (tupleIndex & EMBEDDED_PEAK_TUPLE) {
for (unsigned axis = 0; axis < axisCount; axis++) {
int16_t coordinate;
if (!subtable.ReadS16(&coordinate)) {
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
}
if (coordinate < -0x4000 || coordinate > 0x4000) {
return OTS_FAILURE_MSG("Invalid tuple coordinate");
}
}
}
if (tupleIndex & INTERMEDIATE_REGION) {
std::vector<int16_t> startTuple(axisCount);
for (unsigned axis = 0; axis < axisCount; axis++) {
int16_t coordinate;
if (!subtable.ReadS16(&coordinate)) {
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
}
if (coordinate < -0x4000 || coordinate > 0x4000) {
return OTS_FAILURE_MSG("Invalid tuple coordinate");
}
startTuple.push_back(coordinate);
}
std::vector<int16_t> endTuple(axisCount);
for (unsigned axis = 0; axis < axisCount; axis++) {
int16_t coordinate;
if (!subtable.ReadS16(&coordinate)) {
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
}
if (coordinate < -0x4000 || coordinate > 0x4000) {
return OTS_FAILURE_MSG("Invalid tuple coordinate");
}
endTuple.push_back(coordinate);
}
for (unsigned axis = 0; axis < axisCount; axis++) {
if (startTuple[axis] > endTuple[axis]) {
return OTS_FAILURE_MSG("Invalid intermediate range");
}
}
}
if (!(tupleIndex & EMBEDDED_PEAK_TUPLE)) {
tupleIndex &= TUPLE_INDEX_MASK;
if (tupleIndex >= sharedTupleCount) {
return OTS_FAILURE_MSG("Tuple index out of range");
}
}
}
// TODO: we don't attempt to interpret the serialized data block
return true;
}
} // namespace ots
#undef TABLE_NAME

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

@ -0,0 +1,26 @@
// Copyright (c) 2018 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef OTS_VARIATIONS_H_
#define OTS_VARIATIONS_H_
#include <vector>
#include "ots.h"
// Utility functions for OpenType variations common table formats.
namespace ots {
bool ParseItemVariationStore(const Font* font,
const uint8_t* data, const size_t length,
std::vector<uint16_t>* out_region_index_count = NULL);
bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length);
bool ParseVariationData(const Font* font, const uint8_t* data, size_t length,
size_t axisCount, size_t sharedTupleCount);
} // namespace ots
#endif // OTS_VARIATIONS_H_

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,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,51 +10,30 @@
// 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.
return font->vhea != NULL && font->vmtx != NULL;
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,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,48 +10,20 @@
// 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 is preserved.
return font->vmtx != NULL && font->vhea != NULL;
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

Some files were not shown because too many files have changed in this diff Show More