diff --git a/application/palemoon/components/nsBrowserGlue.js b/application/palemoon/components/nsBrowserGlue.js
index 9aa7c9bc3..6e071f8d5 100644
--- a/application/palemoon/components/nsBrowserGlue.js
+++ b/application/palemoon/components/nsBrowserGlue.js
@@ -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);
},
diff --git a/application/palemoon/components/preferences/security.js b/application/palemoon/components/preferences/security.js
index 9d5f302a2..54fab68ac 100644
--- a/application/palemoon/components/preferences/security.js
+++ b/application/palemoon/components/preferences/security.js
@@ -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;
- }
}
};
diff --git a/application/palemoon/components/preferences/security.xul b/application/palemoon/components/preferences/security.xul
index bc1625275..9aa3f7a8a 100644
--- a/application/palemoon/components/preferences/security.xul
+++ b/application/palemoon/components/preferences/security.xul
@@ -46,9 +46,9 @@
-
+
@@ -150,7 +150,7 @@
+ preference="security.cert_pinning.hpkp.enabled"/>
diff --git a/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js b/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js
index 3ee33669d..39870fd54 100644
--- a/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js
+++ b/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js
@@ -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();
diff --git a/devtools/shared/webconsole/test/test_network_security-hpkp.html b/devtools/shared/webconsole/test/test_network_security-hpkp.html
index 55e2621a8..bc1a9642c 100644
--- a/devtools/shared/webconsole/test/test_network_security-hpkp.html
+++ b/devtools/shared/webconsole/test/test_network_security-hpkp.html
@@ -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"]
diff --git a/gfx/ots/LICENSE b/gfx/ots/LICENSE
index a7531cf7c..d5e3bff5a 100644
--- a/gfx/ots/LICENSE
+++ b/gfx/ots/LICENSE
@@ -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.
diff --git a/gfx/ots/README.mozilla b/gfx/ots/README.mozilla
index b96826735..0353a99c5 100644
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -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
diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-sanitiser.h
index 87d0f3d28..52cbd3aee 100644
--- a/gfx/ots/include/opentype-sanitiser.h
+++ b/gfx/ots/include/opentype-sanitiser.h
@@ -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
#include
+#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
@@ -52,7 +56,7 @@ typedef unsigned __int64 uint64_t;
#include
#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(4) - chksum_offset);
uint32_t tmp = 0;
std::memcpy(reinterpret_cast(&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(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(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(&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.
diff --git a/gfx/ots/include/ots-memory-stream.h b/gfx/ots/include/ots-memory-stream.h
index 579da616f..cd5b089fa 100644
--- a/gfx/ots/include/ots-memory-stream.h
+++ b/gfx/ots/include/ots-memory-stream.h
@@ -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.
diff --git a/gfx/ots/ots-lz4.patch b/gfx/ots/ots-lz4.patch
new file mode 100644
index 000000000..4251e72d6
--- /dev/null
+++ b/gfx/ots/ots-lz4.patch
@@ -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
+
+ 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 decompressed(decompressed_size);
+- int ret = LZ4_decompress_safe_partial(
++ size_t outputSize = 0;
++ bool ret = mozilla::Compression::LZ4::decompressPartial(
+ reinterpret_cast(data + table.offset()),
+- reinterpret_cast(decompressed.data()),
+ table.remaining(), // input buffer size (input size + padding)
++ reinterpret_cast(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
+
+ 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 decompressed(decompressed_size);
+- int ret = LZ4_decompress_safe_partial(
++ size_t outputSize = 0;
++ bool ret = mozilla::Compression::LZ4::decompressPartial(
+ reinterpret_cast(data + table.offset()),
+- reinterpret_cast(decompressed.data()),
+ table.remaining(), // input buffer size (input size + padding)
++ reinterpret_cast(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/ots-visibility.patch b/gfx/ots/ots-visibility.patch
index aeac81c4d..c1265472b 100644
--- a/gfx/ots/ots-visibility.patch
+++ b/gfx/ots/ots-visibility.patch
@@ -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
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.
diff --git a/gfx/ots/src/avar.cc b/gfx/ots/src/avar.cc
new file mode 100644
index 000000000..2a431b1c2
--- /dev/null
+++ b/gfx/ots/src/avar.cc
@@ -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(
+ 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
diff --git a/gfx/ots/src/avar.h b/gfx/ots/src/avar.h
new file mode 100644
index 000000000..756651c04
--- /dev/null
+++ b/gfx/ots/src/avar.h
@@ -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
+
+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> axisSegmentMaps;
+};
+
+} // namespace ots
+
+#endif // OTS_AVAR_H_
diff --git a/gfx/ots/src/cff.cc b/gfx/ots/src/cff.cc
index 23b6dadac..b0affd510 100644
--- a/gfx/ots/src/cff.cc
+++ b/gfx/ots/src/cff.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012-2017 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -9,7 +9,8 @@
#include
#include "maxp.h"
-#include "cff_type2_charstring.h"
+#include "cff_charstring.h"
+#include "variations.h"
// CFF - PostScript font program (Compact Font Format) table
// http://www.microsoft.com/typography/otspec/cff.htm
@@ -28,6 +29,7 @@ enum DICT_OPERAND_TYPE {
enum DICT_DATA_TYPE {
DICT_DATA_TOPLEVEL,
DICT_DATA_FDARRAY,
+ DICT_DATA_PRIVATE,
};
enum FONT_FORMAT {
@@ -39,7 +41,9 @@ enum FONT_FORMAT {
// see Appendix. A
const size_t kNStdString = 390;
-bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
+typedef std::pair Operand;
+
+bool ReadOffset(ots::Buffer &table, uint8_t off_size, uint32_t *offset) {
if (off_size > 4) {
return OTS_FAILURE();
}
@@ -47,7 +51,7 @@ bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
uint32_t tmp32 = 0;
for (unsigned i = 0; i < off_size; ++i) {
uint8_t tmp8 = 0;
- if (!table->ReadU8(&tmp8)) {
+ if (!table.ReadU8(&tmp8)) {
return OTS_FAILURE();
}
tmp32 <<= 8;
@@ -57,39 +61,47 @@ bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
return true;
}
-bool ParseIndex(ots::Buffer *table, ots::CFFIndex *index) {
- index->off_size = 0;
- index->offsets.clear();
+bool ParseIndex(ots::Buffer &table, ots::CFFIndex &index, bool cff2 = false) {
+ index.off_size = 0;
+ index.offsets.clear();
- if (!table->ReadU16(&(index->count))) {
- return OTS_FAILURE();
+ if (cff2) {
+ if (!table.ReadU32(&(index.count))) {
+ return OTS_FAILURE();
+ }
+ } else {
+ uint16_t count;
+ if (!table.ReadU16(&count)) {
+ return OTS_FAILURE();
+ }
+ index.count = count;
}
- if (index->count == 0) {
+
+ if (index.count == 0) {
// An empty INDEX.
- index->offset_to_next = table->offset();
+ index.offset_to_next = table.offset();
return true;
}
- if (!table->ReadU8(&(index->off_size))) {
+ if (!table.ReadU8(&(index.off_size))) {
return OTS_FAILURE();
}
- if ((index->off_size == 0) ||
- (index->off_size > 4)) {
+ if (index.off_size < 1 || index.off_size > 4) {
return OTS_FAILURE();
}
- const size_t array_size = (index->count + 1) * index->off_size;
+ const size_t array_size = (index.count + 1) * index.off_size;
// less than ((64k + 1) * 4), thus does not overflow.
- const size_t object_data_offset = table->offset() + array_size;
+ const size_t object_data_offset = table.offset() + array_size;
// does not overflow too, since offset() <= 1GB.
- if (object_data_offset >= table->length()) {
+ if (object_data_offset >= table.length()) {
return OTS_FAILURE();
}
- for (unsigned i = 0; i <= index->count; ++i) { // '<=' is not a typo.
+ for (unsigned i = 0; i <= index.count; ++i) { // '<=' is not a typo.
uint32_t rel_offset = 0;
- if (!ReadOffset(table, index->off_size, &rel_offset)) {
+ if (!ReadOffset(table, index.off_size, &rel_offset)) {
return OTS_FAILURE();
}
if (rel_offset < 1) {
@@ -99,69 +111,64 @@ bool ParseIndex(ots::Buffer *table, ots::CFFIndex *index) {
return OTS_FAILURE();
}
- if (rel_offset > table->length()) {
+ if (rel_offset > table.length()) {
return OTS_FAILURE();
}
// does not underflow.
- if (object_data_offset > table->length() - (rel_offset - 1)) {
+ if (object_data_offset > table.length() - (rel_offset - 1)) {
return OTS_FAILURE();
}
- index->offsets.push_back(
+ index.offsets.push_back(
object_data_offset + (rel_offset - 1)); // less than length(), 1GB.
}
- for (unsigned i = 1; i < index->offsets.size(); ++i) {
+ for (unsigned i = 1; i < index.offsets.size(); ++i) {
// We allow consecutive identical offsets here for zero-length strings.
// See http://crbug.com/69341 for more details.
- if (index->offsets[i] < index->offsets[i - 1]) {
+ if (index.offsets[i] < index.offsets[i - 1]) {
return OTS_FAILURE();
}
}
- index->offset_to_next = index->offsets.back();
+ index.offset_to_next = index.offsets.back();
return true;
}
bool ParseNameData(
ots::Buffer *table, const ots::CFFIndex &index, std::string* out_name) {
uint8_t name[256] = {0};
- if (index.offsets.size() == 0) { // just in case.
+
+ const size_t length = index.offsets[1] - index.offsets[0];
+ // font names should be no longer than 127 characters.
+ if (length > 127) {
return OTS_FAILURE();
}
- for (unsigned i = 1; i < index.offsets.size(); ++i) {
- const size_t length = index.offsets[i] - index.offsets[i - 1];
- // font names should be no longer than 127 characters.
- if (length > 127) {
+
+ table->set_offset(index.offsets[0]);
+ if (!table->Read(name, length)) {
+ return OTS_FAILURE();
+ }
+
+ for (size_t i = 0; i < length; ++i) {
+ // setting the first byte to NUL is allowed.
+ if (i == 0 && name[i] == 0) continue;
+ // non-ASCII characters are not recommended (except the first character).
+ if (name[i] < 33 || name[i] > 126) {
return OTS_FAILURE();
}
-
- table->set_offset(index.offsets[i - 1]);
- if (!table->Read(name, length)) {
+ // [, ], ... are not allowed.
+ if (std::strchr("[](){}<>/% ", name[i])) {
return OTS_FAILURE();
}
-
- for (size_t j = 0; j < length; ++j) {
- // setting the first byte to NUL is allowed.
- if (j == 0 && name[j] == 0) continue;
- // non-ASCII characters are not recommended (except the first character).
- if (name[j] < 33 || name[j] > 126) {
- return OTS_FAILURE();
- }
- // [, ], ... are not allowed.
- if (std::strchr("[](){}<>/% ", name[j])) {
- return OTS_FAILURE();
- }
- }
}
*out_name = reinterpret_cast(name);
return true;
}
-bool CheckOffset(const std::pair& operand,
- size_t table_length) {
+bool CheckOffset(const Operand& operand, size_t table_length) {
if (operand.second != DICT_OPERAND_INTEGER) {
return OTS_FAILURE();
}
@@ -171,8 +178,7 @@ bool CheckOffset(const std::pair& operand,
return true;
}
-bool CheckSid(const std::pair& operand,
- size_t sid_max) {
+bool CheckSid(const Operand& operand, size_t sid_max) {
if (operand.second != DICT_OPERAND_INTEGER) {
return OTS_FAILURE();
}
@@ -182,30 +188,28 @@ bool CheckSid(const std::pair& operand,
return true;
}
-bool ParseDictDataBcd(
- ots::Buffer *table,
- std::vector > *operands) {
+bool ParseDictDataBcd(ots::Buffer &table, std::vector &operands) {
bool read_decimal_point = false;
bool read_e = false;
uint8_t nibble = 0;
size_t count = 0;
while (true) {
- if (!table->ReadU8(&nibble)) {
+ if (!table.ReadU8(&nibble)) {
return OTS_FAILURE();
}
if ((nibble & 0xf0) == 0xf0) {
if ((nibble & 0xf) == 0xf) {
// TODO(yusukes): would be better to store actual double value,
// rather than the dummy integer.
- operands->push_back(std::make_pair(static_cast(0),
+ operands.push_back(std::make_pair(static_cast(0),
DICT_OPERAND_REAL));
return true;
}
return OTS_FAILURE();
}
if ((nibble & 0x0f) == 0x0f) {
- operands->push_back(std::make_pair(static_cast(0),
+ operands.push_back(std::make_pair(static_cast(0),
DICT_OPERAND_REAL));
return true;
}
@@ -242,18 +246,17 @@ bool ParseDictDataBcd(
}
}
-bool ParseDictDataEscapedOperator(
- ots::Buffer *table,
- std::vector > *operands) {
+bool ParseDictDataEscapedOperator(ots::Buffer &table,
+ std::vector &operands) {
uint8_t op = 0;
- if (!table->ReadU8(&op)) {
+ if (!table.ReadU8(&op)) {
return OTS_FAILURE();
}
if ((op <= 14) ||
(op >= 17 && op <= 23) ||
(op >= 30 && op <= 38)) {
- operands->push_back(std::make_pair((12U << 8) + op, DICT_OPERATOR));
+ operands.push_back(std::make_pair((12U << 8) + op, DICT_OPERATOR));
return true;
}
@@ -261,9 +264,8 @@ bool ParseDictDataEscapedOperator(
return OTS_FAILURE();
}
-bool ParseDictDataNumber(
- ots::Buffer *table, uint8_t b0,
- std::vector > *operands) {
+bool ParseDictDataNumber(ots::Buffer &table, uint8_t b0,
+ std::vector &operands) {
uint8_t b1 = 0;
uint8_t b2 = 0;
uint8_t b3 = 0;
@@ -271,22 +273,22 @@ bool ParseDictDataNumber(
switch (b0) {
case 28: // shortint
- if (!table->ReadU8(&b1) ||
- !table->ReadU8(&b2)) {
+ if (!table.ReadU8(&b1) ||
+ !table.ReadU8(&b2)) {
return OTS_FAILURE();
}
- operands->push_back(std::make_pair(
+ operands.push_back(std::make_pair(
static_cast((b1 << 8) + b2), DICT_OPERAND_INTEGER));
return true;
case 29: // longint
- if (!table->ReadU8(&b1) ||
- !table->ReadU8(&b2) ||
- !table->ReadU8(&b3) ||
- !table->ReadU8(&b4)) {
+ if (!table.ReadU8(&b1) ||
+ !table.ReadU8(&b2) ||
+ !table.ReadU8(&b3) ||
+ !table.ReadU8(&b4)) {
return OTS_FAILURE();
}
- operands->push_back(std::make_pair(
+ operands.push_back(std::make_pair(
static_cast((b1 << 24) + (b2 << 16) + (b3 << 8) + b4),
DICT_OPERAND_INTEGER));
return true;
@@ -302,12 +304,12 @@ bool ParseDictDataNumber(
if (b0 >=32 && b0 <=246) {
result = b0 - 139;
} else if (b0 >=247 && b0 <= 250) {
- if (!table->ReadU8(&b1)) {
+ if (!table.ReadU8(&b1)) {
return OTS_FAILURE();
}
result = (b0 - 247) * 256 + b1 + 108;
} else if (b0 >= 251 && b0 <= 254) {
- if (!table->ReadU8(&b1)) {
+ if (!table.ReadU8(&b1)) {
return OTS_FAILURE();
}
result = -(b0 - 251) * 256 + b1 - 108;
@@ -315,22 +317,21 @@ bool ParseDictDataNumber(
return OTS_FAILURE();
}
- operands->push_back(std::make_pair(result, DICT_OPERAND_INTEGER));
+ operands.push_back(std::make_pair(result, DICT_OPERAND_INTEGER));
return true;
}
-bool ParseDictDataReadNext(
- ots::Buffer *table,
- std::vector > *operands) {
+bool ParseDictDataReadNext(ots::Buffer &table,
+ std::vector &operands) {
uint8_t op = 0;
- if (!table->ReadU8(&op)) {
+ if (!table.ReadU8(&op)) {
return OTS_FAILURE();
}
- if (op <= 21) {
+ if (op <= 24) {
if (op == 12) {
return ParseDictDataEscapedOperator(table, operands);
}
- operands->push_back(std::make_pair(
+ operands.push_back(std::make_pair(
static_cast(op), DICT_OPERATOR));
return true;
} else if (op <= 27 || op == 31 || op == 255) {
@@ -341,12 +342,69 @@ bool ParseDictDataReadNext(
return ParseDictDataNumber(table, op, operands);
}
+bool OperandsOverflow(std::vector& operands, bool cff2) {
+ // An operator may be preceded by up to a maximum of 48 operands in CFF1 and
+ // 513 operands in CFF2.
+ if ((cff2 && operands.size() > ots::kMaxCFF2ArgumentStack) ||
+ (!cff2 && operands.size() > ots::kMaxCFF1ArgumentStack)) {
+ return true;
+ }
+ return false;
+}
+
+bool ParseDictDataReadOperands(ots::Buffer& dict,
+ std::vector& operands,
+ bool cff2) {
+ if (!ParseDictDataReadNext(dict, operands)) {
+ return OTS_FAILURE();
+ }
+ if (operands.empty()) {
+ return OTS_FAILURE();
+ }
+ if (OperandsOverflow(operands, cff2)) {
+ return OTS_FAILURE();
+ }
+ return true;
+}
+
+bool ValidCFF2DictOp(uint32_t op, DICT_DATA_TYPE type) {
+ if (type == DICT_DATA_TOPLEVEL) {
+ switch (op) {
+ case (12U << 8) + 7: // FontMatrix
+ case 17: // CharStrings
+ case (12U << 8) + 36: // FDArray
+ case (12U << 8) + 37: // FDSelect
+ case 24: // vstore
+ return true;
+ default:
+ return false;
+ }
+ } else if (type == DICT_DATA_FDARRAY) {
+ if (op == 18) // Private DICT
+ return true;
+ } else if (type == DICT_DATA_PRIVATE) {
+ switch (op) {
+ case (12U << 8) + 14: // ForceBold
+ case (12U << 8) + 19: // initialRandomSeed
+ case 20: // defaultWidthX
+ case 21: // nominalWidthX
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool ParsePrivateDictData(
- const uint8_t *data,
- size_t table_length, size_t offset, size_t dict_length,
+ ots::Buffer &table, size_t offset, size_t dict_length,
DICT_DATA_TYPE type, ots::OpenTypeCFF *out_cff) {
- ots::Buffer table(data + offset, dict_length);
- std::vector > operands;
+ ots::Buffer dict(table.buffer() + offset, dict_length);
+ std::vector operands;
+ bool cff2 = (out_cff->major == 2);
+ bool blend_seen = false;
+ uint32_t vsindex = 0;
// Since a Private DICT for FDArray might not have a Local Subr (e.g. Hiragino
// Kaku Gothic Std W8), we create an empty Local Subr here to match the size
@@ -355,15 +413,8 @@ bool ParsePrivateDictData(
out_cff->local_subrs_per_font.push_back(new ots::CFFIndex);
}
- while (table.offset() < dict_length) {
- if (!ParseDictDataReadNext(&table, &operands)) {
- return OTS_FAILURE();
- }
- if (operands.empty()) {
- return OTS_FAILURE();
- }
- if (operands.size() > 48) {
- // An operator may be preceded by up to a maximum of 48 operands.
+ while (dict.offset() < dict.length()) {
+ if (!ParseDictDataReadOperands(dict, operands, cff2)) {
return OTS_FAILURE();
}
if (operands.back().second != DICT_OPERATOR) {
@@ -374,13 +425,18 @@ bool ParsePrivateDictData(
const uint32_t op = operands.back().first;
operands.pop_back();
+ if (cff2 && !ValidCFF2DictOp(op, DICT_DATA_PRIVATE)) {
+ return OTS_FAILURE();
+ }
+
+ bool clear_operands = true;
switch (op) {
// hints
case 6: // BlueValues
case 7: // OtherBlues
case 8: // FamilyBlues
case 9: // FamilyOtherBlues
- if (operands.empty() || (operands.size() % 2) != 0) {
+ if ((operands.size() % 2) != 0) {
return OTS_FAILURE();
}
break;
@@ -420,12 +476,11 @@ bool ParsePrivateDictData(
if (operands.back().first >= 1024 * 1024 * 1024) {
return OTS_FAILURE();
}
- if (operands.back().first + offset >= table_length) {
+ if (operands.back().first + offset >= table.length()) {
return OTS_FAILURE();
}
// parse "16. Local Subrs INDEX"
- ots::Buffer cff_table(data, table_length);
- cff_table.set_offset(operands.back().first + offset);
+ table.set_offset(operands.back().first + offset);
ots::CFFIndex *local_subrs_index = NULL;
if (type == DICT_DATA_FDARRAY) {
if (out_cff->local_subrs_per_font.empty()) {
@@ -439,7 +494,7 @@ bool ParsePrivateDictData(
local_subrs_index = new ots::CFFIndex;
out_cff->local_subrs = local_subrs_index;
}
- if (!ParseIndex(&cff_table, local_subrs_index)) {
+ if (!ParseIndex(table, *local_subrs_index, cff2)) {
return OTS_FAILURE();
}
break;
@@ -458,42 +513,125 @@ bool ParsePrivateDictData(
}
break;
+ case 22: { // vsindex
+ if (!cff2) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (blend_seen) {
+ return OTS_FAILURE();
+ }
+ vsindex = operands.back().first;
+ if (vsindex >= out_cff->region_index_count.size()) {
+ return OTS_FAILURE();
+ }
+ break;
+ }
+
+ case 23: { // blend
+ if (!cff2) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() < 1) {
+ return OTS_FAILURE();
+ }
+ if (vsindex >= out_cff->region_index_count.size()) {
+ return OTS_FAILURE();
+ }
+ uint16_t k = out_cff->region_index_count.at(vsindex);
+ uint16_t n = operands.back().first;
+ if (operands.size() < n * (k + 1) + 1) {
+ return OTS_FAILURE();
+ }
+ size_t operands_size = operands.size();
+ // 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 (operands.size() > operands_size - ((n * k) + 1))
+ operands.pop_back();
+ clear_operands = false;
+ blend_seen = true;
+ break;
+ }
+
default:
return OTS_FAILURE();
}
- operands.clear();
+ if (clear_operands) {
+ operands.clear();
+ }
}
return true;
}
-bool ParseDictData(const uint8_t *data, size_t table_length,
- const ots::CFFIndex &index, uint16_t glyphs,
- size_t sid_max, DICT_DATA_TYPE type,
+bool ParseVariationStore(ots::OpenTypeCFF& out_cff, ots::Buffer& table) {
+ uint16_t length;
+
+ if (!table.ReadU16(&length)) {
+ return OTS_FAILURE();
+ }
+
+ // Empty VariationStore is allowed.
+ if (!length) {
+ return true;
+ }
+
+ if (length > table.remaining()) {
+ return OTS_FAILURE();
+ }
+
+ if (!ParseItemVariationStore(out_cff.GetFont(),
+ table.buffer() + table.offset(), length,
+ &(out_cff.region_index_count))) {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+bool ParseDictData(ots::Buffer& table, ots::Buffer& dict,
+ uint16_t glyphs, size_t sid_max, DICT_DATA_TYPE type,
+ ots::OpenTypeCFF *out_cff);
+
+bool ParseDictData(ots::Buffer& table, const ots::CFFIndex &index,
+ uint16_t glyphs, size_t sid_max, DICT_DATA_TYPE type,
ots::OpenTypeCFF *out_cff) {
for (unsigned i = 1; i < index.offsets.size(); ++i) {
- if (type == DICT_DATA_TOPLEVEL) {
- out_cff->char_strings_array.push_back(new ots::CFFIndex);
- }
size_t dict_length = index.offsets[i] - index.offsets[i - 1];
- ots::Buffer table(data + index.offsets[i - 1], dict_length);
+ ots::Buffer dict(table.buffer() + index.offsets[i - 1], dict_length);
- std::vector > operands;
+ if (!ParseDictData(table, dict, glyphs, sid_max, type, out_cff)) {
+ return OTS_FAILURE();
+ }
+ }
+ return true;
+}
- FONT_FORMAT font_format = FORMAT_UNKNOWN;
- bool have_ros = false;
- uint16_t charstring_glyphs = 0;
- size_t charset_offset = 0;
+bool ParseDictData(ots::Buffer& table, ots::Buffer& dict,
+ uint16_t glyphs, size_t sid_max, DICT_DATA_TYPE type,
+ ots::OpenTypeCFF *out_cff) {
+ bool cff2 = (out_cff->major == 2);
+ std::vector operands;
- while (table.offset() < dict_length) {
- if (!ParseDictDataReadNext(&table, &operands)) {
- return OTS_FAILURE();
- }
- if (operands.empty()) {
- return OTS_FAILURE();
- }
- if (operands.size() > 48) {
- // An operator may be preceded by up to a maximum of 48 operands.
+ FONT_FORMAT font_format = FORMAT_UNKNOWN;
+ bool have_ros = false;
+ bool have_charstrings = false;
+ bool have_vstore = false;
+ size_t charset_offset = 0;
+
+ if (cff2) {
+ // Parse VariationStore first, since it might be referenced in other places
+ // (e.g. FDArray) that might be parsed after it.
+ size_t dict_offset = dict.offset();
+ while (dict.offset() < dict.length()) {
+ if (!ParseDictDataReadOperands(dict, operands, cff2)) {
return OTS_FAILURE();
}
if (operands.back().second != DICT_OPERATOR) continue;
@@ -502,399 +640,503 @@ bool ParseDictData(const uint8_t *data, size_t table_length,
const uint32_t op = operands.back().first;
operands.pop_back();
- switch (op) {
- // SID
- case 0: // version
- case 1: // Notice
- case 2: // Copyright
- case 3: // FullName
- case 4: // FamilyName
- case (12U << 8) + 0: // Copyright
- case (12U << 8) + 21: // PostScript
- case (12U << 8) + 22: // BaseFontName
- case (12U << 8) + 38: // FontName
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if (!CheckSid(operands.back(), sid_max)) {
- return OTS_FAILURE();
- }
- break;
-
- // array
- case 5: // FontBBox
- case 14: // XUID
- case (12U << 8) + 7: // FontMatrix
- case (12U << 8) + 23: // BaseFontBlend (delta)
- if (operands.empty()) {
- return OTS_FAILURE();
- }
- break;
-
- // number
- case 13: // UniqueID
- case (12U << 8) + 2: // ItalicAngle
- case (12U << 8) + 3: // UnderlinePosition
- case (12U << 8) + 4: // UnderlineThickness
- case (12U << 8) + 5: // PaintType
- case (12U << 8) + 8: // StrokeWidth
- case (12U << 8) + 20: // SyntheticBase
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- break;
- case (12U << 8) + 31: // CIDFontVersion
- case (12U << 8) + 32: // CIDFontRevision
- case (12U << 8) + 33: // CIDFontType
- case (12U << 8) + 34: // CIDCount
- case (12U << 8) + 35: // UIDBase
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if (font_format != FORMAT_CID_KEYED) {
- return OTS_FAILURE();
- }
- break;
- case (12U << 8) + 6: // CharstringType
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if(operands.back().second != DICT_OPERAND_INTEGER) {
- return OTS_FAILURE();
- }
- if (operands.back().first != 2) {
- // We only support the "Type 2 Charstring Format."
- // TODO(yusukes): Support Type 1 format? Is that still in use?
- return OTS_FAILURE();
- }
- break;
-
- // boolean
- case (12U << 8) + 1: // isFixedPitch
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if (operands.back().second != DICT_OPERAND_INTEGER) {
- return OTS_FAILURE();
- }
- if (operands.back().first >= 2) {
- return OTS_FAILURE();
- }
- break;
-
- // offset(0)
- case 15: // charset
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if (operands.back().first <= 2) {
- // predefined charset, ISOAdobe, Expert or ExpertSubset, is used.
- break;
- }
- if (!CheckOffset(operands.back(), table_length)) {
- return OTS_FAILURE();
- }
- if (charset_offset) {
- return OTS_FAILURE(); // multiple charset tables?
- }
- charset_offset = operands.back().first;
- break;
-
- case 16: { // Encoding
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if (operands.back().first <= 1) {
- break; // predefined encoding, "Standard" or "Expert", is used.
- }
- if (!CheckOffset(operands.back(), table_length)) {
- return OTS_FAILURE();
- }
-
- // parse sub dictionary INDEX.
- ots::Buffer cff_table(data, table_length);
- cff_table.set_offset(operands.back().first);
- uint8_t format = 0;
- if (!cff_table.ReadU8(&format)) {
- return OTS_FAILURE();
- }
- if (format & 0x80) {
- // supplemental encoding is not supported at the moment.
- return OTS_FAILURE();
- }
- // TODO(yusukes): support & parse supplemental encoding tables.
- break;
- }
-
- case 17: { // CharStrings
- if (type != DICT_DATA_TOPLEVEL) {
- return OTS_FAILURE();
- }
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if (!CheckOffset(operands.back(), table_length)) {
- return OTS_FAILURE();
- }
- // parse "14. CharStrings INDEX"
- ots::Buffer cff_table(data, table_length);
- cff_table.set_offset(operands.back().first);
- ots::CFFIndex *charstring_index = out_cff->char_strings_array.back();
- if (!ParseIndex(&cff_table, charstring_index)) {
- return OTS_FAILURE();
- }
- if (charstring_index->count < 2) {
- return OTS_FAILURE();
- }
- if (charstring_glyphs) {
- return OTS_FAILURE(); // multiple charstring tables?
- }
- charstring_glyphs = charstring_index->count;
- if (charstring_glyphs != glyphs) {
- return OTS_FAILURE(); // CFF and maxp have different number of glyphs?
- }
- break;
- }
-
- case (12U << 8) + 36: { // FDArray
- if (type != DICT_DATA_TOPLEVEL) {
- return OTS_FAILURE();
- }
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if (!CheckOffset(operands.back(), table_length)) {
- return OTS_FAILURE();
- }
-
- // parse sub dictionary INDEX.
- ots::Buffer cff_table(data, table_length);
- cff_table.set_offset(operands.back().first);
- ots::CFFIndex sub_dict_index;
- if (!ParseIndex(&cff_table, &sub_dict_index)) {
- return OTS_FAILURE();
- }
- if (!ParseDictData(data, table_length,
- sub_dict_index,
- glyphs, sid_max, DICT_DATA_FDARRAY,
- out_cff)) {
- return OTS_FAILURE();
- }
- if (out_cff->font_dict_length != 0) {
- return OTS_FAILURE(); // two or more FDArray found.
- }
- out_cff->font_dict_length = sub_dict_index.count;
- break;
- }
-
- case (12U << 8) + 37: { // FDSelect
- if (type != DICT_DATA_TOPLEVEL) {
- return OTS_FAILURE();
- }
- if (operands.size() != 1) {
- return OTS_FAILURE();
- }
- if (!CheckOffset(operands.back(), table_length)) {
- return OTS_FAILURE();
- }
-
- // parse FDSelect data structure
- ots::Buffer cff_table(data, table_length);
- cff_table.set_offset(operands.back().first);
- uint8_t format = 0;
- if (!cff_table.ReadU8(&format)) {
- return OTS_FAILURE();
- }
- if (format == 0) {
- for (uint16_t j = 0; j < glyphs; ++j) {
- uint8_t fd_index = 0;
- if (!cff_table.ReadU8(&fd_index)) {
- return OTS_FAILURE();
- }
- (out_cff->fd_select)[j] = fd_index;
- }
- } else if (format == 3) {
- uint16_t n_ranges = 0;
- if (!cff_table.ReadU16(&n_ranges)) {
- return OTS_FAILURE();
- }
- if (n_ranges == 0) {
- return OTS_FAILURE();
- }
-
- uint16_t last_gid = 0;
- uint8_t fd_index = 0;
- for (unsigned j = 0; j < n_ranges; ++j) {
- uint16_t first = 0; // GID
- if (!cff_table.ReadU16(&first)) {
- return OTS_FAILURE();
- }
-
- // Sanity checks.
- if ((j == 0) && (first != 0)) {
- return OTS_FAILURE();
- }
- if ((j != 0) && (last_gid >= first)) {
- return OTS_FAILURE(); // not increasing order.
- }
-
- // Copy the mapping to |out_cff->fd_select|.
- if (j != 0) {
- for (uint16_t k = last_gid; k < first; ++k) {
- if (!out_cff->fd_select.insert(
- std::make_pair(k, fd_index)).second) {
- return OTS_FAILURE();
- }
- }
- }
-
- if (!cff_table.ReadU8(&fd_index)) {
- return OTS_FAILURE();
- }
- last_gid = first;
- // TODO(yusukes): check GID?
- }
- uint16_t sentinel = 0;
- if (!cff_table.ReadU16(&sentinel)) {
- return OTS_FAILURE();
- }
- if (last_gid >= sentinel) {
- return OTS_FAILURE();
- }
- for (uint16_t k = last_gid; k < sentinel; ++k) {
- if (!out_cff->fd_select.insert(
- std::make_pair(k, fd_index)).second) {
- return OTS_FAILURE();
- }
- }
- } else {
- // unknown format
- return OTS_FAILURE();
- }
- break;
- }
-
- // Private DICT (2 * number)
- case 18: {
- if (operands.size() != 2) {
- return OTS_FAILURE();
- }
- if (operands.back().second != DICT_OPERAND_INTEGER) {
- return OTS_FAILURE();
- }
- const uint32_t private_offset = operands.back().first;
- operands.pop_back();
- if (operands.back().second != DICT_OPERAND_INTEGER) {
- return OTS_FAILURE();
- }
- const uint32_t private_length = operands.back().first;
- if (private_offset > table_length) {
- return OTS_FAILURE();
- }
- if (private_length >= table_length) {
- return OTS_FAILURE();
- }
- if (private_length + private_offset > table_length) {
- return OTS_FAILURE();
- }
- // parse "15. Private DICT Data"
- if (!ParsePrivateDictData(data, table_length,
- private_offset, private_length,
- type, out_cff)) {
- return OTS_FAILURE();
- }
- break;
- }
-
- // ROS
- case (12U << 8) + 30:
- if (font_format != FORMAT_UNKNOWN) {
- return OTS_FAILURE();
- }
- font_format = FORMAT_CID_KEYED;
- if (operands.size() != 3) {
- return OTS_FAILURE();
- }
- // check SIDs
- operands.pop_back(); // ignore the first number.
- if (!CheckSid(operands.back(), sid_max)) {
- return OTS_FAILURE();
- }
- operands.pop_back();
- if (!CheckSid(operands.back(), sid_max)) {
- return OTS_FAILURE();
- }
- if (have_ros) {
- return OTS_FAILURE(); // multiple ROS tables?
- }
- have_ros = true;
- break;
-
- default:
+ if (op == 24) { // vstore
+ if (type != DICT_DATA_TOPLEVEL) {
return OTS_FAILURE();
+ }
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckOffset(operands.back(), table.length())) {
+ return OTS_FAILURE();
+ }
+ // parse "VariationStore Data Contents"
+ table.set_offset(operands.back().first);
+ if (!ParseVariationStore(*out_cff, table)) {
+ return OTS_FAILURE();
+ }
+ break;
}
operands.clear();
+ }
+ operands.clear();
+ dict.set_offset(dict_offset);
+ }
- if (font_format == FORMAT_UNKNOWN) {
- font_format = FORMAT_OTHER;
- }
+ while (dict.offset() < dict.length()) {
+ if (!ParseDictDataReadOperands(dict, operands, cff2)) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERATOR) continue;
+
+ // got operator
+ const uint32_t op = operands.back().first;
+ operands.pop_back();
+
+ if (cff2 && !ValidCFF2DictOp(op, type)) {
+ return OTS_FAILURE();
}
- // parse "13. Charsets"
- if (charset_offset) {
- ots::Buffer cff_table(data, table_length);
- cff_table.set_offset(charset_offset);
- uint8_t format = 0;
- if (!cff_table.ReadU8(&format)) {
- return OTS_FAILURE();
- }
- switch (format) {
- case 0:
- for (uint16_t j = 1 /* .notdef is omitted */; j < glyphs; ++j) {
- uint16_t sid = 0;
- if (!cff_table.ReadU16(&sid)) {
- return OTS_FAILURE();
- }
- if (!have_ros && (sid > sid_max)) {
- return OTS_FAILURE();
- }
- // TODO(yusukes): check CIDs when have_ros is true.
- }
- break;
+ switch (op) {
+ // SID
+ case 0: // version
+ case 1: // Notice
+ case 2: // Copyright
+ case 3: // FullName
+ case 4: // FamilyName
+ case (12U << 8) + 0: // Copyright
+ case (12U << 8) + 21: // PostScript
+ case (12U << 8) + 22: // BaseFontName
+ case (12U << 8) + 38: // FontName
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckSid(operands.back(), sid_max)) {
+ return OTS_FAILURE();
+ }
+ break;
- case 1:
- case 2: {
- uint32_t total = 1; // .notdef is omitted.
- while (total < glyphs) {
- uint16_t sid = 0;
- if (!cff_table.ReadU16(&sid)) {
- return OTS_FAILURE();
- }
- if (!have_ros && (sid > sid_max)) {
- return OTS_FAILURE();
- }
- // TODO(yusukes): check CIDs when have_ros is true.
+ // array
+ case 5: // FontBBox
+ case 14: // XUID
+ case (12U << 8) + 7: // FontMatrix
+ case (12U << 8) + 23: // BaseFontBlend (delta)
+ if (operands.empty()) {
+ return OTS_FAILURE();
+ }
+ break;
- if (format == 1) {
- uint8_t left = 0;
- if (!cff_table.ReadU8(&left)) {
- return OTS_FAILURE();
- }
- total += (left + 1);
- } else {
- uint16_t left = 0;
- if (!cff_table.ReadU16(&left)) {
- return OTS_FAILURE();
- }
- total += (left + 1);
- }
- }
+ // number
+ case 13: // UniqueID
+ case (12U << 8) + 2: // ItalicAngle
+ case (12U << 8) + 3: // UnderlinePosition
+ case (12U << 8) + 4: // UnderlineThickness
+ case (12U << 8) + 5: // PaintType
+ case (12U << 8) + 8: // StrokeWidth
+ case (12U << 8) + 20: // SyntheticBase
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ break;
+ case (12U << 8) + 31: // CIDFontVersion
+ case (12U << 8) + 32: // CIDFontRevision
+ case (12U << 8) + 33: // CIDFontType
+ case (12U << 8) + 34: // CIDCount
+ case (12U << 8) + 35: // UIDBase
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (font_format != FORMAT_CID_KEYED) {
+ return OTS_FAILURE();
+ }
+ break;
+ case (12U << 8) + 6: // CharstringType
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if(operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first != 2) {
+ // We only support the "Type 2 Charstring Format."
+ // TODO(yusukes): Support Type 1 format? Is that still in use?
+ return OTS_FAILURE();
+ }
+ break;
+
+ // boolean
+ case (12U << 8) + 1: // isFixedPitch
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first >= 2) {
+ return OTS_FAILURE();
+ }
+ break;
+
+ // offset(0)
+ case 15: // charset
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first <= 2) {
+ // predefined charset, ISOAdobe, Expert or ExpertSubset, is used.
break;
}
-
- default:
+ if (!CheckOffset(operands.back(), table.length())) {
return OTS_FAILURE();
+ }
+ if (charset_offset) {
+ return OTS_FAILURE(); // multiple charset tables?
+ }
+ charset_offset = operands.back().first;
+ break;
+
+ case 16: { // Encoding
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().first <= 1) {
+ break; // predefined encoding, "Standard" or "Expert", is used.
+ }
+ if (!CheckOffset(operands.back(), table.length())) {
+ return OTS_FAILURE();
+ }
+
+ table.set_offset(operands.back().first);
+ uint8_t format = 0;
+ if (!table.ReadU8(&format)) {
+ return OTS_FAILURE();
+ }
+ if (format & 0x80) {
+ // supplemental encoding is not supported at the moment.
+ return OTS_FAILURE();
+ }
+ // TODO(yusukes): support & parse supplemental encoding tables.
+ break;
}
+
+ case 17: { // CharStrings
+ if (type != DICT_DATA_TOPLEVEL) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckOffset(operands.back(), table.length())) {
+ return OTS_FAILURE();
+ }
+ // parse "14. CharStrings INDEX"
+ table.set_offset(operands.back().first);
+ ots::CFFIndex *charstring_index = out_cff->charstrings_index;
+ if (!ParseIndex(table, *charstring_index, cff2)) {
+ return OTS_FAILURE();
+ }
+ if (charstring_index->count < 2) {
+ return OTS_FAILURE();
+ }
+ if (have_charstrings) {
+ return OTS_FAILURE(); // multiple charstring tables?
+ }
+ have_charstrings = true;
+ if (charstring_index->count != glyphs) {
+ return OTS_FAILURE(); // CFF and maxp have different number of glyphs?
+ }
+ break;
+ }
+
+ case 24: { // vstore
+ if (!cff2) {
+ return OTS_FAILURE();
+ }
+ if (have_vstore) {
+ return OTS_FAILURE(); // multiple vstore tables?
+ }
+ have_vstore = true;
+ // parsed above.
+ break;
+ }
+
+ case (12U << 8) + 36: { // FDArray
+ if (type != DICT_DATA_TOPLEVEL) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckOffset(operands.back(), table.length())) {
+ return OTS_FAILURE();
+ }
+
+ // parse Font DICT INDEX.
+ table.set_offset(operands.back().first);
+ ots::CFFIndex sub_dict_index;
+ if (!ParseIndex(table, sub_dict_index, cff2)) {
+ return OTS_FAILURE();
+ }
+ if (!ParseDictData(table, sub_dict_index,
+ glyphs, sid_max, DICT_DATA_FDARRAY,
+ out_cff)) {
+ return OTS_FAILURE();
+ }
+ if (out_cff->font_dict_length != 0) {
+ return OTS_FAILURE(); // two or more FDArray found.
+ }
+ out_cff->font_dict_length = sub_dict_index.count;
+ break;
+ }
+
+ case (12U << 8) + 37: { // FDSelect
+ if (type != DICT_DATA_TOPLEVEL) {
+ return OTS_FAILURE();
+ }
+ if (operands.size() != 1) {
+ return OTS_FAILURE();
+ }
+ if (!CheckOffset(operands.back(), table.length())) {
+ return OTS_FAILURE();
+ }
+
+ // parse FDSelect data structure
+ table.set_offset(operands.back().first);
+ uint8_t format = 0;
+ if (!table.ReadU8(&format)) {
+ return OTS_FAILURE();
+ }
+ if (format == 0) {
+ for (uint16_t j = 0; j < glyphs; ++j) {
+ uint8_t fd_index = 0;
+ if (!table.ReadU8(&fd_index)) {
+ return OTS_FAILURE();
+ }
+ (out_cff->fd_select)[j] = fd_index;
+ }
+ } else if (format == 3) {
+ uint16_t n_ranges = 0;
+ if (!table.ReadU16(&n_ranges)) {
+ return OTS_FAILURE();
+ }
+ if (n_ranges == 0) {
+ return OTS_FAILURE();
+ }
+
+ uint16_t last_gid = 0;
+ uint8_t fd_index = 0;
+ for (unsigned j = 0; j < n_ranges; ++j) {
+ uint16_t first = 0; // GID
+ if (!table.ReadU16(&first)) {
+ return OTS_FAILURE();
+ }
+
+ // Sanity checks.
+ if ((j == 0) && (first != 0)) {
+ return OTS_FAILURE();
+ }
+ if ((j != 0) && (last_gid >= first)) {
+ return OTS_FAILURE(); // not increasing order.
+ }
+ if (first >= glyphs) {
+ return OTS_FAILURE(); // invalid gid.
+ }
+
+ // Copy the mapping to |out_cff->fd_select|.
+ if (j != 0) {
+ for (auto k = last_gid; k < first; ++k) {
+ if (!out_cff->fd_select.insert(
+ std::make_pair(k, fd_index)).second) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+
+ if (!table.ReadU8(&fd_index)) {
+ return OTS_FAILURE();
+ }
+ last_gid = first;
+ }
+ uint16_t sentinel = 0;
+ if (!table.ReadU16(&sentinel)) {
+ return OTS_FAILURE();
+ }
+ if (last_gid >= sentinel) {
+ return OTS_FAILURE();
+ }
+ if (sentinel > glyphs) {
+ return OTS_FAILURE(); // invalid gid.
+ }
+ for (auto k = last_gid; k < sentinel; ++k) {
+ if (!out_cff->fd_select.insert(
+ std::make_pair(k, fd_index)).second) {
+ return OTS_FAILURE();
+ }
+ }
+ } else if (cff2 && format == 4) {
+ uint32_t n_ranges = 0;
+ if (!table.ReadU32(&n_ranges)) {
+ return OTS_FAILURE();
+ }
+ if (n_ranges == 0) {
+ return OTS_FAILURE();
+ }
+
+ uint32_t last_gid = 0;
+ uint16_t fd_index = 0;
+ for (unsigned j = 0; j < n_ranges; ++j) {
+ uint32_t first = 0; // GID
+ if (!table.ReadU32(&first)) {
+ return OTS_FAILURE();
+ }
+
+ // Sanity checks.
+ if ((j == 0) && (first != 0)) {
+ return OTS_FAILURE();
+ }
+ if ((j != 0) && (last_gid >= first)) {
+ return OTS_FAILURE(); // not increasing order.
+ }
+ if (first >= glyphs) {
+ return OTS_FAILURE(); // invalid gid.
+ }
+
+ // Copy the mapping to |out_cff->fd_select|.
+ if (j != 0) {
+ for (auto k = last_gid; k < first; ++k) {
+ if (!out_cff->fd_select.insert(
+ std::make_pair(k, fd_index)).second) {
+ return OTS_FAILURE();
+ }
+ }
+ }
+
+ if (!table.ReadU16(&fd_index)) {
+ return OTS_FAILURE();
+ }
+ last_gid = first;
+ }
+ uint32_t sentinel = 0;
+ if (!table.ReadU32(&sentinel)) {
+ return OTS_FAILURE();
+ }
+ if (last_gid >= sentinel) {
+ return OTS_FAILURE();
+ }
+ if (sentinel > glyphs) {
+ return OTS_FAILURE(); // invalid gid.
+ }
+ for (auto k = last_gid; k < sentinel; ++k) {
+ if (!out_cff->fd_select.insert(
+ std::make_pair(k, fd_index)).second) {
+ return OTS_FAILURE();
+ }
+ }
+ } else {
+ // unknown format
+ return OTS_FAILURE();
+ }
+ break;
+ }
+
+ // Private DICT (2 * number)
+ case 18: {
+ if (operands.size() != 2) {
+ return OTS_FAILURE();
+ }
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ const uint32_t private_offset = operands.back().first;
+ operands.pop_back();
+ if (operands.back().second != DICT_OPERAND_INTEGER) {
+ return OTS_FAILURE();
+ }
+ const uint32_t private_length = operands.back().first;
+ if (private_offset > table.length()) {
+ return OTS_FAILURE();
+ }
+ if (private_length >= table.length()) {
+ return OTS_FAILURE();
+ }
+ if (private_length + private_offset > table.length()) {
+ return OTS_FAILURE();
+ }
+ // parse "15. Private DICT data"
+ if (!ParsePrivateDictData(table, private_offset, private_length,
+ type, out_cff)) {
+ return OTS_FAILURE();
+ }
+ break;
+ }
+
+ // ROS
+ case (12U << 8) + 30:
+ if (font_format != FORMAT_UNKNOWN) {
+ return OTS_FAILURE();
+ }
+ font_format = FORMAT_CID_KEYED;
+ if (operands.size() != 3) {
+ return OTS_FAILURE();
+ }
+ // check SIDs
+ operands.pop_back(); // ignore the first number.
+ if (!CheckSid(operands.back(), sid_max)) {
+ return OTS_FAILURE();
+ }
+ operands.pop_back();
+ if (!CheckSid(operands.back(), sid_max)) {
+ return OTS_FAILURE();
+ }
+ if (have_ros) {
+ return OTS_FAILURE(); // multiple ROS tables?
+ }
+ have_ros = true;
+ break;
+
+ default:
+ return OTS_FAILURE();
+ }
+ operands.clear();
+
+ if (font_format == FORMAT_UNKNOWN) {
+ font_format = FORMAT_OTHER;
+ }
+ }
+
+ // parse "13. Charsets"
+ if (charset_offset) {
+ table.set_offset(charset_offset);
+ uint8_t format = 0;
+ if (!table.ReadU8(&format)) {
+ return OTS_FAILURE();
+ }
+ switch (format) {
+ case 0:
+ for (uint16_t j = 1 /* .notdef is omitted */; j < glyphs; ++j) {
+ uint16_t sid = 0;
+ if (!table.ReadU16(&sid)) {
+ return OTS_FAILURE();
+ }
+ if (!have_ros && (sid > sid_max)) {
+ return OTS_FAILURE();
+ }
+ // TODO(yusukes): check CIDs when have_ros is true.
+ }
+ break;
+
+ case 1:
+ case 2: {
+ uint32_t total = 1; // .notdef is omitted.
+ while (total < glyphs) {
+ uint16_t sid = 0;
+ if (!table.ReadU16(&sid)) {
+ return OTS_FAILURE();
+ }
+ if (!have_ros && (sid > sid_max)) {
+ return OTS_FAILURE();
+ }
+ // TODO(yusukes): check CIDs when have_ros is true.
+
+ if (format == 1) {
+ uint8_t left = 0;
+ if (!table.ReadU8(&left)) {
+ return OTS_FAILURE();
+ }
+ total += (left + 1);
+ } else {
+ uint16_t left = 0;
+ if (!table.ReadU16(&left)) {
+ return OTS_FAILURE();
+ }
+ total += (left + 1);
+ }
+ }
+ break;
+ }
+
+ default:
+ return OTS_FAILURE();
}
}
return true;
@@ -904,147 +1146,218 @@ bool ParseDictData(const uint8_t *data, size_t table_length,
namespace ots {
-bool ots_cff_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeCFF::ValidateFDSelect(uint16_t num_glyphs) {
+ for (const auto& fd_select : this->fd_select) {
+ if (fd_select.first >= num_glyphs) {
+ return Error("Invalid glyph index in FDSelect: %d >= %d\n",
+ fd_select.first, num_glyphs);
+ }
+ if (fd_select.second >= this->font_dict_length) {
+ return Error("Invalid FD index: %d >= %d\n",
+ fd_select.second, this->font_dict_length);
+ }
+ }
+ return true;
+}
+
+bool OpenTypeCFF::Parse(const uint8_t *data, size_t length) {
Buffer table(data, length);
- font->cff = new OpenTypeCFF;
- font->cff->data = data;
- font->cff->length = length;
- font->cff->font_dict_length = 0;
- font->cff->local_subrs = NULL;
+ Font *font = GetFont();
+
+ this->m_data = data;
+ this->m_length = length;
// parse "6. Header" in the Adobe Compact Font Format Specification
uint8_t major = 0;
uint8_t minor = 0;
uint8_t hdr_size = 0;
uint8_t off_size = 0;
- if (!table.ReadU8(&major)) {
- return OTS_FAILURE();
- }
- if (!table.ReadU8(&minor)) {
- return OTS_FAILURE();
- }
- if (!table.ReadU8(&hdr_size)) {
- return OTS_FAILURE();
- }
- if (!table.ReadU8(&off_size)) {
- return OTS_FAILURE();
- }
- if ((off_size == 0) || (off_size > 4)) {
- return OTS_FAILURE();
+ if (!table.ReadU8(&major) ||
+ !table.ReadU8(&minor) ||
+ !table.ReadU8(&hdr_size) ||
+ !table.ReadU8(&off_size)) {
+ return Error("Failed to read table header");
}
- if ((major != 1) ||
- (minor != 0) ||
- (hdr_size != 4)) {
- return OTS_FAILURE();
+ if (off_size < 1 || off_size > 4) {
+ return Error("Bad offSize: %d", off_size);
}
- if (hdr_size >= length) {
- return OTS_FAILURE();
+
+ if (major != 1 || minor != 0) {
+ return Error("Unsupported table version: %d.%d", major, minor);
+ }
+
+ this->major = major;
+
+ if (hdr_size != 4 || hdr_size >= length) {
+ return Error("Bad hdrSize: %d", hdr_size);
}
// parse "7. Name INDEX"
table.set_offset(hdr_size);
CFFIndex name_index;
- if (!ParseIndex(&table, &name_index)) {
- return OTS_FAILURE();
+ if (!ParseIndex(table, name_index)) {
+ return Error("Failed to parse Name INDEX");
}
- if (!ParseNameData(&table, name_index, &(font->cff->name))) {
- return OTS_FAILURE();
+ if (name_index.count != 1 || name_index.offsets.size() != 2) {
+ return Error("Name INDEX must contain only one entry, not %d",
+ name_index.count);
+ }
+ if (!ParseNameData(&table, name_index, &(this->name))) {
+ return Error("Failed to parse Name INDEX data");
}
// parse "8. Top DICT INDEX"
table.set_offset(name_index.offset_to_next);
CFFIndex top_dict_index;
- if (!ParseIndex(&table, &top_dict_index)) {
- return OTS_FAILURE();
+ if (!ParseIndex(table, top_dict_index)) {
+ return Error("Failed to parse Top DICT INDEX");
}
- if (name_index.count != top_dict_index.count) {
- return OTS_FAILURE();
+ if (top_dict_index.count != 1) {
+ return Error("Top DICT INDEX must contain only one entry, not %d",
+ top_dict_index.count);
}
// parse "10. String INDEX"
table.set_offset(top_dict_index.offset_to_next);
CFFIndex string_index;
- if (!ParseIndex(&table, &string_index)) {
- return OTS_FAILURE();
+ if (!ParseIndex(table, string_index)) {
+ return Error("Failed to parse String INDEX");
}
if (string_index.count >= 65000 - kNStdString) {
- return OTS_FAILURE();
+ return Error("Too many entries in String INDEX: %d", string_index.count);
}
- const uint16_t num_glyphs = font->maxp->num_glyphs;
+ OpenTypeMAXP *maxp = static_cast(
+ font->GetTypedTable(OTS_TAG_MAXP));
+ if (!maxp) {
+ return Error("Required maxp table missing");
+ }
+ const uint16_t num_glyphs = maxp->num_glyphs;
const size_t sid_max = string_index.count + kNStdString;
// string_index.count == 0 is allowed.
// parse "9. Top DICT Data"
- if (!ParseDictData(data, length, top_dict_index,
+ this->charstrings_index = new ots::CFFIndex;
+ if (!ParseDictData(table, top_dict_index,
num_glyphs, sid_max,
- DICT_DATA_TOPLEVEL, font->cff)) {
- return OTS_FAILURE();
+ DICT_DATA_TOPLEVEL, this)) {
+ return Error("Failed to parse Top DICT Data");
}
// parse "16. Global Subrs INDEX"
table.set_offset(string_index.offset_to_next);
CFFIndex global_subrs_index;
- if (!ParseIndex(&table, &global_subrs_index)) {
- return OTS_FAILURE();
+ if (!ParseIndex(table, global_subrs_index)) {
+ return Error("Failed to parse Global Subrs INDEX");
}
- // Check if all fd_index in FDSelect are valid.
- std::map::const_iterator iter;
- std::map::const_iterator end = font->cff->fd_select.end();
- for (iter = font->cff->fd_select.begin(); iter != end; ++iter) {
- if (iter->second >= font->cff->font_dict_length) {
- return OTS_FAILURE();
- }
+ // Check if all fd and glyph indices in FDSelect are valid.
+ if (!ValidateFDSelect(num_glyphs)) {
+ return Error("Failed to validate FDSelect");
}
// Check if all charstrings (font hinting code for each glyph) are valid.
- for (size_t i = 0; i < font->cff->char_strings_array.size(); ++i) {
- if (!ValidateType2CharStringIndex(font,
- *(font->cff->char_strings_array.at(i)),
- global_subrs_index,
- font->cff->fd_select,
- font->cff->local_subrs_per_font,
- font->cff->local_subrs,
- &table)) {
- return OTS_FAILURE_MSG("Failed validating charstring set %d", (int) i);
- }
+ if (!ValidateCFFCharStrings(*this, global_subrs_index, &table)) {
+ return Error("Failed validating CharStrings INDEX");
}
return true;
}
-bool ots_cff_should_serialise(Font *font) {
- return font->cff != NULL;
-}
-
-bool ots_cff_serialise(OTSStream *out, Font *font) {
- // TODO(yusukes): would be better to transcode the data,
- // rather than simple memcpy.
- if (!out->Write(font->cff->data, font->cff->length)) {
- return OTS_FAILURE();
+bool OpenTypeCFF::Serialize(OTSStream *out) {
+ if (!out->Write(this->m_data, this->m_length)) {
+ return Error("Failed to write table");
}
return true;
}
-void ots_cff_reuse(Font *font, Font *other) {
- font->cff = other->cff;
- font->cff_reused = true;
+OpenTypeCFF::~OpenTypeCFF() {
+ for (size_t i = 0; i < this->local_subrs_per_font.size(); ++i) {
+ delete (this->local_subrs_per_font)[i];
+ }
+ delete this->charstrings_index;
+ delete this->local_subrs;
}
-void ots_cff_free(Font *font) {
- if (font->cff) {
- for (size_t i = 0; i < font->cff->char_strings_array.size(); ++i) {
- delete (font->cff->char_strings_array)[i];
- }
- for (size_t i = 0; i < font->cff->local_subrs_per_font.size(); ++i) {
- delete (font->cff->local_subrs_per_font)[i];
- }
- delete font->cff->local_subrs;
- delete font->cff;
+bool OpenTypeCFF2::Parse(const uint8_t *data, size_t length) {
+ Buffer table(data, length);
+
+ Font *font = GetFont();
+
+ this->m_data = data;
+ this->m_length = length;
+
+ // parse "6. Header"
+ uint8_t major = 0;
+ uint8_t minor = 0;
+ uint8_t hdr_size = 0;
+ uint16_t top_dict_size = 0;
+ if (!table.ReadU8(&major) ||
+ !table.ReadU8(&minor) ||
+ !table.ReadU8(&hdr_size) ||
+ !table.ReadU16(&top_dict_size)) {
+ return Error("Failed to read table header");
}
+
+ if (major != 2 || minor != 0) {
+ return Error("Unsupported table version: %d.%d", major, minor);
+ }
+
+ this->major = major;
+
+ if (hdr_size >= length) {
+ return Error("Bad hdrSize: %d", hdr_size);
+ }
+
+ if (top_dict_size == 0 || hdr_size + top_dict_size > length) {
+ return Error("Bad topDictLength: %d", top_dict_size);
+ }
+
+ OpenTypeMAXP *maxp = static_cast(
+ font->GetTypedTable(OTS_TAG_MAXP));
+ if (!maxp) {
+ return Error("Required maxp table missing");
+ }
+ const uint16_t num_glyphs = maxp->num_glyphs;
+ const size_t sid_max = kNStdString;
+
+ // parse "7. Top DICT Data"
+ ots::Buffer top_dict(data + hdr_size, top_dict_size);
+ table.set_offset(hdr_size);
+ this->charstrings_index = new ots::CFFIndex;
+ if (!ParseDictData(table, top_dict,
+ num_glyphs, sid_max,
+ DICT_DATA_TOPLEVEL, this)) {
+ return Error("Failed to parse Top DICT Data");
+ }
+
+ // parse "9. Global Subrs INDEX"
+ table.set_offset(hdr_size + top_dict_size);
+ CFFIndex global_subrs_index;
+ if (!ParseIndex(table, global_subrs_index, true)) {
+ return Error("Failed to parse Global Subrs INDEX");
+ }
+
+ // Check if all fd and glyph indices in FDSelect are valid.
+ if (!ValidateFDSelect(num_glyphs)) {
+ return Error("Failed to validate FDSelect");
+ }
+
+ // Check if all charstrings (font hinting code for each glyph) are valid.
+ if (!ValidateCFFCharStrings(*this, global_subrs_index, &table)) {
+ return Error("Failed validating CharStrings INDEX");
+ }
+
+ return true;
+}
+
+bool OpenTypeCFF2::Serialize(OTSStream *out) {
+ if (!out->Write(this->m_data, this->m_length)) {
+ return Error("Failed to write table");
+ }
+ return true;
}
} // namespace ots
diff --git a/gfx/ots/src/cff.h b/gfx/ots/src/cff.h
index 5584acc62..cfce9ab91 100644
--- a/gfx/ots/src/cff.h
+++ b/gfx/ots/src/cff.h
@@ -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
#include
+#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 offsets;
uint32_t offset_to_next;
};
-struct OpenTypeCFF {
- const uint8_t *data;
- size_t length;
+typedef std::map 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 fd_select;
+ CFFFDSelect fd_select;
// A list of char strings.
- std::vector char_strings_array;
+ CFFIndex* charstrings_index;
// A list of Local Subrs associated with FDArrays. Can be empty.
std::vector local_subrs_per_font;
// A Local Subrs associated with Top DICT. Can be NULL.
CFFIndex *local_subrs;
+
+ // CFF2 VariationStore regionIndexCount.
+ std::vector 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
diff --git a/gfx/ots/src/cff_type2_charstring.cc b/gfx/ots/src/cff_charstring.cc
similarity index 71%
rename from gfx/ots/src/cff_type2_charstring.cc
rename to gfx/ots/src/cff_charstring.cc
index 8c5544b2e..23c17d183 100644
--- a/gfx/ots/src/cff_type2_charstring.cc
+++ b/gfx/ots/src/cff_charstring.cc
@@ -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
#include
@@ -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 *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 *argument_stack,
+ bool *out_found_endchar,
+ bool *out_found_width,
+ size_t *in_out_num_stems,
+ bool cff2);
+
+bool ArgumentStackOverflows(std::stack *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 *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 *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 *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 *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 &fd_select,
- const std::vector &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::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 &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 &fd_select,
- const std::vector &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 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();
}
}
diff --git a/gfx/ots/src/cff_type2_charstring.h b/gfx/ots/src/cff_charstring.h
similarity index 89%
rename from gfx/ots/src/cff_type2_charstring.h
rename to gfx/ots/src/cff_charstring.h
index be44bc72c..5a2fa9ff2 100644
--- a/gfx/ots/src/cff_type2_charstring.h
+++ b/gfx/ots/src/cff_charstring.h
@@ -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 &fd_select,
- const std::vector &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,
diff --git a/gfx/ots/src/cmap.cc b/gfx/ots/src/cmap.cc
index 325f8e0d1..72c2a20fc 100644
--- a/gfx/ots/src/cmap.cc
+++ b/gfx/ots/src/cmap.cc
@@ -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(
+ 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 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 &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 &groups
- = font->cmap->subtable_3_10_13;
+ std::vector &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& 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 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(
+ 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(have_034) +
static_cast(have_0514) +
static_cast(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 &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 &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 &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
diff --git a/gfx/ots/src/cmap.h b/gfx/ots/src/cmap.h
index 5b09556b7..feddbc696 100644
--- a/gfx/ots/src/cmap.h
+++ b/gfx/ots/src/cmap.h
@@ -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 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 subtable_3_10_13;
// Platform 1, Encoding 0, Format 0, Mac Roman table.
std::vector 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
diff --git a/gfx/ots/src/cvar.cc b/gfx/ots/src/cvar.cc
new file mode 100644
index 000000000..a2bad7a15
--- /dev/null
+++ b/gfx/ots/src/cvar.cc
@@ -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(
+ 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
diff --git a/gfx/ots/src/cvar.h b/gfx/ots/src/cvar.h
new file mode 100644
index 000000000..8f31e98cd
--- /dev/null
+++ b/gfx/ots/src/cvar.h
@@ -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_
diff --git a/gfx/ots/src/cvt.cc b/gfx/ots/src/cvt.cc
index 1402e7c06..2e0257889 100644
--- a/gfx/ots/src/cvt.cc
+++ b/gfx/ots/src/cvt.cc
@@ -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
diff --git a/gfx/ots/src/cvt.h b/gfx/ots/src/cvt.h
index 3c25f06f6..88a96ca20 100644
--- a/gfx/ots/src/cvt.h
+++ b/gfx/ots/src/cvt.h
@@ -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;
};
diff --git a/gfx/ots/src/feat.cc b/gfx/ots/src/feat.cc
new file mode 100644
index 000000000..374a7ae9f
--- /dev/null
+++ b/gfx/ots/src/feat.cc
@@ -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 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(
+ 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(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(
+ 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
diff --git a/gfx/ots/src/feat.h b/gfx/ots/src/feat.h
new file mode 100644
index 000000000..29c2656ff
--- /dev/null
+++ b/gfx/ots/src/feat.h
@@ -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
+#include
+
+#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 {
+ explicit FeatureDefn(OpenTypeFEAT* parent)
+ : TablePart(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 {
+ explicit FeatureSettingDefn(OpenTypeFEAT* parent)
+ : TablePart(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 features;
+ std::vector featSettings;
+ std::unordered_set feature_ids;
+};
+
+} // namespace ots
+
+#endif // OTS_FEAT_H_
diff --git a/gfx/ots/src/fpgm.cc b/gfx/ots/src/fpgm.cc
index faa9a2392..bb52b367f 100644
--- a/gfx/ots/src/fpgm.cc
+++ b/gfx/ots/src/fpgm.cc
@@ -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
diff --git a/gfx/ots/src/fpgm.h b/gfx/ots/src/fpgm.h
index 8fabac36d..9ed6b34bf 100644
--- a/gfx/ots/src/fpgm.h
+++ b/gfx/ots/src/fpgm.h
@@ -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;
};
diff --git a/gfx/ots/src/fvar.cc b/gfx/ots/src/fvar.cc
new file mode 100644
index 000000000..6f9b4d6eb
--- /dev/null
+++ b/gfx/ots/src/fvar.cc
@@ -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
diff --git a/gfx/ots/src/fvar.h b/gfx/ots/src/fvar.h
new file mode 100644
index 000000000..a469c8cdd
--- /dev/null
+++ b/gfx/ots/src/fvar.h
@@ -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
+
+#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 axes;
+
+ struct InstanceRecord {
+ uint16_t subfamilyNameID;
+ uint16_t flags;
+ std::vector coordinates;
+ uint16_t postScriptNameID; // optional
+ };
+ std::vector instances;
+
+ bool instancesHavePostScriptNameID;
+};
+
+} // namespace ots
+
+#endif // OTS_FVAR_H_
diff --git a/gfx/ots/src/gasp.cc b/gfx/ots/src/gasp.cc
index 5ebf5d84b..2a03c831f 100644
--- a/gfx/ots/src/gasp.cc
+++ b/gfx/ots/src/gasp.cc
@@ -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(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(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
diff --git a/gfx/ots/src/gasp.h b/gfx/ots/src/gasp.h
index 48d7e7c16..ce9e987aa 100644
--- a/gfx/ots/src/gasp.h
+++ b/gfx/ots/src/gasp.h
@@ -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 > gasp_ranges;
diff --git a/gfx/ots/src/gdef.cc b/gfx/ots/src/gdef.cc
index 71c6fc592..71e1075e3 100644
--- a/gfx/ots/src/gdef.cc
+++ b/gfx/ots/src/gdef.cc
@@ -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(glyph_count) + 4;
if (attach_points_end > std::numeric_limits::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 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(lig_glyph_count) + 4;
if (lig_glyphs_end > std::numeric_limits::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 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 caret_value_offsets;
@@ -157,10 +148,10 @@ bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
unsigned caret_value_offsets_end = 2 * static_cast(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(mark_set_count) + 4;
if (mark_sets_end > std::numeric_limits::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(
+ 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
diff --git a/gfx/ots/src/gdef.h b/gfx/ots/src/gdef.h
index f46f419c7..7c7cc0ce5 100644
--- a/gfx/ots/src/gdef.h
+++ b/gfx/ots/src/gdef.h
@@ -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
diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
new file mode 100644
index 000000000..23f7dfd9a
--- /dev/null
+++ b/gfx/ots/src/glat.cc
@@ -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
+
+namespace ots {
+
+// -----------------------------------------------------------------------------
+// OpenTypeGLAT_v1
+// -----------------------------------------------------------------------------
+
+bool OpenTypeGLAT_v1::Parse(const uint8_t* data, size_t length) {
+ Buffer table(data, length);
+ OpenTypeGLOC* gloc = static_cast(
+ 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& locations = gloc->GetLocations();
+ if (locations.empty()) {
+ return DropGraphite("No locations from Gloc table");
+ }
+ std::list 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(
+ 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& locations = gloc->GetLocations();
+ if (locations.empty()) {
+ return DropGraphite("No locations from Gloc table");
+ }
+ std::list 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(
+ 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 decompressed(decompressed_size);
+ size_t outputSize = 0;
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
+ reinterpret_cast(data + table.offset()),
+ table.remaining(), // input buffer size (input size + padding)
+ reinterpret_cast(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& locations = gloc->GetLocations();
+ if (locations.empty()) {
+ return DropGraphite("No locations from Gloc table");
+ }
+ std::list 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
diff --git a/gfx/ots/src/glat.h b/gfx/ots/src/glat.h
new file mode 100644
index 000000000..04c9c1cce
--- /dev/null
+++ b/gfx/ots/src/glat.h
@@ -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
+
+#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 {
+ explicit GlatEntry(OpenTypeGLAT_v1* parent)
+ : TablePart(parent) { }
+ bool ParsePart(Buffer& table);
+ bool SerializePart(OTSStream* out) const;
+ uint8_t attNum;
+ uint8_t num;
+ std::vector attributes;
+ };
+ uint32_t version;
+ std::vector 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 {
+ explicit GlatEntry(OpenTypeGLAT_v2* parent)
+ : TablePart(parent) { }
+ bool ParsePart(Buffer& table);
+ bool SerializePart(OTSStream* out) const;
+ int16_t attNum;
+ int16_t num;
+ std::vector attributes;
+ };
+ uint32_t version;
+ std::vector 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 {
+ explicit GlyphAttrs(OpenTypeGLAT_v3* parent)
+ : TablePart(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 {
+ explicit OctaboxMetrics(OpenTypeGLAT_v3* parent)
+ : TablePart(parent) { }
+ bool ParsePart(Buffer& table);
+ bool SerializePart(OTSStream* out) const;
+ struct SubboxEntry : public TablePart {
+ explicit SubboxEntry(OpenTypeGLAT_v3* parent)
+ : TablePart(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 subboxes;
+ };
+ struct GlatEntry : public TablePart {
+ explicit GlatEntry(OpenTypeGLAT_v3* parent)
+ : TablePart(parent) { }
+ bool ParsePart(Buffer& table);
+ bool SerializePart(OTSStream* out) const;
+ int16_t attNum;
+ int16_t num;
+ std::vector attributes;
+ };
+ OctaboxMetrics octabox;
+ std::vector 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 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_
diff --git a/gfx/ots/src/gloc.cc b/gfx/ots/src/gloc.cc
new file mode 100644
index 000000000..9c5ee3bdf
--- /dev/null
+++ b/gfx/ots/src/gloc.cc
@@ -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(
+ 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(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(location))) {
+ return false;
+ }
+ }
+ return true;
+ }()) ||
+ (this->flags & ATTRIB_IDS && !SerializeParts(this->attribIds, out))) {
+ return Error("Failed to write table");
+ }
+ return true;
+}
+
+const std::vector& OpenTypeGLOC::GetLocations() {
+ return this->locations;
+}
+
+} // namespace ots
diff --git a/gfx/ots/src/gloc.h b/gfx/ots/src/gloc.h
new file mode 100644
index 000000000..60184ffb9
--- /dev/null
+++ b/gfx/ots/src/gloc.h
@@ -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
+
+#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& 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 locations;
+ std::vector attribIds;
+};
+
+} // namespace ots
+
+#endif // OTS_GLOC_H_
diff --git a/gfx/ots/src/glyf.cc b/gfx/ots/src/glyf.cc
index 311916dc0..0c19d6d7b 100644
--- a/gfx/ots/src/glyf.cc
+++ b/gfx/ots/src/glyf.cc
@@ -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(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(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 &offsets = font->loca->offsets;
+ return true;
+}
+
+bool OpenTypeGLYF::Parse(const uint8_t *data, size_t length) {
+ OpenTypeMAXP *maxp = static_cast(
+ GetFont()->GetTypedTable(OTS_TAG_MAXP));
+ OpenTypeLOCA *loca = static_cast(
+ GetFont()->GetTypedTable(OTS_TAG_LOCA));
+ OpenTypeHEAD *head = static_cast(
+ 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 &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 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 shouldn’t 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(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("\x00\x00\x00\x00"),
static_cast(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::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 don’t 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
diff --git a/gfx/ots/src/glyf.h b/gfx/ots/src/glyf.h
index 9a8baf5ec..1da94e4b9 100644
--- a/gfx/ots/src/glyf.h
+++ b/gfx/ots/src/glyf.h
@@ -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 > iov;
};
diff --git a/gfx/ots/src/gpos.cc b/gfx/ots/src/gpos.cc
index 83d9ab053..034f9799d 100644
--- a/gfx/ots/src/gpos.cc
+++ b/gfx/ots/src/gpos.cc
@@ -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(
+ 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(
+ 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(
+ 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(
+ 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(
+ font->GetTypedTable(OTS_TAG_MAXP));
+ if (!maxp) {
+ return OTS_FAILURE_MSG("Required maxp table missing");
+ }
+ ots::OpenTypeGPOS *gpos = static_cast(
+ 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(
+ font->GetTypedTable(OTS_TAG_MAXP));
+ if (!maxp) {
+ return OTS_FAILURE_MSG("Required maxp table missing");
+ }
+ ots::OpenTypeGPOS *gpos = static_cast(
+ 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
diff --git a/gfx/ots/src/gpos.h b/gfx/ots/src/gpos.h
index 3a4034f6f..423c8ae83 100644
--- a/gfx/ots/src/gpos.h
+++ b/gfx/ots/src/gpos.h
@@ -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
diff --git a/gfx/ots/src/graphite.h b/gfx/ots/src/graphite.h
new file mode 100644
index 000000000..452cb26a8
--- /dev/null
+++ b/gfx/ots/src/graphite.h
@@ -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
+#include
+
+namespace ots {
+
+template
+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
+bool SerializeParts(const std::vector& vec, OTSStream* out) {
+ for (const T& part : vec) {
+ if (!part.SerializePart(out)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template
+bool SerializeParts(const std::vector>& vec, OTSStream* out) {
+ for (const std::vector& part : vec) {
+ if (!SerializeParts(part, out)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool SerializeParts(const std::vector& vec, OTSStream* out) {
+ for (uint8_t part : vec) {
+ if (!out->WriteU8(part)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool SerializeParts(const std::vector& vec, OTSStream* out) {
+ for (uint16_t part : vec) {
+ if (!out->WriteU16(part)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool SerializeParts(const std::vector& vec, OTSStream* out) {
+ for (int16_t part : vec) {
+ if (!out->WriteS16(part)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool SerializeParts(const std::vector& vec, OTSStream* out) {
+ for (uint32_t part : vec) {
+ if (!out->WriteU32(part)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool SerializeParts(const std::vector& vec, OTSStream* out) {
+ for (int32_t part : vec) {
+ if (!out->WriteS32(part)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template
+size_t datasize(std::vector vec) {
+ return sizeof(T) * vec.size();
+}
+
+} // namespace ots
+
+#endif // OTS_GRAPHITE_H_
diff --git a/gfx/ots/src/gsub.cc b/gfx/ots/src/gsub.cc
index 9baf2e88b..c90fb48f3 100644
--- a/gfx/ots/src/gsub.cc
+++ b/gfx/ots/src/gsub.cc
@@ -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(
+ 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(
+ 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(6) +
sequence_count * 2;
if (sequence_end > std::numeric_limits::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(
+ 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(6) +
alternate_set_count * 2;
if (alternate_set_end > std::numeric_limits::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(
+ 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(6) +
lig_set_count * 2;
if (ligature_set_end > std::numeric_limits::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(
+ font->GetTypedTable(OTS_TAG_MAXP));
+ if (!maxp) {
+ return OTS_FAILURE_MSG("Required maxp table missing");
+ }
+ ots::OpenTypeGSUB *gsub = static_cast(
+ 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(
+ font->GetTypedTable(OTS_TAG_MAXP));
+ if (!maxp) {
+ return OTS_FAILURE_MSG("Required maxp table missing");
+ }
+ ots::OpenTypeGSUB *gsub = static_cast(
+ 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(
+ 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
diff --git a/gfx/ots/src/gsub.h b/gfx/ots/src/gsub.h
index f6f8cd3b1..76c990465 100644
--- a/gfx/ots/src/gsub.h
+++ b/gfx/ots/src/gsub.h
@@ -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
diff --git a/gfx/ots/src/gvar.cc b/gfx/ots/src/gvar.cc
new file mode 100644
index 000000000..324a0fc83
--- /dev/null
+++ b/gfx/ots/src/gvar.cc
@@ -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(
+ 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(
+ 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
diff --git a/gfx/ots/src/gvar.h b/gfx/ots/src/gvar.h
new file mode 100644
index 000000000..8a90c57a3
--- /dev/null
+++ b/gfx/ots/src/gvar.h
@@ -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_
diff --git a/gfx/ots/src/hdmx.cc b/gfx/ots/src/hdmx.cc
index f57b71f59..42ac9de8d 100644
--- a/gfx/ots/src/hdmx.cc
+++ b/gfx/ots/src/hdmx.cc
@@ -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(
+ GetFont()->GetTypedTable(OTS_TAG_MAXP));
+ OpenTypeHEAD *head = static_cast(
+ 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(hdmx->records.size());
- if (hdmx->records.size() >
+bool OpenTypeHDMX::Serialize(OTSStream *out) {
+ const int16_t num_recs = static_cast(this->records.size());
+ if (this->records.size() >
static_cast(std::numeric_limits::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
diff --git a/gfx/ots/src/hdmx.h b/gfx/ots/src/hdmx.h
index 9ec212437..5d323dd7e 100644
--- a/gfx/ots/src/hdmx.h
+++ b/gfx/ots/src/hdmx.h
@@ -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 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;
diff --git a/gfx/ots/src/head.cc b/gfx/ots/src/head.cc
index 229ea71f9..6088504c8 100644
--- a/gfx/ots/src/head.cc
+++ b/gfx/ots/src/head.cc
@@ -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
diff --git a/gfx/ots/src/head.h b/gfx/ots/src/head.h
index 5967c4b89..a040fcf24 100644
--- a/gfx/ots/src/head.h
+++ b/gfx/ots/src/head.h
@@ -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;
diff --git a/gfx/ots/src/hhea.cc b/gfx/ots/src/hhea.cc
index 624287280..d024aaac4 100644
--- a/gfx/ots/src/hhea.cc
+++ b/gfx/ots/src/hhea.cc
@@ -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
diff --git a/gfx/ots/src/hhea.h b/gfx/ots/src/hhea.h
index bdea9aa0d..0405bd9d1 100644
--- a/gfx/ots/src/hhea.h
+++ b/gfx/ots/src/hhea.h
@@ -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
diff --git a/gfx/ots/src/hmtx.cc b/gfx/ots/src/hmtx.cc
index 667d1fe6f..2ef7771fd 100644
--- a/gfx/ots/src/hmtx.cc
+++ b/gfx/ots/src/hmtx.cc
@@ -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
diff --git a/gfx/ots/src/hmtx.h b/gfx/ots/src/hmtx.h
index 435949c5e..dbea9c80c 100644
--- a/gfx/ots/src/hmtx.h
+++ b/gfx/ots/src/hmtx.h
@@ -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
diff --git a/gfx/ots/src/hvar.cc b/gfx/ots/src/hvar.cc
new file mode 100644
index 000000000..2bcea8680
--- /dev/null
+++ b/gfx/ots/src/hvar.cc
@@ -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
diff --git a/gfx/ots/src/hvar.h b/gfx/ots/src/hvar.h
new file mode 100644
index 000000000..5bfd2e4ea
--- /dev/null
+++ b/gfx/ots/src/hvar.h
@@ -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_
diff --git a/gfx/ots/src/kern.cc b/gfx/ots/src/kern.cc
index d4ae8fcc4..ec41dfb9c 100644
--- a/gfx/ots/src/kern.cc
+++ b/gfx/ots/src/kern.cc
@@ -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(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(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::max() ||
- !out->WriteU16(kern->subtables[i].version) ||
+ !out->WriteU16(this->subtables[i].version) ||
!out->WriteU16(static_cast(length)) ||
- !out->WriteU16(kern->subtables[i].coverage) ||
+ !out->WriteU16(this->subtables[i].coverage) ||
!out->WriteU16(
- static_cast(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(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
diff --git a/gfx/ots/src/kern.h b/gfx/ots/src/kern.h
index 9350ef7f8..38f056851 100644
--- a/gfx/ots/src/kern.h
+++ b/gfx/ots/src/kern.h
@@ -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 subtables;
};
diff --git a/gfx/ots/src/layout.cc b/gfx/ots/src/layout.cc
index 1d87b53d4..8e99f573d 100644
--- a/gfx/ots/src/layout.cc
+++ b/gfx/ots/src/layout.cc
@@ -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
#include
+#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 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(
+ 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(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
diff --git a/gfx/ots/src/layout.h b/gfx/ots/src/layout.h
index d195646d4..d10a3be5b 100644
--- a/gfx/ots/src/layout.h
+++ b/gfx/ots/src/layout.h
@@ -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_
diff --git a/gfx/ots/src/loca.cc b/gfx/ots/src/loca.cc
index aae04c25a..4f322027d 100644
--- a/gfx/ots/src/loca.cc
+++ b/gfx/ots/src/loca.cc
@@ -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(
+ GetFont()->GetTypedTable(OTS_TAG_MAXP));
+ OpenTypeHEAD *head = static_cast(
+ 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(
+ 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(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(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
diff --git a/gfx/ots/src/loca.h b/gfx/ots/src/loca.h
index 255ef06ec..da3241842 100644
--- a/gfx/ots/src/loca.h
+++ b/gfx/ots/src/loca.h
@@ -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 offsets;
};
diff --git a/gfx/ots/src/ltsh.cc b/gfx/ots/src/ltsh.cc
index 5b34cf4ad..4c40c5eb7 100644
--- a/gfx/ots/src/ltsh.cc
+++ b/gfx/ots/src/ltsh.cc
@@ -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(
+ 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(<sh->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(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(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
diff --git a/gfx/ots/src/ltsh.h b/gfx/ots/src/ltsh.h
index 23d97d784..cc9fbf62d 100644
--- a/gfx/ots/src/ltsh.h
+++ b/gfx/ots/src/ltsh.h
@@ -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 ypels;
};
diff --git a/gfx/ots/src/math.cc b/gfx/ots/src/math.cc
index 36417dc29..c94e3bee3 100644
--- a/gfx/ots/src/math.cc
+++ b/gfx/ots/src/math.cc
@@ -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