Update in-tree Brotli, Woff2 and OTS components.
This commit is contained in:
parent
adaffdd732
commit
41d0dc16ca
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -46,9 +46,9 @@
|
|||
<preference id="network.stricttransportsecurity.enabled"
|
||||
name="network.stricttransportsecurity.enabled"
|
||||
type="bool"/>
|
||||
<preference id="security.cert_pinning.enforcement_level"
|
||||
name="security.cert_pinning.enforcement_level"
|
||||
type="int"/>
|
||||
<preference id="security.cert_pinning.hpkp.enabled"
|
||||
name="security.cert_pinning.hpkp.enabled"
|
||||
type="bool"/>
|
||||
|
||||
<!-- Opportunistic Encryption -->
|
||||
|
||||
|
@ -150,7 +150,7 @@
|
|||
<checkbox id="enableHPKP"
|
||||
label="&enableHPKP.label;"
|
||||
accesskey="&enableHPKP.accesskey;"
|
||||
oncommand="gSecurityPane.updateHPKPPref();"/>
|
||||
preference="security.cert_pinning.hpkp.enabled"/>
|
||||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -35,13 +35,17 @@ typedef int int32_t;
|
|||
typedef unsigned int uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#define ntohl(x) _byteswap_ulong (x)
|
||||
#define ntohs(x) _byteswap_ushort (x)
|
||||
#define htonl(x) _byteswap_ulong (x)
|
||||
#define htons(x) _byteswap_ushort (x)
|
||||
#define ots_ntohl(x) _byteswap_ulong (x)
|
||||
#define ots_ntohs(x) _byteswap_ushort (x)
|
||||
#define ots_htonl(x) _byteswap_ulong (x)
|
||||
#define ots_htons(x) _byteswap_ushort (x)
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <stdint.h>
|
||||
#define ots_ntohl(x) ntohl (x)
|
||||
#define ots_ntohs(x) ntohs (x)
|
||||
#define ots_htonl(x) htonl (x)
|
||||
#define ots_htons(x) htons (x)
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
@ -52,7 +56,7 @@ typedef unsigned __int64 uint64_t;
|
|||
#include <cstring>
|
||||
|
||||
#define OTS_TAG(c1,c2,c3,c4) ((uint32_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4))))
|
||||
#define OTS_UNTAG(tag) ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
|
||||
#define OTS_UNTAG(tag) ((char)((tag)>>24)), ((char)((tag)>>16)), ((char)((tag)>>8)), ((char)(tag))
|
||||
|
||||
namespace ots {
|
||||
|
||||
|
@ -80,7 +84,7 @@ class OTSStream {
|
|||
const size_t l = std::min(length, static_cast<size_t>(4) - chksum_offset);
|
||||
uint32_t tmp = 0;
|
||||
std::memcpy(reinterpret_cast<uint8_t *>(&tmp) + chksum_offset, data, l);
|
||||
chksum_ += ntohl(tmp);
|
||||
chksum_ += ots_ntohl(tmp);
|
||||
length -= l;
|
||||
offset += l;
|
||||
}
|
||||
|
@ -89,7 +93,7 @@ class OTSStream {
|
|||
uint32_t tmp;
|
||||
std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
|
||||
sizeof(uint32_t));
|
||||
chksum_ += ntohl(tmp);
|
||||
chksum_ += ots_ntohl(tmp);
|
||||
length -= 4;
|
||||
offset += 4;
|
||||
}
|
||||
|
@ -99,7 +103,7 @@ class OTSStream {
|
|||
uint32_t tmp = 0;
|
||||
std::memcpy(&tmp,
|
||||
reinterpret_cast<const uint8_t*>(data) + offset, length);
|
||||
chksum_ += ntohl(tmp);
|
||||
chksum_ += ots_ntohl(tmp);
|
||||
}
|
||||
|
||||
return WriteRaw(data, orig_length);
|
||||
|
@ -127,27 +131,27 @@ class OTSStream {
|
|||
}
|
||||
|
||||
bool WriteU16(uint16_t v) {
|
||||
v = htons(v);
|
||||
v = ots_htons(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteS16(int16_t v) {
|
||||
v = htons(v);
|
||||
v = ots_htons(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteU24(uint32_t v) {
|
||||
v = htonl(v);
|
||||
v = ots_htonl(v);
|
||||
return Write(reinterpret_cast<uint8_t*>(&v)+1, 3);
|
||||
}
|
||||
|
||||
bool WriteU32(uint32_t v) {
|
||||
v = htonl(v);
|
||||
v = ots_htonl(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
bool WriteS32(int32_t v) {
|
||||
v = htonl(v);
|
||||
v = ots_htonl(v);
|
||||
return Write(&v, sizeof(v));
|
||||
}
|
||||
|
||||
|
@ -176,7 +180,7 @@ class OTSStream {
|
|||
|
||||
enum TableAction {
|
||||
TABLE_ACTION_DEFAULT, // Use OTS's default action for that table
|
||||
TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
|
||||
TABLE_ACTION_SANITIZE, // Sanitize the table, potentially dropping it
|
||||
TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
|
||||
TABLE_ACTION_DROP // Drop the table
|
||||
};
|
||||
|
@ -186,7 +190,7 @@ class OTS_API OTSContext {
|
|||
OTSContext() {}
|
||||
virtual ~OTSContext() {}
|
||||
|
||||
// Process a given OpenType file and write out a sanitised version
|
||||
// Process a given OpenType file and write out a sanitized version
|
||||
// output: a pointer to an object implementing the OTSStream interface. The
|
||||
// sanitisied output will be written to this. In the even of a failure,
|
||||
// partial output may have been written.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
|
||||
--- a/gfx/ots/src/glat.cc
|
||||
+++ b/gfx/ots/src/glat.cc
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "glat.h"
|
||||
|
||||
#include "gloc.h"
|
||||
-#include "lz4.h"
|
||||
+#include "mozilla/Compression.h"
|
||||
#include <list>
|
||||
|
||||
namespace ots {
|
||||
|
||||
@@ -212,16 +212,17 @@ bool OpenTypeGLAT_v3::Parse(const uint8_
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
- int ret = LZ4_decompress_safe_partial(
|
||||
+ size_t outputSize = 0;
|
||||
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
- reinterpret_cast<char*>(decompressed.data()),
|
||||
table.remaining(), // input buffer size (input size + padding)
|
||||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
- decompressed.size()); // output buffer size
|
||||
- if (ret < 0 || unsigned(ret) != decompressed.size()) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
+ &outputSize); // return output size
|
||||
+ if (!ret || outputSize != decompressed.size()) {
|
||||
+ return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
default:
|
||||
diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
|
||||
--- a/gfx/ots/src/silf.cc
|
||||
+++ b/gfx/ots/src/silf.cc
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "silf.h"
|
||||
|
||||
#include "name.h"
|
||||
-#include "lz4.h"
|
||||
+#include "mozilla/Compression.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace ots {
|
||||
|
||||
@@ -50,16 +50,17 @@ bool OpenTypeSILF::Parse(const uint8_t*
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
- int ret = LZ4_decompress_safe_partial(
|
||||
+ size_t outputSize = 0;
|
||||
+ bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
- reinterpret_cast<char*>(decompressed.data()),
|
||||
table.remaining(), // input buffer size (input size + padding)
|
||||
+ reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
- decompressed.size()); // output buffer size
|
||||
- if (ret < 0 || unsigned(ret) != decompressed.size()) {
|
||||
- return DropGraphite("Decompression failed with error code %d", ret);
|
||||
+ &outputSize); // return output size
|
||||
+ if (!ret || outputSize != decompressed.size()) {
|
||||
+ return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
default:
|
|
@ -1,10 +1,7 @@
|
|||
diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-sanitiser.h
|
||||
--- a/gfx/ots/include/opentype-sanitiser.h
|
||||
+++ b/gfx/ots/include/opentype-sanitiser.h
|
||||
@@ -1,15 +1,35 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
@@ -4,8 +4,28 @@
|
||||
|
||||
#ifndef OPENTYPE_SANITISER_H_
|
||||
#define OPENTYPE_SANITISER_H_
|
||||
|
@ -33,15 +30,7 @@ diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-san
|
|||
#include <stdlib.h>
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
@@ -187,17 +207,17 @@ class OTSStream {
|
||||
|
||||
enum TableAction {
|
||||
TABLE_ACTION_DEFAULT, // Use OTS's default action for that table
|
||||
TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
|
||||
@@ -164,9 +184,9 @@ enum TableAction {
|
||||
TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
|
||||
TABLE_ACTION_DROP // Drop the table
|
||||
};
|
||||
|
@ -52,7 +41,3 @@ diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-san
|
|||
OTSContext() {}
|
||||
virtual ~OTSContext() {}
|
||||
|
||||
// Process a given OpenType file and write out a sanitised version
|
||||
// output: a pointer to an object implementing the OTSStream interface. The
|
||||
// sanitisied output will be written to this. In the even of a failure,
|
||||
// partial output may have been written.
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "avar.h"
|
||||
|
||||
#include "fvar.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeAVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeAVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
if (!table.ReadU16(&this->majorVersion) ||
|
||||
!table.ReadU16(&this->minorVersion) ||
|
||||
!table.ReadU16(&this->reserved) ||
|
||||
!table.ReadU16(&this->axisCount)) {
|
||||
return Drop("Failed to read table header");
|
||||
}
|
||||
if (this->majorVersion != 1) {
|
||||
return Drop("Unknown table version");
|
||||
}
|
||||
if (this->minorVersion > 0) {
|
||||
// we only know how to serialize version 1.0
|
||||
Warning("Downgrading minor version to 0");
|
||||
this->minorVersion = 0;
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
Warning("Expected reserved=0");
|
||||
this->reserved = 0;
|
||||
}
|
||||
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return DropVariations("Required fvar table is missing");
|
||||
}
|
||||
if (axisCount != fvar->AxisCount()) {
|
||||
return Drop("Axis count mismatch");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->axisCount; i++) {
|
||||
this->axisSegmentMaps.emplace_back();
|
||||
uint16_t positionMapCount;
|
||||
if (!table.ReadU16(&positionMapCount)) {
|
||||
return Drop("Failed to read position map count");
|
||||
}
|
||||
int foundRequiredMappings = 0;
|
||||
for (size_t j = 0; j < positionMapCount; j++) {
|
||||
AxisValueMap map;
|
||||
if (!table.ReadS16(&map.fromCoordinate) ||
|
||||
!table.ReadS16(&map.toCoordinate)) {
|
||||
return Drop("Failed to read axis value map");
|
||||
}
|
||||
if (map.fromCoordinate < -0x4000 ||
|
||||
map.fromCoordinate > 0x4000 ||
|
||||
map.toCoordinate < -0x4000 ||
|
||||
map.toCoordinate > 0x4000) {
|
||||
return Drop("Axis value map coordinate out of range");
|
||||
}
|
||||
if (j > 0) {
|
||||
if (map.fromCoordinate <= this->axisSegmentMaps[i].back().fromCoordinate ||
|
||||
map.toCoordinate < this->axisSegmentMaps[i].back().toCoordinate) {
|
||||
return Drop("Axis value map out of order");
|
||||
}
|
||||
}
|
||||
if ((map.fromCoordinate == -0x4000 && map.toCoordinate == -0x4000) ||
|
||||
(map.fromCoordinate == 0 && map.toCoordinate == 0) ||
|
||||
(map.fromCoordinate == 0x4000 && map.toCoordinate == 0x4000)) {
|
||||
++foundRequiredMappings;
|
||||
}
|
||||
this->axisSegmentMaps[i].push_back(map);
|
||||
}
|
||||
if (positionMapCount > 0 && foundRequiredMappings != 3) {
|
||||
return Drop("A required mapping (for -1, 0 or 1) is missing");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeAVAR::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU16(this->majorVersion) ||
|
||||
!out->WriteU16(this->minorVersion) ||
|
||||
!out->WriteU16(this->reserved) ||
|
||||
!out->WriteU16(this->axisCount)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->axisCount; i++) {
|
||||
const auto& axisValueMap = this->axisSegmentMaps[i];
|
||||
if (!out->WriteU16(axisValueMap.size())) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
for (size_t j = 0; j < axisValueMap.size(); j++) {
|
||||
if (!out->WriteS16(axisValueMap[j].fromCoordinate) ||
|
||||
!out->WriteS16(axisValueMap[j].toCoordinate)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_AVAR_H_
|
||||
#define OTS_AVAR_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeAVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeAVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeAVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t reserved;
|
||||
uint16_t axisCount;
|
||||
|
||||
struct AxisValueMap {
|
||||
int16_t fromCoordinate;
|
||||
int16_t toCoordinate;
|
||||
};
|
||||
|
||||
std::vector<std::vector<AxisValueMap>> axisSegmentMaps;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_AVAR_H_
|
1481
gfx/ots/src/cff.cc
1481
gfx/ots/src/cff.cc
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -11,34 +11,81 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#undef major // glibc defines major!
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct CFFIndex {
|
||||
CFFIndex()
|
||||
: count(0), off_size(0), offset_to_next(0) {}
|
||||
uint16_t count;
|
||||
uint32_t count;
|
||||
uint8_t off_size;
|
||||
std::vector<uint32_t> offsets;
|
||||
uint32_t offset_to_next;
|
||||
};
|
||||
|
||||
struct OpenTypeCFF {
|
||||
const uint8_t *data;
|
||||
size_t length;
|
||||
typedef std::map<uint32_t, uint16_t> CFFFDSelect;
|
||||
|
||||
class OpenTypeCFF : public Table {
|
||||
public:
|
||||
explicit OpenTypeCFF(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag),
|
||||
major(0),
|
||||
font_dict_length(0),
|
||||
charstrings_index(NULL),
|
||||
local_subrs(NULL),
|
||||
m_data(NULL),
|
||||
m_length(0) {
|
||||
}
|
||||
|
||||
~OpenTypeCFF();
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
// Major version number.
|
||||
uint8_t major;
|
||||
|
||||
// Name INDEX. This name is used in name.cc as a postscript font name.
|
||||
std::string name;
|
||||
|
||||
// The number of fonts the file has.
|
||||
size_t font_dict_length;
|
||||
// A map from glyph # to font #.
|
||||
std::map<uint16_t, uint8_t> fd_select;
|
||||
CFFFDSelect fd_select;
|
||||
|
||||
// A list of char strings.
|
||||
std::vector<CFFIndex *> char_strings_array;
|
||||
CFFIndex* charstrings_index;
|
||||
// A list of Local Subrs associated with FDArrays. Can be empty.
|
||||
std::vector<CFFIndex *> local_subrs_per_font;
|
||||
// A Local Subrs associated with Top DICT. Can be NULL.
|
||||
CFFIndex *local_subrs;
|
||||
|
||||
// CFF2 VariationStore regionIndexCount.
|
||||
std::vector<uint16_t> region_index_count;
|
||||
|
||||
protected:
|
||||
bool ValidateFDSelect(uint16_t num_glyphs);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
class OpenTypeCFF2 : public OpenTypeCFF {
|
||||
public:
|
||||
explicit OpenTypeCFF2(Font *font, uint32_t tag)
|
||||
: OpenTypeCFF(font, tag),
|
||||
m_data(NULL),
|
||||
m_length(0) {
|
||||
}
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2010-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// A parser for the Type 2 Charstring Format.
|
||||
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
|
||||
|
||||
#include "cff_type2_charstring.h"
|
||||
#include "cff_charstring.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
|
@ -22,7 +22,6 @@ namespace {
|
|||
// Note #5177.
|
||||
const int32_t kMaxSubrsCount = 65536;
|
||||
const size_t kMaxCharStringLength = 65535;
|
||||
const size_t kMaxArgumentStack = 48;
|
||||
const size_t kMaxNumberOfStemHints = 96;
|
||||
const size_t kMaxSubrNesting = 10;
|
||||
|
||||
|
@ -30,117 +29,130 @@ const size_t kMaxSubrNesting = 10;
|
|||
// will fail with the dummy value.
|
||||
const int32_t dummy_result = INT_MAX;
|
||||
|
||||
bool ExecuteType2CharString(ots::Font *font,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *out_found_width,
|
||||
size_t *in_out_num_stems);
|
||||
bool ExecuteCharString(ots::OpenTypeCFF& cff,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool cff2);
|
||||
|
||||
bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) {
|
||||
if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) ||
|
||||
(!cff2 && argument_stack->size() > ots::kMaxCFF1ArgumentStack)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DUMP_T2CHARSTRING
|
||||
// Converts |op| to a string and returns it.
|
||||
const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
|
||||
const char *CharStringOperatorToString(ots::CharStringOperator op) {
|
||||
switch (op) {
|
||||
case ots::kHStem:
|
||||
return "HStem";
|
||||
return "hstem";
|
||||
case ots::kVStem:
|
||||
return "VStem";
|
||||
return "vstem";
|
||||
case ots::kVMoveTo:
|
||||
return "VMoveTo";
|
||||
return "vmoveto";
|
||||
case ots::kRLineTo:
|
||||
return "RLineTo";
|
||||
return "rlineto";
|
||||
case ots::kHLineTo:
|
||||
return "HLineTo";
|
||||
return "hlineto";
|
||||
case ots::kVLineTo:
|
||||
return "VLineTo";
|
||||
return "vlineto";
|
||||
case ots::kRRCurveTo:
|
||||
return "RRCurveTo";
|
||||
return "rrcurveto";
|
||||
case ots::kCallSubr:
|
||||
return "CallSubr";
|
||||
return "callsubr";
|
||||
case ots::kReturn:
|
||||
return "Return";
|
||||
return "return";
|
||||
case ots::kEndChar:
|
||||
return "EndChar";
|
||||
return "endchar";
|
||||
case ots::kVSIndex:
|
||||
return "vsindex";
|
||||
case ots::kBlend:
|
||||
return "blend";
|
||||
case ots::kHStemHm:
|
||||
return "HStemHm";
|
||||
return "hstemhm";
|
||||
case ots::kHintMask:
|
||||
return "HintMask";
|
||||
return "hintmask";
|
||||
case ots::kCntrMask:
|
||||
return "CntrMask";
|
||||
return "cntrmask";
|
||||
case ots::kRMoveTo:
|
||||
return "RMoveTo";
|
||||
return "rmoveto";
|
||||
case ots::kHMoveTo:
|
||||
return "HMoveTo";
|
||||
return "hmoveto";
|
||||
case ots::kVStemHm:
|
||||
return "VStemHm";
|
||||
return "vstemhm";
|
||||
case ots::kRCurveLine:
|
||||
return "RCurveLine";
|
||||
return "rcurveline";
|
||||
case ots::kRLineCurve:
|
||||
return "RLineCurve";
|
||||
return "rlinecurve";
|
||||
case ots::kVVCurveTo:
|
||||
return "VVCurveTo";
|
||||
case ots::kHHCurveTo:
|
||||
return "HHCurveTo";
|
||||
return "hhcurveto";
|
||||
case ots::kCallGSubr:
|
||||
return "CallGSubr";
|
||||
return "callgsubr";
|
||||
case ots::kVHCurveTo:
|
||||
return "VHCurveTo";
|
||||
return "vhcurveto";
|
||||
case ots::kHVCurveTo:
|
||||
return "HVCurveTo";
|
||||
case ots::kDotSection:
|
||||
return "DotSection";
|
||||
return "dotsection";
|
||||
case ots::kAnd:
|
||||
return "And";
|
||||
return "and";
|
||||
case ots::kOr:
|
||||
return "Or";
|
||||
return "or";
|
||||
case ots::kNot:
|
||||
return "Not";
|
||||
return "not";
|
||||
case ots::kAbs:
|
||||
return "Abs";
|
||||
return "abs";
|
||||
case ots::kAdd:
|
||||
return "Add";
|
||||
return "add";
|
||||
case ots::kSub:
|
||||
return "Sub";
|
||||
return "sub";
|
||||
case ots::kDiv:
|
||||
return "Div";
|
||||
return "div";
|
||||
case ots::kNeg:
|
||||
return "Neg";
|
||||
return "neg";
|
||||
case ots::kEq:
|
||||
return "Eq";
|
||||
return "eq";
|
||||
case ots::kDrop:
|
||||
return "Drop";
|
||||
return "drop";
|
||||
case ots::kPut:
|
||||
return "Put";
|
||||
return "put";
|
||||
case ots::kGet:
|
||||
return "Get";
|
||||
return "get";
|
||||
case ots::kIfElse:
|
||||
return "IfElse";
|
||||
return "ifelse";
|
||||
case ots::kRandom:
|
||||
return "Random";
|
||||
return "random";
|
||||
case ots::kMul:
|
||||
return "Mul";
|
||||
return "mul";
|
||||
case ots::kSqrt:
|
||||
return "Sqrt";
|
||||
return "sqrt";
|
||||
case ots::kDup:
|
||||
return "Dup";
|
||||
return "dup";
|
||||
case ots::kExch:
|
||||
return "Exch";
|
||||
return "exch";
|
||||
case ots::kIndex:
|
||||
return "Index";
|
||||
return "index";
|
||||
case ots::kRoll:
|
||||
return "Roll";
|
||||
return "roll";
|
||||
case ots::kHFlex:
|
||||
return "HFlex";
|
||||
return "hflex";
|
||||
case ots::kFlex:
|
||||
return "Flex";
|
||||
return "flex";
|
||||
case ots::kHFlex1:
|
||||
return "HFlex1";
|
||||
return "hflex1";
|
||||
case ots::kFlex1:
|
||||
return "Flex1";
|
||||
return "flex1";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
|
@ -150,9 +162,9 @@ const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
|
|||
// Read one or more bytes from the |char_string| buffer and stores the number
|
||||
// read on |out_number|. If the number read is an operator (ex 'vstem'), sets
|
||||
// true on |out_is_operator|. Returns true if the function read a number.
|
||||
bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
||||
int32_t *out_number,
|
||||
bool *out_is_operator) {
|
||||
bool ReadNextNumberFromCharString(ots::Buffer *char_string,
|
||||
int32_t *out_number,
|
||||
bool *out_is_operator) {
|
||||
uint8_t v = 0;
|
||||
if (!char_string->ReadU8(&v)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -174,7 +186,7 @@ bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
|||
*out_is_operator = true;
|
||||
} else if (v <= 27) {
|
||||
// Special handling for v==19 and v==20 are implemented in
|
||||
// ExecuteType2CharStringOperator().
|
||||
// ExecuteCharStringOperator().
|
||||
*out_number = v;
|
||||
*out_is_operator = true;
|
||||
} else if (v == 28) {
|
||||
|
@ -221,23 +233,63 @@ bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ValidCFF2Operator(int32_t op) {
|
||||
switch (op) {
|
||||
case ots::kReturn:
|
||||
case ots::kEndChar:
|
||||
case ots::kAbs:
|
||||
case ots::kAdd:
|
||||
case ots::kSub:
|
||||
case ots::kDiv:
|
||||
case ots::kNeg:
|
||||
case ots::kRandom:
|
||||
case ots::kMul:
|
||||
case ots::kSqrt:
|
||||
case ots::kDrop:
|
||||
case ots::kExch:
|
||||
case ots::kIndex:
|
||||
case ots::kRoll:
|
||||
case ots::kDup:
|
||||
case ots::kPut:
|
||||
case ots::kGet:
|
||||
case ots::kDotSection:
|
||||
case ots::kAnd:
|
||||
case ots::kOr:
|
||||
case ots::kNot:
|
||||
case ots::kEq:
|
||||
case ots::kIfElse:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Executes |op| and updates |argument_stack|. Returns true if the execution
|
||||
// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
|
||||
// calls ExecuteType2CharString() function. The arguments other than |op| and
|
||||
// calls ExecuteCharString() function. The arguments other than |op| and
|
||||
// |argument_stack| are passed for that reason.
|
||||
bool ExecuteType2CharStringOperator(ots::Font *font,
|
||||
int32_t op,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems) {
|
||||
bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff,
|
||||
int32_t op,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool *in_out_have_blend,
|
||||
bool *in_out_have_visindex,
|
||||
int32_t *in_out_vsindex,
|
||||
bool cff2) {
|
||||
ots::Font* font = cff.GetFont();
|
||||
const size_t stack_size = argument_stack->size();
|
||||
|
||||
if (cff2 && !ValidCFF2Operator(op)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case ots::kCallSubr:
|
||||
case ots::kCallGSubr: {
|
||||
|
@ -290,16 +342,17 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
|||
}
|
||||
ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
|
||||
|
||||
return ExecuteType2CharString(font,
|
||||
call_depth + 1,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
&char_string_to_jump,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems);
|
||||
return ExecuteCharString(cff,
|
||||
call_depth + 1,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
&char_string_to_jump,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems,
|
||||
cff2);
|
||||
}
|
||||
|
||||
case ots::kReturn:
|
||||
|
@ -310,6 +363,51 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
|||
*in_out_found_width = true; // just in case.
|
||||
return true;
|
||||
|
||||
case ots::kVSIndex: {
|
||||
if (!cff2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size != 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*in_out_have_blend || *in_out_have_visindex) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (argument_stack->top() >= cff.region_index_count.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*in_out_have_visindex = true;
|
||||
*in_out_vsindex = argument_stack->top();
|
||||
while (!argument_stack->empty())
|
||||
argument_stack->pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
case ots::kBlend: {
|
||||
if (!cff2) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (stack_size < 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*in_out_vsindex >= cff.region_index_count.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t k = cff.region_index_count.at(*in_out_vsindex);
|
||||
uint16_t n = argument_stack->top();
|
||||
if (stack_size < n * (k + 1) + 1) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Keep the 1st n operands on the stack for the next operator to use and
|
||||
// pop the rest. There can be multiple consecutive blend operator, so this
|
||||
// makes sure the operands of all of them are kept on the stack.
|
||||
while (argument_stack->size() > stack_size - ((n * k) + 1))
|
||||
argument_stack->pop();
|
||||
*in_out_have_blend = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
case ots::kHStem:
|
||||
case ots::kVStem:
|
||||
case ots::kHStemHm:
|
||||
|
@ -649,7 +747,7 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
|||
argument_stack->pop();
|
||||
argument_stack->push(dummy_result);
|
||||
argument_stack->push(dummy_result);
|
||||
if (argument_stack->size() > kMaxArgumentStack) {
|
||||
if (ArgumentStackOverflows(argument_stack, cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// TODO(yusukes): Implement this. We should push a real value for all
|
||||
|
@ -729,26 +827,29 @@ bool ExecuteType2CharStringOperator(ots::Font *font,
|
|||
// in_out_found_width: true is set if |char_string| contains 'width' byte (which
|
||||
// is 0 or 1 byte.)
|
||||
// in_out_num_stems: total number of hstems and vstems processed so far.
|
||||
bool ExecuteType2CharString(ots::Font *font,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems) {
|
||||
bool ExecuteCharString(ots::OpenTypeCFF& cff,
|
||||
size_t call_depth,
|
||||
const ots::CFFIndex& global_subrs_index,
|
||||
const ots::CFFIndex& local_subrs_index,
|
||||
ots::Buffer *cff_table,
|
||||
ots::Buffer *char_string,
|
||||
std::stack<int32_t> *argument_stack,
|
||||
bool *out_found_endchar,
|
||||
bool *in_out_found_width,
|
||||
size_t *in_out_num_stems,
|
||||
bool cff2) {
|
||||
if (call_depth > kMaxSubrNesting) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_found_endchar = false;
|
||||
|
||||
bool in_out_have_blend = false, in_out_have_visindex = false;
|
||||
int32_t in_out_vsindex = 0;
|
||||
const size_t length = char_string->length();
|
||||
while (char_string->offset() < length) {
|
||||
int32_t operator_or_operand = 0;
|
||||
bool is_operator = false;
|
||||
if (!ReadNextNumberFromType2CharString(char_string,
|
||||
if (!ReadNextNumberFromCharString(char_string,
|
||||
&operator_or_operand,
|
||||
&is_operator)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -761,35 +862,39 @@ bool ExecuteType2CharString(ots::Font *font,
|
|||
*/
|
||||
|
||||
if (!is_operator) {
|
||||
std::fprintf(stderr, "#%d# ", operator_or_operand);
|
||||
std::fprintf(stderr, "%d ", operator_or_operand);
|
||||
} else {
|
||||
std::fprintf(stderr, "#%s#\n",
|
||||
Type2CharStringOperatorToString(
|
||||
ots::Type2CharStringOperator(operator_or_operand))
|
||||
std::fprintf(stderr, "%s\n",
|
||||
CharStringOperatorToString(
|
||||
ots::CharStringOperator(operator_or_operand))
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!is_operator) {
|
||||
argument_stack->push(operator_or_operand);
|
||||
if (argument_stack->size() > kMaxArgumentStack) {
|
||||
if (ArgumentStackOverflows(argument_stack, cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// An operator is found. Execute it.
|
||||
if (!ExecuteType2CharStringOperator(font,
|
||||
operator_or_operand,
|
||||
call_depth,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
char_string,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems)) {
|
||||
if (!ExecuteCharStringOperator(cff,
|
||||
operator_or_operand,
|
||||
call_depth,
|
||||
global_subrs_index,
|
||||
local_subrs_index,
|
||||
cff_table,
|
||||
char_string,
|
||||
argument_stack,
|
||||
out_found_endchar,
|
||||
in_out_found_width,
|
||||
in_out_num_stems,
|
||||
&in_out_have_blend,
|
||||
&in_out_have_visindex,
|
||||
&in_out_vsindex,
|
||||
cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (*out_found_endchar) {
|
||||
|
@ -801,37 +906,39 @@ bool ExecuteType2CharString(ots::Font *font,
|
|||
}
|
||||
|
||||
// No endchar operator is found.
|
||||
if (cff2)
|
||||
return true;
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
|
||||
// |out_local_subrs_to_use|. Returns true on success.
|
||||
bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<ots::CFFIndex *> &local_subrs_per_font,
|
||||
const ots::CFFIndex *local_subrs,
|
||||
bool SelectLocalSubr(const ots::OpenTypeCFF& cff,
|
||||
uint16_t glyph_index, // 0-origin
|
||||
const ots::CFFIndex **out_local_subrs_to_use) {
|
||||
bool cff2 = (cff.major == 2);
|
||||
*out_local_subrs_to_use = NULL;
|
||||
|
||||
// First, find local subrs from |local_subrs_per_font|.
|
||||
if ((fd_select.size() > 0) &&
|
||||
(!local_subrs_per_font.empty())) {
|
||||
if ((cff.fd_select.size() > 0) &&
|
||||
(!cff.local_subrs_per_font.empty())) {
|
||||
// Look up FDArray index for the glyph.
|
||||
std::map<uint16_t, uint8_t>::const_iterator iter =
|
||||
fd_select.find(glyph_index);
|
||||
if (iter == fd_select.end()) {
|
||||
const auto& iter = cff.fd_select.find(glyph_index);
|
||||
if (iter == cff.fd_select.end()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
const uint8_t fd_index = iter->second;
|
||||
if (fd_index >= local_subrs_per_font.size()) {
|
||||
const auto fd_index = iter->second;
|
||||
if (fd_index >= cff.local_subrs_per_font.size()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
*out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
|
||||
} else if (local_subrs) {
|
||||
*out_local_subrs_to_use = cff.local_subrs_per_font.at(fd_index);
|
||||
} else if (cff.local_subrs) {
|
||||
// Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
|
||||
// entries. If The font has a local subrs index associated with the Top
|
||||
// DICT (not FDArrays), use it.
|
||||
*out_local_subrs_to_use = local_subrs;
|
||||
*out_local_subrs_to_use = cff.local_subrs;
|
||||
} else if (cff2 && cff.local_subrs_per_font.size() == 1) {
|
||||
*out_local_subrs_to_use = cff.local_subrs_per_font.at(0);
|
||||
} else {
|
||||
// Just return NULL.
|
||||
*out_local_subrs_to_use = NULL;
|
||||
|
@ -844,18 +951,16 @@ bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
|
|||
|
||||
namespace ots {
|
||||
|
||||
bool ValidateType2CharStringIndex(
|
||||
ots::Font *font,
|
||||
const CFFIndex& char_strings_index,
|
||||
bool ValidateCFFCharStrings(
|
||||
ots::OpenTypeCFF& cff,
|
||||
const CFFIndex& global_subrs_index,
|
||||
const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<CFFIndex *> &local_subrs_per_font,
|
||||
const CFFIndex *local_subrs,
|
||||
Buffer* cff_table) {
|
||||
const CFFIndex& char_strings_index = *(cff.charstrings_index);
|
||||
if (char_strings_index.offsets.size() == 0) {
|
||||
return OTS_FAILURE(); // no charstring.
|
||||
}
|
||||
|
||||
bool cff2 = (cff.major == 2);
|
||||
// For each glyph, validate the corresponding charstring.
|
||||
for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) {
|
||||
// Prepare a Buffer object, |char_string|, which contains the charstring
|
||||
|
@ -875,9 +980,7 @@ bool ValidateType2CharStringIndex(
|
|||
// Get a local subrs for the glyph.
|
||||
const unsigned glyph_index = i - 1; // index in the map is 0-origin.
|
||||
const CFFIndex *local_subrs_to_use = NULL;
|
||||
if (!SelectLocalSubr(fd_select,
|
||||
local_subrs_per_font,
|
||||
local_subrs,
|
||||
if (!SelectLocalSubr(cff,
|
||||
glyph_index,
|
||||
&local_subrs_to_use)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -891,16 +994,19 @@ bool ValidateType2CharStringIndex(
|
|||
// Check a charstring for the |i|-th glyph.
|
||||
std::stack<int32_t> argument_stack;
|
||||
bool found_endchar = false;
|
||||
bool found_width = false;
|
||||
// CFF2 CharString has no value for width, so we start with true here to
|
||||
// error out if width is found.
|
||||
bool found_width = cff2;
|
||||
size_t num_stems = 0;
|
||||
if (!ExecuteType2CharString(font,
|
||||
0 /* initial call_depth is zero */,
|
||||
global_subrs_index, *local_subrs_to_use,
|
||||
cff_table, &char_string, &argument_stack,
|
||||
&found_endchar, &found_width, &num_stems)) {
|
||||
if (!ExecuteCharString(cff,
|
||||
0 /* initial call_depth is zero */,
|
||||
global_subrs_index, *local_subrs_to_use,
|
||||
cff_table, &char_string, &argument_stack,
|
||||
&found_endchar, &found_width, &num_stems,
|
||||
cff2)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!found_endchar) {
|
||||
if (!cff2 && !found_endchar) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2010-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -13,6 +13,9 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
const size_t kMaxCFF1ArgumentStack = 48;
|
||||
const size_t kMaxCFF2ArgumentStack = 513;
|
||||
|
||||
// Validates all charstrings in |char_strings_index|. Charstring is a small
|
||||
// language for font hinting defined in Adobe Technical Note #5177.
|
||||
// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
|
||||
|
@ -34,17 +37,14 @@ namespace ots {
|
|||
// local_subrs: A Local Subrs associated with Top DICT. Can be NULL.
|
||||
// cff_table: A buffer which contains actual byte code of charstring, global
|
||||
// subroutines and local subroutines.
|
||||
bool ValidateType2CharStringIndex(
|
||||
Font *font,
|
||||
const CFFIndex &char_strings_index,
|
||||
bool ValidateCFFCharStrings(
|
||||
OpenTypeCFF& cff,
|
||||
const CFFIndex &global_subrs_index,
|
||||
const std::map<uint16_t, uint8_t> &fd_select,
|
||||
const std::vector<CFFIndex *> &local_subrs_per_font,
|
||||
const CFFIndex *local_subrs,
|
||||
Buffer *cff_table);
|
||||
|
||||
// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
|
||||
enum Type2CharStringOperator {
|
||||
// and https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr
|
||||
enum CharStringOperator {
|
||||
kHStem = 1,
|
||||
kVStem = 3,
|
||||
kVMoveTo = 4,
|
||||
|
@ -55,6 +55,8 @@ enum Type2CharStringOperator {
|
|||
kCallSubr = 10,
|
||||
kReturn = 11,
|
||||
kEndChar = 14,
|
||||
kVSIndex = 15,
|
||||
kBlend = 16,
|
||||
kHStemHm = 18,
|
||||
kHintMask = 19,
|
||||
kCntrMask = 20,
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -15,8 +15,6 @@
|
|||
// cmap - Character To Glyph Index Mapping Table
|
||||
// http://www.microsoft.com/typography/otspec/cmap.htm
|
||||
|
||||
#define TABLE_NAME "cmap"
|
||||
|
||||
namespace {
|
||||
|
||||
struct CMAPSubtableHeader {
|
||||
|
@ -56,8 +54,12 @@ const uint32_t kIVSStart = 0xE0100;
|
|||
const uint32_t kIVSEnd = 0xE01EF;
|
||||
const uint32_t kUVSUpperLimit = 0xFFFFFF;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
// Parses Format 4 tables
|
||||
bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
||||
bool OpenTypeCMAP::ParseFormat4(int platform, int encoding,
|
||||
const uint8_t *data, size_t length, uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
|
@ -65,20 +67,22 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
|||
// whole thing and recompacting it, we validate it and include it verbatim
|
||||
// in the output.
|
||||
|
||||
if (!font->os2) {
|
||||
return OTS_FAILURE_MSG("Required OS/2 table missing");
|
||||
OpenTypeOS2 *os2 = static_cast<OpenTypeOS2*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_OS2));
|
||||
if (!os2) {
|
||||
return Error("Required OS/2 table missing");
|
||||
}
|
||||
|
||||
if (!subtable.Skip(4)) {
|
||||
return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable");
|
||||
return Error("Can't read 4 bytes at start of cmap format 4 subtable");
|
||||
}
|
||||
uint16_t language = 0;
|
||||
if (!subtable.ReadU16(&language)) {
|
||||
return OTS_FAILURE_MSG("Can't read language");
|
||||
return Error("Can't read language");
|
||||
}
|
||||
if (language) {
|
||||
// Platform ID 3 (windows) subtables should have language '0'.
|
||||
return OTS_FAILURE_MSG("Languages should be 0 (%d)", language);
|
||||
return Error("Languages should be 0 (%d)", language);
|
||||
}
|
||||
|
||||
uint16_t segcountx2, search_range, entry_selector, range_shift;
|
||||
|
@ -87,16 +91,16 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
|||
!subtable.ReadU16(&search_range) ||
|
||||
!subtable.ReadU16(&entry_selector) ||
|
||||
!subtable.ReadU16(&range_shift)) {
|
||||
return OTS_FAILURE_MSG("Failed to read subcmap structure");
|
||||
return Error("Failed to read subcmap structure");
|
||||
}
|
||||
|
||||
if (segcountx2 & 1 || search_range & 1) {
|
||||
return OTS_FAILURE_MSG("Bad subcmap structure");
|
||||
return Error("Bad subcmap structure");
|
||||
}
|
||||
const uint16_t segcount = segcountx2 >> 1;
|
||||
// There must be at least one segment according the spec.
|
||||
if (segcount < 1) {
|
||||
return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount);
|
||||
return Error("Segcount < 1 (%d)", segcount);
|
||||
}
|
||||
|
||||
// log2segcount is the maximal x s.t. 2^x < segcount
|
||||
|
@ -107,48 +111,48 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
|||
|
||||
const uint16_t expected_search_range = 2 * 1u << log2segcount;
|
||||
if (expected_search_range != search_range) {
|
||||
return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range);
|
||||
return Error("expected search range != search range (%d != %d)", expected_search_range, search_range);
|
||||
}
|
||||
|
||||
if (entry_selector != log2segcount) {
|
||||
return OTS_FAILURE_MSG("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
|
||||
return Error("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
|
||||
}
|
||||
|
||||
const uint16_t expected_range_shift = segcountx2 - search_range;
|
||||
if (range_shift != expected_range_shift) {
|
||||
return OTS_FAILURE_MSG("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
|
||||
return Error("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
|
||||
}
|
||||
|
||||
std::vector<Subtable314Range> ranges(segcount);
|
||||
|
||||
for (unsigned i = 0; i < segcount; ++i) {
|
||||
if (!subtable.ReadU16(&ranges[i].end_range)) {
|
||||
return OTS_FAILURE_MSG("Failed to read segment %d", i);
|
||||
return Error("Failed to read segment %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t padding;
|
||||
if (!subtable.ReadU16(&padding)) {
|
||||
return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding");
|
||||
return Error("Failed to read cmap subtable segment padding");
|
||||
}
|
||||
if (padding) {
|
||||
return OTS_FAILURE_MSG("Non zero cmap subtable segment padding (%d)", padding);
|
||||
return Error("Non zero cmap subtable segment padding (%d)", padding);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < segcount; ++i) {
|
||||
if (!subtable.ReadU16(&ranges[i].start_range)) {
|
||||
return OTS_FAILURE_MSG("Failed to read segment start range %d", i);
|
||||
return Error("Failed to read segment start range %d", i);
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < segcount; ++i) {
|
||||
if (!subtable.ReadS16(&ranges[i].id_delta)) {
|
||||
return OTS_FAILURE_MSG("Failed to read segment delta %d", i);
|
||||
return Error("Failed to read segment delta %d", i);
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < segcount; ++i) {
|
||||
ranges[i].id_range_offset_offset = subtable.offset();
|
||||
if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read segment range offset %d", i);
|
||||
return Error("Failed to read segment range offset %d", i);
|
||||
}
|
||||
|
||||
if (ranges[i].id_range_offset & 1) {
|
||||
|
@ -156,12 +160,12 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
|||
// for 0xFFFF-0xFFFF range.
|
||||
// (e.g., many fonts in http://www.princexml.com/fonts/)
|
||||
if (i == segcount - 1u) {
|
||||
OTS_WARNING("bad id_range_offset");
|
||||
Warning("bad id_range_offset");
|
||||
ranges[i].id_range_offset = 0;
|
||||
// The id_range_offset value in the transcoded font will not change
|
||||
// since this table is not actually "transcoded" yet.
|
||||
} else {
|
||||
return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset);
|
||||
return Error("Bad segment offset (%d)", ranges[i].id_range_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -176,36 +180,36 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
|||
(ranges[i].end_range == 0xffff)) {
|
||||
// Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
|
||||
// We'll accept them as an exception.
|
||||
OTS_WARNING("multiple 0xffff terminators found");
|
||||
Warning("multiple 0xffff terminators found");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
|
||||
// unsorted table...
|
||||
if (ranges[i].end_range <= ranges[i - 1].end_range) {
|
||||
return OTS_FAILURE_MSG("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
|
||||
return Error("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
|
||||
}
|
||||
if (ranges[i].start_range <= ranges[i - 1].end_range) {
|
||||
return OTS_FAILURE_MSG("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
|
||||
return Error("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
|
||||
}
|
||||
|
||||
// On many fonts, the value of {first, last}_char_index are incorrect.
|
||||
// Fix them.
|
||||
if (font->os2->first_char_index != 0xFFFF &&
|
||||
if (os2->table.first_char_index != 0xFFFF &&
|
||||
ranges[i].start_range != 0xFFFF &&
|
||||
font->os2->first_char_index > ranges[i].start_range) {
|
||||
font->os2->first_char_index = ranges[i].start_range;
|
||||
os2->table.first_char_index > ranges[i].start_range) {
|
||||
os2->table.first_char_index = ranges[i].start_range;
|
||||
}
|
||||
if (font->os2->last_char_index != 0xFFFF &&
|
||||
if (os2->table.last_char_index != 0xFFFF &&
|
||||
ranges[i].end_range != 0xFFFF &&
|
||||
font->os2->last_char_index < ranges[i].end_range) {
|
||||
font->os2->last_char_index = ranges[i].end_range;
|
||||
os2->table.last_char_index < ranges[i].end_range) {
|
||||
os2->table.last_char_index = ranges[i].end_range;
|
||||
}
|
||||
}
|
||||
|
||||
// The last range must end at 0xffff
|
||||
if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
|
||||
return OTS_FAILURE_MSG("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
|
||||
return Error("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
|
||||
ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
|
||||
}
|
||||
|
||||
|
@ -219,7 +223,7 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
|||
// this is explictly allowed to overflow in the spec
|
||||
const uint16_t glyph = code_point + ranges[i].id_delta;
|
||||
if (glyph >= num_glyphs) {
|
||||
return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
|
||||
return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
|
||||
}
|
||||
} else {
|
||||
const uint16_t range_delta = code_point - ranges[i].start_range;
|
||||
|
@ -230,13 +234,13 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
|||
range_delta * 2;
|
||||
// We need to be able to access a 16-bit value from this offset
|
||||
if (glyph_id_offset + 1 >= length) {
|
||||
return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
|
||||
return Error("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
|
||||
}
|
||||
uint16_t glyph;
|
||||
std::memcpy(&glyph, data + glyph_id_offset, 2);
|
||||
glyph = ntohs(glyph);
|
||||
glyph = ots_ntohs(glyph);
|
||||
if (glyph >= num_glyphs) {
|
||||
return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
|
||||
return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,85 +249,85 @@ bool ParseFormat4(ots::Font *font, int platform, int encoding,
|
|||
// We accept the table.
|
||||
// TODO(yusukes): transcode the subtable.
|
||||
if (platform == 3 && encoding == 0) {
|
||||
font->cmap->subtable_3_0_4_data = data;
|
||||
font->cmap->subtable_3_0_4_length = length;
|
||||
this->subtable_3_0_4_data = data;
|
||||
this->subtable_3_0_4_length = length;
|
||||
} else if (platform == 3 && encoding == 1) {
|
||||
font->cmap->subtable_3_1_4_data = data;
|
||||
font->cmap->subtable_3_1_4_length = length;
|
||||
this->subtable_3_1_4_data = data;
|
||||
this->subtable_3_1_4_length = length;
|
||||
} else if (platform == 0 && encoding == 3) {
|
||||
font->cmap->subtable_0_3_4_data = data;
|
||||
font->cmap->subtable_0_3_4_length = length;
|
||||
this->subtable_0_3_4_data = data;
|
||||
this->subtable_0_3_4_length = length;
|
||||
} else {
|
||||
return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
|
||||
return Error("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Parse31012(ots::Font *font,
|
||||
const uint8_t *data, size_t length, uint16_t num_glyphs) {
|
||||
bool OpenTypeCMAP::Parse31012(const uint8_t *data, size_t length,
|
||||
uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Format 12 tables are simple. We parse these and fully serialise them
|
||||
// later.
|
||||
|
||||
if (!subtable.Skip(8)) {
|
||||
return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable");
|
||||
return Error("failed to skip the first 8 bytes of format 12 subtable");
|
||||
}
|
||||
uint32_t language = 0;
|
||||
if (!subtable.ReadU32(&language)) {
|
||||
return OTS_FAILURE_MSG("can't read format 12 subtable language");
|
||||
return Error("can't read format 12 subtable language");
|
||||
}
|
||||
if (language) {
|
||||
return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language);
|
||||
return Error("format 12 subtable language should be zero (%d)", language);
|
||||
}
|
||||
|
||||
uint32_t num_groups = 0;
|
||||
if (!subtable.ReadU32(&num_groups)) {
|
||||
return OTS_FAILURE_MSG("can't read number of format 12 subtable groups");
|
||||
return Error("can't read number of format 12 subtable groups");
|
||||
}
|
||||
if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
|
||||
return OTS_FAILURE_MSG("Bad format 12 subtable group count %d", num_groups);
|
||||
return Error("Bad format 12 subtable group count %d", num_groups);
|
||||
}
|
||||
|
||||
std::vector<ots::OpenTypeCMAPSubtableRange> &groups
|
||||
= font->cmap->subtable_3_10_12;
|
||||
= this->subtable_3_10_12;
|
||||
groups.resize(num_groups);
|
||||
|
||||
for (unsigned i = 0; i < num_groups; ++i) {
|
||||
if (!subtable.ReadU32(&groups[i].start_range) ||
|
||||
!subtable.ReadU32(&groups[i].end_range) ||
|
||||
!subtable.ReadU32(&groups[i].start_glyph_id)) {
|
||||
return OTS_FAILURE_MSG("can't read format 12 subtable group");
|
||||
return Error("can't read format 12 subtable group");
|
||||
}
|
||||
|
||||
if (groups[i].start_range > kUnicodeUpperLimit ||
|
||||
groups[i].end_range > kUnicodeUpperLimit ||
|
||||
groups[i].start_glyph_id > 0xFFFF) {
|
||||
return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
|
||||
return Error("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
|
||||
groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
|
||||
}
|
||||
|
||||
// We assert that the glyph value is within range. Because of the range
|
||||
// limits, above, we don't need to worry about overflow.
|
||||
if (groups[i].end_range < groups[i].start_range) {
|
||||
return OTS_FAILURE_MSG("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
|
||||
return Error("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
|
||||
groups[i].end_range, groups[i].start_range);
|
||||
}
|
||||
if ((groups[i].end_range - groups[i].start_range) +
|
||||
groups[i].start_glyph_id > num_glyphs) {
|
||||
return OTS_FAILURE_MSG("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
|
||||
return Error("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
|
||||
}
|
||||
}
|
||||
|
||||
// the groups must be sorted by start code and may not overlap
|
||||
for (unsigned i = 1; i < num_groups; ++i) {
|
||||
if (groups[i].start_range <= groups[i - 1].start_range) {
|
||||
return OTS_FAILURE_MSG("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
|
||||
return Error("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
|
||||
groups[i].start_range, groups[i-1].start_range);
|
||||
}
|
||||
if (groups[i].start_range <= groups[i - 1].end_range) {
|
||||
return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
|
||||
return Error("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
|
||||
groups[i].start_range, groups[i-1].end_range);
|
||||
}
|
||||
}
|
||||
|
@ -331,44 +335,43 @@ bool Parse31012(ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Parse31013(ots::Font *font,
|
||||
const uint8_t *data, size_t length, uint16_t num_glyphs) {
|
||||
bool OpenTypeCMAP::Parse31013(const uint8_t *data, size_t length,
|
||||
uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Format 13 tables are simple. We parse these and fully serialise them
|
||||
// later.
|
||||
|
||||
if (!subtable.Skip(8)) {
|
||||
return OTS_FAILURE_MSG("Bad cmap subtable length");
|
||||
return Error("Bad cmap subtable length");
|
||||
}
|
||||
uint32_t language = 0;
|
||||
if (!subtable.ReadU32(&language)) {
|
||||
return OTS_FAILURE_MSG("Can't read cmap subtable language");
|
||||
return Error("Can't read cmap subtable language");
|
||||
}
|
||||
if (language) {
|
||||
return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
|
||||
return Error("Cmap subtable language should be zero but is %d", language);
|
||||
}
|
||||
|
||||
uint32_t num_groups = 0;
|
||||
if (!subtable.ReadU32(&num_groups)) {
|
||||
return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable");
|
||||
return Error("Can't read number of groups in a cmap subtable");
|
||||
}
|
||||
|
||||
// We limit the number of groups in the same way as in 3.10.12 tables. See
|
||||
// the comment there in
|
||||
if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
|
||||
return OTS_FAILURE_MSG("Bad format 13 subtable group count %d", num_groups);
|
||||
return Error("Bad format 13 subtable group count %d", num_groups);
|
||||
}
|
||||
|
||||
std::vector<ots::OpenTypeCMAPSubtableRange> &groups
|
||||
= font->cmap->subtable_3_10_13;
|
||||
std::vector<ots::OpenTypeCMAPSubtableRange> &groups = this->subtable_3_10_13;
|
||||
groups.resize(num_groups);
|
||||
|
||||
for (unsigned i = 0; i < num_groups; ++i) {
|
||||
if (!subtable.ReadU32(&groups[i].start_range) ||
|
||||
!subtable.ReadU32(&groups[i].end_range) ||
|
||||
!subtable.ReadU32(&groups[i].start_glyph_id)) {
|
||||
return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable");
|
||||
return Error("Can't read subrange structure in a cmap subtable");
|
||||
}
|
||||
|
||||
// We conservatively limit all of the values to protect some parsers from
|
||||
|
@ -376,29 +379,29 @@ bool Parse31013(ots::Font *font,
|
|||
if (groups[i].start_range > kUnicodeUpperLimit ||
|
||||
groups[i].end_range > kUnicodeUpperLimit ||
|
||||
groups[i].start_glyph_id > 0xFFFF) {
|
||||
return OTS_FAILURE_MSG("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
|
||||
return Error("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
|
||||
}
|
||||
|
||||
if (groups[i].start_glyph_id >= num_glyphs) {
|
||||
return OTS_FAILURE_MSG("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
|
||||
return Error("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
|
||||
}
|
||||
}
|
||||
|
||||
// the groups must be sorted by start code and may not overlap
|
||||
for (unsigned i = 1; i < num_groups; ++i) {
|
||||
if (groups[i].start_range <= groups[i - 1].start_range) {
|
||||
return OTS_FAILURE_MSG("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
|
||||
return Error("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
|
||||
}
|
||||
if (groups[i].start_range <= groups[i - 1].end_range) {
|
||||
return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
|
||||
return Error("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Parse0514(ots::Font *font,
|
||||
const uint8_t *data, size_t length, uint16_t num_glyphs) {
|
||||
bool OpenTypeCMAP::Parse0514(const uint8_t *data, size_t length,
|
||||
uint16_t num_glyphs) {
|
||||
// Unicode Variation Selector table
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
|
@ -407,26 +410,26 @@ bool Parse0514(ots::Font *font,
|
|||
|
||||
// Skip format (USHORT) and length (ULONG)
|
||||
if (!subtable.Skip(6)) {
|
||||
return OTS_FAILURE_MSG("Can't read start of cmap subtable");
|
||||
return Error("Can't read start of cmap subtable");
|
||||
}
|
||||
|
||||
uint32_t num_records = 0;
|
||||
if (!subtable.ReadU32(&num_records)) {
|
||||
return OTS_FAILURE_MSG("Can't read number of records in cmap subtable");
|
||||
return Error("Can't read number of records in cmap subtable");
|
||||
}
|
||||
if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
|
||||
return OTS_FAILURE_MSG("Bad format 14 subtable records count %d", num_records);
|
||||
return Error("Bad format 14 subtable records count %d", num_records);
|
||||
}
|
||||
|
||||
std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
|
||||
= font->cmap->subtable_0_5_14;
|
||||
= this->subtable_0_5_14;
|
||||
records.resize(num_records);
|
||||
|
||||
for (unsigned i = 0; i < num_records; ++i) {
|
||||
if (!subtable.ReadU24(&records[i].var_selector) ||
|
||||
!subtable.ReadU32(&records[i].default_offset) ||
|
||||
!subtable.ReadU32(&records[i].non_default_offset)) {
|
||||
return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i);
|
||||
return Error("Can't read record structure of record %d in cmap subtale", i);
|
||||
}
|
||||
// Checks the value of variation selector
|
||||
if (!((records[i].var_selector >= kMongolianVSStart &&
|
||||
|
@ -435,24 +438,24 @@ bool Parse0514(ots::Font *font,
|
|||
records[i].var_selector <= kVSEnd) ||
|
||||
(records[i].var_selector >= kIVSStart &&
|
||||
records[i].var_selector <= kIVSEnd))) {
|
||||
return OTS_FAILURE_MSG("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
|
||||
return Error("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
|
||||
}
|
||||
if (i > 0 &&
|
||||
records[i-1].var_selector >= records[i].var_selector) {
|
||||
return OTS_FAILURE_MSG("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
|
||||
return Error("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
|
||||
}
|
||||
|
||||
// Checks offsets
|
||||
if (!records[i].default_offset && !records[i].non_default_offset) {
|
||||
return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i);
|
||||
return Error("No default aoffset in variation selector record %d", i);
|
||||
}
|
||||
if (records[i].default_offset &&
|
||||
records[i].default_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
|
||||
return Error("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
|
||||
}
|
||||
if (records[i].non_default_offset &&
|
||||
records[i].non_default_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
|
||||
return Error("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,10 +465,10 @@ bool Parse0514(ots::Font *font,
|
|||
subtable.set_offset(records[i].default_offset);
|
||||
uint32_t num_ranges = 0;
|
||||
if (!subtable.ReadU32(&num_ranges)) {
|
||||
return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i);
|
||||
return Error("Can't read number of ranges in record %d", i);
|
||||
}
|
||||
if (num_ranges == 0 || subtable.remaining() / 4 < num_ranges) {
|
||||
return OTS_FAILURE_MSG("Bad number of ranges (%d) in record %d", num_ranges, i);
|
||||
return Error("Bad number of ranges (%d) in record %d", num_ranges, i);
|
||||
}
|
||||
|
||||
uint32_t last_unicode_value = 0;
|
||||
|
@ -476,7 +479,7 @@ bool Parse0514(ots::Font *font,
|
|||
for (unsigned j = 0; j < num_ranges; ++j) {
|
||||
if (!subtable.ReadU24(&ranges[j].unicode_value) ||
|
||||
!subtable.ReadU8(&ranges[j].additional_count)) {
|
||||
return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i);
|
||||
return Error("Can't read range info in variation selector record %d", i);
|
||||
}
|
||||
const uint32_t check_value =
|
||||
ranges[j].unicode_value + ranges[j].additional_count;
|
||||
|
@ -485,7 +488,7 @@ bool Parse0514(ots::Font *font,
|
|||
check_value > kUVSUpperLimit ||
|
||||
(last_unicode_value &&
|
||||
ranges[j].unicode_value <= last_unicode_value)) {
|
||||
return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
|
||||
return Error("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
|
||||
}
|
||||
last_unicode_value = check_value;
|
||||
}
|
||||
|
@ -496,10 +499,10 @@ bool Parse0514(ots::Font *font,
|
|||
subtable.set_offset(records[i].non_default_offset);
|
||||
uint32_t num_mappings = 0;
|
||||
if (!subtable.ReadU32(&num_mappings)) {
|
||||
return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i);
|
||||
return Error("Can't read number of mappings in variation selector record %d", i);
|
||||
}
|
||||
if (num_mappings == 0 || subtable.remaining() / 5 < num_mappings) {
|
||||
return OTS_FAILURE_MSG("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
|
||||
return Error("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
|
||||
}
|
||||
|
||||
uint32_t last_unicode_value = 0;
|
||||
|
@ -510,14 +513,14 @@ bool Parse0514(ots::Font *font,
|
|||
for (unsigned j = 0; j < num_mappings; ++j) {
|
||||
if (!subtable.ReadU24(&mappings[j].unicode_value) ||
|
||||
!subtable.ReadU16(&mappings[j].glyph_id)) {
|
||||
return OTS_FAILURE_MSG("Can't read mapping %d in variation selector record %d", j, i);
|
||||
return Error("Can't read mapping %d in variation selector record %d", j, i);
|
||||
}
|
||||
if (mappings[j].glyph_id == 0 ||
|
||||
mappings[j].unicode_value == 0 ||
|
||||
mappings[j].unicode_value > kUnicodeUpperLimit ||
|
||||
(last_unicode_value &&
|
||||
mappings[j].unicode_value <= last_unicode_value)) {
|
||||
return OTS_FAILURE_MSG("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
|
||||
return Error("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
|
||||
}
|
||||
last_unicode_value = mappings[j].unicode_value;
|
||||
}
|
||||
|
@ -525,60 +528,55 @@ bool Parse0514(ots::Font *font,
|
|||
}
|
||||
|
||||
if (subtable.offset() != length) {
|
||||
return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
|
||||
return Error("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
|
||||
}
|
||||
font->cmap->subtable_0_5_14_length = subtable.offset();
|
||||
this->subtable_0_5_14_length = subtable.offset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Parse100(ots::Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeCMAP::Parse100(const uint8_t *data, size_t length) {
|
||||
// Mac Roman table
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
if (!subtable.Skip(4)) {
|
||||
return OTS_FAILURE_MSG("Bad cmap subtable");
|
||||
return Error("Bad cmap subtable");
|
||||
}
|
||||
uint16_t language = 0;
|
||||
if (!subtable.ReadU16(&language)) {
|
||||
return OTS_FAILURE_MSG("Can't read language in cmap subtable");
|
||||
return Error("Can't read language in cmap subtable");
|
||||
}
|
||||
if (language) {
|
||||
// simsun.ttf has non-zero language id.
|
||||
OTS_WARNING("language id should be zero: %u", language);
|
||||
Warning("language id should be zero: %u", language);
|
||||
}
|
||||
|
||||
font->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
|
||||
this->subtable_1_0_0.reserve(kFormat0ArraySize);
|
||||
for (size_t i = 0; i < kFormat0ArraySize; ++i) {
|
||||
uint8_t glyph_id = 0;
|
||||
if (!subtable.ReadU8(&glyph_id)) {
|
||||
return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i);
|
||||
return Error("Can't read glyph id at array[%ld] in cmap subtable", i);
|
||||
}
|
||||
font->cmap->subtable_1_0_0.push_back(glyph_id);
|
||||
this->subtable_1_0_0.push_back(glyph_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeCMAP::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
font->cmap = new OpenTypeCMAP;
|
||||
|
||||
uint16_t version = 0;
|
||||
uint16_t num_tables = 0;
|
||||
if (!table.ReadU16(&version) ||
|
||||
!table.ReadU16(&num_tables)) {
|
||||
return OTS_FAILURE_MSG("Can't read structure of cmap");
|
||||
return Error("Can't read structure of cmap");
|
||||
}
|
||||
|
||||
if (version != 0) {
|
||||
return OTS_FAILURE_MSG("Non zero cmap version (%d)", version);
|
||||
return Error("Non zero cmap version (%d)", version);
|
||||
}
|
||||
if (!num_tables) {
|
||||
return OTS_FAILURE_MSG("No subtables in cmap!");
|
||||
return Error("No subtables in cmap!");
|
||||
}
|
||||
|
||||
std::vector<CMAPSubtableHeader> subtable_headers;
|
||||
|
@ -591,7 +589,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
if (!table.ReadU16(&subt.platform) ||
|
||||
!table.ReadU16(&subt.encoding) ||
|
||||
!table.ReadU32(&subt.offset)) {
|
||||
return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i);
|
||||
return Error("Can't read subtable information cmap subtable %d", i);
|
||||
}
|
||||
|
||||
subtable_headers.push_back(subt);
|
||||
|
@ -602,11 +600,11 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
// make sure that all the offsets are valid.
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
|
||||
return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i);
|
||||
return Error("Bad subtable offset in cmap subtable %d", i);
|
||||
}
|
||||
if (subtable_headers[i].offset < data_offset ||
|
||||
subtable_headers[i].offset >= length) {
|
||||
return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
|
||||
return Error("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,7 +613,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
table.set_offset(subtable_headers[i].offset);
|
||||
if (!table.ReadU16(&subtable_headers[i].format)) {
|
||||
return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i);
|
||||
return Error("Can't read cmap subtable header format %d", i);
|
||||
}
|
||||
|
||||
uint16_t len = 0;
|
||||
|
@ -624,10 +622,10 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
case 0:
|
||||
case 4:
|
||||
if (!table.ReadU16(&len)) {
|
||||
return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
|
||||
return Error("Can't read cmap subtable %d length", i);
|
||||
}
|
||||
if (!table.ReadU16(&lang)) {
|
||||
return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
|
||||
return Error("Can't read cmap subtable %d language", i);
|
||||
}
|
||||
subtable_headers[i].length = len;
|
||||
subtable_headers[i].language = lang;
|
||||
|
@ -635,18 +633,18 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
case 12:
|
||||
case 13:
|
||||
if (!table.Skip(2)) {
|
||||
return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i);
|
||||
return Error("Bad cmap subtable %d structure", i);
|
||||
}
|
||||
if (!table.ReadU32(&subtable_headers[i].length)) {
|
||||
return OTS_FAILURE_MSG("Can read cmap subtable %d length", i);
|
||||
return Error("Can read cmap subtable %d length", i);
|
||||
}
|
||||
if (!table.ReadU32(&subtable_headers[i].language)) {
|
||||
return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
|
||||
return Error("Can't read cmap subtable %d language", i);
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
if (!table.ReadU32(&subtable_headers[i].length)) {
|
||||
return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
|
||||
return Error("Can't read cmap subtable %d length", i);
|
||||
}
|
||||
subtable_headers[i].language = 0;
|
||||
break;
|
||||
|
@ -664,7 +662,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
(subtable_headers[i - 1].encoding > subtable_headers[i].encoding ||
|
||||
(subtable_headers[i - 1].encoding == subtable_headers[i].encoding &&
|
||||
subtable_headers[i - 1].language > subtable_headers[i].language))))
|
||||
OTS_WARNING("subtable %d with platform ID %d, encoding ID %d, language ID %d "
|
||||
Warning("subtable %d with platform ID %d, encoding ID %d, language ID %d "
|
||||
"following subtable with platform ID %d, encoding ID %d, language ID %d",
|
||||
i,
|
||||
subtable_headers[i].platform,
|
||||
|
@ -679,14 +677,14 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
if (!subtable_headers[i].length) continue;
|
||||
if (subtable_headers[i].length > 1024 * 1024 * 1024) {
|
||||
return OTS_FAILURE_MSG("Bad cmap subtable %d length", i);
|
||||
return Error("Bad cmap subtable %d length", i);
|
||||
}
|
||||
// We know that both the offset and length are < 1GB, so the following
|
||||
// addition doesn't overflow
|
||||
const uint32_t end_byte
|
||||
= subtable_headers[i].offset + subtable_headers[i].length;
|
||||
if (end_byte > length) {
|
||||
return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
|
||||
return Error("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -714,16 +712,18 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
for (unsigned i = 0; i < overlap_checker.size(); ++i) {
|
||||
overlap_count += (overlap_checker[i].second ? 1 : -1);
|
||||
if (overlap_count > 1) {
|
||||
return OTS_FAILURE_MSG("Excessive overlap count %d", overlap_count);
|
||||
return Error("Excessive overlap count %d", overlap_count);
|
||||
}
|
||||
}
|
||||
|
||||
// we grab the number of glyphs in the file from the maxp table to make sure
|
||||
// that the character map isn't referencing anything beyound this range.
|
||||
if (!font->maxp) {
|
||||
return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap.");
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return Error("No maxp table in font! Needed by cmap.");
|
||||
}
|
||||
const uint16_t num_glyphs = font->maxp->num_glyphs;
|
||||
const uint16_t num_glyphs = maxp->num_glyphs;
|
||||
|
||||
// We only support a subset of the possible character map tables. Microsoft
|
||||
// 'strongly recommends' that everyone supports the Unicode BMP table with
|
||||
|
@ -760,29 +760,30 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
// table actually points to MS symbol data and thus should be parsed as
|
||||
// 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
|
||||
// recovered in ots_cmap_serialise().
|
||||
if (!ParseFormat4(font, 3, 1, data + subtable_headers[i].offset,
|
||||
if (!ParseFormat4(3, 1, data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
|
||||
return Error("Failed to parse format 4 cmap subtable %d", i);
|
||||
}
|
||||
} else if ((subtable_headers[i].encoding == 3) &&
|
||||
(subtable_headers[i].format == 4)) {
|
||||
// parse and output the 0-3-4 table as 0-3-4 table.
|
||||
if (!ParseFormat4(font, 0, 3, data + subtable_headers[i].offset,
|
||||
if (!ParseFormat4(0, 3, data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
|
||||
return Error("Failed to parse format 4 cmap subtable %d", i);
|
||||
}
|
||||
} else if ((subtable_headers[i].encoding == 3) &&
|
||||
} else if ((subtable_headers[i].encoding == 3 ||
|
||||
subtable_headers[i].encoding == 4) &&
|
||||
(subtable_headers[i].format == 12)) {
|
||||
// parse and output the 0-3-12 table as 3-10-12 table.
|
||||
if (!Parse31012(font, data + subtable_headers[i].offset,
|
||||
// parse and output the 0-3-12 or 0-4-12 tables as 3-10-12 table.
|
||||
if (!Parse31012(data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i);
|
||||
return Error("Failed to parse format 12 cmap subtable %d", i);
|
||||
}
|
||||
} else if ((subtable_headers[i].encoding == 5) &&
|
||||
(subtable_headers[i].format == 14)) {
|
||||
if (!Parse0514(font, data + subtable_headers[i].offset,
|
||||
if (!Parse0514(data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i);
|
||||
return Error("Failed to parse format 14 cmap subtable %d", i);
|
||||
}
|
||||
}
|
||||
} else if (subtable_headers[i].platform == 1) {
|
||||
|
@ -791,7 +792,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
if ((subtable_headers[i].encoding == 0) &&
|
||||
(subtable_headers[i].format == 0)) {
|
||||
// parse and output the 1-0-0 table.
|
||||
if (!Parse100(font, data + subtable_headers[i].offset,
|
||||
if (!Parse100(data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
@ -804,7 +805,7 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
case 1:
|
||||
if (subtable_headers[i].format == 4) {
|
||||
// parse 3-0-4 or 3-1-4 table.
|
||||
if (!ParseFormat4(font, subtable_headers[i].platform,
|
||||
if (!ParseFormat4(subtable_headers[i].platform,
|
||||
subtable_headers[i].encoding,
|
||||
data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
|
@ -814,14 +815,14 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
break;
|
||||
case 10:
|
||||
if (subtable_headers[i].format == 12) {
|
||||
font->cmap->subtable_3_10_12.clear();
|
||||
if (!Parse31012(font, data + subtable_headers[i].offset,
|
||||
this->subtable_3_10_12.clear();
|
||||
if (!Parse31012(data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else if (subtable_headers[i].format == 13) {
|
||||
font->cmap->subtable_3_10_13.clear();
|
||||
if (!Parse31013(font, data + subtable_headers[i].offset,
|
||||
this->subtable_3_10_13.clear();
|
||||
if (!Parse31013(data + subtable_headers[i].offset,
|
||||
subtable_headers[i].length, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
@ -834,20 +835,16 @@ bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ots_cmap_should_serialise(Font *font) {
|
||||
return font->cmap != NULL;
|
||||
}
|
||||
|
||||
bool ots_cmap_serialise(OTSStream *out, Font *font) {
|
||||
const bool have_034 = font->cmap->subtable_0_3_4_data != NULL;
|
||||
const bool have_0514 = font->cmap->subtable_0_5_14.size() != 0;
|
||||
const bool have_100 = font->cmap->subtable_1_0_0.size() != 0;
|
||||
const bool have_304 = font->cmap->subtable_3_0_4_data != NULL;
|
||||
bool OpenTypeCMAP::Serialize(OTSStream *out) {
|
||||
const bool have_034 = this->subtable_0_3_4_data != NULL;
|
||||
const bool have_0514 = this->subtable_0_5_14.size() != 0;
|
||||
const bool have_100 = this->subtable_1_0_0.size() != 0;
|
||||
const bool have_304 = this->subtable_3_0_4_data != NULL;
|
||||
// MS Symbol and MS Unicode tables should not co-exist.
|
||||
// See the comment above in 0-0-4 parser.
|
||||
const bool have_314 = (!have_304) && font->cmap->subtable_3_1_4_data;
|
||||
const bool have_31012 = font->cmap->subtable_3_10_12.size() != 0;
|
||||
const bool have_31013 = font->cmap->subtable_3_10_13.size() != 0;
|
||||
const bool have_314 = (!have_304) && this->subtable_3_1_4_data;
|
||||
const bool have_31012 = this->subtable_3_10_12.size() != 0;
|
||||
const bool have_31013 = this->subtable_3_10_13.size() != 0;
|
||||
const uint16_t num_subtables = static_cast<uint16_t>(have_034) +
|
||||
static_cast<uint16_t>(have_0514) +
|
||||
static_cast<uint16_t>(have_100) +
|
||||
|
@ -860,7 +857,7 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
|
|||
// Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
|
||||
// (e.g., old fonts for Mac). We don't support them.
|
||||
if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
|
||||
return OTS_FAILURE_MSG("no supported subtables were found");
|
||||
return Error("no supported subtables were found");
|
||||
}
|
||||
|
||||
if (!out->WriteU16(0) ||
|
||||
|
@ -875,8 +872,8 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
|
|||
|
||||
const off_t offset_034 = out->Tell();
|
||||
if (have_034) {
|
||||
if (!out->Write(font->cmap->subtable_0_3_4_data,
|
||||
font->cmap->subtable_0_3_4_length)) {
|
||||
if (!out->Write(this->subtable_0_3_4_data,
|
||||
this->subtable_0_3_4_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
@ -884,10 +881,10 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
|
|||
const off_t offset_0514 = out->Tell();
|
||||
if (have_0514) {
|
||||
const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
|
||||
= font->cmap->subtable_0_5_14;
|
||||
= this->subtable_0_5_14;
|
||||
const unsigned num_records = records.size();
|
||||
if (!out->WriteU16(14) ||
|
||||
!out->WriteU32(font->cmap->subtable_0_5_14_length) ||
|
||||
!out->WriteU32(this->subtable_0_5_14_length) ||
|
||||
!out->WriteU32(num_records)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
@ -939,23 +936,23 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
|
|||
!out->WriteU16(0)) { // language
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!out->Write(&(font->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
|
||||
if (!out->Write(&(this->subtable_1_0_0[0]), kFormat0ArraySize)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
const off_t offset_304 = out->Tell();
|
||||
if (have_304) {
|
||||
if (!out->Write(font->cmap->subtable_3_0_4_data,
|
||||
font->cmap->subtable_3_0_4_length)) {
|
||||
if (!out->Write(this->subtable_3_0_4_data,
|
||||
this->subtable_3_0_4_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
const off_t offset_314 = out->Tell();
|
||||
if (have_314) {
|
||||
if (!out->Write(font->cmap->subtable_3_1_4_data,
|
||||
font->cmap->subtable_3_1_4_length)) {
|
||||
if (!out->Write(this->subtable_3_1_4_data,
|
||||
this->subtable_3_1_4_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
@ -963,7 +960,7 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
|
|||
const off_t offset_31012 = out->Tell();
|
||||
if (have_31012) {
|
||||
std::vector<OpenTypeCMAPSubtableRange> &groups
|
||||
= font->cmap->subtable_3_10_12;
|
||||
= this->subtable_3_10_12;
|
||||
const unsigned num_groups = groups.size();
|
||||
if (!out->WriteU16(12) ||
|
||||
!out->WriteU16(0) ||
|
||||
|
@ -985,7 +982,7 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
|
|||
const off_t offset_31013 = out->Tell();
|
||||
if (have_31013) {
|
||||
std::vector<OpenTypeCMAPSubtableRange> &groups
|
||||
= font->cmap->subtable_3_10_13;
|
||||
= this->subtable_3_10_13;
|
||||
const unsigned num_groups = groups.size();
|
||||
if (!out->WriteU16(13) ||
|
||||
!out->WriteU16(0) ||
|
||||
|
@ -1074,15 +1071,4 @@ bool ots_cmap_serialise(OTSStream *out, Font *font) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ots_cmap_reuse(Font *font, Font *other) {
|
||||
font->cmap = other->cmap;
|
||||
font->cmap_reused = true;
|
||||
}
|
||||
|
||||
void ots_cmap_free(Font *font) {
|
||||
delete font->cmap;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -35,9 +35,11 @@ struct OpenTypeCMAPSubtableVSRecord {
|
|||
std::vector<OpenTypeCMAPSubtableVSMapping> mappings;
|
||||
};
|
||||
|
||||
struct OpenTypeCMAP {
|
||||
OpenTypeCMAP()
|
||||
: subtable_0_3_4_data(NULL),
|
||||
class OpenTypeCMAP : public Table {
|
||||
public:
|
||||
explicit OpenTypeCMAP(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag),
|
||||
subtable_0_3_4_data(NULL),
|
||||
subtable_0_3_4_length(0),
|
||||
subtable_0_5_14_length(0),
|
||||
subtable_3_0_4_data(NULL),
|
||||
|
@ -46,6 +48,10 @@ struct OpenTypeCMAP {
|
|||
subtable_3_1_4_length(0) {
|
||||
}
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
// Platform 0, Encoding 3, Format 4, Unicode BMP table.
|
||||
const uint8_t *subtable_0_3_4_data;
|
||||
size_t subtable_0_3_4_length;
|
||||
|
@ -67,6 +73,13 @@ struct OpenTypeCMAP {
|
|||
std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
|
||||
// Platform 1, Encoding 0, Format 0, Mac Roman table.
|
||||
std::vector<uint8_t> subtable_1_0_0;
|
||||
|
||||
bool ParseFormat4(int platform, int encoding, const uint8_t *data,
|
||||
size_t length, uint16_t num_glyphs);
|
||||
bool Parse31012(const uint8_t *data, size_t length, uint16_t num_glyphs);
|
||||
bool Parse31013(const uint8_t *data, size_t length, uint16_t num_glyphs);
|
||||
bool Parse0514(const uint8_t *data, size_t length, uint16_t num_glyphs);
|
||||
bool Parse100(const uint8_t *data, size_t length);
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "cvar.h"
|
||||
|
||||
#include "fvar.h"
|
||||
#include "variations.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeCVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeCVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
|
||||
if (!table.ReadU16(&majorVersion) ||
|
||||
!table.ReadU16(&minorVersion)) {
|
||||
return Drop("Failed to read table header");
|
||||
}
|
||||
|
||||
if (majorVersion != 1) {
|
||||
return Drop("Unknown table version");
|
||||
}
|
||||
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return DropVariations("Required fvar table is missing");
|
||||
}
|
||||
|
||||
if (!ParseVariationData(GetFont(), data + table.offset(), length - table.offset(),
|
||||
fvar->AxisCount(), 0)) {
|
||||
return Drop("Failed to parse variation data");
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeCVAR::Serialize(OTSStream* out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write cvar table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -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_
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "feat.h"
|
||||
|
||||
#include "name.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeFEAT::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!table.ReadU32(&this->version)) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
if (this->version >> 16 != 1 && this->version >> 16 != 2) {
|
||||
return DropGraphite("Unsupported table version: %u", this->version >> 16);
|
||||
}
|
||||
if (!table.ReadU16(&this->numFeat)) {
|
||||
return DropGraphite("Failed to read numFeat");
|
||||
}
|
||||
if (!table.ReadU16(&this->reserved)) {
|
||||
return DropGraphite("Failed to read reserved");
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
Warning("Nonzero reserved");
|
||||
}
|
||||
if (!table.ReadU32(&this->reserved2)) {
|
||||
return DropGraphite("Failed to read valid reserved2");
|
||||
}
|
||||
if (this->reserved2 != 0) {
|
||||
Warning("Nonzero reserved2");
|
||||
}
|
||||
|
||||
std::unordered_set<size_t> unverified;
|
||||
//this->features.resize(this->numFeat, this);
|
||||
for (unsigned i = 0; i < this->numFeat; ++i) {
|
||||
this->features.emplace_back(this);
|
||||
FeatureDefn& feature = this->features[i];
|
||||
if (!feature.ParsePart(table)) {
|
||||
return DropGraphite("Failed to read features[%u]", i);
|
||||
}
|
||||
this->feature_ids.insert(feature.id);
|
||||
for (unsigned j = 0; j < feature.numSettings; ++j) {
|
||||
size_t offset = feature.offset + j * 4;
|
||||
if (offset < feature.offset || offset > length) {
|
||||
return DropGraphite("Invalid FeatSettingDefn offset %zu/%zu",
|
||||
offset, length);
|
||||
}
|
||||
unverified.insert(offset);
|
||||
// need to verify that this FeatureDefn points to valid
|
||||
// FeatureSettingDefn
|
||||
}
|
||||
}
|
||||
|
||||
while (table.remaining()) {
|
||||
bool used = unverified.erase(table.offset());
|
||||
FeatureSettingDefn featSetting(this);
|
||||
if (!featSetting.ParsePart(table, used)) {
|
||||
return DropGraphite("Failed to read a FeatureSettingDefn");
|
||||
}
|
||||
featSettings.push_back(featSetting);
|
||||
}
|
||||
|
||||
if (!unverified.empty()) {
|
||||
return DropGraphite("%zu incorrect offsets into featSettings",
|
||||
unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU16(this->numFeat) ||
|
||||
!out->WriteU16(this->reserved) ||
|
||||
!out->WriteU32(this->reserved2) ||
|
||||
!SerializeParts(this->features, out) ||
|
||||
!SerializeParts(this->featSettings, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::IsValidFeatureId(uint32_t id) const {
|
||||
return feature_ids.count(id);
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::FeatureDefn::ParsePart(Buffer& table) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
parent->GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
if (!name) {
|
||||
return parent->Error("FeatureDefn: Required name table is missing");
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 2 && !table.ReadU32(&this->id)) {
|
||||
return parent->Error("FeatureDefn: Failed to read id");
|
||||
}
|
||||
if (parent->version >> 16 == 1) {
|
||||
uint16_t id;
|
||||
if (!table.ReadU16(&id)) {
|
||||
return parent->Error("FeatureDefn: Failed to read id");
|
||||
}
|
||||
this->id = id;
|
||||
}
|
||||
if (!table.ReadU16(&this->numSettings)) {
|
||||
return parent->Error("FeatureDefn: Failed to read numSettings");
|
||||
}
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU16(&this->reserved)) {
|
||||
return parent->Error("FeatureDefn: Failed to read reserved");
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
parent->Warning("FeatureDefn: Nonzero reserved");
|
||||
}
|
||||
}
|
||||
if (!table.ReadU32(&this->offset)) {
|
||||
return parent->Error("FeatureDefn: Failed to read offset");
|
||||
} // validity of offset verified in OpenTypeFEAT::Parse
|
||||
if (!table.ReadU16(&this->flags)) {
|
||||
return parent->Error("FeatureDefn: Failed to read flags");
|
||||
}
|
||||
if ((this->flags & RESERVED) != 0) {
|
||||
this->flags &= ~RESERVED;
|
||||
parent->Warning("FeatureDefn: Nonzero (flags & 0x%x) repaired", RESERVED);
|
||||
}
|
||||
if (this->flags & HAS_DEFAULT_SETTING &&
|
||||
(this->flags & DEFAULT_SETTING) >= this->numSettings) {
|
||||
return parent->Error("FeatureDefn: (flags & 0x%x) is set but (flags & 0x%x "
|
||||
"is not a valid setting index", HAS_DEFAULT_SETTING,
|
||||
DEFAULT_SETTING);
|
||||
}
|
||||
if (!table.ReadU16(&this->label)) {
|
||||
return parent->Error("FeatureDefn: Failed to read label");
|
||||
}
|
||||
if (!name->IsValidNameId(this->label)) {
|
||||
if (this->id == 1 && name->IsValidNameId(this->label, true)) {
|
||||
parent->Warning("FeatureDefn: Missing NameRecord repaired for feature"
|
||||
" with id=%u, label=%u", this->id, this->label);
|
||||
}
|
||||
else {
|
||||
return parent->Error("FeatureDefn: Invalid label");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::FeatureDefn::SerializePart(OTSStream* out) const {
|
||||
if ((parent->version >> 16 >= 2 && !out->WriteU32(this->id)) ||
|
||||
(parent->version >> 16 == 1 &&
|
||||
!out->WriteU16(static_cast<uint16_t>(this->id))) ||
|
||||
!out->WriteU16(this->numSettings) ||
|
||||
(parent->version >> 16 >= 2 && !out->WriteU16(this->reserved)) ||
|
||||
!out->WriteU32(this->offset) ||
|
||||
!out->WriteU16(this->flags) ||
|
||||
!out->WriteU16(this->label)) {
|
||||
return parent->Error("FeatureDefn: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::FeatureSettingDefn::ParsePart(Buffer& table, bool used) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
parent->GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
if (!name) {
|
||||
return parent->Error("FeatureSettingDefn: Required name table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadS16(&this->value)) {
|
||||
return parent->Error("FeatureSettingDefn: Failed to read value");
|
||||
}
|
||||
if (!table.ReadU16(&this->label) ||
|
||||
(used && !name->IsValidNameId(this->label))) {
|
||||
return parent->Error("FeatureSettingDefn: Failed to read valid label");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeFEAT::FeatureSettingDefn::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteS16(this->value) ||
|
||||
!out->WriteU16(this->label)) {
|
||||
return parent->Error("FeatureSettingDefn: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_FEAT_H_
|
||||
#define OTS_FEAT_H_
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeFEAT : public Table {
|
||||
public:
|
||||
explicit OpenTypeFEAT(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
bool IsValidFeatureId(uint32_t id) const;
|
||||
|
||||
private:
|
||||
struct FeatureDefn : public TablePart<OpenTypeFEAT> {
|
||||
explicit FeatureDefn(OpenTypeFEAT* parent)
|
||||
: TablePart<OpenTypeFEAT>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint32_t id;
|
||||
uint16_t numSettings;
|
||||
uint16_t reserved;
|
||||
uint32_t offset;
|
||||
uint16_t flags;
|
||||
static const uint16_t HAS_DEFAULT_SETTING = 0x4000;
|
||||
static const uint16_t RESERVED = 0x3F00;
|
||||
static const uint16_t DEFAULT_SETTING = 0x00FF;
|
||||
uint16_t label;
|
||||
};
|
||||
struct FeatureSettingDefn : public TablePart<OpenTypeFEAT> {
|
||||
explicit FeatureSettingDefn(OpenTypeFEAT* parent)
|
||||
: TablePart<OpenTypeFEAT>(parent) { }
|
||||
bool ParsePart(Buffer& table) { return ParsePart(table, true); }
|
||||
bool ParsePart(Buffer& table, bool used);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
int16_t value;
|
||||
uint16_t label;
|
||||
};
|
||||
uint32_t version;
|
||||
uint16_t numFeat;
|
||||
uint16_t reserved;
|
||||
uint32_t reserved2;
|
||||
std::vector<FeatureDefn> features;
|
||||
std::vector<FeatureSettingDefn> featSettings;
|
||||
std::unordered_set<uint32_t> feature_ids;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_FEAT_H_
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_FVAR_H_
|
||||
#define OTS_FVAR_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeFVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeFVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeFVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
uint16_t AxisCount() const { return axisCount; }
|
||||
|
||||
private:
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t axesArrayOffset;
|
||||
uint16_t reserved;
|
||||
uint16_t axisCount;
|
||||
uint16_t axisSize;
|
||||
uint16_t instanceCount;
|
||||
uint16_t instanceSize;
|
||||
|
||||
typedef int32_t Fixed; /* 16.16 fixed-point value */
|
||||
|
||||
struct VariationAxisRecord {
|
||||
uint32_t axisTag;
|
||||
Fixed minValue;
|
||||
Fixed defaultValue;
|
||||
Fixed maxValue;
|
||||
uint16_t flags;
|
||||
uint16_t axisNameID;
|
||||
};
|
||||
std::vector<VariationAxisRecord> axes;
|
||||
|
||||
struct InstanceRecord {
|
||||
uint16_t subfamilyNameID;
|
||||
uint16_t flags;
|
||||
std::vector<Fixed> coordinates;
|
||||
uint16_t postScriptNameID; // optional
|
||||
};
|
||||
std::vector<InstanceRecord> instances;
|
||||
|
||||
bool instancesHavePostScriptNameID;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_FVAR_H_
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -7,113 +7,78 @@
|
|||
// gasp - Grid-fitting And Scan-conversion Procedure
|
||||
// http://www.microsoft.com/typography/otspec/gasp.htm
|
||||
|
||||
#define TABLE_NAME "gasp"
|
||||
|
||||
#define DROP_THIS_TABLE(...) \
|
||||
do { \
|
||||
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
|
||||
OTS_FAILURE_MSG("Table discarded"); \
|
||||
delete font->gasp; \
|
||||
font->gasp = 0; \
|
||||
} while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_gasp_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeGASP::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeGASP *gasp = new OpenTypeGASP;
|
||||
font->gasp = gasp;
|
||||
|
||||
uint16_t num_ranges = 0;
|
||||
if (!table.ReadU16(&gasp->version) ||
|
||||
if (!table.ReadU16(&this->version) ||
|
||||
!table.ReadU16(&num_ranges)) {
|
||||
return OTS_FAILURE_MSG("Failed to read table header");
|
||||
return Error("Failed to read table header");
|
||||
}
|
||||
|
||||
if (gasp->version > 1) {
|
||||
if (this->version > 1) {
|
||||
// Lots of Linux fonts have bad version numbers...
|
||||
DROP_THIS_TABLE("bad version: %u", gasp->version);
|
||||
return true;
|
||||
return Drop("Unsupported version: %u", this->version);
|
||||
}
|
||||
|
||||
if (num_ranges == 0) {
|
||||
DROP_THIS_TABLE("num_ranges is zero");
|
||||
return true;
|
||||
return Drop("numRanges is zero");
|
||||
}
|
||||
|
||||
gasp->gasp_ranges.reserve(num_ranges);
|
||||
this->gasp_ranges.reserve(num_ranges);
|
||||
for (unsigned i = 0; i < num_ranges; ++i) {
|
||||
uint16_t max_ppem = 0;
|
||||
uint16_t behavior = 0;
|
||||
if (!table.ReadU16(&max_ppem) ||
|
||||
!table.ReadU16(&behavior)) {
|
||||
return OTS_FAILURE_MSG("Failed to read subrange %d", i);
|
||||
return Error("Failed to read GASPRANGE %d", i);
|
||||
}
|
||||
if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
|
||||
if ((i > 0) && (this->gasp_ranges[i - 1].first >= max_ppem)) {
|
||||
// The records in the gaspRange[] array must be sorted in order of
|
||||
// increasing rangeMaxPPEM value.
|
||||
DROP_THIS_TABLE("ranges are not sorted");
|
||||
return true;
|
||||
return Drop("Ranges are not sorted");
|
||||
}
|
||||
if ((i == num_ranges - 1u) && // never underflow.
|
||||
(max_ppem != 0xffffu)) {
|
||||
DROP_THIS_TABLE("The last record should be 0xFFFF as a sentinel value "
|
||||
return Drop("The last record should be 0xFFFF as a sentinel value "
|
||||
"for rangeMaxPPEM");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (behavior >> 8) {
|
||||
OTS_WARNING("undefined bits are used: %x", behavior);
|
||||
Warning("Undefined bits are used: %x", behavior);
|
||||
// mask undefined bits.
|
||||
behavior &= 0x000fu;
|
||||
}
|
||||
|
||||
if (gasp->version == 0 && (behavior >> 2) != 0) {
|
||||
OTS_WARNING("changed the version number to 1");
|
||||
gasp->version = 1;
|
||||
if (this->version == 0 && (behavior >> 2) != 0) {
|
||||
Warning("Changed the version number to 1");
|
||||
this->version = 1;
|
||||
}
|
||||
|
||||
gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
|
||||
this->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_gasp_should_serialise(Font *font) {
|
||||
return font->gasp != NULL;
|
||||
}
|
||||
|
||||
bool ots_gasp_serialise(OTSStream *out, Font *font) {
|
||||
const OpenTypeGASP *gasp = font->gasp;
|
||||
|
||||
const uint16_t num_ranges = static_cast<uint16_t>(gasp->gasp_ranges.size());
|
||||
if (num_ranges != gasp->gasp_ranges.size() ||
|
||||
!out->WriteU16(gasp->version) ||
|
||||
bool OpenTypeGASP::Serialize(OTSStream *out) {
|
||||
const uint16_t num_ranges = static_cast<uint16_t>(this->gasp_ranges.size());
|
||||
if (num_ranges != this->gasp_ranges.size() ||
|
||||
!out->WriteU16(this->version) ||
|
||||
!out->WriteU16(num_ranges)) {
|
||||
return OTS_FAILURE_MSG("failed to write gasp header");
|
||||
return Error("Failed to write table header");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < num_ranges; ++i) {
|
||||
if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
|
||||
!out->WriteU16(gasp->gasp_ranges[i].second)) {
|
||||
return OTS_FAILURE_MSG("Failed to write gasp subtable %d", i);
|
||||
if (!out->WriteU16(this->gasp_ranges[i].first) ||
|
||||
!out->WriteU16(this->gasp_ranges[i].second)) {
|
||||
return Error("Failed to write GASPRANGE %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_gasp_reuse(Font *font, Font *other) {
|
||||
font->gasp = other->gasp;
|
||||
font->gasp_reused = true;
|
||||
}
|
||||
|
||||
void ots_gasp_free(Font *font) {
|
||||
delete font->gasp;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
#undef DROP_THIS_TABLE
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -13,7 +13,15 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeGASP {
|
||||
class OpenTypeGASP : public Table {
|
||||
public:
|
||||
explicit OpenTypeGASP(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
uint16_t version;
|
||||
// A array of (max PPEM, GASP behavior) pairs.
|
||||
std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -11,68 +11,60 @@
|
|||
#include "gsub.h"
|
||||
#include "layout.h"
|
||||
#include "maxp.h"
|
||||
#include "variations.h"
|
||||
|
||||
// GDEF - The Glyph Definition Table
|
||||
// http://www.microsoft.com/typography/otspec/gdef.htm
|
||||
|
||||
#define TABLE_NAME "GDEF"
|
||||
|
||||
namespace {
|
||||
|
||||
// The maximum class value in class definition tables.
|
||||
const uint16_t kMaxClassDefValue = 0xFFFF;
|
||||
// The maximum class value in the glyph class definision table.
|
||||
const uint16_t kMaxGlyphClassDefValue = 4;
|
||||
// The maximum format number of caret value tables.
|
||||
// We don't support format 3 for now. See the comment in
|
||||
// ParseLigCaretListTable() for the reason.
|
||||
const uint16_t kMaxCaretValueFormat = 2;
|
||||
const uint16_t kMaxCaretValueFormat = 3;
|
||||
|
||||
bool ParseGlyphClassDefTable(ots::Font *font, const uint8_t *data,
|
||||
size_t length, const uint16_t num_glyphs) {
|
||||
return ots::ParseClassDefTable(font, data, length, num_glyphs,
|
||||
kMaxGlyphClassDefValue);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool ParseAttachListTable(ots::Font *font, const uint8_t *data,
|
||||
size_t length, const uint16_t num_glyphs) {
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeGDEF::ParseAttachListTable(const uint8_t *data, size_t length) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
uint16_t offset_coverage = 0;
|
||||
uint16_t glyph_count = 0;
|
||||
if (!subtable.ReadU16(&offset_coverage) ||
|
||||
!subtable.ReadU16(&glyph_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read gdef header");
|
||||
return Error("Failed to read gdef header");
|
||||
}
|
||||
const unsigned attach_points_end =
|
||||
2 * static_cast<unsigned>(glyph_count) + 4;
|
||||
if (attach_points_end > std::numeric_limits<uint16_t>::max()) {
|
||||
return OTS_FAILURE_MSG("Bad glyph count in gdef");
|
||||
return Error("Bad glyph count in gdef");
|
||||
}
|
||||
if (offset_coverage == 0 || offset_coverage >= length ||
|
||||
offset_coverage < attach_points_end) {
|
||||
return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
|
||||
return Error("Bad coverage offset %d", offset_coverage);
|
||||
}
|
||||
if (glyph_count > num_glyphs) {
|
||||
return OTS_FAILURE_MSG("Bad glyph count %u", glyph_count);
|
||||
if (glyph_count > this->m_num_glyphs) {
|
||||
return Error("Bad glyph count %u", glyph_count);
|
||||
}
|
||||
|
||||
std::vector<uint16_t> attach_points;
|
||||
attach_points.resize(glyph_count);
|
||||
for (unsigned i = 0; i < glyph_count; ++i) {
|
||||
if (!subtable.ReadU16(&attach_points[i])) {
|
||||
return OTS_FAILURE_MSG("Can't read attachment point %d", i);
|
||||
return Error("Can't read attachment point %d", i);
|
||||
}
|
||||
if (attach_points[i] >= length ||
|
||||
attach_points[i] < attach_points_end) {
|
||||
return OTS_FAILURE_MSG("Bad attachment point %d of %d", i, attach_points[i]);
|
||||
return Error("Bad attachment point %d of %d", i, attach_points[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse coverage table
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
||||
length - offset_coverage, num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Bad coverage table");
|
||||
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
|
||||
length - offset_coverage, this->m_num_glyphs)) {
|
||||
return Error("Bad coverage table");
|
||||
}
|
||||
|
||||
// Parse attach point table
|
||||
|
@ -80,20 +72,20 @@ bool ParseAttachListTable(ots::Font *font, const uint8_t *data,
|
|||
subtable.set_offset(attach_points[i]);
|
||||
uint16_t point_count = 0;
|
||||
if (!subtable.ReadU16(&point_count)) {
|
||||
return OTS_FAILURE_MSG("Can't read point count %d", i);
|
||||
return Error("Can't read point count %d", i);
|
||||
}
|
||||
if (point_count == 0) {
|
||||
return OTS_FAILURE_MSG("zero point count %d", i);
|
||||
return Error("zero point count %d", i);
|
||||
}
|
||||
uint16_t last_point_index = 0;
|
||||
uint16_t point_index = 0;
|
||||
for (unsigned j = 0; j < point_count; ++j) {
|
||||
if (!subtable.ReadU16(&point_index)) {
|
||||
return OTS_FAILURE_MSG("Can't read point index %d in point %d", j, i);
|
||||
return Error("Can't read point index %d in point %d", j, i);
|
||||
}
|
||||
// Contour point indeces are in increasing numerical order
|
||||
if (last_point_index != 0 && last_point_index >= point_index) {
|
||||
return OTS_FAILURE_MSG("bad contour indeces: %u >= %u",
|
||||
return Error("bad contour indeces: %u >= %u",
|
||||
last_point_index, point_index);
|
||||
}
|
||||
last_point_index = point_index;
|
||||
|
@ -102,43 +94,42 @@ bool ParseAttachListTable(ots::Font *font, const uint8_t *data,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
|
||||
size_t length, const uint16_t num_glyphs) {
|
||||
bool OpenTypeGDEF::ParseLigCaretListTable(const uint8_t *data, size_t length) {
|
||||
ots::Buffer subtable(data, length);
|
||||
uint16_t offset_coverage = 0;
|
||||
uint16_t lig_glyph_count = 0;
|
||||
if (!subtable.ReadU16(&offset_coverage) ||
|
||||
!subtable.ReadU16(&lig_glyph_count)) {
|
||||
return OTS_FAILURE_MSG("Can't read caret structure");
|
||||
return Error("Can't read caret structure");
|
||||
}
|
||||
const unsigned lig_glyphs_end =
|
||||
2 * static_cast<unsigned>(lig_glyph_count) + 4;
|
||||
if (lig_glyphs_end > std::numeric_limits<uint16_t>::max()) {
|
||||
return OTS_FAILURE_MSG("Bad caret structure");
|
||||
return Error("Bad caret structure");
|
||||
}
|
||||
if (offset_coverage == 0 || offset_coverage >= length ||
|
||||
offset_coverage < lig_glyphs_end) {
|
||||
return OTS_FAILURE_MSG("Bad caret coverate offset %d", offset_coverage);
|
||||
return Error("Bad caret coverate offset %d", offset_coverage);
|
||||
}
|
||||
if (lig_glyph_count > num_glyphs) {
|
||||
return OTS_FAILURE_MSG("bad ligature glyph count: %u", lig_glyph_count);
|
||||
if (lig_glyph_count > this->m_num_glyphs) {
|
||||
return Error("bad ligature glyph count: %u", lig_glyph_count);
|
||||
}
|
||||
|
||||
std::vector<uint16_t> lig_glyphs;
|
||||
lig_glyphs.resize(lig_glyph_count);
|
||||
for (unsigned i = 0; i < lig_glyph_count; ++i) {
|
||||
if (!subtable.ReadU16(&lig_glyphs[i])) {
|
||||
return OTS_FAILURE_MSG("Can't read ligature glyph location %d", i);
|
||||
return Error("Can't read ligature glyph location %d", i);
|
||||
}
|
||||
if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
|
||||
return OTS_FAILURE_MSG("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
|
||||
return Error("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse coverage table
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
||||
length - offset_coverage, num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Can't parse caret coverage table");
|
||||
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
|
||||
length - offset_coverage, this->m_num_glyphs)) {
|
||||
return Error("Can't parse caret coverage table");
|
||||
}
|
||||
|
||||
// Parse ligature glyph table
|
||||
|
@ -146,10 +137,10 @@ bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
|
|||
subtable.set_offset(lig_glyphs[i]);
|
||||
uint16_t caret_count = 0;
|
||||
if (!subtable.ReadU16(&caret_count)) {
|
||||
return OTS_FAILURE_MSG("Can't read caret count for glyph %d", i);
|
||||
return Error("Can't read caret count for glyph %d", i);
|
||||
}
|
||||
if (caret_count == 0) {
|
||||
return OTS_FAILURE_MSG("bad caret value count: %u", caret_count);
|
||||
return Error("bad caret value count: %u", caret_count);
|
||||
}
|
||||
|
||||
std::vector<uint16_t> caret_value_offsets;
|
||||
|
@ -157,10 +148,10 @@ bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
|
|||
unsigned caret_value_offsets_end = 2 * static_cast<unsigned>(caret_count) + 2;
|
||||
for (unsigned j = 0; j < caret_count; ++j) {
|
||||
if (!subtable.ReadU16(&caret_value_offsets[j])) {
|
||||
return OTS_FAILURE_MSG("Can't read caret offset %d for glyph %d", j, i);
|
||||
return Error("Can't read caret offset %d for glyph %d", j, i);
|
||||
}
|
||||
if (caret_value_offsets[j] >= length || caret_value_offsets[j] < caret_value_offsets_end) {
|
||||
return OTS_FAILURE_MSG("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
|
||||
return Error("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,91 +160,93 @@ bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
|
|||
subtable.set_offset(lig_glyphs[i] + caret_value_offsets[j]);
|
||||
uint16_t caret_format = 0;
|
||||
if (!subtable.ReadU16(&caret_format)) {
|
||||
return OTS_FAILURE_MSG("Can't read caret values table %d in glyph %d", j, i);
|
||||
return Error("Can't read caret values table %d in glyph %d", j, i);
|
||||
}
|
||||
// TODO(bashi): We only support caret value format 1 and 2 for now
|
||||
// because there are no fonts which contain caret value format 3
|
||||
// as far as we investigated.
|
||||
if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
|
||||
return OTS_FAILURE_MSG("bad caret value format: %u", caret_format);
|
||||
return Error("bad caret value format: %u", caret_format);
|
||||
}
|
||||
// CaretValueFormats contain a 2-byte field which could be
|
||||
// arbitrary value.
|
||||
if (!subtable.Skip(2)) {
|
||||
return OTS_FAILURE_MSG("Bad caret value table structure %d in glyph %d", j, i);
|
||||
return Error("Bad caret value table structure %d in glyph %d", j, i);
|
||||
}
|
||||
if (caret_format == 3) {
|
||||
uint16_t offset_device = 0;
|
||||
if (!subtable.ReadU16(&offset_device)) {
|
||||
return Error("Can't read device offset for caret value %d "
|
||||
"in glyph %d", j, i);
|
||||
}
|
||||
uint16_t absolute_offset = lig_glyphs[i] + caret_value_offsets[j]
|
||||
+ offset_device;
|
||||
if (offset_device == 0 || absolute_offset >= length) {
|
||||
return Error("Bad device offset for caret value %d in glyph %d: %d",
|
||||
j, i, offset_device);
|
||||
}
|
||||
if (!ots::ParseDeviceTable(GetFont(), data + absolute_offset,
|
||||
length - absolute_offset)) {
|
||||
return Error("Bad device table for caret value %d in glyph %d",
|
||||
j, i, offset_device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseMarkAttachClassDefTable(ots::Font *font, const uint8_t *data,
|
||||
size_t length, const uint16_t num_glyphs) {
|
||||
return ots::ParseClassDefTable(font, data, length, num_glyphs, kMaxClassDefValue);
|
||||
}
|
||||
|
||||
bool ParseMarkGlyphSetsDefTable(ots::Font *font, const uint8_t *data,
|
||||
size_t length, const uint16_t num_glyphs) {
|
||||
bool OpenTypeGDEF::ParseMarkGlyphSetsDefTable(const uint8_t *data, size_t length) {
|
||||
ots::Buffer subtable(data, length);
|
||||
uint16_t format = 0;
|
||||
uint16_t mark_set_count = 0;
|
||||
if (!subtable.ReadU16(&format) ||
|
||||
!subtable.ReadU16(&mark_set_count)) {
|
||||
return OTS_FAILURE_MSG("Can' read mark glyph table structure");
|
||||
return Error("Can' read mark glyph table structure");
|
||||
}
|
||||
if (format != 1) {
|
||||
return OTS_FAILURE_MSG("bad mark glyph set table format: %u", format);
|
||||
return Error("bad mark glyph set table format: %u", format);
|
||||
}
|
||||
|
||||
const unsigned mark_sets_end = 2 * static_cast<unsigned>(mark_set_count) + 4;
|
||||
if (mark_sets_end > std::numeric_limits<uint16_t>::max()) {
|
||||
return OTS_FAILURE_MSG("Bad mark_set %d", mark_sets_end);
|
||||
return Error("Bad mark_set %d", mark_sets_end);
|
||||
}
|
||||
for (unsigned i = 0; i < mark_set_count; ++i) {
|
||||
uint32_t offset_coverage = 0;
|
||||
if (!subtable.ReadU32(&offset_coverage)) {
|
||||
return OTS_FAILURE_MSG("Can't read covrage location for mark set %d", i);
|
||||
return Error("Can't read covrage location for mark set %d", i);
|
||||
}
|
||||
if (offset_coverage >= length ||
|
||||
offset_coverage < mark_sets_end) {
|
||||
return OTS_FAILURE_MSG("Bad coverage location %d for mark set %d", offset_coverage, i);
|
||||
return Error("Bad coverage location %d for mark set %d", offset_coverage, i);
|
||||
}
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
||||
length - offset_coverage, num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse coverage table for mark set %d", i);
|
||||
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
|
||||
length - offset_coverage, this->m_num_glyphs)) {
|
||||
return Error("Failed to parse coverage table for mark set %d", i);
|
||||
}
|
||||
}
|
||||
font->gdef->num_mark_glyph_sets = mark_set_count;
|
||||
this->num_mark_glyph_sets = mark_set_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_gdef_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
// Grab the number of glyphs in the font from the maxp table to check
|
||||
// GlyphIDs in GDEF table.
|
||||
if (!font->maxp) {
|
||||
return OTS_FAILURE_MSG("No maxp table in font, needed by GDEF");
|
||||
if (!maxp) {
|
||||
return Error("No maxp table in font, needed by GDEF");
|
||||
}
|
||||
const uint16_t num_glyphs = font->maxp->num_glyphs;
|
||||
this->m_num_glyphs = maxp->num_glyphs;
|
||||
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeGDEF *gdef = new OpenTypeGDEF;
|
||||
font->gdef = gdef;
|
||||
|
||||
uint32_t version = 0;
|
||||
if (!table.ReadU32(&version)) {
|
||||
return OTS_FAILURE_MSG("Incomplete table");
|
||||
uint16_t version_major = 0, version_minor = 0;
|
||||
if (!table.ReadU16(&version_major) ||
|
||||
!table.ReadU16(&version_minor)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
if (version < 0x00010000 || version == 0x00010001) {
|
||||
return OTS_FAILURE_MSG("Bad version");
|
||||
}
|
||||
|
||||
if (version >= 0x00010002) {
|
||||
gdef->version_2 = true;
|
||||
if (version_major != 1 || version_minor == 1) { // there is no v1.1
|
||||
return Error("Bad version");
|
||||
}
|
||||
|
||||
uint16_t offset_glyph_class_def = 0;
|
||||
|
@ -264,110 +257,108 @@ bool ots_gdef_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
!table.ReadU16(&offset_attach_list) ||
|
||||
!table.ReadU16(&offset_lig_caret_list) ||
|
||||
!table.ReadU16(&offset_mark_attach_class_def)) {
|
||||
return OTS_FAILURE_MSG("Incomplete table");
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
uint16_t offset_mark_glyph_sets_def = 0;
|
||||
if (gdef->version_2) {
|
||||
if (version_minor >= 2) {
|
||||
if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
|
||||
return OTS_FAILURE_MSG("Incomplete table");
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
}
|
||||
uint32_t item_var_store_offset = 0;
|
||||
if (version_minor >= 3) {
|
||||
if (!table.ReadU32(&item_var_store_offset)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned gdef_header_end = 4 + 4 * 2;
|
||||
if (gdef->version_2)
|
||||
if (version_minor >= 2)
|
||||
gdef_header_end += 2;
|
||||
if (version_minor >= 3)
|
||||
gdef_header_end += 4;
|
||||
|
||||
// Parse subtables
|
||||
if (offset_glyph_class_def) {
|
||||
if (offset_glyph_class_def >= length ||
|
||||
offset_glyph_class_def < gdef_header_end) {
|
||||
return OTS_FAILURE_MSG("Invalid offset to glyph classes");
|
||||
return Error("Invalid offset to glyph classes");
|
||||
}
|
||||
if (!ParseGlyphClassDefTable(font, data + offset_glyph_class_def,
|
||||
if (!ots::ParseClassDefTable(GetFont(), data + offset_glyph_class_def,
|
||||
length - offset_glyph_class_def,
|
||||
num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Invalid glyph classes");
|
||||
this->m_num_glyphs, kMaxGlyphClassDefValue)) {
|
||||
return Error("Invalid glyph classes");
|
||||
}
|
||||
gdef->has_glyph_class_def = true;
|
||||
}
|
||||
|
||||
if (offset_attach_list) {
|
||||
if (offset_attach_list >= length ||
|
||||
offset_attach_list < gdef_header_end) {
|
||||
return OTS_FAILURE_MSG("Invalid offset to attachment list");
|
||||
return Error("Invalid offset to attachment list");
|
||||
}
|
||||
if (!ParseAttachListTable(font, data + offset_attach_list,
|
||||
length - offset_attach_list,
|
||||
num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Invalid attachment list");
|
||||
if (!ParseAttachListTable(data + offset_attach_list,
|
||||
length - offset_attach_list)) {
|
||||
return Error("Invalid attachment list");
|
||||
}
|
||||
}
|
||||
|
||||
if (offset_lig_caret_list) {
|
||||
if (offset_lig_caret_list >= length ||
|
||||
offset_lig_caret_list < gdef_header_end) {
|
||||
return OTS_FAILURE_MSG("Invalid offset to ligature caret list");
|
||||
return Error("Invalid offset to ligature caret list");
|
||||
}
|
||||
if (!ParseLigCaretListTable(font, data + offset_lig_caret_list,
|
||||
length - offset_lig_caret_list,
|
||||
num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Invalid ligature caret list");
|
||||
if (!ParseLigCaretListTable(data + offset_lig_caret_list,
|
||||
length - offset_lig_caret_list)) {
|
||||
return Error("Invalid ligature caret list");
|
||||
}
|
||||
}
|
||||
|
||||
if (offset_mark_attach_class_def) {
|
||||
if (offset_mark_attach_class_def >= length ||
|
||||
offset_mark_attach_class_def < gdef_header_end) {
|
||||
return OTS_FAILURE_MSG("Invalid offset to mark attachment list");
|
||||
return Error("Invalid offset to mark attachment list");
|
||||
}
|
||||
if (!ParseMarkAttachClassDefTable(font,
|
||||
data + offset_mark_attach_class_def,
|
||||
length - offset_mark_attach_class_def,
|
||||
num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Invalid mark attachment list");
|
||||
if (!ots::ParseClassDefTable(GetFont(),
|
||||
data + offset_mark_attach_class_def,
|
||||
length - offset_mark_attach_class_def,
|
||||
this->m_num_glyphs, kMaxClassDefValue)) {
|
||||
return Error("Invalid mark attachment list");
|
||||
}
|
||||
gdef->has_mark_attachment_class_def = true;
|
||||
}
|
||||
|
||||
if (offset_mark_glyph_sets_def) {
|
||||
if (offset_mark_glyph_sets_def >= length ||
|
||||
offset_mark_glyph_sets_def < gdef_header_end) {
|
||||
return OTS_FAILURE_MSG("invalid offset to mark glyph sets");
|
||||
return Error("invalid offset to mark glyph sets");
|
||||
}
|
||||
if (!ParseMarkGlyphSetsDefTable(font,
|
||||
data + offset_mark_glyph_sets_def,
|
||||
length - offset_mark_glyph_sets_def,
|
||||
num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Invalid mark glyph sets");
|
||||
if (!ParseMarkGlyphSetsDefTable(data + offset_mark_glyph_sets_def,
|
||||
length - offset_mark_glyph_sets_def)) {
|
||||
return Error("Invalid mark glyph sets");
|
||||
}
|
||||
gdef->has_mark_glyph_sets_def = true;
|
||||
}
|
||||
gdef->data = data;
|
||||
gdef->length = length;
|
||||
|
||||
if (item_var_store_offset) {
|
||||
if (item_var_store_offset >= length ||
|
||||
item_var_store_offset < gdef_header_end) {
|
||||
return Error("invalid offset to item variation store");
|
||||
}
|
||||
if (!ParseItemVariationStore(GetFont(), data + item_var_store_offset,
|
||||
length - item_var_store_offset)) {
|
||||
return Error("Invalid item variation store");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_gdef_should_serialise(Font *font) {
|
||||
return font->gdef != NULL && font->gdef->data != NULL;
|
||||
}
|
||||
|
||||
bool ots_gdef_serialise(OTSStream *out, Font *font) {
|
||||
if (!out->Write(font->gdef->data, font->gdef->length)) {
|
||||
return OTS_FAILURE_MSG("Failed to write GDEF table");
|
||||
bool OpenTypeGDEF::Serialize(OTSStream *out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_gdef_reuse(Font *font, Font *other) {
|
||||
font->gdef = other->gdef;
|
||||
font->gdef_reused = true;
|
||||
}
|
||||
|
||||
void ots_gdef_free(Font *font) {
|
||||
delete font->gdef;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,459 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "glat.h"
|
||||
|
||||
#include "gloc.h"
|
||||
#include "mozilla/Compression.h"
|
||||
#include <list>
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v1
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeGLAT_v1::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_GLOC));
|
||||
if (!gloc) {
|
||||
return DropGraphite("Required Gloc table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& locations = gloc->GetLocations();
|
||||
if (locations.empty()) {
|
||||
return DropGraphite("No locations from Gloc table");
|
||||
}
|
||||
std::list<uint32_t> unverified(locations.begin(), locations.end());
|
||||
while (table.remaining()) {
|
||||
GlatEntry entry(this);
|
||||
if (table.offset() > unverified.front()) {
|
||||
return DropGraphite("Offset check failed for a GlatEntry");
|
||||
}
|
||||
if (table.offset() == unverified.front()) {
|
||||
unverified.pop_front();
|
||||
}
|
||||
if (unverified.empty()) {
|
||||
return DropGraphite("Expected more locations");
|
||||
}
|
||||
if (!entry.ParsePart(table)) {
|
||||
return DropGraphite("Failed to read a GlatEntry");
|
||||
}
|
||||
this->entries.push_back(entry);
|
||||
}
|
||||
|
||||
if (unverified.size() != 1 || unverified.front() != table.offset()) {
|
||||
return DropGraphite("%zu location(s) could not be verified", unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v1::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!SerializeParts(this->entries, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v1::GlatEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->attNum)) {
|
||||
return parent->Error("GlatEntry: Failed to read attNum");
|
||||
}
|
||||
if (!table.ReadU8(&this->num)) {
|
||||
return parent->Error("GlatEntry: Failed to read num");
|
||||
}
|
||||
|
||||
//this->attributes.resize(this->num);
|
||||
for (int i = 0; i < this->num; ++i) {
|
||||
this->attributes.emplace_back();
|
||||
if (!table.ReadS16(&this->attributes[i])) {
|
||||
return parent->Error("GlatEntry: Failed to read attribute %u", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v1::GlatEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->attNum) ||
|
||||
!out->WriteU8(this->num) ||
|
||||
!SerializeParts(this->attributes, out)) {
|
||||
return parent->Error("GlatEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v2
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeGLAT_v2::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_GLOC));
|
||||
if (!gloc) {
|
||||
return DropGraphite("Required Gloc table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& locations = gloc->GetLocations();
|
||||
if (locations.empty()) {
|
||||
return DropGraphite("No locations from Gloc table");
|
||||
}
|
||||
std::list<uint32_t> unverified(locations.begin(), locations.end());
|
||||
while (table.remaining()) {
|
||||
GlatEntry entry(this);
|
||||
if (table.offset() > unverified.front()) {
|
||||
return DropGraphite("Offset check failed for a GlatEntry");
|
||||
}
|
||||
if (table.offset() == unverified.front()) {
|
||||
unverified.pop_front();
|
||||
}
|
||||
if (unverified.empty()) {
|
||||
return DropGraphite("Expected more locations");
|
||||
}
|
||||
if (!entry.ParsePart(table)) {
|
||||
return DropGraphite("Failed to read a GlatEntry");
|
||||
}
|
||||
this->entries.push_back(entry);
|
||||
}
|
||||
|
||||
if (unverified.size() != 1 || unverified.front() != table.offset()) {
|
||||
return DropGraphite("%zu location(s) could not be verified", unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v2::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!SerializeParts(this->entries, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v2::GlatEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadS16(&this->attNum)) {
|
||||
return parent->Error("GlatEntry: Failed to read attNum");
|
||||
}
|
||||
if (!table.ReadS16(&this->num) || this->num < 0) {
|
||||
return parent->Error("GlatEntry: Failed to read valid num");
|
||||
}
|
||||
|
||||
//this->attributes.resize(this->num);
|
||||
for (int i = 0; i < this->num; ++i) {
|
||||
this->attributes.emplace_back();
|
||||
if (!table.ReadS16(&this->attributes[i])) {
|
||||
return parent->Error("GlatEntry: Failed to read attribute %u", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v2::GlatEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteS16(this->attNum) ||
|
||||
!out->WriteS16(this->num) ||
|
||||
!SerializeParts(this->attributes, out)) {
|
||||
return parent->Error("GlatEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v3
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
|
||||
bool prevent_decompression) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeGLOC* gloc = static_cast<OpenTypeGLOC*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_GLOC));
|
||||
if (!gloc) {
|
||||
return DropGraphite("Required Gloc table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 3) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
if (!table.ReadU32(&this->compHead)) {
|
||||
return DropGraphite("Failed to read compression header");
|
||||
}
|
||||
switch ((this->compHead & SCHEME) >> 27) {
|
||||
case 0: // uncompressed
|
||||
break;
|
||||
case 1: { // lz4
|
||||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
size_t decompressed_size = this->compHead & FULL_SIZE;
|
||||
if (decompressed_size < length) {
|
||||
return DropGraphite("Decompressed size is less than compressed size");
|
||||
}
|
||||
if (decompressed_size == 0) {
|
||||
return DropGraphite("Decompressed size is set to 0");
|
||||
}
|
||||
// decompressed table must be <= 30MB
|
||||
if (decompressed_size > 30 * 1024 * 1024) {
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
size_t outputSize = 0;
|
||||
bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
table.remaining(), // input buffer size (input size + padding)
|
||||
reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
&outputSize); // return output size
|
||||
if (!ret || outputSize != decompressed.size()) {
|
||||
return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
default:
|
||||
return DropGraphite("Unknown compression scheme");
|
||||
}
|
||||
if (this->compHead & RESERVED) {
|
||||
Warning("Nonzero reserved");
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& locations = gloc->GetLocations();
|
||||
if (locations.empty()) {
|
||||
return DropGraphite("No locations from Gloc table");
|
||||
}
|
||||
std::list<uint32_t> unverified(locations.begin(), locations.end());
|
||||
//this->entries.resize(locations.size() - 1, this);
|
||||
for (size_t i = 0; i < locations.size() - 1; ++i) {
|
||||
this->entries.emplace_back(this);
|
||||
if (table.offset() != unverified.front()) {
|
||||
return DropGraphite("Offset check failed for a GlyphAttrs");
|
||||
}
|
||||
unverified.pop_front();
|
||||
if (!this->entries[i].ParsePart(table,
|
||||
unverified.front() - table.offset())) {
|
||||
// unverified.front() is guaranteed to exist because of the number of
|
||||
// iterations of this loop
|
||||
return DropGraphite("Failed to read a GlyphAttrs");
|
||||
}
|
||||
}
|
||||
|
||||
if (unverified.size() != 1 || unverified.front() != table.offset()) {
|
||||
return DropGraphite("%zu location(s) could not be verified", unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU32(this->compHead) ||
|
||||
!SerializeParts(this->entries, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::ParsePart(Buffer& table, const size_t size) {
|
||||
size_t init_offset = table.offset();
|
||||
if (parent->compHead & OCTABOXES && !octabox.ParsePart(table)) {
|
||||
// parent->flags & 0b1: octaboxes are present flag
|
||||
return parent->Error("GlyphAttrs: Failed to read octabox");
|
||||
}
|
||||
|
||||
while (table.offset() < init_offset + size) {
|
||||
GlatEntry entry(parent);
|
||||
if (!entry.ParsePart(table)) {
|
||||
return parent->Error("GlyphAttrs: Failed to read a GlatEntry");
|
||||
}
|
||||
this->entries.push_back(entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::SerializePart(OTSStream* out) const {
|
||||
if ((parent->compHead & OCTABOXES && !octabox.SerializePart(out)) ||
|
||||
!SerializeParts(this->entries, out)) {
|
||||
return parent->Error("GlyphAttrs: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::
|
||||
OctaboxMetrics::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU16(&this->subbox_bitmap)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read subbox_bitmap");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_neg_min)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read diag_neg_min");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_neg_max) ||
|
||||
this->diag_neg_max < this->diag_neg_min) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read valid diag_neg_max");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_pos_min)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read diag_pos_min");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_pos_max) ||
|
||||
this->diag_pos_max < this->diag_pos_min) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read valid diag_pos_max");
|
||||
}
|
||||
|
||||
unsigned subboxes_len = 0; // count of 1's in this->subbox_bitmap
|
||||
for (uint16_t i = this->subbox_bitmap; i; i >>= 1) {
|
||||
if (i & 0b1) {
|
||||
++subboxes_len;
|
||||
}
|
||||
}
|
||||
//this->subboxes.resize(subboxes_len, parent);
|
||||
for (unsigned i = 0; i < subboxes_len; i++) {
|
||||
this->subboxes.emplace_back(parent);
|
||||
if (!this->subboxes[i].ParsePart(table)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to read subbox[%u]", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::
|
||||
OctaboxMetrics::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->subbox_bitmap) ||
|
||||
!out->WriteU8(this->diag_neg_min) ||
|
||||
!out->WriteU8(this->diag_neg_max) ||
|
||||
!out->WriteU8(this->diag_pos_min) ||
|
||||
!out->WriteU8(this->diag_pos_max) ||
|
||||
!SerializeParts(this->subboxes, out)) {
|
||||
return parent->Error("OctaboxMetrics: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::OctaboxMetrics::
|
||||
SubboxEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->left)) {
|
||||
return parent->Error("SubboxEntry: Failed to read left");
|
||||
}
|
||||
if (!table.ReadU8(&this->right) || this->right < this->left) {
|
||||
return parent->Error("SubboxEntry: Failed to read valid right");
|
||||
}
|
||||
if (!table.ReadU8(&this->bottom)) {
|
||||
return parent->Error("SubboxEntry: Failed to read bottom");
|
||||
}
|
||||
if (!table.ReadU8(&this->top) || this->top < this->bottom) {
|
||||
return parent->Error("SubboxEntry: Failed to read valid top");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_pos_min)) {
|
||||
return parent->Error("SubboxEntry: Failed to read diag_pos_min");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_pos_max) ||
|
||||
this->diag_pos_max < this->diag_pos_min) {
|
||||
return parent->Error("SubboxEntry: Failed to read valid diag_pos_max");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_neg_min)) {
|
||||
return parent->Error("SubboxEntry: Failed to read diag_neg_min");
|
||||
}
|
||||
if (!table.ReadU8(&this->diag_neg_max) ||
|
||||
this->diag_neg_max < this->diag_neg_min) {
|
||||
return parent->Error("SubboxEntry: Failed to read valid diag_neg_max");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::OctaboxMetrics::
|
||||
SubboxEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->left) ||
|
||||
!out->WriteU8(this->right) ||
|
||||
!out->WriteU8(this->bottom) ||
|
||||
!out->WriteU8(this->top) ||
|
||||
!out->WriteU8(this->diag_pos_min) ||
|
||||
!out->WriteU8(this->diag_pos_max) ||
|
||||
!out->WriteU8(this->diag_neg_min) ||
|
||||
!out->WriteU8(this->diag_neg_max)) {
|
||||
return parent->Error("SubboxEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::
|
||||
GlatEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadS16(&this->attNum)) {
|
||||
return parent->Error("GlatEntry: Failed to read attNum");
|
||||
}
|
||||
if (!table.ReadS16(&this->num) || this->num < 0) {
|
||||
return parent->Error("GlatEntry: Failed to read valid num");
|
||||
}
|
||||
|
||||
//this->attributes.resize(this->num);
|
||||
for (int i = 0; i < this->num; ++i) {
|
||||
this->attributes.emplace_back();
|
||||
if (!table.ReadS16(&this->attributes[i])) {
|
||||
return parent->Error("GlatEntry: Failed to read attribute %u", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT_v3::GlyphAttrs::
|
||||
GlatEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteS16(this->attNum) ||
|
||||
!out->WriteS16(this->num) ||
|
||||
!SerializeParts(this->attributes, out)) {
|
||||
return parent->Error("GlatEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeGLAT::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
uint32_t version;
|
||||
if (!table.ReadU32(&version)) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
switch (version >> 16) {
|
||||
case 1:
|
||||
this->handler = new OpenTypeGLAT_v1(this->font, this->tag);
|
||||
break;
|
||||
case 2:
|
||||
this->handler = new OpenTypeGLAT_v2(this->font, this->tag);
|
||||
break;
|
||||
case 3: {
|
||||
this->handler = new OpenTypeGLAT_v3(this->font, this->tag);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return DropGraphite("Unsupported table version: %u", version >> 16);
|
||||
}
|
||||
return this->handler->Parse(data, length);
|
||||
}
|
||||
|
||||
bool OpenTypeGLAT::Serialize(OTSStream* out) {
|
||||
if (!this->handler) {
|
||||
return Error("No Glat table parsed");
|
||||
}
|
||||
return this->handler->Serialize(out);
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GLAT_H_
|
||||
#define OTS_GLAT_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_Basic Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT_Basic : public Table {
|
||||
public:
|
||||
explicit OpenTypeGLAT_Basic(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
virtual bool Parse(const uint8_t* data, size_t length) = 0;
|
||||
virtual bool Serialize(OTSStream* out) = 0;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v1
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT_v1 : public OpenTypeGLAT_Basic {
|
||||
public:
|
||||
explicit OpenTypeGLAT_v1(Font* font, uint32_t tag)
|
||||
: OpenTypeGLAT_Basic(font, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
struct GlatEntry : public TablePart<OpenTypeGLAT_v1> {
|
||||
explicit GlatEntry(OpenTypeGLAT_v1* parent)
|
||||
: TablePart<OpenTypeGLAT_v1>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint8_t attNum;
|
||||
uint8_t num;
|
||||
std::vector<int16_t> attributes;
|
||||
};
|
||||
uint32_t version;
|
||||
std::vector<GlatEntry> entries;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v2
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT_v2 : public OpenTypeGLAT_Basic {
|
||||
public:
|
||||
explicit OpenTypeGLAT_v2(Font* font, uint32_t tag)
|
||||
: OpenTypeGLAT_Basic(font, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
struct GlatEntry : public TablePart<OpenTypeGLAT_v2> {
|
||||
explicit GlatEntry(OpenTypeGLAT_v2* parent)
|
||||
: TablePart<OpenTypeGLAT_v2>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
int16_t attNum;
|
||||
int16_t num;
|
||||
std::vector<int16_t> attributes;
|
||||
};
|
||||
uint32_t version;
|
||||
std::vector<GlatEntry> entries;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT_v3
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT_v3 : public OpenTypeGLAT_Basic {
|
||||
public:
|
||||
explicit OpenTypeGLAT_v3(Font* font, uint32_t tag)
|
||||
: OpenTypeGLAT_Basic(font, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length) {
|
||||
return this->Parse(data, length, false);
|
||||
}
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
bool Parse(const uint8_t* data, size_t length, bool prevent_decompression);
|
||||
struct GlyphAttrs : public TablePart<OpenTypeGLAT_v3> {
|
||||
explicit GlyphAttrs(OpenTypeGLAT_v3* parent)
|
||||
: TablePart<OpenTypeGLAT_v3>(parent), octabox(parent) { }
|
||||
bool ParsePart(Buffer& table) { return false; }
|
||||
bool ParsePart(Buffer& table, const size_t size);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct OctaboxMetrics : public TablePart<OpenTypeGLAT_v3> {
|
||||
explicit OctaboxMetrics(OpenTypeGLAT_v3* parent)
|
||||
: TablePart<OpenTypeGLAT_v3>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct SubboxEntry : public TablePart<OpenTypeGLAT_v3> {
|
||||
explicit SubboxEntry(OpenTypeGLAT_v3* parent)
|
||||
: TablePart<OpenTypeGLAT_v3>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint8_t left;
|
||||
uint8_t right;
|
||||
uint8_t bottom;
|
||||
uint8_t top;
|
||||
uint8_t diag_pos_min;
|
||||
uint8_t diag_pos_max;
|
||||
uint8_t diag_neg_min;
|
||||
uint8_t diag_neg_max;
|
||||
};
|
||||
uint16_t subbox_bitmap;
|
||||
uint8_t diag_neg_min;
|
||||
uint8_t diag_neg_max;
|
||||
uint8_t diag_pos_min;
|
||||
uint8_t diag_pos_max;
|
||||
std::vector<SubboxEntry> subboxes;
|
||||
};
|
||||
struct GlatEntry : public TablePart<OpenTypeGLAT_v3> {
|
||||
explicit GlatEntry(OpenTypeGLAT_v3* parent)
|
||||
: TablePart<OpenTypeGLAT_v3>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
int16_t attNum;
|
||||
int16_t num;
|
||||
std::vector<int16_t> attributes;
|
||||
};
|
||||
OctaboxMetrics octabox;
|
||||
std::vector<GlatEntry> entries;
|
||||
};
|
||||
uint32_t version;
|
||||
uint32_t compHead; // compression header
|
||||
static const uint32_t SCHEME = 0xF8000000;
|
||||
static const uint32_t FULL_SIZE = 0x07FFFFFF;
|
||||
static const uint32_t RESERVED = 0x07FFFFFE;
|
||||
static const uint32_t OCTABOXES = 0x00000001;
|
||||
std::vector<GlyphAttrs> entries;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGLAT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeGLAT : public Table {
|
||||
public:
|
||||
explicit OpenTypeGLAT(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag), font(font), tag(tag) { }
|
||||
OpenTypeGLAT(const OpenTypeGLAT& other) = delete;
|
||||
OpenTypeGLAT& operator=(const OpenTypeGLAT& other) = delete;
|
||||
~OpenTypeGLAT() { delete handler; }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
Font* font;
|
||||
uint32_t tag;
|
||||
OpenTypeGLAT_Basic* handler = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GLAT_H_
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "gloc.h"
|
||||
|
||||
#include "name.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeGLOC::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
if (!name) {
|
||||
return DropGraphite("Required name table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->version)) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
if (this->version >> 16 != 1) {
|
||||
return DropGraphite("Unsupported table version: %u", this->version >> 16);
|
||||
}
|
||||
if (!table.ReadU16(&this->flags) || this->flags > 0b11) {
|
||||
return DropGraphite("Failed to read valid flags");
|
||||
}
|
||||
if (!table.ReadU16(&this->numAttribs)) {
|
||||
return DropGraphite("Failed to read numAttribs");
|
||||
}
|
||||
|
||||
if (this->flags & ATTRIB_IDS && this->numAttribs * sizeof(uint16_t) >
|
||||
table.remaining()) {
|
||||
return DropGraphite("Failed to calulate length of locations");
|
||||
}
|
||||
size_t locations_len = (table.remaining() -
|
||||
(this->flags & ATTRIB_IDS ? this->numAttribs * sizeof(uint16_t) : 0)) /
|
||||
(this->flags & LONG_FORMAT ? sizeof(uint32_t) : sizeof(uint16_t));
|
||||
//this->locations.resize(locations_len);
|
||||
if (this->flags & LONG_FORMAT) {
|
||||
unsigned long last_location = 0;
|
||||
for (size_t i = 0; i < locations_len; ++i) {
|
||||
this->locations.emplace_back();
|
||||
uint32_t& location = this->locations[i];
|
||||
if (!table.ReadU32(&location) || location < last_location) {
|
||||
return DropGraphite("Failed to read valid locations[%lu]", i);
|
||||
}
|
||||
last_location = location;
|
||||
}
|
||||
} else { // short (16-bit) offsets
|
||||
unsigned last_location = 0;
|
||||
for (size_t i = 0; i < locations_len; ++i) {
|
||||
uint16_t location;
|
||||
if (!table.ReadU16(&location) || location < last_location) {
|
||||
return DropGraphite("Failed to read valid locations[%lu]", i);
|
||||
}
|
||||
last_location = location;
|
||||
this->locations.push_back(static_cast<uint32_t>(location));
|
||||
}
|
||||
}
|
||||
if (this->locations.empty()) {
|
||||
return DropGraphite("No locations");
|
||||
}
|
||||
|
||||
if (this->flags & ATTRIB_IDS) { // attribIds array present
|
||||
//this->attribIds.resize(numAttribs);
|
||||
for (unsigned i = 0; i < this->numAttribs; ++i) {
|
||||
this->attribIds.emplace_back();
|
||||
if (!table.ReadU16(&this->attribIds[i]) ||
|
||||
!name->IsValidNameId(this->attribIds[i])) {
|
||||
return DropGraphite("Failed to read valid attribIds[%u]", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLOC::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU16(this->flags) ||
|
||||
!out->WriteU16(this->numAttribs) ||
|
||||
(this->flags & LONG_FORMAT ? !SerializeParts(this->locations, out) :
|
||||
![&] {
|
||||
for (uint32_t location : this->locations) {
|
||||
if (!out->WriteU16(static_cast<uint16_t>(location))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}()) ||
|
||||
(this->flags & ATTRIB_IDS && !SerializeParts(this->attribIds, out))) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& OpenTypeGLOC::GetLocations() {
|
||||
return this->locations;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GLOC_H_
|
||||
#define OTS_GLOC_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeGLOC : public Table {
|
||||
public:
|
||||
explicit OpenTypeGLOC(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
const std::vector<uint32_t>& GetLocations();
|
||||
|
||||
private:
|
||||
uint32_t version;
|
||||
uint16_t flags;
|
||||
static const uint16_t LONG_FORMAT = 0b1;
|
||||
static const uint16_t ATTRIB_IDS = 0b10;
|
||||
uint16_t numAttribs;
|
||||
std::vector<uint32_t> locations;
|
||||
std::vector<uint16_t> attribIds;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GLOC_H_
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -14,20 +14,15 @@
|
|||
// glyf - Glyph Data
|
||||
// http://www.microsoft.com/typography/otspec/glyf.htm
|
||||
|
||||
#define TABLE_NAME "glyf"
|
||||
namespace ots {
|
||||
|
||||
namespace {
|
||||
|
||||
bool ParseFlagsForSimpleGlyph(ots::Font *font,
|
||||
ots::Buffer *table,
|
||||
uint32_t gly_length,
|
||||
uint32_t num_flags,
|
||||
uint32_t *flags_count_logical,
|
||||
uint32_t *flags_count_physical,
|
||||
uint32_t *xy_coordinates_length) {
|
||||
bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
|
||||
uint32_t num_flags,
|
||||
uint32_t *flag_index,
|
||||
uint32_t *coordinates_length) {
|
||||
uint8_t flag = 0;
|
||||
if (!table->ReadU8(&flag)) {
|
||||
return OTS_FAILURE_MSG("Can't read flag");
|
||||
if (!glyph.ReadU8(&flag)) {
|
||||
return Error("Can't read flag");
|
||||
}
|
||||
|
||||
uint32_t delta = 0;
|
||||
|
@ -43,140 +38,205 @@ bool ParseFlagsForSimpleGlyph(ots::Font *font,
|
|||
delta += 2;
|
||||
}
|
||||
|
||||
/* MS and Apple specs say this bit is reserved and must be set to zero, but
|
||||
* Apple spec then contradicts itself and says it should be set on the first
|
||||
* contour flag for simple glyphs with overlapping contours:
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html
|
||||
* (“Overlapping contours” section) */
|
||||
if (flag & (1u << 6) && *flag_index != 0) {
|
||||
return Error("Bad glyph flag (%d), "
|
||||
"bit 6 must be set to zero for flag %d", flag, *flag_index);
|
||||
}
|
||||
|
||||
if (flag & (1u << 3)) { // repeat
|
||||
if (*flags_count_logical + 1 >= num_flags) {
|
||||
return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags);
|
||||
if (*flag_index + 1 >= num_flags) {
|
||||
return Error("Count too high (%d + 1 >= %d)", *flag_index, num_flags);
|
||||
}
|
||||
uint8_t repeat = 0;
|
||||
if (!table->ReadU8(&repeat)) {
|
||||
return OTS_FAILURE_MSG("Can't read repeat value");
|
||||
if (!glyph.ReadU8(&repeat)) {
|
||||
return Error("Can't read repeat value");
|
||||
}
|
||||
if (repeat == 0) {
|
||||
return OTS_FAILURE_MSG("Zero repeat");
|
||||
return Error("Zero repeat");
|
||||
}
|
||||
delta += (delta * repeat);
|
||||
|
||||
*flags_count_logical += repeat;
|
||||
if (*flags_count_logical >= num_flags) {
|
||||
return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags);
|
||||
*flag_index += repeat;
|
||||
if (*flag_index >= num_flags) {
|
||||
return Error("Count too high (%d >= %d)", *flag_index, num_flags);
|
||||
}
|
||||
++(*flags_count_physical);
|
||||
}
|
||||
|
||||
if ((flag & (1u << 6)) || (flag & (1u << 7))) { // reserved flags
|
||||
return OTS_FAILURE_MSG("Bad glyph flag value (%d), reserved flags must be set to zero", flag);
|
||||
if (flag & (1u << 7)) { // reserved flag
|
||||
return Error("Bad glyph flag (%d), reserved bit 7 must be set to zero", flag);
|
||||
}
|
||||
|
||||
*xy_coordinates_length += delta;
|
||||
if (gly_length < *xy_coordinates_length) {
|
||||
return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length);
|
||||
*coordinates_length += delta;
|
||||
if (glyph.length() < *coordinates_length) {
|
||||
return Error("Glyph coordinates length bigger than glyph length (%d > %d)",
|
||||
*coordinates_length, glyph.length());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseSimpleGlyph(ots::Font *font, const uint8_t *data,
|
||||
ots::Buffer *table, int16_t num_contours,
|
||||
uint32_t gly_offset, uint32_t gly_length,
|
||||
uint32_t *new_size) {
|
||||
ots::OpenTypeGLYF *glyf = font->glyf;
|
||||
|
||||
bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,
|
||||
int16_t num_contours) {
|
||||
// read the end-points array
|
||||
uint16_t num_flags = 0;
|
||||
for (int i = 0; i < num_contours; ++i) {
|
||||
uint16_t tmp_index = 0;
|
||||
if (!table->ReadU16(&tmp_index)) {
|
||||
return OTS_FAILURE_MSG("Can't read contour index %d", i);
|
||||
if (!glyph.ReadU16(&tmp_index)) {
|
||||
return Error("Can't read contour index %d", i);
|
||||
}
|
||||
if (tmp_index == 0xffffu) {
|
||||
return OTS_FAILURE_MSG("Bad contour index %d", i);
|
||||
return Error("Bad contour index %d", i);
|
||||
}
|
||||
// check if the indices are monotonically increasing
|
||||
if (i && (tmp_index + 1 <= num_flags)) {
|
||||
return OTS_FAILURE_MSG("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
|
||||
return Error("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
|
||||
}
|
||||
num_flags = tmp_index + 1;
|
||||
}
|
||||
|
||||
uint16_t bytecode_length = 0;
|
||||
if (!table->ReadU16(&bytecode_length)) {
|
||||
return OTS_FAILURE_MSG("Can't read bytecode length");
|
||||
}
|
||||
if ((font->maxp->version_1) &&
|
||||
(font->maxp->max_size_glyf_instructions < bytecode_length)) {
|
||||
return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length);
|
||||
if (!glyph.ReadU16(&bytecode_length)) {
|
||||
return Error("Can't read bytecode length");
|
||||
}
|
||||
|
||||
const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
|
||||
if (gly_length < (gly_header_length + bytecode_length)) {
|
||||
return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length);
|
||||
if (this->maxp->version_1 &&
|
||||
this->maxp->max_size_glyf_instructions < bytecode_length) {
|
||||
this->maxp->max_size_glyf_instructions = bytecode_length;
|
||||
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions %d: %d",
|
||||
this->maxp->max_size_glyf_instructions, bytecode_length);
|
||||
}
|
||||
|
||||
glyf->iov.push_back(std::make_pair(
|
||||
data + gly_offset,
|
||||
static_cast<size_t>(gly_header_length + bytecode_length)));
|
||||
|
||||
if (!table->Skip(bytecode_length)) {
|
||||
return OTS_FAILURE_MSG("Can't skip bytecode of length %d", bytecode_length);
|
||||
if (!glyph.Skip(bytecode_length)) {
|
||||
return Error("Can't read bytecode of length %d", bytecode_length);
|
||||
}
|
||||
|
||||
uint32_t flags_count_physical = 0; // on memory
|
||||
uint32_t xy_coordinates_length = 0;
|
||||
for (uint32_t flags_count_logical = 0;
|
||||
flags_count_logical < num_flags;
|
||||
++flags_count_logical, ++flags_count_physical) {
|
||||
if (!ParseFlagsForSimpleGlyph(font,
|
||||
table,
|
||||
gly_length,
|
||||
num_flags,
|
||||
&flags_count_logical,
|
||||
&flags_count_physical,
|
||||
&xy_coordinates_length)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical);
|
||||
uint32_t coordinates_length = 0;
|
||||
for (uint32_t i = 0; i < num_flags; ++i) {
|
||||
if (!ParseFlagsForSimpleGlyph(glyph, num_flags, &i, &coordinates_length)) {
|
||||
return Error("Failed to parse glyph flags %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (gly_length < (gly_header_length + bytecode_length +
|
||||
flags_count_physical + xy_coordinates_length)) {
|
||||
return OTS_FAILURE_MSG("Glyph too short %d", gly_length);
|
||||
if (!glyph.Skip(coordinates_length)) {
|
||||
return Error("Glyph too short %d", glyph.length());
|
||||
}
|
||||
|
||||
if (gly_length - (gly_header_length + bytecode_length +
|
||||
flags_count_physical + xy_coordinates_length) > 3) {
|
||||
if (glyph.remaining() > 3) {
|
||||
// We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
|
||||
// zero-padded length.
|
||||
return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length);
|
||||
Warning("Extra bytes at end of the glyph: %d", glyph.remaining());
|
||||
}
|
||||
|
||||
glyf->iov.push_back(std::make_pair(
|
||||
data + gly_offset + gly_header_length + bytecode_length,
|
||||
static_cast<size_t>(flags_count_physical + xy_coordinates_length)));
|
||||
|
||||
*new_size
|
||||
= gly_header_length + flags_count_physical + xy_coordinates_length + bytecode_length;
|
||||
this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#define ARG_1_AND_2_ARE_WORDS (1u << 0)
|
||||
#define WE_HAVE_A_SCALE (1u << 3)
|
||||
#define MORE_COMPONENTS (1u << 5)
|
||||
#define WE_HAVE_AN_X_AND_Y_SCALE (1u << 6)
|
||||
#define WE_HAVE_A_TWO_BY_TWO (1u << 7)
|
||||
#define WE_HAVE_INSTRUCTIONS (1u << 8)
|
||||
|
||||
namespace ots {
|
||||
bool OpenTypeGLYF::ParseCompositeGlyph(Buffer &glyph) {
|
||||
uint16_t flags = 0;
|
||||
uint16_t gid = 0;
|
||||
do {
|
||||
if (!glyph.ReadU16(&flags) || !glyph.ReadU16(&gid)) {
|
||||
return Error("Can't read composite glyph flags or glyphIndex");
|
||||
}
|
||||
|
||||
bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
if (gid >= this->maxp->num_glyphs) {
|
||||
return Error("Invalid glyph id used in composite glyph: %d", gid);
|
||||
}
|
||||
|
||||
if (!font->maxp || !font->loca || !font->head) {
|
||||
return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table");
|
||||
if (flags & ARG_1_AND_2_ARE_WORDS) {
|
||||
int16_t argument1;
|
||||
int16_t argument2;
|
||||
if (!glyph.ReadS16(&argument1) || !glyph.ReadS16(&argument2)) {
|
||||
return Error("Can't read argument1 or argument2");
|
||||
}
|
||||
} else {
|
||||
uint8_t argument1;
|
||||
uint8_t argument2;
|
||||
if (!glyph.ReadU8(&argument1) || !glyph.ReadU8(&argument2)) {
|
||||
return Error("Can't read argument1 or argument2");
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & WE_HAVE_A_SCALE) {
|
||||
int16_t scale;
|
||||
if (!glyph.ReadS16(&scale)) {
|
||||
return Error("Can't read scale");
|
||||
}
|
||||
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
|
||||
int16_t xscale;
|
||||
int16_t yscale;
|
||||
if (!glyph.ReadS16(&xscale) || !glyph.ReadS16(&yscale)) {
|
||||
return Error("Can't read xscale or yscale");
|
||||
}
|
||||
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
||||
int16_t xscale;
|
||||
int16_t scale01;
|
||||
int16_t scale10;
|
||||
int16_t yscale;
|
||||
if (!glyph.ReadS16(&xscale) ||
|
||||
!glyph.ReadS16(&scale01) ||
|
||||
!glyph.ReadS16(&scale10) ||
|
||||
!glyph.ReadS16(&yscale)) {
|
||||
return Error("Can't read transform");
|
||||
}
|
||||
}
|
||||
} while (flags & MORE_COMPONENTS);
|
||||
|
||||
if (flags & WE_HAVE_INSTRUCTIONS) {
|
||||
uint16_t bytecode_length;
|
||||
if (!glyph.ReadU16(&bytecode_length)) {
|
||||
return Error("Can't read instructions size");
|
||||
}
|
||||
|
||||
if (this->maxp->version_1 &&
|
||||
this->maxp->max_size_glyf_instructions < bytecode_length) {
|
||||
this->maxp->max_size_glyf_instructions = bytecode_length;
|
||||
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions "
|
||||
"%d: %d",
|
||||
this->maxp->max_size_glyf_instructions, bytecode_length);
|
||||
}
|
||||
|
||||
if (!glyph.Skip(bytecode_length)) {
|
||||
return Error("Can't read bytecode of length %d", bytecode_length);
|
||||
}
|
||||
}
|
||||
|
||||
OpenTypeGLYF *glyf = new OpenTypeGLYF;
|
||||
font->glyf = glyf;
|
||||
this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
|
||||
|
||||
const unsigned num_glyphs = font->maxp->num_glyphs;
|
||||
std::vector<uint32_t> &offsets = font->loca->offsets;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGLYF::Parse(const uint8_t *data, size_t length) {
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
OpenTypeLOCA *loca = static_cast<OpenTypeLOCA*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_LOCA));
|
||||
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_HEAD));
|
||||
if (!maxp || !loca || !head) {
|
||||
return Error("Missing maxp or loca or head table needed by glyf table");
|
||||
}
|
||||
|
||||
this->maxp = maxp;
|
||||
|
||||
const unsigned num_glyphs = maxp->num_glyphs;
|
||||
std::vector<uint32_t> &offsets = loca->offsets;
|
||||
|
||||
if (offsets.size() != num_glyphs + 1) {
|
||||
return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
|
||||
return Error("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
|
||||
|
@ -193,30 +253,31 @@ bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
}
|
||||
|
||||
if (gly_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Glyph %d offset %d too high %ld", i, gly_offset, length);
|
||||
return Error("Glyph %d offset %d too high %ld", i, gly_offset, length);
|
||||
}
|
||||
// Since these are unsigned types, the compiler is not allowed to assume
|
||||
// that they never overflow.
|
||||
if (gly_offset + gly_length < gly_offset) {
|
||||
return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length);
|
||||
return Error("Glyph %d length (%d < 0)!", i, gly_length);
|
||||
}
|
||||
if (gly_offset + gly_length > length) {
|
||||
return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length);
|
||||
return Error("Glyph %d length %d too high", i, gly_length);
|
||||
}
|
||||
|
||||
table.set_offset(gly_offset);
|
||||
Buffer glyph(data + gly_offset, gly_length);
|
||||
|
||||
int16_t num_contours, xmin, ymin, xmax, ymax;
|
||||
if (!table.ReadS16(&num_contours) ||
|
||||
!table.ReadS16(&xmin) ||
|
||||
!table.ReadS16(&ymin) ||
|
||||
!table.ReadS16(&xmax) ||
|
||||
!table.ReadS16(&ymax)) {
|
||||
return OTS_FAILURE_MSG("Can't read glyph %d header", i);
|
||||
if (!glyph.ReadS16(&num_contours) ||
|
||||
!glyph.ReadS16(&xmin) ||
|
||||
!glyph.ReadS16(&ymin) ||
|
||||
!glyph.ReadS16(&xmax) ||
|
||||
!glyph.ReadS16(&ymax)) {
|
||||
return Error("Can't read glyph %d header", i);
|
||||
}
|
||||
|
||||
if (num_contours <= -2) {
|
||||
// -2, -3, -4, ... are reserved for future use.
|
||||
return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i);
|
||||
return Error("Bad number of contours %d in glyph %d", num_contours, i);
|
||||
}
|
||||
|
||||
// workaround for fonts in http://www.princexml.com/fonts/
|
||||
|
@ -224,35 +285,36 @@ bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
(xmax == -32767) &&
|
||||
(ymin == 32767) &&
|
||||
(ymax == -32767)) {
|
||||
OTS_WARNING("bad xmin/xmax/ymin/ymax values");
|
||||
Warning("bad xmin/xmax/ymin/ymax values");
|
||||
xmin = xmax = ymin = ymax = 0;
|
||||
}
|
||||
|
||||
if (xmin > xmax || ymin > ymax) {
|
||||
return OTS_FAILURE_MSG("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
|
||||
return Error("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
|
||||
}
|
||||
|
||||
unsigned new_size = 0;
|
||||
if (num_contours >= 0) {
|
||||
// this is a simple glyph and might contain bytecode
|
||||
if (!ParseSimpleGlyph(font, data, &table,
|
||||
num_contours, gly_offset, gly_length, &new_size)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse glyph %d", i);
|
||||
if (num_contours == 0) {
|
||||
// This is an empty glyph and 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<size_t>(gly_length)));
|
||||
new_size = gly_length;
|
||||
if (!ParseCompositeGlyph(glyph)) {
|
||||
return Error("Failed to parse glyph %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
size_t new_size = glyph.offset();
|
||||
resulting_offsets[i] = current_offset;
|
||||
// glyphs must be four byte aligned
|
||||
// TODO(yusukes): investigate whether this padding is really necessary.
|
||||
// Which part of the spec requires this?
|
||||
const unsigned padding = (4 - (new_size & 3)) % 4;
|
||||
if (padding) {
|
||||
glyf->iov.push_back(std::make_pair(
|
||||
this->iov.push_back(std::make_pair(
|
||||
reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"),
|
||||
static_cast<size_t>(padding)));
|
||||
new_size += padding;
|
||||
|
@ -264,40 +326,32 @@ bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
const uint16_t max16 = std::numeric_limits<uint16_t>::max();
|
||||
if ((*std::max_element(resulting_offsets.begin(),
|
||||
resulting_offsets.end()) >= (max16 * 2u)) &&
|
||||
(font->head->index_to_loc_format != 1)) {
|
||||
OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
|
||||
font->head->index_to_loc_format = 1;
|
||||
(head->index_to_loc_format != 1)) {
|
||||
head->index_to_loc_format = 1;
|
||||
}
|
||||
|
||||
loca->offsets = resulting_offsets;
|
||||
|
||||
if (this->iov.empty()) {
|
||||
// As a special case when all glyph in the font are empty, add a zero byte
|
||||
// to the table, so that we 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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -12,8 +12,26 @@
|
|||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
class OpenTypeMAXP;
|
||||
|
||||
class OpenTypeGLYF : public Table {
|
||||
public:
|
||||
explicit OpenTypeGLYF(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag), maxp(NULL) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
bool ParseFlagsForSimpleGlyph(Buffer &glyph,
|
||||
uint32_t num_flags,
|
||||
uint32_t *flag_index,
|
||||
uint32_t *coordinates_length);
|
||||
bool ParseSimpleGlyph(Buffer &glyph, int16_t num_contours);
|
||||
bool ParseCompositeGlyph(Buffer &glyph);
|
||||
|
||||
OpenTypeMAXP* maxp;
|
||||
|
||||
struct OpenTypeGLYF {
|
||||
std::vector<std::pair<const uint8_t*, size_t> > iov;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -30,12 +30,12 @@ enum GPOS_TYPE {
|
|||
GPOS_TYPE_RESERVED = 10
|
||||
};
|
||||
|
||||
// The size of gpos header.
|
||||
const unsigned kGposHeaderSize = 10;
|
||||
// The size of gpos header, version 1.0.
|
||||
const unsigned kGposHeaderSize_1_0 = 10;
|
||||
// The size of gpos header, version 1.1.
|
||||
const unsigned kGposHeaderSize_1_1 = 14;
|
||||
// The maximum format number for anchor tables.
|
||||
const uint16_t kMaxAnchorFormat = 3;
|
||||
// The maximum number of class value.
|
||||
const uint16_t kMaxClassDefValue = 0xFFFF;
|
||||
|
||||
// Lookup type parsers.
|
||||
bool ParseSingleAdjustment(const ots::Font *font,
|
||||
|
@ -76,9 +76,23 @@ const ots::LookupSubtableParser kGposLookupSubtableParser = {
|
|||
|
||||
// Shared Tables: ValueRecord, Anchor Table, and MarkArray
|
||||
|
||||
size_t CalcValueRecordSize(const uint16_t value_format) {
|
||||
size_t size = 0;
|
||||
for (unsigned i = 0; i < 8; ++i) {
|
||||
if ((value_format >> i) & 0x1) {
|
||||
size += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
bool ParseValueRecord(const ots::Font *font,
|
||||
ots::Buffer* subtable, const uint8_t *data,
|
||||
const size_t length, const uint16_t value_format) {
|
||||
ots::Buffer* subtable,
|
||||
const uint16_t value_format) {
|
||||
const uint8_t *data = subtable->buffer();
|
||||
const size_t length = subtable->length();
|
||||
|
||||
// Check existence of adjustment fields.
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
if ((value_format >> i) & 0x1) {
|
||||
|
@ -207,6 +221,12 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
|
|||
const size_t length) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
|
||||
uint16_t format = 0;
|
||||
uint16_t offset_coverage = 0;
|
||||
uint16_t value_format = 0;
|
||||
|
@ -218,7 +238,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
|
|||
|
||||
if (format == 1) {
|
||||
// Format 1 exactly one value record.
|
||||
if (!ParseValueRecord(font, &subtable, data, length, value_format)) {
|
||||
if (!ParseValueRecord(font, &subtable, value_format)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table");
|
||||
}
|
||||
} else if (format == 2) {
|
||||
|
@ -227,7 +247,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
|
|||
return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table");
|
||||
}
|
||||
for (unsigned i = 0; i < value_count; ++i) {
|
||||
if (!ParseValueRecord(font, &subtable, data, length, value_format)) {
|
||||
if (!ParseValueRecord(font, &subtable, value_format)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i);
|
||||
}
|
||||
}
|
||||
|
@ -241,7 +261,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
|
|||
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
||||
length - offset_coverage,
|
||||
font->maxp->num_glyphs)) {
|
||||
maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table");
|
||||
}
|
||||
|
||||
|
@ -268,10 +288,10 @@ bool ParsePairSetTable(const ots::Font *font,
|
|||
if (glyph_id >= num_glyphs) {
|
||||
return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs);
|
||||
}
|
||||
if (!ParseValueRecord(font, &subtable, data, length, value_format1)) {
|
||||
if (!ParseValueRecord(font, &subtable, value_format1)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table");
|
||||
}
|
||||
if (!ParseValueRecord(font, &subtable, data, length, value_format2)) {
|
||||
if (!ParseValueRecord(font, &subtable, value_format2)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table");
|
||||
}
|
||||
}
|
||||
|
@ -341,34 +361,44 @@ bool ParsePairPosFormat2(const ots::Font *font,
|
|||
return OTS_FAILURE_MSG("Failed to read pair pos format 2 data");
|
||||
}
|
||||
|
||||
size_t value_record1_size = CalcValueRecordSize(value_format1);
|
||||
size_t value_record2_size = CalcValueRecordSize(value_format2);
|
||||
size_t value_records_size = size_t(class1_count) * size_t(class2_count) *
|
||||
(value_record1_size + value_record2_size);
|
||||
|
||||
// Check the validity of class definition offsets.
|
||||
if (offset_class_def1 < subtable.offset() + value_records_size ||
|
||||
offset_class_def2 < subtable.offset() + value_records_size ||
|
||||
offset_class_def1 >= length || offset_class_def2 >= length) {
|
||||
return OTS_FAILURE_MSG("Bad ParsePairPosFormat2 class definition offsets %d or %d", offset_class_def1, offset_class_def2);
|
||||
}
|
||||
|
||||
// Check class 1 records.
|
||||
for (unsigned i = 0; i < class1_count; ++i) {
|
||||
// Check class 2 records.
|
||||
for (unsigned j = 0; j < class2_count; ++j) {
|
||||
if (value_format1 && !ParseValueRecord(font, &subtable, data, length,
|
||||
value_format1)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
|
||||
}
|
||||
if (value_format2 && !ParseValueRecord(font, &subtable, data, length,
|
||||
value_format2)) {
|
||||
return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
|
||||
if (value_record1_size || value_record2_size) {
|
||||
for (unsigned i = 0; i < class1_count; ++i) {
|
||||
// Check class 2 records.
|
||||
for (unsigned j = 0; j < class2_count; ++j) {
|
||||
if (value_format1 && value_record2_size &&
|
||||
!ParseValueRecord(font, &subtable, value_format1)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
|
||||
}
|
||||
if (value_format2 && value_record2_size &&
|
||||
!ParseValueRecord(font, &subtable, value_format2)) {
|
||||
return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check class definition tables.
|
||||
if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
|
||||
offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
|
||||
return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2);
|
||||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_class_def1,
|
||||
length - offset_class_def1,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse class definition table 1");
|
||||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_class_def2,
|
||||
length - offset_class_def2,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse class definition table 2");
|
||||
}
|
||||
|
||||
|
@ -381,6 +411,12 @@ bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
|
|||
const size_t length) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
|
||||
uint16_t format = 0;
|
||||
uint16_t offset_coverage = 0;
|
||||
uint16_t value_format1 = 0;
|
||||
|
@ -394,12 +430,12 @@ bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
|
|||
|
||||
if (format == 1) {
|
||||
if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2,
|
||||
font->maxp->num_glyphs)) {
|
||||
maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse pair pos format 1");
|
||||
}
|
||||
} else if (format == 2) {
|
||||
if (!ParsePairPosFormat2(font, data, length, value_format1, value_format2,
|
||||
font->maxp->num_glyphs)) {
|
||||
maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse pair format 2");
|
||||
}
|
||||
} else {
|
||||
|
@ -411,7 +447,7 @@ bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
|
|||
}
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
||||
length - offset_coverage,
|
||||
font->maxp->num_glyphs)) {
|
||||
maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse coverage table");
|
||||
}
|
||||
|
||||
|
@ -424,6 +460,12 @@ bool ParseCursiveAttachment(const ots::Font *font, const uint8_t *data,
|
|||
const size_t length) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
|
||||
uint16_t format = 0;
|
||||
uint16_t offset_coverage = 0;
|
||||
uint16_t entry_exit_count = 0;
|
||||
|
@ -478,7 +520,7 @@ bool ParseCursiveAttachment(const ots::Font *font, const uint8_t *data,
|
|||
}
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
||||
length - offset_coverage,
|
||||
font->maxp->num_glyphs)) {
|
||||
maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment");
|
||||
}
|
||||
|
||||
|
@ -552,6 +594,12 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font,
|
|||
const GPOS_TYPE type) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
|
||||
uint16_t format = 0;
|
||||
uint16_t offset_coverage1 = 0;
|
||||
uint16_t offset_coverage2 = 0;
|
||||
|
@ -580,7 +628,7 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage1,
|
||||
length - offset_coverage1,
|
||||
font->maxp->num_glyphs)) {
|
||||
maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse converge 1 table");
|
||||
}
|
||||
if (offset_coverage2 < header_end || offset_coverage2 >= length) {
|
||||
|
@ -588,7 +636,7 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage2,
|
||||
length - offset_coverage2,
|
||||
font->maxp->num_glyphs)) {
|
||||
maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse coverage table 2");
|
||||
}
|
||||
|
||||
|
@ -652,17 +700,37 @@ bool ParseMarkToMarkAttachment(const ots::Font *font,
|
|||
// Contextual Positioning Subtables
|
||||
bool ParseContextPositioning(const ots::Font *font,
|
||||
const uint8_t *data, const size_t length) {
|
||||
return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs,
|
||||
font->gpos->num_lookups);
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
ots::OpenTypeGPOS *gpos = static_cast<ots::OpenTypeGPOS*>(
|
||||
font->GetTypedTable(OTS_TAG_GPOS));
|
||||
if (!gpos) {
|
||||
return OTS_FAILURE_MSG("Internal error!");
|
||||
}
|
||||
return ots::ParseContextSubtable(font, data, length, maxp->num_glyphs,
|
||||
gpos->num_lookups);
|
||||
}
|
||||
|
||||
// Lookup Type 8:
|
||||
// Chaining Contexual Positioning Subtable
|
||||
bool ParseChainedContextPositioning(const ots::Font *font,
|
||||
const uint8_t *data, const size_t length) {
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
ots::OpenTypeGPOS *gpos = static_cast<ots::OpenTypeGPOS*>(
|
||||
font->GetTypedTable(OTS_TAG_GPOS));
|
||||
if (!gpos) {
|
||||
return OTS_FAILURE_MSG("Internal error!");
|
||||
}
|
||||
return ots::ParseChainingContextSubtable(font, data, length,
|
||||
font->maxp->num_glyphs,
|
||||
font->gpos->num_lookups);
|
||||
maxp->num_glyphs,
|
||||
gpos->num_lookups);
|
||||
}
|
||||
|
||||
// Lookup Type 9:
|
||||
|
@ -677,141 +745,98 @@ bool ParseExtensionPositioning(const ots::Font *font,
|
|||
|
||||
namespace ots {
|
||||
|
||||
// As far as I checked, following fonts contain invalid GPOS table and
|
||||
// OTS will drop their GPOS table.
|
||||
//
|
||||
// # invalid delta format in device table
|
||||
// samanata.ttf
|
||||
//
|
||||
// # bad size range in device table
|
||||
// Sarai_07.ttf
|
||||
//
|
||||
// # bad offset to PairSetTable
|
||||
// chandas1-2.ttf
|
||||
//
|
||||
// # bad offset to FeatureTable
|
||||
// glrso12.ttf
|
||||
// gllr12.ttf
|
||||
// glbo12.ttf
|
||||
// glb12.ttf
|
||||
// glro12.ttf
|
||||
// glbso12.ttf
|
||||
// glrc12.ttf
|
||||
// glrsc12.ttf
|
||||
// glbs12.ttf
|
||||
// glrs12.ttf
|
||||
// glr12.ttf
|
||||
//
|
||||
// # ScriptRecords aren't sorted by tag
|
||||
// Garogier_unhinted.otf
|
||||
//
|
||||
// # bad start coverage index in CoverageFormat2
|
||||
// AndBasR.ttf
|
||||
// CharisSILB.ttf
|
||||
// CharisSILBI.ttf
|
||||
// CharisSILI.ttf
|
||||
// CharisSILR.ttf
|
||||
// DoulosSILR.ttf
|
||||
// GenBasBI.ttf
|
||||
// GenBasI.ttf
|
||||
// GenBkBasI.ttf
|
||||
// GenBkBasB.ttf
|
||||
// GenBkBasR.ttf
|
||||
// Padauk-Bold.ttf
|
||||
// Padauk.ttf
|
||||
//
|
||||
// # Contour point indexes aren't sorted
|
||||
// Arial Unicode.ttf
|
||||
|
||||
bool ots_gpos_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
// Parsing GPOS table requires num_glyphs which is contained in maxp table.
|
||||
if (!font->maxp) {
|
||||
return OTS_FAILURE_MSG("missing maxp table needed in GPOS");
|
||||
}
|
||||
|
||||
bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
|
||||
Font *font = GetFont();
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeGPOS *gpos = new OpenTypeGPOS;
|
||||
font->gpos = gpos;
|
||||
|
||||
uint32_t version = 0;
|
||||
uint16_t version_major = 0, version_minor = 0;
|
||||
uint16_t offset_script_list = 0;
|
||||
uint16_t offset_feature_list = 0;
|
||||
uint16_t offset_lookup_list = 0;
|
||||
if (!table.ReadU32(&version) ||
|
||||
uint32_t offset_feature_variations = 0;
|
||||
if (!table.ReadU16(&version_major) ||
|
||||
!table.ReadU16(&version_minor) ||
|
||||
!table.ReadU16(&offset_script_list) ||
|
||||
!table.ReadU16(&offset_feature_list) ||
|
||||
!table.ReadU16(&offset_lookup_list)) {
|
||||
return OTS_FAILURE_MSG("Incomplete table");
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
|
||||
if (version != 0x00010000) {
|
||||
return OTS_FAILURE_MSG("Bad version");
|
||||
if (version_major != 1 || version_minor > 1) {
|
||||
return Error("Bad version");
|
||||
}
|
||||
|
||||
if (version_minor > 0) {
|
||||
if (!table.ReadU32(&offset_feature_variations)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
}
|
||||
|
||||
const size_t header_size =
|
||||
(version_minor == 0) ? kGposHeaderSize_1_0 : kGposHeaderSize_1_1;
|
||||
|
||||
if (offset_lookup_list) {
|
||||
if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
|
||||
return OTS_FAILURE_MSG("Bad lookup list offset in table header");
|
||||
if (offset_lookup_list < header_size || offset_lookup_list >= length) {
|
||||
return Error("Bad lookup list offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseLookupListTable(font, data + offset_lookup_list,
|
||||
length - offset_lookup_list,
|
||||
&kGposLookupSubtableParser,
|
||||
&gpos->num_lookups)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse lookup list table");
|
||||
&this->num_lookups)) {
|
||||
return Error("Failed to parse lookup list table");
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t num_features = 0;
|
||||
if (offset_feature_list) {
|
||||
if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
|
||||
return OTS_FAILURE_MSG("Bad feature list offset in table header");
|
||||
if (offset_feature_list < header_size || offset_feature_list >= length) {
|
||||
return Error("Bad feature list offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseFeatureListTable(font, data + offset_feature_list,
|
||||
length - offset_feature_list, gpos->num_lookups,
|
||||
length - offset_feature_list, this->num_lookups,
|
||||
&num_features)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse feature list table");
|
||||
return Error("Failed to parse feature list table");
|
||||
}
|
||||
}
|
||||
|
||||
if (offset_script_list) {
|
||||
if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
|
||||
return OTS_FAILURE_MSG("Bad script list offset in table header");
|
||||
if (offset_script_list < header_size || offset_script_list >= length) {
|
||||
return Error("Bad script list offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseScriptListTable(font, data + offset_script_list,
|
||||
length - offset_script_list, num_features)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse script list table");
|
||||
return Error("Failed to parse script list table");
|
||||
}
|
||||
}
|
||||
|
||||
gpos->data = data;
|
||||
gpos->length = length;
|
||||
if (offset_feature_variations) {
|
||||
if (offset_feature_variations < header_size || offset_feature_variations >= length) {
|
||||
return Error("Bad feature variations offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
|
||||
length - offset_feature_variations,
|
||||
this->num_lookups)) {
|
||||
return Error("Failed to parse feature variations table");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_gpos_should_serialise(Font *font) {
|
||||
return font->gpos != NULL && font->gpos->data != NULL;
|
||||
}
|
||||
|
||||
bool ots_gpos_serialise(OTSStream *out, Font *font) {
|
||||
if (!out->Write(font->gpos->data, font->gpos->length)) {
|
||||
return OTS_FAILURE_MSG("Failed to write GPOS table");
|
||||
bool OpenTypeGPOS::Serialize(OTSStream *out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write GPOS table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_gpos_reuse(Font *font, Font *other) {
|
||||
font->gpos = other->gpos;
|
||||
font->gpos_reused = true;
|
||||
}
|
||||
|
||||
void ots_gpos_free(Font *font) {
|
||||
delete font->gpos;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_GRAPHITE_H_
|
||||
#define OTS_GRAPHITE_H_
|
||||
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ots {
|
||||
|
||||
template<typename ParentType>
|
||||
class TablePart {
|
||||
public:
|
||||
TablePart(ParentType* parent) : parent(parent) { }
|
||||
virtual ~TablePart() { }
|
||||
virtual bool ParsePart(Buffer& table) = 0;
|
||||
virtual bool SerializePart(OTSStream* out) const = 0;
|
||||
protected:
|
||||
ParentType* parent;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool SerializeParts(const std::vector<T>& vec, OTSStream* out) {
|
||||
for (const T& part : vec) {
|
||||
if (!part.SerializePart(out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool SerializeParts(const std::vector<std::vector<T>>& vec, OTSStream* out) {
|
||||
for (const std::vector<T>& part : vec) {
|
||||
if (!SerializeParts(part, out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<uint8_t>& vec, OTSStream* out) {
|
||||
for (uint8_t part : vec) {
|
||||
if (!out->WriteU8(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<uint16_t>& vec, OTSStream* out) {
|
||||
for (uint16_t part : vec) {
|
||||
if (!out->WriteU16(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<int16_t>& vec, OTSStream* out) {
|
||||
for (int16_t part : vec) {
|
||||
if (!out->WriteS16(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<uint32_t>& vec, OTSStream* out) {
|
||||
for (uint32_t part : vec) {
|
||||
if (!out->WriteU32(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SerializeParts(const std::vector<int32_t>& vec, OTSStream* out) {
|
||||
for (int32_t part : vec) {
|
||||
if (!out->WriteS32(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t datasize(std::vector<T> vec) {
|
||||
return sizeof(T) * vec.size();
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_GRAPHITE_H_
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -17,8 +17,10 @@
|
|||
|
||||
namespace {
|
||||
|
||||
// The GSUB header size
|
||||
const size_t kGsubHeaderSize = 4 + 3 * 2;
|
||||
// The GSUB header size for table version 1.0
|
||||
const size_t kGsubHeaderSize_1_0 = 4 + 3 * 2;
|
||||
// GSUB header size v1.1
|
||||
const size_t kGsubHeaderSize_1_1 = 4 + 3 * 2 + 4;
|
||||
|
||||
enum GSUB_TYPE {
|
||||
GSUB_TYPE_SINGLE = 1,
|
||||
|
@ -82,7 +84,12 @@ bool ParseSingleSubstitution(const ots::Font *font,
|
|||
return OTS_FAILURE_MSG("Failed to read single subst table header");
|
||||
}
|
||||
|
||||
const uint16_t num_glyphs = font->maxp->num_glyphs;
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
const uint16_t num_glyphs = maxp->num_glyphs;
|
||||
if (format == 1) {
|
||||
// Parse SingleSubstFormat1
|
||||
int16_t delta_glyph_id = 0;
|
||||
|
@ -170,7 +177,12 @@ bool ParseMutipleSubstitution(const ots::Font *font,
|
|||
return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
|
||||
}
|
||||
|
||||
const uint16_t num_glyphs = font->maxp->num_glyphs;
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
const uint16_t num_glyphs = maxp->num_glyphs;
|
||||
const unsigned sequence_end = static_cast<unsigned>(6) +
|
||||
sequence_count * 2;
|
||||
if (sequence_end > std::numeric_limits<uint16_t>::max()) {
|
||||
|
@ -245,7 +257,12 @@ bool ParseAlternateSubstitution(const ots::Font *font,
|
|||
return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
|
||||
}
|
||||
|
||||
const uint16_t num_glyphs = font->maxp->num_glyphs;
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
const uint16_t num_glyphs = maxp->num_glyphs;
|
||||
const unsigned alternate_set_end = static_cast<unsigned>(6) +
|
||||
alternate_set_count * 2;
|
||||
if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
|
||||
|
@ -362,7 +379,12 @@ bool ParseLigatureSubstitution(const ots::Font *font,
|
|||
return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
|
||||
}
|
||||
|
||||
const uint16_t num_glyphs = font->maxp->num_glyphs;
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
const uint16_t num_glyphs = maxp->num_glyphs;
|
||||
const unsigned ligature_set_end = static_cast<unsigned>(6) +
|
||||
lig_set_count * 2;
|
||||
if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
|
||||
|
@ -398,8 +420,18 @@ bool ParseLigatureSubstitution(const ots::Font *font,
|
|||
// Contextual Substitution Subtable
|
||||
bool ParseContextSubstitution(const ots::Font *font,
|
||||
const uint8_t *data, const size_t length) {
|
||||
return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs,
|
||||
font->gsub->num_lookups);
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
ots::OpenTypeGSUB *gsub = static_cast<ots::OpenTypeGSUB*>(
|
||||
font->GetTypedTable(OTS_TAG_GSUB));
|
||||
if (!gsub) {
|
||||
return OTS_FAILURE_MSG("Internal error!");
|
||||
}
|
||||
return ots::ParseContextSubtable(font, data, length, maxp->num_glyphs,
|
||||
gsub->num_lookups);
|
||||
}
|
||||
|
||||
// Lookup Type 6:
|
||||
|
@ -407,9 +439,19 @@ bool ParseContextSubstitution(const ots::Font *font,
|
|||
bool ParseChainingContextSubstitution(const ots::Font *font,
|
||||
const uint8_t *data,
|
||||
const size_t length) {
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
ots::OpenTypeGSUB *gsub = static_cast<ots::OpenTypeGSUB*>(
|
||||
font->GetTypedTable(OTS_TAG_GSUB));
|
||||
if (!gsub) {
|
||||
return OTS_FAILURE_MSG("Internal error!");
|
||||
}
|
||||
return ots::ParseChainingContextSubtable(font, data, length,
|
||||
font->maxp->num_glyphs,
|
||||
font->gsub->num_lookups);
|
||||
maxp->num_glyphs,
|
||||
gsub->num_lookups);
|
||||
}
|
||||
|
||||
// Lookup Type 7:
|
||||
|
@ -434,7 +476,12 @@ bool ParseReverseChainingContextSingleSubstitution(
|
|||
return OTS_FAILURE_MSG("Failed to read reverse chaining header");
|
||||
}
|
||||
|
||||
const uint16_t num_glyphs = font->maxp->num_glyphs;
|
||||
ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
||||
font->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return OTS_FAILURE_MSG("Required maxp table missing");
|
||||
}
|
||||
const uint16_t num_glyphs = maxp->num_glyphs;
|
||||
|
||||
uint16_t backtrack_glyph_count = 0;
|
||||
if (!subtable.ReadU16(&backtrack_glyph_count)) {
|
||||
|
@ -530,145 +577,99 @@ bool ParseReverseChainingContextSingleSubstitution(
|
|||
|
||||
namespace ots {
|
||||
|
||||
// As far as I checked, following fonts contain invalid values in GSUB table.
|
||||
// OTS will drop their GSUB table.
|
||||
//
|
||||
// # too large substitute (value is 0xFFFF)
|
||||
// kaiu.ttf
|
||||
// mingliub2.ttf
|
||||
// mingliub1.ttf
|
||||
// mingliub0.ttf
|
||||
// GraublauWeb.otf
|
||||
// GraublauWebBold.otf
|
||||
//
|
||||
// # too large alternate (value is 0xFFFF)
|
||||
// ManchuFont.ttf
|
||||
//
|
||||
// # bad offset to lang sys table (NULL offset)
|
||||
// DejaVuMonoSansBold.ttf
|
||||
// DejaVuMonoSansBoldOblique.ttf
|
||||
// DejaVuMonoSansOblique.ttf
|
||||
// DejaVuSansMono-BoldOblique.ttf
|
||||
// DejaVuSansMono-Oblique.ttf
|
||||
// DejaVuSansMono-Bold.ttf
|
||||
//
|
||||
// # bad start coverage index
|
||||
// GenBasBI.ttf
|
||||
// GenBasI.ttf
|
||||
// AndBasR.ttf
|
||||
// GenBkBasI.ttf
|
||||
// CharisSILR.ttf
|
||||
// CharisSILBI.ttf
|
||||
// CharisSILI.ttf
|
||||
// CharisSILB.ttf
|
||||
// DoulosSILR.ttf
|
||||
// CharisSILBI.ttf
|
||||
// GenBkBasB.ttf
|
||||
// GenBkBasR.ttf
|
||||
// GenBkBasBI.ttf
|
||||
// GenBasB.ttf
|
||||
// GenBasR.ttf
|
||||
//
|
||||
// # glyph range is overlapping
|
||||
// KacstTitleL.ttf
|
||||
// KacstDecorative.ttf
|
||||
// KacstTitle.ttf
|
||||
// KacstArt.ttf
|
||||
// KacstPoster.ttf
|
||||
// KacstQurn.ttf
|
||||
// KacstDigital.ttf
|
||||
// KacstBook.ttf
|
||||
// KacstFarsi.ttf
|
||||
|
||||
bool ots_gsub_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
// Parsing gsub table requires |font->maxp->num_glyphs|
|
||||
if (!font->maxp) {
|
||||
return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
|
||||
}
|
||||
|
||||
bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
|
||||
// Parsing gsub table requires |maxp->num_glyphs|
|
||||
Font *font = GetFont();
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeGSUB *gsub = new OpenTypeGSUB;
|
||||
font->gsub = gsub;
|
||||
|
||||
uint32_t version = 0;
|
||||
uint16_t version_major = 0, version_minor = 0;
|
||||
uint16_t offset_script_list = 0;
|
||||
uint16_t offset_feature_list = 0;
|
||||
uint16_t offset_lookup_list = 0;
|
||||
if (!table.ReadU32(&version) ||
|
||||
uint32_t offset_feature_variations = 0;
|
||||
if (!table.ReadU16(&version_major) ||
|
||||
!table.ReadU16(&version_minor) ||
|
||||
!table.ReadU16(&offset_script_list) ||
|
||||
!table.ReadU16(&offset_feature_list) ||
|
||||
!table.ReadU16(&offset_lookup_list)) {
|
||||
return OTS_FAILURE_MSG("Incomplete table");
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
|
||||
if (version != 0x00010000) {
|
||||
return OTS_FAILURE_MSG("Bad version");
|
||||
if (version_major != 1 || version_minor > 1) {
|
||||
return Error("Bad version");
|
||||
}
|
||||
|
||||
if (version_minor > 0) {
|
||||
if (!table.ReadU32(&offset_feature_variations)) {
|
||||
return Error("Incomplete table");
|
||||
}
|
||||
}
|
||||
|
||||
const size_t header_size =
|
||||
(version_minor == 0) ? kGsubHeaderSize_1_0 : kGsubHeaderSize_1_1;
|
||||
|
||||
if (offset_lookup_list) {
|
||||
if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
|
||||
return OTS_FAILURE_MSG("Bad lookup list offset in table header");
|
||||
if (offset_lookup_list < header_size || offset_lookup_list >= length) {
|
||||
return Error("Bad lookup list offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseLookupListTable(font, data + offset_lookup_list,
|
||||
length - offset_lookup_list,
|
||||
&kGsubLookupSubtableParser,
|
||||
&gsub->num_lookups)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse lookup list table");
|
||||
&this->num_lookups)) {
|
||||
return Error("Failed to parse lookup list table");
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t num_features = 0;
|
||||
if (offset_feature_list) {
|
||||
if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
|
||||
return OTS_FAILURE_MSG("Bad feature list offset in table header");
|
||||
if (offset_feature_list < header_size || offset_feature_list >= length) {
|
||||
return Error("Bad feature list offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseFeatureListTable(font, data + offset_feature_list,
|
||||
length - offset_feature_list, gsub->num_lookups,
|
||||
length - offset_feature_list, this->num_lookups,
|
||||
&num_features)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse feature list table");
|
||||
return Error("Failed to parse feature list table");
|
||||
}
|
||||
}
|
||||
|
||||
if (offset_script_list) {
|
||||
if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
|
||||
return OTS_FAILURE_MSG("Bad script list offset in table header");
|
||||
if (offset_script_list < header_size || offset_script_list >= length) {
|
||||
return Error("Bad script list offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseScriptListTable(font, data + offset_script_list,
|
||||
length - offset_script_list, num_features)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse script list table");
|
||||
return Error("Failed to parse script list table");
|
||||
}
|
||||
}
|
||||
|
||||
gsub->data = data;
|
||||
gsub->length = length;
|
||||
if (offset_feature_variations) {
|
||||
if (offset_feature_variations < header_size || offset_feature_variations >= length) {
|
||||
return Error("Bad feature variations offset in table header");
|
||||
}
|
||||
|
||||
if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
|
||||
length - offset_feature_variations,
|
||||
this->num_lookups)) {
|
||||
return Error("Failed to parse feature variations table");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_gsub_should_serialise(Font *font) {
|
||||
return font->gsub != NULL && font->gsub->data != NULL;
|
||||
}
|
||||
|
||||
bool ots_gsub_serialise(OTSStream *out, Font *font) {
|
||||
if (!out->Write(font->gsub->data, font->gsub->length)) {
|
||||
return OTS_FAILURE_MSG("Failed to write GSUB table");
|
||||
bool OpenTypeGSUB::Serialize(OTSStream *out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write GSUB table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_gsub_reuse(Font *font, Font *other) {
|
||||
font->gsub = other->gsub;
|
||||
font->gsub_reused = true;
|
||||
}
|
||||
|
||||
void ots_gsub_free(Font *font) {
|
||||
delete font->gsub;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "gvar.h"
|
||||
|
||||
#include "fvar.h"
|
||||
#include "maxp.h"
|
||||
#include "variations.h"
|
||||
|
||||
#define TABLE_NAME "gvar"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeGVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static bool ParseSharedTuples(const Font* font, const uint8_t* data, size_t length,
|
||||
size_t sharedTupleCount, size_t axisCount) {
|
||||
Buffer subtable(data, length);
|
||||
for (unsigned i = 0; i < sharedTupleCount; i++) {
|
||||
for (unsigned j = 0; j < axisCount; j++) {
|
||||
int16_t coordinate;
|
||||
if (!subtable.ReadS16(&coordinate)) {
|
||||
return OTS_FAILURE_MSG("Failed to read shared tuple coordinate");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseGlyphVariationDataArray(const Font* font, const uint8_t* data, size_t length,
|
||||
uint16_t flags, size_t glyphCount, size_t axisCount,
|
||||
size_t sharedTupleCount,
|
||||
const uint8_t* glyphVariationData,
|
||||
size_t glyphVariationDataLength) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
bool glyphVariationDataOffsetsAreLong = (flags & 0x0001u);
|
||||
uint32_t prevOffset = 0;
|
||||
for (size_t i = 0; i < glyphCount + 1; i++) {
|
||||
uint32_t offset;
|
||||
if (glyphVariationDataOffsetsAreLong) {
|
||||
if (!subtable.ReadU32(&offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read GlyphVariationData offset");
|
||||
}
|
||||
} else {
|
||||
uint16_t halfOffset;
|
||||
if (!subtable.ReadU16(&halfOffset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read GlyphVariationData offset");
|
||||
}
|
||||
offset = halfOffset * 2;
|
||||
}
|
||||
|
||||
if (i > 0 && offset > prevOffset) {
|
||||
if (prevOffset > glyphVariationDataLength) {
|
||||
return OTS_FAILURE_MSG("Invalid GlyphVariationData offset");
|
||||
}
|
||||
if (!ParseVariationData(font, glyphVariationData + prevOffset,
|
||||
glyphVariationDataLength - prevOffset,
|
||||
axisCount, sharedTupleCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse GlyphVariationData");
|
||||
}
|
||||
}
|
||||
prevOffset = offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t axisCount;
|
||||
uint16_t sharedTupleCount;
|
||||
uint32_t sharedTuplesOffset;
|
||||
uint16_t glyphCount;
|
||||
uint16_t flags;
|
||||
uint32_t glyphVariationDataArrayOffset;
|
||||
|
||||
if (!table.ReadU16(&majorVersion) ||
|
||||
!table.ReadU16(&minorVersion) ||
|
||||
!table.ReadU16(&axisCount) ||
|
||||
!table.ReadU16(&sharedTupleCount) ||
|
||||
!table.ReadU32(&sharedTuplesOffset) ||
|
||||
!table.ReadU16(&glyphCount) ||
|
||||
!table.ReadU16(&flags) ||
|
||||
!table.ReadU32(&glyphVariationDataArrayOffset)) {
|
||||
return DropVariations("Failed to read table header");
|
||||
}
|
||||
if (majorVersion != 1) {
|
||||
return DropVariations("Unknown table version");
|
||||
}
|
||||
|
||||
// check axisCount == fvar->axisCount
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return DropVariations("Required fvar table is missing");
|
||||
}
|
||||
if (axisCount != fvar->AxisCount()) {
|
||||
return DropVariations("Axis count mismatch");
|
||||
}
|
||||
|
||||
// check glyphCount == maxp->num_glyphs
|
||||
OpenTypeMAXP* maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return DropVariations("Required maxp table is missing");
|
||||
}
|
||||
if (glyphCount != maxp->num_glyphs) {
|
||||
return DropVariations("Glyph count mismatch");
|
||||
}
|
||||
|
||||
if (sharedTupleCount > 0) {
|
||||
if (sharedTuplesOffset < table.offset() || sharedTuplesOffset > length) {
|
||||
return DropVariations("Invalid sharedTuplesOffset");
|
||||
}
|
||||
if (!ParseSharedTuples(GetFont(),
|
||||
data + sharedTuplesOffset, length - sharedTuplesOffset,
|
||||
sharedTupleCount, axisCount)) {
|
||||
return DropVariations("Failed to parse shared tuples");
|
||||
}
|
||||
}
|
||||
|
||||
if (glyphVariationDataArrayOffset) {
|
||||
if (glyphVariationDataArrayOffset > length) {
|
||||
return DropVariations("Invalid glyphVariationDataArrayOffset");
|
||||
}
|
||||
if (!ParseGlyphVariationDataArray(GetFont(),
|
||||
data + table.offset(), length - table.offset(),
|
||||
flags, glyphCount, axisCount, sharedTupleCount,
|
||||
data + glyphVariationDataArrayOffset,
|
||||
length - glyphVariationDataArrayOffset)) {
|
||||
return DropVariations("Failed to read glyph variation data array");
|
||||
}
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeGVAR::Serialize(OTSStream* out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write gvar table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
|
@ -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_
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,139 +9,112 @@
|
|||
// hdmx - Horizontal Device Metrics
|
||||
// http://www.microsoft.com/typography/otspec/hdmx.htm
|
||||
|
||||
#define TABLE_NAME "hdmx"
|
||||
|
||||
#define DROP_THIS_TABLE(...) \
|
||||
do { \
|
||||
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
|
||||
OTS_FAILURE_MSG("Table discarded"); \
|
||||
delete font->hdmx; \
|
||||
font->hdmx = 0; \
|
||||
} while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_hdmx_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeHDMX::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
font->hdmx = new OpenTypeHDMX;
|
||||
OpenTypeHDMX * const hdmx = font->hdmx;
|
||||
|
||||
if (!font->head || !font->maxp) {
|
||||
return OTS_FAILURE_MSG("Missing maxp or head tables in font, needed by hdmx");
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_HEAD));
|
||||
if (!head || !maxp) {
|
||||
return Error("Missing maxp or head tables in font, needed by hdmx");
|
||||
}
|
||||
|
||||
if ((font->head->flags & 0x14) == 0) {
|
||||
// http://www.microsoft.com/typography/otspec/recom.htm
|
||||
DROP_THIS_TABLE("the table should not be present when bit 2 and 4 of the "
|
||||
"head->flags are not set");
|
||||
return true;
|
||||
if ((head->flags & 0x14) == 0) {
|
||||
// http://www.microsoft.com/typography/otspec/recom.htm#hdmx
|
||||
return Drop("the table should not be present when bit 2 and 4 of the "
|
||||
"head->flags are not set");
|
||||
}
|
||||
|
||||
int16_t num_recs;
|
||||
if (!table.ReadU16(&hdmx->version) ||
|
||||
if (!table.ReadU16(&this->version) ||
|
||||
!table.ReadS16(&num_recs) ||
|
||||
!table.ReadS32(&hdmx->size_device_record)) {
|
||||
return OTS_FAILURE_MSG("Failed to read hdmx header");
|
||||
!table.ReadS32(&this->size_device_record)) {
|
||||
return Error("Failed to read table header");
|
||||
}
|
||||
if (hdmx->version != 0) {
|
||||
DROP_THIS_TABLE("bad version: %u", hdmx->version);
|
||||
return true;
|
||||
if (this->version != 0) {
|
||||
return Drop("Unsupported version: %u", this->version);
|
||||
}
|
||||
if (num_recs <= 0) {
|
||||
DROP_THIS_TABLE("bad num_recs: %d", num_recs);
|
||||
return true;
|
||||
return Drop("Bad numRecords: %d", num_recs);
|
||||
}
|
||||
const int32_t actual_size_device_record = font->maxp->num_glyphs + 2;
|
||||
if (hdmx->size_device_record < actual_size_device_record) {
|
||||
DROP_THIS_TABLE("bad hdmx->size_device_record: %d", hdmx->size_device_record);
|
||||
return true;
|
||||
const int32_t actual_size_device_record = maxp->num_glyphs + 2;
|
||||
if (this->size_device_record < actual_size_device_record) {
|
||||
return Drop("Bad sizeDeviceRecord: %d", this->size_device_record);
|
||||
}
|
||||
|
||||
hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
|
||||
if (hdmx->pad_len > 3) {
|
||||
return OTS_FAILURE_MSG("Bad padding %d", hdmx->pad_len);
|
||||
this->pad_len = this->size_device_record - actual_size_device_record;
|
||||
if (this->pad_len > 3) {
|
||||
return Error("Bad DeviceRecord padding %d", this->pad_len);
|
||||
}
|
||||
|
||||
uint8_t last_pixel_size = 0;
|
||||
hdmx->records.reserve(num_recs);
|
||||
this->records.reserve(num_recs);
|
||||
for (int i = 0; i < num_recs; ++i) {
|
||||
OpenTypeHDMXDeviceRecord rec;
|
||||
|
||||
if (!table.ReadU8(&rec.pixel_size) ||
|
||||
!table.ReadU8(&rec.max_width)) {
|
||||
return OTS_FAILURE_MSG("Failed to read hdmx record %d", i);
|
||||
return Error("Failed to read DeviceRecord %d", i);
|
||||
}
|
||||
if ((i != 0) &&
|
||||
(rec.pixel_size <= last_pixel_size)) {
|
||||
DROP_THIS_TABLE("records are not sorted");
|
||||
return true;
|
||||
return Drop("DeviceRecord's are not sorted");
|
||||
}
|
||||
last_pixel_size = rec.pixel_size;
|
||||
|
||||
rec.widths.reserve(font->maxp->num_glyphs);
|
||||
for (unsigned j = 0; j < font->maxp->num_glyphs; ++j) {
|
||||
rec.widths.reserve(maxp->num_glyphs);
|
||||
for (unsigned j = 0; j < maxp->num_glyphs; ++j) {
|
||||
uint8_t width;
|
||||
if (!table.ReadU8(&width)) {
|
||||
return OTS_FAILURE_MSG("Failed to read glyph width %d in record %d", j, i);
|
||||
return Error("Failed to read glyph width %d in DeviceRecord %d", j, i);
|
||||
}
|
||||
rec.widths.push_back(width);
|
||||
}
|
||||
|
||||
if ((hdmx->pad_len > 0) &&
|
||||
!table.Skip(hdmx->pad_len)) {
|
||||
return OTS_FAILURE_MSG("Failed to skip padding %d", hdmx->pad_len);
|
||||
if ((this->pad_len > 0) &&
|
||||
!table.Skip(this->pad_len)) {
|
||||
return Error("DeviceRecord %d should be padded by %d", i, this->pad_len);
|
||||
}
|
||||
|
||||
hdmx->records.push_back(rec);
|
||||
this->records.push_back(rec);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_hdmx_should_serialise(Font *font) {
|
||||
if (!font->hdmx) return false;
|
||||
if (!font->glyf) return false; // this table is not for CFF fonts.
|
||||
return true;
|
||||
bool OpenTypeHDMX::ShouldSerialize() {
|
||||
return Table::ShouldSerialize() &&
|
||||
// this table is not for CFF fonts.
|
||||
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
|
||||
}
|
||||
|
||||
bool ots_hdmx_serialise(OTSStream *out, Font *font) {
|
||||
OpenTypeHDMX * const hdmx = font->hdmx;
|
||||
|
||||
const int16_t num_recs = static_cast<int16_t>(hdmx->records.size());
|
||||
if (hdmx->records.size() >
|
||||
bool OpenTypeHDMX::Serialize(OTSStream *out) {
|
||||
const int16_t num_recs = static_cast<int16_t>(this->records.size());
|
||||
if (this->records.size() >
|
||||
static_cast<size_t>(std::numeric_limits<int16_t>::max()) ||
|
||||
!out->WriteU16(hdmx->version) ||
|
||||
!out->WriteU16(this->version) ||
|
||||
!out->WriteS16(num_recs) ||
|
||||
!out->WriteS32(hdmx->size_device_record)) {
|
||||
return OTS_FAILURE_MSG("Failed to write hdmx header");
|
||||
!out->WriteS32(this->size_device_record)) {
|
||||
return Error("Failed to write table header");
|
||||
}
|
||||
|
||||
for (int16_t i = 0; i < num_recs; ++i) {
|
||||
const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
|
||||
const OpenTypeHDMXDeviceRecord& rec = this->records[i];
|
||||
if (!out->Write(&rec.pixel_size, 1) ||
|
||||
!out->Write(&rec.max_width, 1) ||
|
||||
!out->Write(&rec.widths[0], rec.widths.size())) {
|
||||
return OTS_FAILURE_MSG("Failed to write hdmx record %d", i);
|
||||
return Error("Failed to write DeviceRecord %d", i);
|
||||
}
|
||||
if ((hdmx->pad_len > 0) &&
|
||||
!out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
|
||||
return OTS_FAILURE_MSG("Failed to write hdmx padding of length %d", hdmx->pad_len);
|
||||
if ((this->pad_len > 0) &&
|
||||
!out->Write((const uint8_t *)"\x00\x00\x00", this->pad_len)) {
|
||||
return Error("Failed to write padding of length %d", this->pad_len);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_hdmx_reuse(Font *font, Font *other) {
|
||||
font->hdmx = other->hdmx;
|
||||
font->hdmx_reused = true;
|
||||
}
|
||||
|
||||
void ots_hdmx_free(Font *font) {
|
||||
delete font->hdmx;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
#undef DROP_THIS_TABLE
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -17,7 +17,16 @@ struct OpenTypeHDMXDeviceRecord {
|
|||
std::vector<uint8_t> widths;
|
||||
};
|
||||
|
||||
struct OpenTypeHDMX {
|
||||
class OpenTypeHDMX : public Table {
|
||||
public:
|
||||
explicit OpenTypeHDMX(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool ShouldSerialize();
|
||||
|
||||
private:
|
||||
uint16_t version;
|
||||
int32_t size_device_record;
|
||||
int32_t pad_len;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -7,75 +7,61 @@
|
|||
// kern - Kerning
|
||||
// http://www.microsoft.com/typography/otspec/kern.htm
|
||||
|
||||
#define TABLE_NAME "kern"
|
||||
|
||||
#define DROP_THIS_TABLE(msg_) \
|
||||
do { \
|
||||
OTS_FAILURE_MSG(msg_ ", table discarded"); \
|
||||
delete font->kern; \
|
||||
font->kern = 0; \
|
||||
} while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeKERN::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeKERN *kern = new OpenTypeKERN;
|
||||
font->kern = kern;
|
||||
|
||||
uint16_t num_tables = 0;
|
||||
if (!table.ReadU16(&kern->version) ||
|
||||
if (!table.ReadU16(&this->version) ||
|
||||
!table.ReadU16(&num_tables)) {
|
||||
return OTS_FAILURE_MSG("Failed to read kern header");
|
||||
return Error("Failed to read table header");
|
||||
}
|
||||
|
||||
if (kern->version > 0) {
|
||||
DROP_THIS_TABLE("bad table version");
|
||||
return true;
|
||||
if (this->version > 0) {
|
||||
return Drop("Unsupported table version: %d", this->version);
|
||||
}
|
||||
|
||||
if (num_tables == 0) {
|
||||
DROP_THIS_TABLE("num_tables is zero");
|
||||
return true;
|
||||
return Drop("nTables is zero");
|
||||
}
|
||||
|
||||
kern->subtables.reserve(num_tables);
|
||||
this->subtables.reserve(num_tables);
|
||||
for (unsigned i = 0; i < num_tables; ++i) {
|
||||
OpenTypeKERNFormat0 subtable;
|
||||
uint16_t sub_length = 0;
|
||||
|
||||
if (!table.ReadU16(&subtable.version) ||
|
||||
!table.ReadU16(&sub_length)) {
|
||||
return OTS_FAILURE_MSG("Failed to read kern subtable %d header", i);
|
||||
return Error("Failed to read subtable %d header", i);
|
||||
}
|
||||
|
||||
if (subtable.version > 0) {
|
||||
OTS_WARNING("Bad subtable version: %d", subtable.version);
|
||||
Warning("Ignoring subtable %d with unsupported version: %d",
|
||||
i, subtable.version);
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t current_offset = table.offset();
|
||||
if (current_offset - 4 + sub_length > length) {
|
||||
return OTS_FAILURE_MSG("Bad kern subtable %d offset %ld", i, current_offset);
|
||||
return Error("Bad subtable %d offset %ld", i, current_offset);
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&subtable.coverage)) {
|
||||
return OTS_FAILURE_MSG("Cailed to read kern subtable %d coverage", i);
|
||||
return Error("Failed to read subtable %d coverage", i);
|
||||
}
|
||||
|
||||
if (!(subtable.coverage & 0x1)) {
|
||||
OTS_WARNING(
|
||||
Warning(
|
||||
"We don't support vertical data as the renderer doesn't support it.");
|
||||
continue;
|
||||
}
|
||||
if (subtable.coverage & 0xF0) {
|
||||
DROP_THIS_TABLE("Reserved fields should zero-filled");
|
||||
return true;
|
||||
return Drop("Reserved fields should be zero");
|
||||
}
|
||||
const uint32_t format = (subtable.coverage & 0xFF00) >> 8;
|
||||
if (format != 0) {
|
||||
OTS_WARNING("Format %d is not supported.", format);
|
||||
Warning("Ignoring subtable %d with unsupported format: %d", i, format);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -85,12 +71,11 @@ bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
!table.ReadU16(&subtable.search_range) ||
|
||||
!table.ReadU16(&subtable.entry_selector) ||
|
||||
!table.ReadU16(&subtable.range_shift)) {
|
||||
return OTS_FAILURE_MSG("Failed to read kern subtable %d format 0 fields", i);
|
||||
return Error("Failed to read subtable %d format 0 fields", i);
|
||||
}
|
||||
|
||||
if (!num_pairs) {
|
||||
DROP_THIS_TABLE("Zero length subtable is found");
|
||||
return true;
|
||||
return Drop("Zero length subtable is found");
|
||||
}
|
||||
|
||||
// Sanity checks for search_range, entry_selector, and range_shift. See the
|
||||
|
@ -98,8 +83,7 @@ bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
const size_t kFormat0PairSize = 6; // left, right, and value. 2 bytes each.
|
||||
if (num_pairs > (65536 / kFormat0PairSize)) {
|
||||
// Some fonts (e.g. calibri.ttf, pykes_peak_zero.ttf) have pairs >= 10923.
|
||||
DROP_THIS_TABLE("Too large subtable");
|
||||
return true;
|
||||
return Drop("Too large subtable");
|
||||
}
|
||||
unsigned max_pow2 = 0;
|
||||
while (1u << (max_pow2 + 1) <= num_pairs) {
|
||||
|
@ -107,16 +91,16 @@ bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
}
|
||||
const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize;
|
||||
if (subtable.search_range != expected_search_range) {
|
||||
OTS_WARNING("bad search range");
|
||||
Warning("bad search range");
|
||||
subtable.search_range = expected_search_range;
|
||||
}
|
||||
if (subtable.entry_selector != max_pow2) {
|
||||
return OTS_FAILURE_MSG("Bad subtable %d entry selector %d", i, subtable.entry_selector);
|
||||
return Error("Bad subtable %d entry selector %d", i, subtable.entry_selector);
|
||||
}
|
||||
const uint16_t expected_range_shift =
|
||||
kFormat0PairSize * num_pairs - subtable.search_range;
|
||||
if (subtable.range_shift != expected_range_shift) {
|
||||
OTS_WARNING("bad range shift");
|
||||
Warning("bad range shift");
|
||||
subtable.range_shift = expected_range_shift;
|
||||
}
|
||||
|
||||
|
@ -128,64 +112,55 @@ bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
if (!table.ReadU16(&kerning_pair.left) ||
|
||||
!table.ReadU16(&kerning_pair.right) ||
|
||||
!table.ReadS16(&kerning_pair.value)) {
|
||||
return OTS_FAILURE_MSG("Failed to read subtable %d kerning pair %d", i, j);
|
||||
return Error("Failed to read subtable %d kerning pair %d", i, j);
|
||||
}
|
||||
const uint32_t current_pair
|
||||
= (kerning_pair.left << 16) + kerning_pair.right;
|
||||
if (j != 0 && current_pair <= last_pair) {
|
||||
// Many free fonts don't follow this rule, so we don't call OTS_FAILURE
|
||||
// in order to support these fonts.
|
||||
DROP_THIS_TABLE("Kerning pairs are not sorted");
|
||||
return true;
|
||||
return Drop("Kerning pairs are not sorted");
|
||||
}
|
||||
last_pair = current_pair;
|
||||
subtable.pairs.push_back(kerning_pair);
|
||||
}
|
||||
|
||||
kern->subtables.push_back(subtable);
|
||||
this->subtables.push_back(subtable);
|
||||
}
|
||||
|
||||
if (!kern->subtables.size()) {
|
||||
DROP_THIS_TABLE("All subtables are removed");
|
||||
return true;
|
||||
if (!this->subtables.size()) {
|
||||
return Drop("All subtables were removed");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_kern_should_serialise(Font *font) {
|
||||
if (!font->glyf) return false; // this table is not for CFF fonts.
|
||||
return font->kern != NULL;
|
||||
}
|
||||
|
||||
bool ots_kern_serialise(OTSStream *out, Font *font) {
|
||||
const OpenTypeKERN *kern = font->kern;
|
||||
|
||||
const uint16_t num_subtables = static_cast<uint16_t>(kern->subtables.size());
|
||||
if (num_subtables != kern->subtables.size() ||
|
||||
!out->WriteU16(kern->version) ||
|
||||
bool OpenTypeKERN::Serialize(OTSStream *out) {
|
||||
const uint16_t num_subtables = static_cast<uint16_t>(this->subtables.size());
|
||||
if (num_subtables != this->subtables.size() ||
|
||||
!out->WriteU16(this->version) ||
|
||||
!out->WriteU16(num_subtables)) {
|
||||
return OTS_FAILURE_MSG("Can't write kern table header");
|
||||
return Error("Failed to write kern table header");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < num_subtables; ++i) {
|
||||
const size_t length = 14 + (6 * kern->subtables[i].pairs.size());
|
||||
const size_t length = 14 + (6 * this->subtables[i].pairs.size());
|
||||
if (length > std::numeric_limits<uint16_t>::max() ||
|
||||
!out->WriteU16(kern->subtables[i].version) ||
|
||||
!out->WriteU16(this->subtables[i].version) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(length)) ||
|
||||
!out->WriteU16(kern->subtables[i].coverage) ||
|
||||
!out->WriteU16(this->subtables[i].coverage) ||
|
||||
!out->WriteU16(
|
||||
static_cast<uint16_t>(kern->subtables[i].pairs.size())) ||
|
||||
!out->WriteU16(kern->subtables[i].search_range) ||
|
||||
!out->WriteU16(kern->subtables[i].entry_selector) ||
|
||||
!out->WriteU16(kern->subtables[i].range_shift)) {
|
||||
return OTS_FAILURE_MSG("Failed to write kern subtable %d", i);
|
||||
static_cast<uint16_t>(this->subtables[i].pairs.size())) ||
|
||||
!out->WriteU16(this->subtables[i].search_range) ||
|
||||
!out->WriteU16(this->subtables[i].entry_selector) ||
|
||||
!out->WriteU16(this->subtables[i].range_shift)) {
|
||||
return Error("Failed to write kern subtable %d", i);
|
||||
}
|
||||
for (unsigned j = 0; j < kern->subtables[i].pairs.size(); ++j) {
|
||||
if (!out->WriteU16(kern->subtables[i].pairs[j].left) ||
|
||||
!out->WriteU16(kern->subtables[i].pairs[j].right) ||
|
||||
!out->WriteS16(kern->subtables[i].pairs[j].value)) {
|
||||
return OTS_FAILURE_MSG("Failed to write kern pair %d for subtable %d", j, i);
|
||||
for (unsigned j = 0; j < this->subtables[i].pairs.size(); ++j) {
|
||||
if (!out->WriteU16(this->subtables[i].pairs[j].left) ||
|
||||
!out->WriteU16(this->subtables[i].pairs[j].right) ||
|
||||
!out->WriteS16(this->subtables[i].pairs[j].value)) {
|
||||
return Error("Failed to write kern pair %d for subtable %d", j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,16 +168,10 @@ bool ots_kern_serialise(OTSStream *out, Font *font) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ots_kern_reuse(Font *font, Font *other) {
|
||||
font->kern = other->kern;
|
||||
font->kern_reused = true;
|
||||
}
|
||||
|
||||
void ots_kern_free(Font *font) {
|
||||
delete font->kern;
|
||||
bool OpenTypeKERN::ShouldSerialize() {
|
||||
return Table::ShouldSerialize() &&
|
||||
// this table is not for CFF fonts.
|
||||
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
#undef DROP_THIS_TABLE
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -30,7 +30,16 @@ struct OpenTypeKERNFormat0 {
|
|||
// WebFonts unlikely use it. I've checked thousands of proprietary fonts and
|
||||
// free fonts, and found no font uses the format.
|
||||
|
||||
struct OpenTypeKERN {
|
||||
class OpenTypeKERN : public Table {
|
||||
public:
|
||||
explicit OpenTypeKERN(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool ShouldSerialize();
|
||||
|
||||
private:
|
||||
uint16_t version;
|
||||
std::vector<OpenTypeKERNFormat0> subtables;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
|||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "fvar.h"
|
||||
#include "gdef.h"
|
||||
|
||||
// OpenType Layout Common Table Formats
|
||||
|
@ -22,14 +23,11 @@ const uint32_t kScriptTableTagDflt = 0x44464c54;
|
|||
const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF;
|
||||
// The lookup flag bit which indicates existence of MarkFilteringSet.
|
||||
const uint16_t kUseMarkFilteringSetBit = 0x0010;
|
||||
// The lookup flags which require GDEF table.
|
||||
const uint16_t kGdefRequiredFlags = 0x0002 | 0x0004 | 0x0008;
|
||||
// The mask for MarkAttachmentType.
|
||||
const uint16_t kMarkAttachmentTypeMask = 0xFF00;
|
||||
// The maximum type number of format for device tables.
|
||||
const uint16_t kMaxDeltaFormatType = 3;
|
||||
// The maximum number of class value.
|
||||
const uint16_t kMaxClassDefValue = 0xFFFF;
|
||||
// In variation fonts, Device Tables are replaced by VariationIndex tables,
|
||||
// indicated by this flag in the deltaFormat field.
|
||||
const uint16_t kVariationIndex = 0x8000;
|
||||
|
||||
struct ScriptRecord {
|
||||
uint32_t tag;
|
||||
|
@ -94,15 +92,12 @@ bool ParseScriptTable(const ots::Font *font,
|
|||
}
|
||||
|
||||
// The spec requires a script table for 'DFLT' tag must contain non-NULL
|
||||
// |offset_default_lang_sys| and |lang_sys_count| == 0
|
||||
// |offset_default_lang_sys|.
|
||||
// https://www.microsoft.com/typography/otspec/chapter2.htm
|
||||
if (tag == kScriptTableTagDflt) {
|
||||
if (offset_default_lang_sys == 0) {
|
||||
return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. DefaultLangSys is NULL");
|
||||
}
|
||||
if (lang_sys_count != 0) {
|
||||
return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. LangSysCount is not zero: %d", lang_sys_count);
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned lang_sys_record_end =
|
||||
|
@ -197,27 +192,7 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data,
|
|||
return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
|
||||
}
|
||||
|
||||
// Check lookup flags.
|
||||
if ((lookup_flag & kGdefRequiredFlags) &&
|
||||
(!font->gdef || !font->gdef->has_glyph_class_def)) {
|
||||
return OTS_FAILURE_MSG("Lookup flags require GDEF table, "
|
||||
"but none was found: %d", lookup_flag);
|
||||
}
|
||||
if ((lookup_flag & kMarkAttachmentTypeMask) &&
|
||||
(!font->gdef || !font->gdef->has_mark_attachment_class_def)) {
|
||||
return OTS_FAILURE_MSG("Lookup flags ask for mark attachment, "
|
||||
"but there is no GDEF table or it has no "
|
||||
"mark attachment classes: %d", lookup_flag);
|
||||
}
|
||||
bool use_mark_filtering_set = false;
|
||||
if (lookup_flag & kUseMarkFilteringSetBit) {
|
||||
if (!font->gdef || !font->gdef->has_mark_glyph_sets_def) {
|
||||
return OTS_FAILURE_MSG("Lookup flags ask for mark filtering, "
|
||||
"but there is no GDEF table or it has no "
|
||||
"mark filtering sets: %d", lookup_flag);
|
||||
}
|
||||
use_mark_filtering_set = true;
|
||||
}
|
||||
bool use_mark_filtering_set = lookup_flag & kUseMarkFilteringSetBit;
|
||||
|
||||
std::vector<uint16_t> subtables;
|
||||
subtables.reserve(subtable_count);
|
||||
|
@ -248,8 +223,12 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data,
|
|||
if (!subtable.ReadU16(&mark_filtering_set)) {
|
||||
return OTS_FAILURE_MSG("Failed to read mark filtering set");
|
||||
}
|
||||
if (font->gdef->num_mark_glyph_sets == 0 ||
|
||||
mark_filtering_set >= font->gdef->num_mark_glyph_sets) {
|
||||
|
||||
ots::OpenTypeGDEF *gdef = static_cast<ots::OpenTypeGDEF*>(
|
||||
font->GetTypedTable(OTS_TAG_GDEF));
|
||||
|
||||
if (gdef && (gdef->num_mark_glyph_sets == 0 ||
|
||||
mark_filtering_set >= gdef->num_mark_glyph_sets)) {
|
||||
return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
|
||||
}
|
||||
}
|
||||
|
@ -311,15 +290,15 @@ bool ParseClassDefFormat2(const ots::Font *font,
|
|||
|
||||
// Skip format field.
|
||||
if (!subtable.Skip(2)) {
|
||||
return OTS_FAILURE_MSG("Failed to skip format of class defintion header");
|
||||
return OTS_FAILURE_MSG("Failed to read class definition format");
|
||||
}
|
||||
|
||||
uint16_t range_count = 0;
|
||||
if (!subtable.ReadU16(&range_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read range count in class definition");
|
||||
return OTS_FAILURE_MSG("Failed to read classRangeCount");
|
||||
}
|
||||
if (range_count > num_glyphs) {
|
||||
return OTS_FAILURE_MSG("bad range count: %u", range_count);
|
||||
return OTS_FAILURE_MSG("classRangeCount > glyph count: %u > %u", range_count, num_glyphs);
|
||||
}
|
||||
|
||||
uint16_t last_end = 0;
|
||||
|
@ -330,13 +309,16 @@ bool ParseClassDefFormat2(const ots::Font *font,
|
|||
if (!subtable.ReadU16(&start) ||
|
||||
!subtable.ReadU16(&end) ||
|
||||
!subtable.ReadU16(&class_value)) {
|
||||
return OTS_FAILURE_MSG("Failed to read class definition reange %d", i);
|
||||
return OTS_FAILURE_MSG("Failed to read ClassRangeRecord %d", i);
|
||||
}
|
||||
if (start > end || (last_end && start <= last_end)) {
|
||||
return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i);
|
||||
if (start > end) {
|
||||
return OTS_FAILURE_MSG("ClassRangeRecord %d, start > end: %u > %u", i, start, end);
|
||||
}
|
||||
if (last_end && start <= last_end) {
|
||||
return OTS_FAILURE_MSG("ClassRangeRecord %d start overlaps with end of the previous one: %u <= %u", i, start, last_end);
|
||||
}
|
||||
if (class_value > num_classes) {
|
||||
return OTS_FAILURE_MSG("bad class value: %u", class_value);
|
||||
return OTS_FAILURE_MSG("ClassRangeRecord %d class > number of classes: %u > %u", i, class_value, num_classes);
|
||||
}
|
||||
last_end = end;
|
||||
}
|
||||
|
@ -661,7 +643,7 @@ bool ParseContextFormat2(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_class_def,
|
||||
length - offset_class_def,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2");
|
||||
}
|
||||
|
||||
|
@ -1014,7 +996,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_backtrack_class_def,
|
||||
length - offset_backtrack_class_def,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2");
|
||||
}
|
||||
}
|
||||
|
@ -1025,7 +1007,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_input_class_def,
|
||||
length - offset_input_class_def,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2");
|
||||
}
|
||||
|
||||
|
@ -1036,7 +1018,7 @@ bool ParseChainContextFormat2(const ots::Font *font,
|
|||
}
|
||||
if (!ots::ParseClassDefTable(font, data + offset_lookahead_class_def,
|
||||
length - offset_lookahead_class_def,
|
||||
num_glyphs, kMaxClassDefValue)) {
|
||||
num_glyphs, ots::kMaxClassDefValue)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2");
|
||||
}
|
||||
}
|
||||
|
@ -1400,11 +1382,17 @@ bool ParseDeviceTable(const ots::Font *font,
|
|||
!subtable.ReadU16(&delta_format)) {
|
||||
return OTS_FAILURE_MSG("Failed to read device table header");
|
||||
}
|
||||
if (delta_format == kVariationIndex) {
|
||||
// start_size and end_size are replaced by deltaSetOuterIndex
|
||||
// and deltaSetInnerIndex respectively, but we don't attempt to
|
||||
// check them here, so nothing more to do.
|
||||
return true;
|
||||
}
|
||||
if (start_size > end_size) {
|
||||
return OTS_FAILURE_MSG("bad size range: %u > %u", start_size, end_size);
|
||||
return OTS_FAILURE_MSG("Bad device table size range: %u > %u", start_size, end_size);
|
||||
}
|
||||
if (delta_format == 0 || delta_format > kMaxDeltaFormatType) {
|
||||
return OTS_FAILURE_MSG("bad delta format: %u", delta_format);
|
||||
return OTS_FAILURE_MSG("Bad device table delta format: 0x%x", delta_format);
|
||||
}
|
||||
// The number of delta values per uint16. The device table should contain
|
||||
// at least |num_units| * 2 bytes compressed data.
|
||||
|
@ -1516,6 +1504,173 @@ bool ParseExtensionSubtable(const Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseConditionTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t axis_count) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t format = 0;
|
||||
if (!subtable.ReadU16(&format)) {
|
||||
return OTS_FAILURE_MSG("Failed to read condition table format");
|
||||
}
|
||||
|
||||
if (format != 1) {
|
||||
// An unknown format is not an error, but should be ignored per spec.
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t axis_index = 0;
|
||||
int16_t filter_range_min_value = 0;
|
||||
int16_t filter_range_max_value = 0;
|
||||
if (!subtable.ReadU16(&axis_index) ||
|
||||
!subtable.ReadS16(&filter_range_min_value) ||
|
||||
!subtable.ReadS16(&filter_range_max_value)) {
|
||||
return OTS_FAILURE_MSG("Failed to read condition table (format 1)");
|
||||
}
|
||||
|
||||
if (axis_index >= axis_count) {
|
||||
return OTS_FAILURE_MSG("Axis index out of range in condition");
|
||||
}
|
||||
|
||||
// Check min/max values are within range -1.0 .. 1.0 and properly ordered
|
||||
if (filter_range_min_value < -0x4000 || // -1.0 in F2DOT14 format
|
||||
filter_range_max_value > 0x4000 || // +1.0 in F2DOT14 format
|
||||
filter_range_min_value > filter_range_max_value) {
|
||||
return OTS_FAILURE_MSG("Invalid filter range in condition");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseConditionSetTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t axis_count) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t condition_count = 0;
|
||||
if (!subtable.ReadU16(&condition_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read condition count");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < condition_count; i++) {
|
||||
uint32_t condition_offset = 0;
|
||||
if (!subtable.ReadU32(&condition_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read condition offset");
|
||||
}
|
||||
if (condition_offset < subtable.offset() || condition_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Offset out of range");
|
||||
}
|
||||
if (!ParseConditionTable(font, data + condition_offset, length - condition_offset,
|
||||
axis_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse condition table");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseFeatureTableSubstitutionTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t num_lookups) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t version_major = 0;
|
||||
uint16_t version_minor = 0;
|
||||
uint16_t substitution_count = 0;
|
||||
const size_t kFeatureTableSubstitutionHeaderSize = 3 * sizeof(uint16_t);
|
||||
|
||||
if (!subtable.ReadU16(&version_major) ||
|
||||
!subtable.ReadU16(&version_minor) ||
|
||||
!subtable.ReadU16(&substitution_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature table substitution table header");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < substitution_count; i++) {
|
||||
uint16_t feature_index = 0;
|
||||
uint32_t alternate_feature_table_offset = 0;
|
||||
const size_t kFeatureTableSubstitutionRecordSize = sizeof(uint16_t) + sizeof(uint32_t);
|
||||
|
||||
if (!subtable.ReadU16(&feature_index) ||
|
||||
!subtable.ReadU32(&alternate_feature_table_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature table substitution record");
|
||||
}
|
||||
|
||||
if (alternate_feature_table_offset < kFeatureTableSubstitutionHeaderSize +
|
||||
kFeatureTableSubstitutionRecordSize * substitution_count ||
|
||||
alternate_feature_table_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Invalid alternate feature table offset");
|
||||
}
|
||||
|
||||
if (!ParseFeatureTable(font, data + alternate_feature_table_offset,
|
||||
length - alternate_feature_table_offset, num_lookups)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse alternate feature table");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseFeatureVariationsTable(const Font *font,
|
||||
const uint8_t *data, const size_t length,
|
||||
const uint16_t num_lookups) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t version_major = 0;
|
||||
uint16_t version_minor = 0;
|
||||
uint32_t feature_variation_record_count = 0;
|
||||
|
||||
if (!subtable.ReadU16(&version_major) ||
|
||||
!subtable.ReadU16(&version_minor) ||
|
||||
!subtable.ReadU32(&feature_variation_record_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature variations table header");
|
||||
}
|
||||
|
||||
OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return OTS_FAILURE_MSG("Not a variation font");
|
||||
}
|
||||
const uint16_t axis_count = fvar->AxisCount();
|
||||
|
||||
const size_t kEndOfFeatureVariationRecords =
|
||||
2 * sizeof(uint16_t) + sizeof(uint32_t) +
|
||||
feature_variation_record_count * 2 * sizeof(uint32_t);
|
||||
|
||||
for (uint32_t i = 0; i < feature_variation_record_count; i++) {
|
||||
uint32_t condition_set_offset = 0;
|
||||
uint32_t feature_table_substitution_offset = 0;
|
||||
if (!subtable.ReadU32(&condition_set_offset) ||
|
||||
!subtable.ReadU32(&feature_table_substitution_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read feature variation record");
|
||||
}
|
||||
|
||||
if (condition_set_offset) {
|
||||
if (condition_set_offset < kEndOfFeatureVariationRecords ||
|
||||
condition_set_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Condition set offset out of range");
|
||||
}
|
||||
if (!ParseConditionSetTable(font, data + condition_set_offset,
|
||||
length - condition_set_offset,
|
||||
axis_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse condition set table");
|
||||
}
|
||||
}
|
||||
|
||||
if (feature_table_substitution_offset) {
|
||||
if (feature_table_substitution_offset < kEndOfFeatureVariationRecords ||
|
||||
feature_table_substitution_offset >= length) {
|
||||
return OTS_FAILURE_MSG("Feature table substitution offset out of range");
|
||||
}
|
||||
if (!ParseFeatureTableSubstitutionTable(font, data + feature_table_substitution_offset,
|
||||
length - feature_table_substitution_offset,
|
||||
num_lookups)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse feature table substitution table");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -10,84 +10,79 @@
|
|||
// loca - Index to Location
|
||||
// http://www.microsoft.com/typography/otspec/loca.htm
|
||||
|
||||
#define TABLE_NAME "loca"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_loca_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeLOCA::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
// We can't do anything useful in validating this data except to ensure that
|
||||
// the values are monotonically increasing.
|
||||
|
||||
OpenTypeLOCA *loca = new OpenTypeLOCA;
|
||||
font->loca = loca;
|
||||
|
||||
if (!font->maxp || !font->head) {
|
||||
return OTS_FAILURE_MSG("maxp or head tables missing from font, needed by loca");
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_HEAD));
|
||||
if (!maxp || !head) {
|
||||
return Error("Required maxp or head tables are missing");
|
||||
}
|
||||
|
||||
const unsigned num_glyphs = font->maxp->num_glyphs;
|
||||
const unsigned num_glyphs = maxp->num_glyphs;
|
||||
unsigned last_offset = 0;
|
||||
loca->offsets.resize(num_glyphs + 1);
|
||||
this->offsets.resize(num_glyphs + 1);
|
||||
// maxp->num_glyphs is uint16_t, thus the addition never overflows.
|
||||
|
||||
if (font->head->index_to_loc_format == 0) {
|
||||
if (head->index_to_loc_format == 0) {
|
||||
// Note that the <= here (and below) is correct. There is one more offset
|
||||
// than the number of glyphs in order to give the length of the final
|
||||
// glyph.
|
||||
for (unsigned i = 0; i <= num_glyphs; ++i) {
|
||||
uint16_t offset = 0;
|
||||
if (!table.ReadU16(&offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
|
||||
return Error("Failed to read offset for glyph %d", i);
|
||||
}
|
||||
if (offset < last_offset) {
|
||||
return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
|
||||
return Error("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
|
||||
}
|
||||
last_offset = offset;
|
||||
loca->offsets[i] = offset * 2;
|
||||
this->offsets[i] = offset * 2;
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i <= num_glyphs; ++i) {
|
||||
uint32_t offset = 0;
|
||||
if (!table.ReadU32(&offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
|
||||
return Error("Failed to read offset for glyph %d", i);
|
||||
}
|
||||
if (offset < last_offset) {
|
||||
return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
|
||||
return Error("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
|
||||
}
|
||||
last_offset = offset;
|
||||
loca->offsets[i] = offset;
|
||||
this->offsets[i] = offset;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_loca_should_serialise(Font *font) {
|
||||
return font->loca != NULL;
|
||||
}
|
||||
|
||||
bool ots_loca_serialise(OTSStream *out, Font *font) {
|
||||
const OpenTypeLOCA *loca = font->loca;
|
||||
const OpenTypeHEAD *head = font->head;
|
||||
bool OpenTypeLOCA::Serialize(OTSStream *out) {
|
||||
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_HEAD));
|
||||
|
||||
if (!head) {
|
||||
return OTS_FAILURE_MSG("Missing head table in font needed by loca");
|
||||
return Error("Required head table is missing");
|
||||
}
|
||||
|
||||
if (head->index_to_loc_format == 0) {
|
||||
for (unsigned i = 0; i < loca->offsets.size(); ++i) {
|
||||
const uint16_t offset = static_cast<uint16_t>(loca->offsets[i] >> 1);
|
||||
if ((offset != (loca->offsets[i] >> 1)) ||
|
||||
for (unsigned i = 0; i < this->offsets.size(); ++i) {
|
||||
const uint16_t offset = static_cast<uint16_t>(this->offsets[i] >> 1);
|
||||
if ((offset != (this->offsets[i] >> 1)) ||
|
||||
!out->WriteU16(offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
|
||||
return Error("Failed to write glyph offset for glyph %d", i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < loca->offsets.size(); ++i) {
|
||||
if (!out->WriteU32(loca->offsets[i])) {
|
||||
return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
|
||||
for (unsigned i = 0; i < this->offsets.size(); ++i) {
|
||||
if (!out->WriteU32(this->offsets[i])) {
|
||||
return Error("Failed to write glyph offset for glyph %d", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,15 +90,4 @@ bool ots_loca_serialise(OTSStream *out, Font *font) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ots_loca_reuse(Font *font, Font *other) {
|
||||
font->loca = other->loca;
|
||||
font->loca_reused = true;
|
||||
}
|
||||
|
||||
void ots_loca_free(Font *font) {
|
||||
delete font->loca;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -11,7 +11,14 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeLOCA {
|
||||
class OpenTypeLOCA : public Table {
|
||||
public:
|
||||
explicit OpenTypeLOCA(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
std::vector<uint32_t> offsets;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,89 +9,63 @@
|
|||
// LTSH - Linear Threshold
|
||||
// http://www.microsoft.com/typography/otspec/ltsh.htm
|
||||
|
||||
#define TABLE_NAME "LTSH"
|
||||
|
||||
#define DROP_THIS_TABLE(...) \
|
||||
do { \
|
||||
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
|
||||
OTS_FAILURE_MSG("Table discarded"); \
|
||||
delete font->ltsh; \
|
||||
font->ltsh = 0; \
|
||||
} while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_ltsh_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeLTSH::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!font->maxp) {
|
||||
return OTS_FAILURE_MSG("Missing maxp table from font needed by ltsh");
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return Error("Required maxp table is missing");
|
||||
}
|
||||
|
||||
OpenTypeLTSH *ltsh = new OpenTypeLTSH;
|
||||
font->ltsh = ltsh;
|
||||
|
||||
uint16_t num_glyphs = 0;
|
||||
if (!table.ReadU16(<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<uint16_t>(ltsh->ypels.size());
|
||||
if (num_ypels != ltsh->ypels.size() ||
|
||||
!out->WriteU16(ltsh->version) ||
|
||||
bool OpenTypeLTSH::Serialize(OTSStream *out) {
|
||||
const uint16_t num_ypels = static_cast<uint16_t>(this->ypels.size());
|
||||
if (num_ypels != this->ypels.size() ||
|
||||
!out->WriteU16(this->version) ||
|
||||
!out->WriteU16(num_ypels)) {
|
||||
return OTS_FAILURE_MSG("Failed to write pels size");
|
||||
return Error("Failed to write table header");
|
||||
}
|
||||
for (uint16_t i = 0; i < num_ypels; ++i) {
|
||||
if (!out->Write(&(ltsh->ypels[i]), 1)) {
|
||||
return OTS_FAILURE_MSG("Failed to write pixel size for glyph %d", i);
|
||||
if (!out->Write(&(this->ypels[i]), 1)) {
|
||||
return Error("Failed to write pixel size for glyph %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_ltsh_reuse(Font *font, Font *other) {
|
||||
font->ltsh = other->ltsh;
|
||||
font->ltsh_reused = true;
|
||||
}
|
||||
|
||||
void ots_ltsh_free(Font *font) {
|
||||
delete font->ltsh;
|
||||
bool OpenTypeLTSH::ShouldSerialize() {
|
||||
return Table::ShouldSerialize() &&
|
||||
// this table is not for CFF fonts.
|
||||
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
#undef DROP_THIS_TABLE
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -11,7 +11,16 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeLTSH {
|
||||
class OpenTypeLTSH : public Table {
|
||||
public:
|
||||
explicit OpenTypeLTSH(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool ShouldSerialize();
|
||||
|
||||
private:
|
||||
uint16_t version;
|
||||
std::vector<uint8_t> ypels;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2014-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -12,12 +12,7 @@
|
|||
#include "maxp.h"
|
||||
|
||||
// MATH - The MATH Table
|
||||
// The specification is not yet public but has been submitted to the MPEG group
|
||||
// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font
|
||||
// Format" Color Font Technology and MATH layout support'. Meanwhile, you can
|
||||
// contact Microsoft's engineer Murray Sargent to obtain a copy.
|
||||
|
||||
#define TABLE_NAME "MATH"
|
||||
// http://www.microsoft.com/typography/otspec/math.htm
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -48,11 +43,15 @@ const unsigned kMathValueRecordSize = 2 * 2;
|
|||
// PartFlags
|
||||
const unsigned kGlyphPartRecordSize = 5 * 2;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
// Shared Table: MathValueRecord
|
||||
|
||||
bool ParseMathValueRecord(const ots::Font *font,
|
||||
ots::Buffer* subtable, const uint8_t *data,
|
||||
const size_t length) {
|
||||
bool OpenTypeMATH::ParseMathValueRecord(ots::Buffer* subtable,
|
||||
const uint8_t *data,
|
||||
const size_t length) {
|
||||
// Check the Value field.
|
||||
if (!subtable->Skip(2)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -67,7 +66,7 @@ bool ParseMathValueRecord(const ots::Font *font,
|
|||
if (offset >= length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!ots::ParseDeviceTable(font, data + offset, length - offset)) {
|
||||
if (!ots::ParseDeviceTable(GetFont(), data + offset, length - offset)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
@ -75,8 +74,8 @@ bool ParseMathValueRecord(const ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseMathConstantsTable(const ots::Font *font,
|
||||
const uint8_t *data, size_t length) {
|
||||
bool OpenTypeMATH::ParseMathConstantsTable(const uint8_t *data,
|
||||
size_t length) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Part 1: int16 or uint16 constants.
|
||||
|
@ -146,7 +145,7 @@ bool ParseMathConstantsTable(const ots::Font *font,
|
|||
//
|
||||
// RadicalKernAfterDegree
|
||||
for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
|
||||
if (!ParseMathValueRecord(font, &subtable, data, length)) {
|
||||
if (!ParseMathValueRecord(&subtable, data, length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
@ -160,11 +159,10 @@ bool ParseMathConstantsTable(const ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
|
||||
ots::Buffer* subtable,
|
||||
const uint8_t *data,
|
||||
const size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
bool OpenTypeMATH::ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
|
||||
const uint8_t *data,
|
||||
const size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
// Check the header.
|
||||
uint16_t offset_coverage = 0;
|
||||
uint16_t sequence_count = 0;
|
||||
|
@ -183,7 +181,7 @@ bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
|
|||
if (offset_coverage < sequence_end || offset_coverage >= length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
||||
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
|
||||
length - offset_coverage,
|
||||
num_glyphs, sequence_count)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -191,7 +189,7 @@ bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
|
|||
|
||||
// Check sequence.
|
||||
for (unsigned i = 0; i < sequence_count; ++i) {
|
||||
if (!ParseMathValueRecord(font, subtable, data, length)) {
|
||||
if (!ParseMathValueRecord(subtable, data, length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
@ -199,26 +197,23 @@ bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseMathItalicsCorrectionInfoTable(const ots::Font *font,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
bool OpenTypeMATH::ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
return ParseMathValueRecordSequenceForGlyphs(font, &subtable, data, length,
|
||||
return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
|
||||
num_glyphs);
|
||||
}
|
||||
|
||||
bool ParseMathTopAccentAttachmentTable(const ots::Font *font,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
bool OpenTypeMATH::ParseMathTopAccentAttachmentTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
return ParseMathValueRecordSequenceForGlyphs(font, &subtable, data, length,
|
||||
return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
|
||||
num_glyphs);
|
||||
}
|
||||
|
||||
bool ParseMathKernTable(const ots::Font *font,
|
||||
const uint8_t *data, size_t length) {
|
||||
bool OpenTypeMATH::ParseMathKernTable(const uint8_t *data, size_t length) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Check the Height count.
|
||||
|
@ -229,14 +224,14 @@ bool ParseMathKernTable(const ots::Font *font,
|
|||
|
||||
// Check the Correction Heights.
|
||||
for (unsigned i = 0; i < height_count; ++i) {
|
||||
if (!ParseMathValueRecord(font, &subtable, data, length)) {
|
||||
if (!ParseMathValueRecord(&subtable, data, length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
// Check the Kern Values.
|
||||
for (unsigned i = 0; i <= height_count; ++i) {
|
||||
if (!ParseMathValueRecord(font, &subtable, data, length)) {
|
||||
if (!ParseMathValueRecord(&subtable, data, length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
|
@ -244,9 +239,9 @@ bool ParseMathKernTable(const ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseMathKernInfoTable(const ots::Font *font,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
bool OpenTypeMATH::ParseMathKernInfoTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Check the header.
|
||||
|
@ -267,7 +262,7 @@ bool ParseMathKernInfoTable(const ots::Font *font,
|
|||
if (offset_coverage < sequence_end || offset_coverage >= length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage, length - offset_coverage,
|
||||
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage, length - offset_coverage,
|
||||
num_glyphs, sequence_count)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
@ -282,7 +277,7 @@ bool ParseMathKernInfoTable(const ots::Font *font,
|
|||
}
|
||||
if (offset_math_kern) {
|
||||
if (offset_math_kern < sequence_end || offset_math_kern >= length ||
|
||||
!ParseMathKernTable(font, data + offset_math_kern,
|
||||
!ParseMathKernTable(data + offset_math_kern,
|
||||
length - offset_math_kern)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
@ -293,9 +288,9 @@ bool ParseMathKernInfoTable(const ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseMathGlyphInfoTable(const ots::Font *font,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
bool OpenTypeMATH::ParseMathGlyphInfoTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Check Header.
|
||||
|
@ -318,7 +313,7 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
|
|||
if (offset_math_italics_correction_info >= length ||
|
||||
offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
|
||||
!ParseMathItalicsCorrectionInfoTable(
|
||||
font, data + offset_math_italics_correction_info,
|
||||
data + offset_math_italics_correction_info,
|
||||
length - offset_math_italics_correction_info,
|
||||
num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -327,7 +322,7 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
|
|||
if (offset_math_top_accent_attachment) {
|
||||
if (offset_math_top_accent_attachment >= length ||
|
||||
offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
|
||||
!ParseMathTopAccentAttachmentTable(font, data +
|
||||
!ParseMathTopAccentAttachmentTable(data +
|
||||
offset_math_top_accent_attachment,
|
||||
length -
|
||||
offset_math_top_accent_attachment,
|
||||
|
@ -338,7 +333,7 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
|
|||
if (offset_extended_shaped_coverage) {
|
||||
if (offset_extended_shaped_coverage >= length ||
|
||||
offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
|
||||
!ots::ParseCoverageTable(font, data + offset_extended_shaped_coverage,
|
||||
!ots::ParseCoverageTable(GetFont(), data + offset_extended_shaped_coverage,
|
||||
length - offset_extended_shaped_coverage,
|
||||
num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -347,7 +342,7 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
|
|||
if (offset_math_kern_info) {
|
||||
if (offset_math_kern_info >= length ||
|
||||
offset_math_kern_info < kMathGlyphInfoHeaderSize ||
|
||||
!ParseMathKernInfoTable(font, data + offset_math_kern_info,
|
||||
!ParseMathKernInfoTable(data + offset_math_kern_info,
|
||||
length - offset_math_kern_info, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
@ -356,14 +351,14 @@ bool ParseMathGlyphInfoTable(const ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseGlyphAssemblyTable(const ots::Font *font,
|
||||
const uint8_t *data,
|
||||
size_t length, const uint16_t num_glyphs) {
|
||||
bool OpenTypeMATH::ParseGlyphAssemblyTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Check the header.
|
||||
uint16_t part_count = 0;
|
||||
if (!ParseMathValueRecord(font, &subtable, data, length) ||
|
||||
if (!ParseMathValueRecord(&subtable, data, length) ||
|
||||
!subtable.ReadU16(&part_count)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
@ -384,19 +379,19 @@ bool ParseGlyphAssemblyTable(const ots::Font *font,
|
|||
return OTS_FAILURE();
|
||||
}
|
||||
if (glyph >= num_glyphs) {
|
||||
return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
|
||||
return Error("bad glyph ID: %u", glyph);
|
||||
}
|
||||
if (part_flags & ~0x00000001) {
|
||||
return OTS_FAILURE_MSG("unknown part flag: %u", part_flags);
|
||||
return Error("unknown part flag: %u", part_flags);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseMathGlyphConstructionTable(const ots::Font *font,
|
||||
const uint8_t *data,
|
||||
size_t length, const uint16_t num_glyphs) {
|
||||
bool OpenTypeMATH::ParseMathGlyphConstructionTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Check the header.
|
||||
|
@ -419,7 +414,7 @@ bool ParseMathGlyphConstructionTable(const ots::Font *font,
|
|||
offset_glyph_assembly < sequence_end) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!ParseGlyphAssemblyTable(font, data + offset_glyph_assembly,
|
||||
if (!ParseGlyphAssemblyTable(data + offset_glyph_assembly,
|
||||
length - offset_glyph_assembly, num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
@ -433,21 +428,20 @@ bool ParseMathGlyphConstructionTable(const ots::Font *font,
|
|||
return OTS_FAILURE();
|
||||
}
|
||||
if (glyph >= num_glyphs) {
|
||||
return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
|
||||
return Error("bad glyph ID: %u", glyph);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseMathGlyphConstructionSequence(const ots::Font *font,
|
||||
ots::Buffer* subtable,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs,
|
||||
uint16_t offset_coverage,
|
||||
uint16_t glyph_count,
|
||||
const unsigned sequence_end) {
|
||||
bool OpenTypeMATH::ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs,
|
||||
uint16_t offset_coverage,
|
||||
uint16_t glyph_count,
|
||||
const unsigned sequence_end) {
|
||||
// Zero glyph count, nothing to parse.
|
||||
if (!glyph_count) {
|
||||
return true;
|
||||
|
@ -457,7 +451,7 @@ bool ParseMathGlyphConstructionSequence(const ots::Font *font,
|
|||
if (offset_coverage < sequence_end || offset_coverage >= length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
||||
if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
|
||||
length - offset_coverage,
|
||||
num_glyphs, glyph_count)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -471,7 +465,7 @@ bool ParseMathGlyphConstructionSequence(const ots::Font *font,
|
|||
}
|
||||
if (offset_glyph_construction < sequence_end ||
|
||||
offset_glyph_construction >= length ||
|
||||
!ParseMathGlyphConstructionTable(font, data + offset_glyph_construction,
|
||||
!ParseMathGlyphConstructionTable(data + offset_glyph_construction,
|
||||
length - offset_glyph_construction,
|
||||
num_glyphs)) {
|
||||
return OTS_FAILURE();
|
||||
|
@ -481,9 +475,9 @@ bool ParseMathGlyphConstructionSequence(const ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ParseMathVariantsTable(const ots::Font *font,
|
||||
const uint8_t *data,
|
||||
size_t length, const uint16_t num_glyphs) {
|
||||
bool OpenTypeMATH::ParseMathVariantsTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
// Check the header.
|
||||
|
@ -505,11 +499,11 @@ bool ParseMathVariantsTable(const ots::Font *font,
|
|||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if (!ParseMathGlyphConstructionSequence(font, &subtable, data, length, num_glyphs,
|
||||
if (!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
|
||||
offset_vert_glyph_coverage,
|
||||
vert_glyph_count,
|
||||
sequence_end) ||
|
||||
!ParseMathGlyphConstructionSequence(font, &subtable, data, length, num_glyphs,
|
||||
!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
|
||||
offset_horiz_glyph_coverage,
|
||||
horiz_glyph_count,
|
||||
sequence_end)) {
|
||||
|
@ -519,37 +513,24 @@ bool ParseMathVariantsTable(const ots::Font *font,
|
|||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#define DROP_THIS_TABLE(msg_) \
|
||||
do { \
|
||||
OTS_FAILURE_MSG(msg_ ", table discarded"); \
|
||||
font->math->data = 0; \
|
||||
font->math->length = 0; \
|
||||
} while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_math_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeMATH::Parse(const uint8_t *data, size_t length) {
|
||||
// Grab the number of glyphs in the font from the maxp table to check
|
||||
// GlyphIDs in MATH table.
|
||||
if (!font->maxp) {
|
||||
return OTS_FAILURE();
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return Error("Required maxp table missing");
|
||||
}
|
||||
const uint16_t num_glyphs = font->maxp->num_glyphs;
|
||||
const uint16_t num_glyphs = maxp->num_glyphs;
|
||||
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeMATH* math = new OpenTypeMATH;
|
||||
font->math = math;
|
||||
|
||||
uint32_t version = 0;
|
||||
if (!table.ReadU32(&version)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (version != 0x00010000) {
|
||||
DROP_THIS_TABLE("bad MATH version");
|
||||
return true;
|
||||
return Drop("bad MATH version");
|
||||
}
|
||||
|
||||
uint16_t offset_math_constants = 0;
|
||||
|
@ -567,53 +548,37 @@ bool ots_math_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
offset_math_glyph_info < kMathHeaderSize ||
|
||||
offset_math_variants >= length ||
|
||||
offset_math_variants < kMathHeaderSize) {
|
||||
DROP_THIS_TABLE("bad offset in MATH header");
|
||||
return true;
|
||||
return Drop("bad offset in MATH header");
|
||||
}
|
||||
|
||||
if (!ParseMathConstantsTable(font, data + offset_math_constants,
|
||||
if (!ParseMathConstantsTable(data + offset_math_constants,
|
||||
length - offset_math_constants)) {
|
||||
DROP_THIS_TABLE("failed to parse MathConstants table");
|
||||
return true;
|
||||
return Drop("failed to parse MathConstants table");
|
||||
}
|
||||
if (!ParseMathGlyphInfoTable(font, data + offset_math_glyph_info,
|
||||
if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info,
|
||||
length - offset_math_glyph_info, num_glyphs)) {
|
||||
DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
|
||||
return true;
|
||||
return Drop("failed to parse MathGlyphInfo table");
|
||||
}
|
||||
if (!ParseMathVariantsTable(font, data + offset_math_variants,
|
||||
if (!ParseMathVariantsTable(data + offset_math_variants,
|
||||
length - offset_math_variants, num_glyphs)) {
|
||||
DROP_THIS_TABLE("failed to parse MathVariants table");
|
||||
return true;
|
||||
return Drop("failed to parse MathVariants table");
|
||||
}
|
||||
|
||||
math->data = data;
|
||||
math->length = length;
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_math_should_serialise(Font *font) {
|
||||
return font->math != NULL && font->math->data != NULL;
|
||||
}
|
||||
|
||||
bool ots_math_serialise(OTSStream *out, Font *font) {
|
||||
if (!out->Write(font->math->data, font->math->length)) {
|
||||
bool OpenTypeMATH::Serialize(OTSStream *out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_math_reuse(Font *font, Font *other) {
|
||||
font->math = other->math;
|
||||
font->math_reused = true;
|
||||
}
|
||||
|
||||
void ots_math_free(Font *font) {
|
||||
delete font->math;
|
||||
bool OpenTypeMATH::ShouldSerialize() {
|
||||
return Table::ShouldSerialize() && this->m_data != NULL;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
#undef DROP_THIS_TABLE
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2014-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,14 +9,58 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeMATH {
|
||||
OpenTypeMATH()
|
||||
: data(NULL),
|
||||
length(0) {
|
||||
}
|
||||
class OpenTypeMATH : public Table {
|
||||
public:
|
||||
explicit OpenTypeMATH(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag),
|
||||
m_data(NULL),
|
||||
m_length(0) { }
|
||||
|
||||
const uint8_t *data;
|
||||
size_t length;
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool ShouldSerialize();
|
||||
|
||||
private:
|
||||
bool ParseMathValueRecord(ots::Buffer* subtable,
|
||||
const uint8_t *data,
|
||||
const size_t length);
|
||||
bool ParseMathConstantsTable(const uint8_t *data, size_t length);
|
||||
bool ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
|
||||
const uint8_t *data,
|
||||
const size_t length,
|
||||
const uint16_t num_glyphs);
|
||||
bool ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs);
|
||||
bool ParseMathTopAccentAttachmentTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs);
|
||||
bool ParseMathKernTable(const uint8_t *data, size_t length);
|
||||
bool ParseMathKernInfoTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs);
|
||||
bool ParseMathGlyphInfoTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs);
|
||||
bool ParseGlyphAssemblyTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs);
|
||||
bool ParseMathGlyphConstructionTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs);
|
||||
bool ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs,
|
||||
uint16_t offset_coverage,
|
||||
uint16_t glyph_count,
|
||||
const unsigned sequence_end);
|
||||
bool ParseMathVariantsTable(const uint8_t *data,
|
||||
size_t length,
|
||||
const uint16_t num_glyphs);
|
||||
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -7,119 +7,97 @@
|
|||
// maxp - Maximum Profile
|
||||
// http://www.microsoft.com/typography/otspec/maxp.htm
|
||||
|
||||
#define TABLE_NAME "maxp"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_maxp_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeMAXP::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeMAXP *maxp = new OpenTypeMAXP;
|
||||
font->maxp = maxp;
|
||||
|
||||
uint32_t version = 0;
|
||||
if (!table.ReadU32(&version)) {
|
||||
return OTS_FAILURE_MSG("Failed to read version of maxp table");
|
||||
return Error("Failed to read table version");
|
||||
}
|
||||
|
||||
if (version >> 16 > 1) {
|
||||
return OTS_FAILURE_MSG("Bad maxp version %d", version);
|
||||
return Error("Unsupported table version 0x%x", version);
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to read number of glyphs from maxp table");
|
||||
if (!table.ReadU16(&this->num_glyphs)) {
|
||||
return Error("Failed to read numGlyphs");
|
||||
}
|
||||
|
||||
if (!maxp->num_glyphs) {
|
||||
return OTS_FAILURE_MSG("Bad number of glyphs 0 in maxp table");
|
||||
if (!this->num_glyphs) {
|
||||
return Error("numGlyphs is 0");
|
||||
}
|
||||
|
||||
if (version >> 16 == 1) {
|
||||
maxp->version_1 = true;
|
||||
if (!table.ReadU16(&maxp->max_points) ||
|
||||
!table.ReadU16(&maxp->max_contours) ||
|
||||
!table.ReadU16(&maxp->max_c_points) ||
|
||||
!table.ReadU16(&maxp->max_c_contours) ||
|
||||
!table.ReadU16(&maxp->max_zones) ||
|
||||
!table.ReadU16(&maxp->max_t_points) ||
|
||||
!table.ReadU16(&maxp->max_storage) ||
|
||||
!table.ReadU16(&maxp->max_fdefs) ||
|
||||
!table.ReadU16(&maxp->max_idefs) ||
|
||||
!table.ReadU16(&maxp->max_stack) ||
|
||||
!table.ReadU16(&maxp->max_size_glyf_instructions) ||
|
||||
!table.ReadU16(&maxp->max_c_components) ||
|
||||
!table.ReadU16(&maxp->max_c_depth)) {
|
||||
return OTS_FAILURE_MSG("Failed to read maxp table");
|
||||
this->version_1 = true;
|
||||
if (!table.ReadU16(&this->max_points) ||
|
||||
!table.ReadU16(&this->max_contours) ||
|
||||
!table.ReadU16(&this->max_c_points) ||
|
||||
!table.ReadU16(&this->max_c_contours) ||
|
||||
!table.ReadU16(&this->max_zones) ||
|
||||
!table.ReadU16(&this->max_t_points) ||
|
||||
!table.ReadU16(&this->max_storage) ||
|
||||
!table.ReadU16(&this->max_fdefs) ||
|
||||
!table.ReadU16(&this->max_idefs) ||
|
||||
!table.ReadU16(&this->max_stack) ||
|
||||
!table.ReadU16(&this->max_size_glyf_instructions) ||
|
||||
!table.ReadU16(&this->max_c_components) ||
|
||||
!table.ReadU16(&this->max_c_depth)) {
|
||||
return Error("Failed to read version 1 table data");
|
||||
}
|
||||
|
||||
if (maxp->max_zones == 0) {
|
||||
if (this->max_zones == 0) {
|
||||
// workaround for ipa*.ttf Japanese fonts.
|
||||
OTS_WARNING("bad max_zones: %u", maxp->max_zones);
|
||||
maxp->max_zones = 1;
|
||||
} else if (maxp->max_zones == 3) {
|
||||
Warning("Bad maxZones: %u", this->max_zones);
|
||||
this->max_zones = 1;
|
||||
} else if (this->max_zones == 3) {
|
||||
// workaround for Ecolier-*.ttf fonts.
|
||||
OTS_WARNING("bad max_zones: %u", maxp->max_zones);
|
||||
maxp->max_zones = 2;
|
||||
Warning("Bad maxZones: %u", this->max_zones);
|
||||
this->max_zones = 2;
|
||||
}
|
||||
|
||||
if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) {
|
||||
return OTS_FAILURE_MSG("Bad max zones %d in maxp", maxp->max_zones);
|
||||
if ((this->max_zones != 1) && (this->max_zones != 2)) {
|
||||
return Error("Bad maxZones: %u", this->max_zones);
|
||||
}
|
||||
} else {
|
||||
maxp->version_1 = false;
|
||||
this->version_1 = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_maxp_should_serialise(Font *font) {
|
||||
return font->maxp != NULL;
|
||||
}
|
||||
|
||||
bool ots_maxp_serialise(OTSStream *out, Font *font) {
|
||||
const OpenTypeMAXP *maxp = font->maxp;
|
||||
|
||||
if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
|
||||
!out->WriteU16(maxp->num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to write maxp version or number of glyphs");
|
||||
bool OpenTypeMAXP::Serialize(OTSStream *out) {
|
||||
if (!out->WriteU32(this->version_1 ? 0x00010000 : 0x00005000) ||
|
||||
!out->WriteU16(this->num_glyphs)) {
|
||||
return Error("Failed to write version or numGlyphs");
|
||||
}
|
||||
|
||||
if (!maxp->version_1) return true;
|
||||
if (!this->version_1) return true;
|
||||
|
||||
if (!out->WriteU16(maxp->max_points) ||
|
||||
!out->WriteU16(maxp->max_contours) ||
|
||||
!out->WriteU16(maxp->max_c_points) ||
|
||||
!out->WriteU16(maxp->max_c_contours)) {
|
||||
return OTS_FAILURE_MSG("Failed to write maxp");
|
||||
if (!out->WriteU16(this->max_points) ||
|
||||
!out->WriteU16(this->max_contours) ||
|
||||
!out->WriteU16(this->max_c_points) ||
|
||||
!out->WriteU16(this->max_c_contours)) {
|
||||
return Error("Failed to write maxp");
|
||||
}
|
||||
|
||||
if (!out->WriteU16(maxp->max_zones) ||
|
||||
!out->WriteU16(maxp->max_t_points) ||
|
||||
!out->WriteU16(maxp->max_storage) ||
|
||||
!out->WriteU16(maxp->max_fdefs) ||
|
||||
!out->WriteU16(maxp->max_idefs) ||
|
||||
!out->WriteU16(maxp->max_stack) ||
|
||||
!out->WriteU16(maxp->max_size_glyf_instructions)) {
|
||||
return OTS_FAILURE_MSG("Failed to write more maxp");
|
||||
if (!out->WriteU16(this->max_zones) ||
|
||||
!out->WriteU16(this->max_t_points) ||
|
||||
!out->WriteU16(this->max_storage) ||
|
||||
!out->WriteU16(this->max_fdefs) ||
|
||||
!out->WriteU16(this->max_idefs) ||
|
||||
!out->WriteU16(this->max_stack) ||
|
||||
!out->WriteU16(this->max_size_glyf_instructions)) {
|
||||
return Error("Failed to write more maxp");
|
||||
}
|
||||
|
||||
if (!out->WriteU16(maxp->max_c_components) ||
|
||||
!out->WriteU16(maxp->max_c_depth)) {
|
||||
return OTS_FAILURE_MSG("Failed to write yet more maxp");
|
||||
if (!out->WriteU16(this->max_c_components) ||
|
||||
!out->WriteU16(this->max_c_depth)) {
|
||||
return Error("Failed to write yet more maxp");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_maxp_reuse(Font *font, Font *other) {
|
||||
font->maxp = other->maxp;
|
||||
font->maxp_reused = true;
|
||||
}
|
||||
|
||||
void ots_maxp_free(Font *font) {
|
||||
delete font->maxp;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,7 +9,14 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeMAXP {
|
||||
class OpenTypeMAXP : public Table {
|
||||
public:
|
||||
explicit OpenTypeMAXP(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
uint16_t num_glyphs;
|
||||
bool version_1;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -11,149 +11,163 @@
|
|||
// http://www.microsoft.com/typography/otspec/hhea.htm
|
||||
// http://www.microsoft.com/typography/otspec/vhea.htm
|
||||
|
||||
#define TABLE_NAME "metrics" // XXX: use individual table names
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ParseMetricsHeader(Font *font, Buffer *table,
|
||||
OpenTypeMetricsHeader *header) {
|
||||
if (!table->ReadS16(&header->ascent) ||
|
||||
!table->ReadS16(&header->descent) ||
|
||||
!table->ReadS16(&header->linegap) ||
|
||||
!table->ReadU16(&header->adv_width_max) ||
|
||||
!table->ReadS16(&header->min_sb1) ||
|
||||
!table->ReadS16(&header->min_sb2) ||
|
||||
!table->ReadS16(&header->max_extent) ||
|
||||
!table->ReadS16(&header->caret_slope_rise) ||
|
||||
!table->ReadS16(&header->caret_slope_run) ||
|
||||
!table->ReadS16(&header->caret_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read metrics header");
|
||||
bool OpenTypeMetricsHeader::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
// Skip already read version.
|
||||
if (!table.Skip(4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header->ascent < 0) {
|
||||
OTS_WARNING("bad ascent: %d", header->ascent);
|
||||
header->ascent = 0;
|
||||
}
|
||||
if (header->linegap < 0) {
|
||||
OTS_WARNING("bad linegap: %d", header->linegap);
|
||||
header->linegap = 0;
|
||||
if (!table.ReadS16(&this->ascent) ||
|
||||
!table.ReadS16(&this->descent) ||
|
||||
!table.ReadS16(&this->linegap) ||
|
||||
!table.ReadU16(&this->adv_width_max) ||
|
||||
!table.ReadS16(&this->min_sb1) ||
|
||||
!table.ReadS16(&this->min_sb2) ||
|
||||
!table.ReadS16(&this->max_extent) ||
|
||||
!table.ReadS16(&this->caret_slope_rise) ||
|
||||
!table.ReadS16(&this->caret_slope_run) ||
|
||||
!table.ReadS16(&this->caret_offset)) {
|
||||
return Error("Failed to read table");
|
||||
}
|
||||
|
||||
if (!font->head) {
|
||||
return OTS_FAILURE_MSG("Missing head font table");
|
||||
if (this->ascent < 0) {
|
||||
Warning("bad ascent: %d", this->ascent);
|
||||
this->ascent = 0;
|
||||
}
|
||||
if (this->linegap < 0) {
|
||||
Warning("bad linegap: %d", this->linegap);
|
||||
this->linegap = 0;
|
||||
}
|
||||
|
||||
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_HEAD));
|
||||
if (!head) {
|
||||
return Error("Missing head font table");
|
||||
}
|
||||
|
||||
// if the font is non-slanted, caret_offset should be zero.
|
||||
if (!(font->head->mac_style & 2) &&
|
||||
(header->caret_offset != 0)) {
|
||||
OTS_WARNING("bad caret offset: %d", header->caret_offset);
|
||||
header->caret_offset = 0;
|
||||
if (!(head->mac_style & 2) &&
|
||||
(this->caret_offset != 0)) {
|
||||
Warning("bad caret offset: %d", this->caret_offset);
|
||||
this->caret_offset = 0;
|
||||
}
|
||||
|
||||
// skip the reserved bytes
|
||||
if (!table->Skip(8)) {
|
||||
return OTS_FAILURE_MSG("Failed to skip reserverd bytes");
|
||||
if (!table.Skip(8)) {
|
||||
return Error("Failed to read reserverd bytes");
|
||||
}
|
||||
|
||||
int16_t data_format;
|
||||
if (!table->ReadS16(&data_format)) {
|
||||
return OTS_FAILURE_MSG("Failed to read data format");
|
||||
if (!table.ReadS16(&data_format)) {
|
||||
return Error("Failed to read metricDataFormat");
|
||||
}
|
||||
if (data_format) {
|
||||
return OTS_FAILURE_MSG("Bad data format %d", data_format);
|
||||
return Error("Bad metricDataFormat: %d", data_format);
|
||||
}
|
||||
|
||||
if (!table->ReadU16(&header->num_metrics)) {
|
||||
return OTS_FAILURE_MSG("Failed to read number of metrics");
|
||||
if (!table.ReadU16(&this->num_metrics)) {
|
||||
return Error("Failed to read number of metrics");
|
||||
}
|
||||
|
||||
if (!font->maxp) {
|
||||
return OTS_FAILURE_MSG("Missing maxp font table");
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return Error("Missing maxp font table");
|
||||
}
|
||||
|
||||
if (header->num_metrics > font->maxp->num_glyphs) {
|
||||
return OTS_FAILURE_MSG("Bad number of metrics %d", header->num_metrics);
|
||||
if (this->num_metrics > maxp->num_glyphs) {
|
||||
return Error("Bad number of metrics %d", this->num_metrics);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerialiseMetricsHeader(const ots::Font *font,
|
||||
OTSStream *out,
|
||||
const OpenTypeMetricsHeader *header) {
|
||||
if (!out->WriteU32(header->version) ||
|
||||
!out->WriteS16(header->ascent) ||
|
||||
!out->WriteS16(header->descent) ||
|
||||
!out->WriteS16(header->linegap) ||
|
||||
!out->WriteU16(header->adv_width_max) ||
|
||||
!out->WriteS16(header->min_sb1) ||
|
||||
!out->WriteS16(header->min_sb2) ||
|
||||
!out->WriteS16(header->max_extent) ||
|
||||
!out->WriteS16(header->caret_slope_rise) ||
|
||||
!out->WriteS16(header->caret_slope_run) ||
|
||||
!out->WriteS16(header->caret_offset) ||
|
||||
bool OpenTypeMetricsHeader::Serialize(OTSStream *out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteS16(this->ascent) ||
|
||||
!out->WriteS16(this->descent) ||
|
||||
!out->WriteS16(this->linegap) ||
|
||||
!out->WriteU16(this->adv_width_max) ||
|
||||
!out->WriteS16(this->min_sb1) ||
|
||||
!out->WriteS16(this->min_sb2) ||
|
||||
!out->WriteS16(this->max_extent) ||
|
||||
!out->WriteS16(this->caret_slope_rise) ||
|
||||
!out->WriteS16(this->caret_slope_run) ||
|
||||
!out->WriteS16(this->caret_offset) ||
|
||||
!out->WriteR64(0) || // reserved
|
||||
!out->WriteS16(0) || // metric data format
|
||||
!out->WriteU16(header->num_metrics)) {
|
||||
return OTS_FAILURE_MSG("Failed to write metrics");
|
||||
!out->WriteU16(this->num_metrics)) {
|
||||
return Error("Failed to write metrics");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseMetricsTable(const ots::Font *font,
|
||||
Buffer *table,
|
||||
const uint16_t num_glyphs,
|
||||
const OpenTypeMetricsHeader *header,
|
||||
OpenTypeMetricsTable *metrics) {
|
||||
bool OpenTypeMetricsTable::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
// OpenTypeMetricsHeader is a superclass of both 'hhea' and 'vhea',
|
||||
// so the cast here is OK, whichever m_header_tag we have.
|
||||
OpenTypeMetricsHeader *header = static_cast<OpenTypeMetricsHeader*>(
|
||||
GetFont()->GetTypedTable(m_header_tag));
|
||||
if (!header) {
|
||||
return Error("Required %c%c%c%c table missing", OTS_UNTAG(m_header_tag));
|
||||
}
|
||||
// |num_metrics| is a uint16_t, so it's bounded < 65536. This limits that
|
||||
// amount of memory that we'll allocate for this to a sane amount.
|
||||
const unsigned num_metrics = header->num_metrics;
|
||||
|
||||
if (num_metrics > num_glyphs) {
|
||||
return OTS_FAILURE_MSG("Bad number of metrics %d", num_metrics);
|
||||
OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return Error("Required maxp table missing");
|
||||
}
|
||||
if (num_metrics > maxp->num_glyphs) {
|
||||
return Error("Bad number of metrics %d", num_metrics);
|
||||
}
|
||||
if (!num_metrics) {
|
||||
return OTS_FAILURE_MSG("No metrics!");
|
||||
return Error("No metrics!");
|
||||
}
|
||||
const unsigned num_sbs = num_glyphs - num_metrics;
|
||||
const unsigned num_sbs = maxp->num_glyphs - num_metrics;
|
||||
|
||||
metrics->entries.reserve(num_metrics);
|
||||
this->entries.reserve(num_metrics);
|
||||
for (unsigned i = 0; i < num_metrics; ++i) {
|
||||
uint16_t adv = 0;
|
||||
int16_t sb = 0;
|
||||
if (!table->ReadU16(&adv) || !table->ReadS16(&sb)) {
|
||||
return OTS_FAILURE_MSG("Failed to read metric %d", i);
|
||||
if (!table.ReadU16(&adv) || !table.ReadS16(&sb)) {
|
||||
return Error("Failed to read metric %d", i);
|
||||
}
|
||||
metrics->entries.push_back(std::make_pair(adv, sb));
|
||||
this->entries.push_back(std::make_pair(adv, sb));
|
||||
}
|
||||
|
||||
metrics->sbs.reserve(num_sbs);
|
||||
this->sbs.reserve(num_sbs);
|
||||
for (unsigned i = 0; i < num_sbs; ++i) {
|
||||
int16_t sb;
|
||||
if (!table->ReadS16(&sb)) {
|
||||
if (!table.ReadS16(&sb)) {
|
||||
// Some Japanese fonts (e.g., mona.ttf) fail this test.
|
||||
return OTS_FAILURE_MSG("Failed to read side bearing %d", i + num_metrics);
|
||||
return Error("Failed to read side bearing %d", i + num_metrics);
|
||||
}
|
||||
metrics->sbs.push_back(sb);
|
||||
this->sbs.push_back(sb);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerialiseMetricsTable(const ots::Font *font,
|
||||
OTSStream *out,
|
||||
const OpenTypeMetricsTable *metrics) {
|
||||
for (unsigned i = 0; i < metrics->entries.size(); ++i) {
|
||||
if (!out->WriteU16(metrics->entries[i].first) ||
|
||||
!out->WriteS16(metrics->entries[i].second)) {
|
||||
return OTS_FAILURE_MSG("Failed to write metric %d", i);
|
||||
bool OpenTypeMetricsTable::Serialize(OTSStream *out) {
|
||||
for (unsigned i = 0; i < this->entries.size(); ++i) {
|
||||
if (!out->WriteU16(this->entries[i].first) ||
|
||||
!out->WriteS16(this->entries[i].second)) {
|
||||
return Error("Failed to write metric %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < metrics->sbs.size(); ++i) {
|
||||
if (!out->WriteS16(metrics->sbs[i])) {
|
||||
return OTS_FAILURE_MSG("Failed to write side bearing %ld", i + metrics->entries.size());
|
||||
for (unsigned i = 0; i < this->sbs.size(); ++i) {
|
||||
if (!out->WriteS16(this->sbs[i])) {
|
||||
return Error("Failed to write side bearing %ld", i + this->entries.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,5 +175,3 @@ bool SerialiseMetricsTable(const ots::Font *font,
|
|||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -13,7 +13,14 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeMetricsHeader {
|
||||
class OpenTypeMetricsHeader : public Table {
|
||||
public:
|
||||
explicit OpenTypeMetricsHeader(Font *font, uint32_t tag, uint32_t type)
|
||||
: Table(font, tag, type) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
uint32_t version;
|
||||
int16_t ascent;
|
||||
int16_t descent;
|
||||
|
@ -28,27 +35,22 @@ struct OpenTypeMetricsHeader {
|
|||
uint16_t num_metrics;
|
||||
};
|
||||
|
||||
struct OpenTypeMetricsTable {
|
||||
struct OpenTypeMetricsTable : public Table {
|
||||
public:
|
||||
explicit OpenTypeMetricsTable(Font *font, uint32_t tag, uint32_t type,
|
||||
uint32_t header_tag)
|
||||
: Table(font, tag, type), m_header_tag(header_tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
uint32_t m_header_tag;
|
||||
|
||||
std::vector<std::pair<uint16_t, int16_t> > entries;
|
||||
std::vector<int16_t> sbs;
|
||||
};
|
||||
|
||||
bool ParseMetricsHeader(Font *font, Buffer *table,
|
||||
OpenTypeMetricsHeader *header);
|
||||
bool SerialiseMetricsHeader(const ots::Font *font,
|
||||
OTSStream *out,
|
||||
const OpenTypeMetricsHeader *header);
|
||||
|
||||
bool ParseMetricsTable(const ots::Font *font,
|
||||
Buffer *table,
|
||||
const uint16_t num_glyphs,
|
||||
const OpenTypeMetricsHeader *header,
|
||||
OpenTypeMetricsTable *metrics);
|
||||
bool SerialiseMetricsTable(const ots::Font *font,
|
||||
OTSStream *out,
|
||||
const OpenTypeMetricsTable *metrics);
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_METRICS_H_
|
||||
|
||||
|
|
|
@ -9,41 +9,52 @@ EXPORTS += [
|
|||
'../include/ots-memory-stream.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
# don't unify sources that use a (file-specific) DROP_THIS_TABLE macro
|
||||
UNIFIED_SOURCES += [
|
||||
'avar.cc',
|
||||
'cff.cc',
|
||||
'cff_charstring.cc',
|
||||
'cmap.cc',
|
||||
'cvar.cc',
|
||||
'cvt.cc',
|
||||
'feat.cc',
|
||||
'fpgm.cc',
|
||||
'fvar.cc',
|
||||
'gasp.cc',
|
||||
'gdef.cc',
|
||||
'glat.cc',
|
||||
'gloc.cc',
|
||||
'glyf.cc',
|
||||
'gpos.cc',
|
||||
'gsub.cc',
|
||||
'gvar.cc',
|
||||
'hdmx.cc',
|
||||
'kern.cc',
|
||||
'ltsh.cc',
|
||||
'math.cc',
|
||||
'vdmx.cc',
|
||||
'vorg.cc',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'cff.cc',
|
||||
'cff_type2_charstring.cc',
|
||||
'cmap.cc',
|
||||
'cvt.cc',
|
||||
'fpgm.cc',
|
||||
'glyf.cc',
|
||||
'head.cc',
|
||||
'hhea.cc',
|
||||
'hmtx.cc',
|
||||
'hvar.cc',
|
||||
'kern.cc',
|
||||
'layout.cc',
|
||||
'loca.cc',
|
||||
'ltsh.cc',
|
||||
'math.cc',
|
||||
'maxp.cc',
|
||||
'metrics.cc',
|
||||
'mvar.cc',
|
||||
'name.cc',
|
||||
'os2.cc',
|
||||
'ots.cc',
|
||||
'post.cc',
|
||||
'prep.cc',
|
||||
'sile.cc',
|
||||
'silf.cc',
|
||||
'sill.cc',
|
||||
'stat.cc',
|
||||
'variations.cc',
|
||||
'vdmx.cc',
|
||||
'vhea.cc',
|
||||
'vmtx.cc',
|
||||
'vorg.cc',
|
||||
'vvar.cc',
|
||||
]
|
||||
|
||||
# We allow warnings for third-party code that can be updated from upstream.
|
||||
|
@ -53,8 +64,14 @@ FINAL_LIBRARY = 'gkmedias'
|
|||
|
||||
DEFINES['PACKAGE_VERSION'] = '"moz"'
|
||||
DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
|
||||
DEFINES['OTS_GRAPHITE'] = 1
|
||||
DEFINES['OTS_VARIATIONS'] = 1
|
||||
|
||||
USE_LIBS += [
|
||||
'brotli',
|
||||
'woff2',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/modules/woff2/src',
|
||||
]
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "mvar.h"
|
||||
|
||||
#include "variations.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeMVAR
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeMVAR::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t reserved;
|
||||
uint16_t valueRecordSize;
|
||||
uint16_t valueRecordCount;
|
||||
uint16_t itemVariationStoreOffset;
|
||||
|
||||
if (!table.ReadU16(&majorVersion) ||
|
||||
!table.ReadU16(&minorVersion) ||
|
||||
!table.ReadU16(&reserved) ||
|
||||
!table.ReadU16(&valueRecordSize) ||
|
||||
!table.ReadU16(&valueRecordCount) ||
|
||||
!table.ReadU16(&itemVariationStoreOffset)) {
|
||||
return DropVariations("Failed to read table header");
|
||||
}
|
||||
|
||||
if (majorVersion != 1) {
|
||||
return DropVariations("Unknown table version");
|
||||
}
|
||||
|
||||
if (reserved != 0) {
|
||||
Warning("Expected reserved=0");
|
||||
}
|
||||
|
||||
// The spec says that valueRecordSize "must be greater than zero",
|
||||
// but we don't enforce this in the case where valueRecordCount
|
||||
// is zero.
|
||||
// The minimum size for a valueRecord to be valid is 8, for the
|
||||
// three fields currently defined in the record (see below).
|
||||
if (valueRecordSize < 8) {
|
||||
if (valueRecordCount != 0) {
|
||||
return DropVariations("Value record size too small");
|
||||
}
|
||||
}
|
||||
|
||||
if (valueRecordCount == 0) {
|
||||
if (itemVariationStoreOffset != 0) {
|
||||
// The spec says "if valueRecordCount is zero, set to zero",
|
||||
// but having a variation store even when record count is zero
|
||||
// should be harmless -- it just won't be useful for anything.
|
||||
// But we don't need to reject altogether.
|
||||
Warning("Unexpected item variation store");
|
||||
}
|
||||
} else {
|
||||
if (itemVariationStoreOffset < table.offset() || itemVariationStoreOffset > length) {
|
||||
return DropVariations("Invalid item variation store offset");
|
||||
}
|
||||
if (!ParseItemVariationStore(GetFont(), data + itemVariationStoreOffset,
|
||||
length - itemVariationStoreOffset)) {
|
||||
return DropVariations("Failed to parse item variation store");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t prevTag = 0;
|
||||
size_t offset = table.offset();
|
||||
for (unsigned i = 0; i < valueRecordCount; i++) {
|
||||
uint32_t tag;
|
||||
uint16_t deltaSetOuterIndex, deltaSetInnerIndex;
|
||||
if (!table.ReadU32(&tag) ||
|
||||
!table.ReadU16(&deltaSetOuterIndex) ||
|
||||
!table.ReadU16(&deltaSetInnerIndex)) {
|
||||
return DropVariations("Failed to read value record");
|
||||
}
|
||||
if (tag <= prevTag) {
|
||||
return DropVariations("Invalid or out-of-order value tag");
|
||||
}
|
||||
prevTag = tag;
|
||||
// Adjust offset in case additional fields have been added to the
|
||||
// valueRecord by a new minor version (allowed by spec).
|
||||
offset += valueRecordSize;
|
||||
table.set_offset(offset);
|
||||
}
|
||||
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeMVAR::Serialize(OTSStream* out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write MVAR table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_MVAR_H_
|
||||
#define OTS_MVAR_H_
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeMVAR Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeMVAR : public Table {
|
||||
public:
|
||||
explicit OpenTypeMVAR(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_MVAR_H_
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -10,8 +10,6 @@
|
|||
// name - Naming Table
|
||||
// http://www.microsoft.com/typography/otspec/name.htm
|
||||
|
||||
#define TABLE_NAME "name"
|
||||
|
||||
namespace {
|
||||
|
||||
bool ValidInPsName(char c) {
|
||||
|
@ -56,25 +54,22 @@ void AssignToUtf16BeFromAscii(std::string* target,
|
|||
|
||||
namespace ots {
|
||||
|
||||
bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
|
||||
bool OpenTypeNAME::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeNAME* name = new OpenTypeNAME;
|
||||
font->name = name;
|
||||
|
||||
uint16_t format = 0;
|
||||
if (!table.ReadU16(&format) || format > 1) {
|
||||
return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
|
||||
return Error("Failed to read table format or bad format %d", format);
|
||||
}
|
||||
|
||||
uint16_t count = 0;
|
||||
if (!table.ReadU16(&count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read name count");
|
||||
return Error("Failed to read name count");
|
||||
}
|
||||
|
||||
uint16_t string_offset = 0;
|
||||
if (!table.ReadU16(&string_offset) || string_offset > length) {
|
||||
return OTS_FAILURE_MSG("Failed to read strings offset");
|
||||
return Error("Failed to read or bad stringOffset");
|
||||
}
|
||||
const char* string_base = reinterpret_cast<const char*>(data) +
|
||||
string_offset;
|
||||
|
@ -94,7 +89,7 @@ bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
|
|||
!table.ReadU16(&rec.name_id) ||
|
||||
!table.ReadU16(&name_length) ||
|
||||
!table.ReadU16(&name_offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read name entry %d", i);
|
||||
return Error("Failed to read name entry %d", i);
|
||||
}
|
||||
// check platform & encoding, discard names with unknown values
|
||||
switch (rec.platform_id) {
|
||||
|
@ -148,40 +143,49 @@ bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!name->names.empty() && !(name->names.back() < rec)) {
|
||||
OTS_WARNING("name records are not sorted.");
|
||||
if (!this->names.empty() && !(this->names.back() < rec)) {
|
||||
Warning("name records are not sorted.");
|
||||
sort_required = true;
|
||||
}
|
||||
|
||||
name->names.push_back(rec);
|
||||
this->names.push_back(rec);
|
||||
this->name_ids.insert(rec.name_id);
|
||||
}
|
||||
|
||||
if (format == 1) {
|
||||
// extended name table format with language tags
|
||||
uint16_t lang_tag_count;
|
||||
if (!table.ReadU16(&lang_tag_count)) {
|
||||
return OTS_FAILURE_MSG("Failed to read language tag count");
|
||||
return Error("Failed to read langTagCount");
|
||||
}
|
||||
for (unsigned i = 0; i < lang_tag_count; ++i) {
|
||||
uint16_t tag_length = 0;
|
||||
uint16_t tag_offset = 0;
|
||||
if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
|
||||
return OTS_FAILURE_MSG("Faile to read tag length or offset");
|
||||
return Error("Faile to read length or offset for langTagRecord %d", i);
|
||||
}
|
||||
const unsigned tag_end = static_cast<unsigned>(string_offset) +
|
||||
tag_offset + tag_length;
|
||||
if (tag_end > length) {
|
||||
return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
|
||||
return Error("bad end of tag %d > %ld for langTagRecord %d", tag_end, length, i);
|
||||
}
|
||||
// Lang tag is BCP 47 tag per the spec, the recommonded BCP 47 max tag
|
||||
// length is 35:
|
||||
// https://tools.ietf.org/html/bcp47#section-4.4.1
|
||||
// We are being too generous and allowing for 100 (multiplied by 2 since
|
||||
// this is UTF-16 string).
|
||||
if (tag_length > 100 * 2) {
|
||||
return Error("Too long language tag for LangTagRecord %d: %d", i, tag_length);
|
||||
}
|
||||
std::string tag(string_base + tag_offset, tag_length);
|
||||
name->lang_tags.push_back(tag);
|
||||
this->lang_tags.push_back(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (table.offset() > string_offset) {
|
||||
// the string storage apparently overlapped the name/tag records;
|
||||
// consider this font to be badly broken
|
||||
return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
|
||||
return Error("Bad table offset %ld > %d", table.offset(), string_offset);
|
||||
}
|
||||
|
||||
// check existence of required name strings (synthesize if necessary)
|
||||
|
@ -207,17 +211,16 @@ bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
|
|||
// if not, we'll add our fixed versions here
|
||||
bool mac_name[kStdNameCount] = { 0 };
|
||||
bool win_name[kStdNameCount] = { 0 };
|
||||
for (std::vector<NameRecord>::iterator name_iter = name->names.begin();
|
||||
name_iter != name->names.end(); ++name_iter) {
|
||||
const uint16_t id = name_iter->name_id;
|
||||
for (const auto& name : this->names) {
|
||||
const uint16_t id = name.name_id;
|
||||
if (id >= kStdNameCount || kStdNames[id] == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (name_iter->platform_id == 1) {
|
||||
if (name.platform_id == 1) {
|
||||
mac_name[id] = true;
|
||||
continue;
|
||||
}
|
||||
if (name_iter->platform_id == 3) {
|
||||
if (name.platform_id == 3) {
|
||||
win_name[id] = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -236,49 +239,41 @@ bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
|
|||
1033 /* language_id */ , i /* name_id */);
|
||||
AssignToUtf16BeFromAscii(&win_rec.text, std::string(kStdNames[i]));
|
||||
|
||||
name->names.push_back(mac_rec);
|
||||
name->names.push_back(win_rec);
|
||||
this->names.push_back(mac_rec);
|
||||
this->names.push_back(win_rec);
|
||||
sort_required = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sort_required) {
|
||||
std::sort(name->names.begin(), name->names.end());
|
||||
std::sort(this->names.begin(), this->names.end());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_name_should_serialise(Font *font) {
|
||||
return font->name != NULL;
|
||||
}
|
||||
|
||||
bool ots_name_serialise(OTSStream* out, Font *font) {
|
||||
const OpenTypeNAME* name = font->name;
|
||||
|
||||
uint16_t name_count = static_cast<uint16_t>(name->names.size());
|
||||
uint16_t lang_tag_count = static_cast<uint16_t>(name->lang_tags.size());
|
||||
bool OpenTypeNAME::Serialize(OTSStream* out) {
|
||||
uint16_t name_count = static_cast<uint16_t>(this->names.size());
|
||||
uint16_t lang_tag_count = static_cast<uint16_t>(this->lang_tags.size());
|
||||
uint16_t format = 0;
|
||||
size_t string_offset = 6 + name_count * 12;
|
||||
|
||||
if (name->lang_tags.size() > 0) {
|
||||
if (this->lang_tags.size() > 0) {
|
||||
// lang tags require a format-1 name table
|
||||
format = 1;
|
||||
string_offset += 2 + lang_tag_count * 4;
|
||||
}
|
||||
if (string_offset > 0xffff) {
|
||||
return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
|
||||
return Error("Bad stringOffset: %ld", string_offset);
|
||||
}
|
||||
if (!out->WriteU16(format) ||
|
||||
!out->WriteU16(name_count) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(string_offset))) {
|
||||
return OTS_FAILURE_MSG("Failed to write name header");
|
||||
return Error("Failed to write name header");
|
||||
}
|
||||
|
||||
std::string string_data;
|
||||
for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
|
||||
name_iter != name->names.end(); ++name_iter) {
|
||||
const NameRecord& rec = *name_iter;
|
||||
for (const auto& rec : this->names) {
|
||||
if (string_data.size() + rec.text.size() >
|
||||
std::numeric_limits<uint16_t>::max() ||
|
||||
!out->WriteU16(rec.platform_id) ||
|
||||
|
@ -287,44 +282,77 @@ bool ots_name_serialise(OTSStream* out, Font *font) {
|
|||
!out->WriteU16(rec.name_id) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(rec.text.size())) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(string_data.size())) ) {
|
||||
return OTS_FAILURE_MSG("Faile to write name entry");
|
||||
return Error("Faile to write nameRecord");
|
||||
}
|
||||
string_data.append(rec.text);
|
||||
}
|
||||
|
||||
if (format == 1) {
|
||||
if (!out->WriteU16(lang_tag_count)) {
|
||||
return OTS_FAILURE_MSG("Faile to write language tag count");
|
||||
return Error("Faile to write langTagCount");
|
||||
}
|
||||
for (std::vector<std::string>::const_iterator tag_iter =
|
||||
name->lang_tags.begin();
|
||||
tag_iter != name->lang_tags.end(); ++tag_iter) {
|
||||
if (string_data.size() + tag_iter->size() >
|
||||
for (const auto& tag : this->lang_tags) {
|
||||
if (string_data.size() + tag.size() >
|
||||
std::numeric_limits<uint16_t>::max() ||
|
||||
!out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(tag.size())) ||
|
||||
!out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
|
||||
return OTS_FAILURE_MSG("Failed to write string");
|
||||
return Error("Failed to write langTagRecord");
|
||||
}
|
||||
string_data.append(*tag_iter);
|
||||
string_data.append(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (!out->Write(string_data.data(), string_data.size())) {
|
||||
return OTS_FAILURE_MSG("Faile to write string data");
|
||||
return Error("Faile to write string data");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_name_reuse(Font *font, Font *other) {
|
||||
font->name = other->name;
|
||||
font->name_reused = true;
|
||||
}
|
||||
|
||||
void ots_name_free(Font *font) {
|
||||
delete font->name;
|
||||
bool OpenTypeNAME::IsValidNameId(uint16_t nameID, bool addIfMissing) {
|
||||
if (addIfMissing && !this->name_ids.count(nameID)) {
|
||||
bool added_unicode = false;
|
||||
bool added_macintosh = false;
|
||||
bool added_windows = false;
|
||||
const size_t names_size = this->names.size(); // original size
|
||||
for (size_t i = 0; i < names_size; ++i) switch (names[i].platform_id) {
|
||||
case 0:
|
||||
if (!added_unicode) {
|
||||
// If there is an existing NameRecord with platform_id == 0 (Unicode),
|
||||
// then add a NameRecord for the the specified nameID with arguments
|
||||
// 0 (Unicode), 0 (v1.0), 0 (unspecified language).
|
||||
this->names.emplace_back(0, 0, 0, nameID);
|
||||
this->names.back().text = "NoName";
|
||||
added_unicode = true;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (!added_macintosh) {
|
||||
// If there is an existing NameRecord with platform_id == 1 (Macintosh),
|
||||
// then add a NameRecord for the specified nameID with arguments
|
||||
// 1 (Macintosh), 0 (Roman), 0 (English).
|
||||
this->names.emplace_back(1, 0, 0, nameID);
|
||||
this->names.back().text = "NoName";
|
||||
added_macintosh = true;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!added_windows) {
|
||||
// If there is an existing NameRecord with platform_id == 3 (Windows),
|
||||
// then add a NameRecord for the specified nameID with arguments
|
||||
// 3 (Windows), 1 (UCS), 1033 (US English).
|
||||
this->names.emplace_back(3, 1, 1033, nameID);
|
||||
this->names.back().text = "NoName";
|
||||
added_windows = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (added_unicode || added_macintosh || added_windows) {
|
||||
std::sort(this->names.begin(), this->names.end());
|
||||
this->name_ids.insert(nameID);
|
||||
}
|
||||
}
|
||||
return this->name_ids.count(nameID);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
|
@ -43,9 +44,19 @@ struct NameRecord {
|
|||
}
|
||||
};
|
||||
|
||||
struct OpenTypeNAME {
|
||||
class OpenTypeNAME : public Table {
|
||||
public:
|
||||
explicit OpenTypeNAME(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool IsValidNameId(uint16_t nameID, bool addIfMissing = false);
|
||||
|
||||
private:
|
||||
std::vector<NameRecord> names;
|
||||
std::vector<std::string> lang_tags;
|
||||
std::unordered_set<uint16_t> name_ids;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -10,77 +10,73 @@
|
|||
// OS/2 - OS/2 and Windows Metrics
|
||||
// http://www.microsoft.com/typography/otspec/os2.htm
|
||||
|
||||
#define TABLE_NAME "OS/2"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_os2_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeOS2::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypeOS2 *os2 = new OpenTypeOS2;
|
||||
font->os2 = os2;
|
||||
|
||||
if (!table.ReadU16(&os2->version) ||
|
||||
!table.ReadS16(&os2->avg_char_width) ||
|
||||
!table.ReadU16(&os2->weight_class) ||
|
||||
!table.ReadU16(&os2->width_class) ||
|
||||
!table.ReadU16(&os2->type) ||
|
||||
!table.ReadS16(&os2->subscript_x_size) ||
|
||||
!table.ReadS16(&os2->subscript_y_size) ||
|
||||
!table.ReadS16(&os2->subscript_x_offset) ||
|
||||
!table.ReadS16(&os2->subscript_y_offset) ||
|
||||
!table.ReadS16(&os2->superscript_x_size) ||
|
||||
!table.ReadS16(&os2->superscript_y_size) ||
|
||||
!table.ReadS16(&os2->superscript_x_offset) ||
|
||||
!table.ReadS16(&os2->superscript_y_offset) ||
|
||||
!table.ReadS16(&os2->strikeout_size) ||
|
||||
!table.ReadS16(&os2->strikeout_position) ||
|
||||
!table.ReadS16(&os2->family_class)) {
|
||||
return OTS_FAILURE_MSG("Error reading basic table elements");
|
||||
if (!table.ReadU16(&this->table.version) ||
|
||||
!table.ReadS16(&this->table.avg_char_width) ||
|
||||
!table.ReadU16(&this->table.weight_class) ||
|
||||
!table.ReadU16(&this->table.width_class) ||
|
||||
!table.ReadU16(&this->table.type) ||
|
||||
!table.ReadS16(&this->table.subscript_x_size) ||
|
||||
!table.ReadS16(&this->table.subscript_y_size) ||
|
||||
!table.ReadS16(&this->table.subscript_x_offset) ||
|
||||
!table.ReadS16(&this->table.subscript_y_offset) ||
|
||||
!table.ReadS16(&this->table.superscript_x_size) ||
|
||||
!table.ReadS16(&this->table.superscript_y_size) ||
|
||||
!table.ReadS16(&this->table.superscript_x_offset) ||
|
||||
!table.ReadS16(&this->table.superscript_y_offset) ||
|
||||
!table.ReadS16(&this->table.strikeout_size) ||
|
||||
!table.ReadS16(&this->table.strikeout_position) ||
|
||||
!table.ReadS16(&this->table.family_class)) {
|
||||
return Error("Error reading basic table elements");
|
||||
}
|
||||
|
||||
if (os2->version > 5) {
|
||||
return OTS_FAILURE_MSG("Unsupported table version: %u", os2->version);
|
||||
if (this->table.version > 5) {
|
||||
return Error("Unsupported table version: %u", this->table.version);
|
||||
}
|
||||
|
||||
// Follow WPF Font Selection Model's advice.
|
||||
if (1 <= os2->weight_class && os2->weight_class <= 9) {
|
||||
OTS_WARNING("Bad usWeightClass: %u, changing it to: %u", os2->weight_class, os2->weight_class * 100);
|
||||
os2->weight_class *= 100;
|
||||
}
|
||||
// Ditto.
|
||||
if (os2->weight_class > 999) {
|
||||
OTS_WARNING("Bad usWeightClass: %u, changing it to: %d", os2->weight_class, 999);
|
||||
os2->weight_class = 999;
|
||||
if (this->table.weight_class < 1) {
|
||||
Warning("Bad usWeightClass: %u, changing it to %d",
|
||||
this->table.weight_class, 1);
|
||||
this->table.weight_class = 1;
|
||||
} else if (this->table.weight_class > 1000) {
|
||||
Warning("Bad usWeightClass: %u, changing it to %d",
|
||||
this->table.weight_class, 1000);
|
||||
this->table.weight_class = 1000;
|
||||
}
|
||||
|
||||
if (os2->width_class < 1) {
|
||||
OTS_WARNING("Bad usWidthClass: %u, changing it to: %d", os2->width_class, 1);
|
||||
os2->width_class = 1;
|
||||
} else if (os2->width_class > 9) {
|
||||
OTS_WARNING("Bad usWidthClass: %u, changing it to: %d", os2->width_class, 9);
|
||||
os2->width_class = 9;
|
||||
if (this->table.width_class < 1) {
|
||||
Warning("Bad usWidthClass: %u, changing it to %d",
|
||||
this->table.width_class, 1);
|
||||
this->table.width_class = 1;
|
||||
} else if (this->table.width_class > 9) {
|
||||
Warning("Bad usWidthClass: %u, changing it to %d",
|
||||
this->table.width_class, 9);
|
||||
this->table.width_class = 9;
|
||||
}
|
||||
|
||||
// lowest 3 bits of fsType are exclusive.
|
||||
if (os2->type & 0x2) {
|
||||
if (this->table.type & 0x2) {
|
||||
// mask bits 2 & 3.
|
||||
os2->type &= 0xfff3u;
|
||||
} else if (os2->type & 0x4) {
|
||||
this->table.type &= 0xfff3u;
|
||||
} else if (this->table.type & 0x4) {
|
||||
// mask bits 1 & 3.
|
||||
os2->type &= 0xfff4u;
|
||||
} else if (os2->type & 0x8) {
|
||||
this->table.type &= 0xfff4u;
|
||||
} else if (this->table.type & 0x8) {
|
||||
// mask bits 1 & 2.
|
||||
os2->type &= 0xfff9u;
|
||||
this->table.type &= 0xfff9u;
|
||||
}
|
||||
|
||||
// mask reserved bits. use only 0..3, 8, 9 bits.
|
||||
os2->type &= 0x30f;
|
||||
this->table.type &= 0x30f;
|
||||
|
||||
#define SET_TO_ZERO(a, b) \
|
||||
if (os2->b < 0) { \
|
||||
OTS_WARNING("Bad " a ": %d, setting it to zero", os2->b); \
|
||||
os2->b = 0; \
|
||||
#define SET_TO_ZERO(a, b) \
|
||||
if (this->table.b < 0) { \
|
||||
Warning("Bad " a ": %d, setting it to zero", this->table.b); \
|
||||
this->table.b = 0; \
|
||||
}
|
||||
|
||||
SET_TO_ZERO("ySubscriptXSize", subscript_x_size);
|
||||
|
@ -90,7 +86,7 @@ bool ots_os2_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
SET_TO_ZERO("yStrikeoutSize", strikeout_size);
|
||||
#undef SET_TO_ZERO
|
||||
|
||||
static std::string panose_strings[10] = {
|
||||
static const char* panose_strings[10] = {
|
||||
"bFamilyType",
|
||||
"bSerifStyle",
|
||||
"bWeight",
|
||||
|
@ -103,234 +99,222 @@ bool ots_os2_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
"bXHeight",
|
||||
};
|
||||
for (unsigned i = 0; i < 10; ++i) {
|
||||
if (!table.ReadU8(&os2->panose[i])) {
|
||||
return OTS_FAILURE_MSG("Error reading PANOSE %s", panose_strings[i].c_str());
|
||||
if (!table.ReadU8(&this->table.panose[i])) {
|
||||
return Error("Failed to read PANOSE %s", panose_strings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&os2->unicode_range_1) ||
|
||||
!table.ReadU32(&os2->unicode_range_2) ||
|
||||
!table.ReadU32(&os2->unicode_range_3) ||
|
||||
!table.ReadU32(&os2->unicode_range_4) ||
|
||||
!table.ReadU32(&os2->vendor_id) ||
|
||||
!table.ReadU16(&os2->selection) ||
|
||||
!table.ReadU16(&os2->first_char_index) ||
|
||||
!table.ReadU16(&os2->last_char_index) ||
|
||||
!table.ReadS16(&os2->typo_ascender) ||
|
||||
!table.ReadS16(&os2->typo_descender) ||
|
||||
!table.ReadS16(&os2->typo_linegap) ||
|
||||
!table.ReadU16(&os2->win_ascent) ||
|
||||
!table.ReadU16(&os2->win_descent)) {
|
||||
return OTS_FAILURE_MSG("Error reading more basic table fields");
|
||||
if (!table.ReadU32(&this->table.unicode_range_1) ||
|
||||
!table.ReadU32(&this->table.unicode_range_2) ||
|
||||
!table.ReadU32(&this->table.unicode_range_3) ||
|
||||
!table.ReadU32(&this->table.unicode_range_4) ||
|
||||
!table.ReadU32(&this->table.vendor_id) ||
|
||||
!table.ReadU16(&this->table.selection) ||
|
||||
!table.ReadU16(&this->table.first_char_index) ||
|
||||
!table.ReadU16(&this->table.last_char_index) ||
|
||||
!table.ReadS16(&this->table.typo_ascender) ||
|
||||
!table.ReadS16(&this->table.typo_descender) ||
|
||||
!table.ReadS16(&this->table.typo_linegap) ||
|
||||
!table.ReadU16(&this->table.win_ascent) ||
|
||||
!table.ReadU16(&this->table.win_descent)) {
|
||||
return Error("Error reading more basic table fields");
|
||||
}
|
||||
|
||||
// If bit 6 is set, then bits 0 and 5 must be clear.
|
||||
if (os2->selection & 0x40) {
|
||||
os2->selection &= 0xffdeu;
|
||||
if (this->table.selection & 0x40) {
|
||||
this->table.selection &= 0xffdeu;
|
||||
}
|
||||
|
||||
// the settings of bits 0 and 1 must be reflected in the macStyle bits
|
||||
// in the 'head' table.
|
||||
if (!font->head) {
|
||||
return OTS_FAILURE_MSG("Needed head table is missing from the font");
|
||||
OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_HEAD));
|
||||
|
||||
if ((this->table.selection & 0x1) &&
|
||||
head && !(head->mac_style & 0x2)) {
|
||||
Warning("Adjusting head.macStyle (italic) to match fsSelection");
|
||||
head->mac_style |= 0x2;
|
||||
}
|
||||
if ((os2->selection & 0x1) &&
|
||||
!(font->head->mac_style & 0x2)) {
|
||||
OTS_WARNING("adjusting Mac style (italic)");
|
||||
font->head->mac_style |= 0x2;
|
||||
}
|
||||
if ((os2->selection & 0x2) &&
|
||||
!(font->head->mac_style & 0x4)) {
|
||||
OTS_WARNING("adjusting Mac style (underscore)");
|
||||
font->head->mac_style |= 0x4;
|
||||
if ((this->table.selection & 0x2) &&
|
||||
head && !(head->mac_style & 0x4)) {
|
||||
Warning("Adjusting head.macStyle (underscore) to match fsSelection");
|
||||
head->mac_style |= 0x4;
|
||||
}
|
||||
|
||||
// While bit 6 on implies that bits 0 and 1 of macStyle are clear,
|
||||
// the reverse is not true.
|
||||
if ((os2->selection & 0x40) &&
|
||||
(font->head->mac_style & 0x3)) {
|
||||
OTS_WARNING("adjusting Mac style (regular)");
|
||||
font->head->mac_style &= 0xfffcu;
|
||||
if ((this->table.selection & 0x40) &&
|
||||
head && (head->mac_style & 0x3)) {
|
||||
Warning("Adjusting head.macStyle (regular) to match fsSelection");
|
||||
head->mac_style &= 0xfffcu;
|
||||
}
|
||||
|
||||
if ((os2->version < 4) &&
|
||||
(os2->selection & 0x300)) {
|
||||
if ((this->table.version < 4) &&
|
||||
(this->table.selection & 0x300)) {
|
||||
// bit 8 and 9 must be unset in OS/2 table versions less than 4.
|
||||
return OTS_FAILURE_MSG("Version %d incompatible with selection %d", os2->version, os2->selection);
|
||||
Warning("fsSelection bits 8 and 9 must be unset for table version %d",
|
||||
this->table.version);
|
||||
}
|
||||
|
||||
// mask reserved bits. use only 0..9 bits.
|
||||
os2->selection &= 0x3ff;
|
||||
this->table.selection &= 0x3ff;
|
||||
|
||||
if (os2->first_char_index > os2->last_char_index) {
|
||||
return OTS_FAILURE_MSG("first char index %d > last char index %d in os2", os2->first_char_index, os2->last_char_index);
|
||||
if (this->table.first_char_index > this->table.last_char_index) {
|
||||
Warning("usFirstCharIndex %d > usLastCharIndex %d",
|
||||
this->table.first_char_index, this->table.last_char_index);
|
||||
this->table.first_char_index = this->table.last_char_index;
|
||||
}
|
||||
if (os2->typo_linegap < 0) {
|
||||
OTS_WARNING("bad linegap: %d", os2->typo_linegap);
|
||||
os2->typo_linegap = 0;
|
||||
if (this->table.typo_linegap < 0) {
|
||||
Warning("Bad sTypoLineGap, setting it to 0: %d", this->table.typo_linegap);
|
||||
this->table.typo_linegap = 0;
|
||||
}
|
||||
|
||||
if (os2->version < 1) {
|
||||
if (this->table.version < 1) {
|
||||
// http://www.microsoft.com/typography/otspec/os2ver0.htm
|
||||
return true;
|
||||
}
|
||||
|
||||
if (length < offsetof(OpenTypeOS2, code_page_range_2)) {
|
||||
OTS_WARNING("bad version number: %u", os2->version);
|
||||
if (length < offsetof(OS2Data, code_page_range_2)) {
|
||||
Warning("Bad version number, setting it to 0: %u", this->table.version);
|
||||
// Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version
|
||||
// numbers. Fix them.
|
||||
os2->version = 0;
|
||||
this->table.version = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&os2->code_page_range_1) ||
|
||||
!table.ReadU32(&os2->code_page_range_2)) {
|
||||
return OTS_FAILURE_MSG("Failed to read codepage ranges");
|
||||
if (!table.ReadU32(&this->table.code_page_range_1) ||
|
||||
!table.ReadU32(&this->table.code_page_range_2)) {
|
||||
return Error("Failed to read ulCodePageRange1 or ulCodePageRange2");
|
||||
}
|
||||
|
||||
if (os2->version < 2) {
|
||||
if (this->table.version < 2) {
|
||||
// http://www.microsoft.com/typography/otspec/os2ver1.htm
|
||||
return true;
|
||||
}
|
||||
|
||||
if (length < offsetof(OpenTypeOS2, max_context)) {
|
||||
OTS_WARNING("bad version number: %u", os2->version);
|
||||
if (length < offsetof(OS2Data, max_context)) {
|
||||
Warning("Bad version number, setting it to 1: %u", this->table.version);
|
||||
// some Japanese fonts (e.g., mona.ttf) have weird version number.
|
||||
// fix them.
|
||||
os2->version = 1;
|
||||
this->table.version = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!table.ReadS16(&os2->x_height) ||
|
||||
!table.ReadS16(&os2->cap_height) ||
|
||||
!table.ReadU16(&os2->default_char) ||
|
||||
!table.ReadU16(&os2->break_char) ||
|
||||
!table.ReadU16(&os2->max_context)) {
|
||||
return OTS_FAILURE_MSG("Failed to read version 2-specific fields");
|
||||
if (!table.ReadS16(&this->table.x_height) ||
|
||||
!table.ReadS16(&this->table.cap_height) ||
|
||||
!table.ReadU16(&this->table.default_char) ||
|
||||
!table.ReadU16(&this->table.break_char) ||
|
||||
!table.ReadU16(&this->table.max_context)) {
|
||||
return Error("Failed to read version 2-specific fields");
|
||||
}
|
||||
|
||||
if (os2->x_height < 0) {
|
||||
OTS_WARNING("bad x_height: %d", os2->x_height);
|
||||
os2->x_height = 0;
|
||||
if (this->table.x_height < 0) {
|
||||
Warning("Bad sxHeight settig it to 0: %d", this->table.x_height);
|
||||
this->table.x_height = 0;
|
||||
}
|
||||
if (os2->cap_height < 0) {
|
||||
OTS_WARNING("bad cap_height: %d", os2->cap_height);
|
||||
os2->cap_height = 0;
|
||||
if (this->table.cap_height < 0) {
|
||||
Warning("Bad sCapHeight setting it to 0: %d", this->table.cap_height);
|
||||
this->table.cap_height = 0;
|
||||
}
|
||||
|
||||
if (os2->version < 5) {
|
||||
if (this->table.version < 5) {
|
||||
// http://www.microsoft.com/typography/otspec/os2ver4.htm
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&os2->lower_optical_pointsize) ||
|
||||
!table.ReadU16(&os2->upper_optical_pointsize)) {
|
||||
return OTS_FAILURE_MSG("Failed to read version 5-specific fields");
|
||||
if (!table.ReadU16(&this->table.lower_optical_pointsize) ||
|
||||
!table.ReadU16(&this->table.upper_optical_pointsize)) {
|
||||
return Error("Failed to read version 5-specific fields");
|
||||
}
|
||||
|
||||
if (os2->lower_optical_pointsize > 0xFFFE) {
|
||||
OTS_WARNING("'usLowerOpticalPointSize' is bigger than 0xFFFE: %d", os2->lower_optical_pointsize);
|
||||
os2->lower_optical_pointsize = 0xFFFE;
|
||||
if (this->table.lower_optical_pointsize > 0xFFFE) {
|
||||
Warning("usLowerOpticalPointSize is bigger than 0xFFFE: %d",
|
||||
this->table.lower_optical_pointsize);
|
||||
this->table.lower_optical_pointsize = 0xFFFE;
|
||||
}
|
||||
|
||||
if (os2->upper_optical_pointsize < 2) {
|
||||
OTS_WARNING("'usUpperOpticalPointSize' is lower than 2: %d", os2->upper_optical_pointsize);
|
||||
os2->upper_optical_pointsize = 2;
|
||||
if (this->table.upper_optical_pointsize < 2) {
|
||||
Warning("usUpperOpticalPointSize is lower than 2: %d",
|
||||
this->table.upper_optical_pointsize);
|
||||
this->table.upper_optical_pointsize = 2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_os2_should_serialise(Font *font) {
|
||||
return font->os2 != NULL;
|
||||
}
|
||||
|
||||
bool ots_os2_serialise(OTSStream *out, Font *font) {
|
||||
const OpenTypeOS2 *os2 = font->os2;
|
||||
|
||||
if (!out->WriteU16(os2->version) ||
|
||||
!out->WriteS16(os2->avg_char_width) ||
|
||||
!out->WriteU16(os2->weight_class) ||
|
||||
!out->WriteU16(os2->width_class) ||
|
||||
!out->WriteU16(os2->type) ||
|
||||
!out->WriteS16(os2->subscript_x_size) ||
|
||||
!out->WriteS16(os2->subscript_y_size) ||
|
||||
!out->WriteS16(os2->subscript_x_offset) ||
|
||||
!out->WriteS16(os2->subscript_y_offset) ||
|
||||
!out->WriteS16(os2->superscript_x_size) ||
|
||||
!out->WriteS16(os2->superscript_y_size) ||
|
||||
!out->WriteS16(os2->superscript_x_offset) ||
|
||||
!out->WriteS16(os2->superscript_y_offset) ||
|
||||
!out->WriteS16(os2->strikeout_size) ||
|
||||
!out->WriteS16(os2->strikeout_position) ||
|
||||
!out->WriteS16(os2->family_class)) {
|
||||
return OTS_FAILURE_MSG("Failed to write basic OS2 information");
|
||||
bool OpenTypeOS2::Serialize(OTSStream *out) {
|
||||
if (!out->WriteU16(this->table.version) ||
|
||||
!out->WriteS16(this->table.avg_char_width) ||
|
||||
!out->WriteU16(this->table.weight_class) ||
|
||||
!out->WriteU16(this->table.width_class) ||
|
||||
!out->WriteU16(this->table.type) ||
|
||||
!out->WriteS16(this->table.subscript_x_size) ||
|
||||
!out->WriteS16(this->table.subscript_y_size) ||
|
||||
!out->WriteS16(this->table.subscript_x_offset) ||
|
||||
!out->WriteS16(this->table.subscript_y_offset) ||
|
||||
!out->WriteS16(this->table.superscript_x_size) ||
|
||||
!out->WriteS16(this->table.superscript_y_size) ||
|
||||
!out->WriteS16(this->table.superscript_x_offset) ||
|
||||
!out->WriteS16(this->table.superscript_y_offset) ||
|
||||
!out->WriteS16(this->table.strikeout_size) ||
|
||||
!out->WriteS16(this->table.strikeout_position) ||
|
||||
!out->WriteS16(this->table.family_class)) {
|
||||
return Error("Failed to write basic table data");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 10; ++i) {
|
||||
if (!out->Write(&os2->panose[i], 1)) {
|
||||
return OTS_FAILURE_MSG("Failed to write os2 panose information");
|
||||
if (!out->Write(&this->table.panose[i], 1)) {
|
||||
return Error("Failed to write PANOSE data");
|
||||
}
|
||||
}
|
||||
|
||||
if (!out->WriteU32(os2->unicode_range_1) ||
|
||||
!out->WriteU32(os2->unicode_range_2) ||
|
||||
!out->WriteU32(os2->unicode_range_3) ||
|
||||
!out->WriteU32(os2->unicode_range_4) ||
|
||||
!out->WriteU32(os2->vendor_id) ||
|
||||
!out->WriteU16(os2->selection) ||
|
||||
!out->WriteU16(os2->first_char_index) ||
|
||||
!out->WriteU16(os2->last_char_index) ||
|
||||
!out->WriteS16(os2->typo_ascender) ||
|
||||
!out->WriteS16(os2->typo_descender) ||
|
||||
!out->WriteS16(os2->typo_linegap) ||
|
||||
!out->WriteU16(os2->win_ascent) ||
|
||||
!out->WriteU16(os2->win_descent)) {
|
||||
return OTS_FAILURE_MSG("Failed to write version 1-specific fields");
|
||||
if (!out->WriteU32(this->table.unicode_range_1) ||
|
||||
!out->WriteU32(this->table.unicode_range_2) ||
|
||||
!out->WriteU32(this->table.unicode_range_3) ||
|
||||
!out->WriteU32(this->table.unicode_range_4) ||
|
||||
!out->WriteU32(this->table.vendor_id) ||
|
||||
!out->WriteU16(this->table.selection) ||
|
||||
!out->WriteU16(this->table.first_char_index) ||
|
||||
!out->WriteU16(this->table.last_char_index) ||
|
||||
!out->WriteS16(this->table.typo_ascender) ||
|
||||
!out->WriteS16(this->table.typo_descender) ||
|
||||
!out->WriteS16(this->table.typo_linegap) ||
|
||||
!out->WriteU16(this->table.win_ascent) ||
|
||||
!out->WriteU16(this->table.win_descent)) {
|
||||
return Error("Failed to write version 1-specific fields");
|
||||
}
|
||||
|
||||
if (os2->version < 1) {
|
||||
if (this->table.version < 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!out->WriteU32(os2->code_page_range_1) ||
|
||||
!out->WriteU32(os2->code_page_range_2)) {
|
||||
return OTS_FAILURE_MSG("Failed to write codepage ranges");
|
||||
if (!out->WriteU32(this->table.code_page_range_1) ||
|
||||
!out->WriteU32(this->table.code_page_range_2)) {
|
||||
return Error("Failed to write codepage ranges");
|
||||
}
|
||||
|
||||
if (os2->version < 2) {
|
||||
if (this->table.version < 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!out->WriteS16(os2->x_height) ||
|
||||
!out->WriteS16(os2->cap_height) ||
|
||||
!out->WriteU16(os2->default_char) ||
|
||||
!out->WriteU16(os2->break_char) ||
|
||||
!out->WriteU16(os2->max_context)) {
|
||||
return OTS_FAILURE_MSG("Failed to write version 2-specific fields");
|
||||
if (!out->WriteS16(this->table.x_height) ||
|
||||
!out->WriteS16(this->table.cap_height) ||
|
||||
!out->WriteU16(this->table.default_char) ||
|
||||
!out->WriteU16(this->table.break_char) ||
|
||||
!out->WriteU16(this->table.max_context)) {
|
||||
return Error("Failed to write version 2-specific fields");
|
||||
}
|
||||
|
||||
if (os2->version < 5) {
|
||||
if (this->table.version < 5) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!out->WriteU16(os2->lower_optical_pointsize) ||
|
||||
!out->WriteU16(os2->upper_optical_pointsize)) {
|
||||
return OTS_FAILURE_MSG("Failed to write version 5-specific fields");
|
||||
if (!out->WriteU16(this->table.lower_optical_pointsize) ||
|
||||
!out->WriteU16(this->table.upper_optical_pointsize)) {
|
||||
return Error("Failed to write version 5-specific fields");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_os2_reuse(Font *font, Font *other) {
|
||||
font->os2 = other->os2;
|
||||
font->os2_reused = true;
|
||||
}
|
||||
|
||||
void ots_os2_free(Font *font) {
|
||||
delete font->os2;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeOS2 {
|
||||
struct OS2Data {
|
||||
uint16_t version;
|
||||
int16_t avg_char_width;
|
||||
uint16_t weight_class;
|
||||
|
@ -51,6 +51,17 @@ struct OpenTypeOS2 {
|
|||
uint16_t upper_optical_pointsize;
|
||||
};
|
||||
|
||||
class OpenTypeOS2 : public Table {
|
||||
public:
|
||||
explicit OpenTypeOS2(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
OS2Data table;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_OS2_H_
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -14,44 +14,64 @@
|
|||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "woff2_dec.h"
|
||||
#include <woff2/decode.h>
|
||||
|
||||
// The OpenType Font File
|
||||
// http://www.microsoft.com/typography/otspec/cmap.htm
|
||||
// http://www.microsoft.com/typography/otspec/otff.htm
|
||||
|
||||
namespace {
|
||||
#include "avar.h"
|
||||
#include "cff.h"
|
||||
#include "cmap.h"
|
||||
#include "cvar.h"
|
||||
#include "cvt.h"
|
||||
#include "fpgm.h"
|
||||
#include "fvar.h"
|
||||
#include "gasp.h"
|
||||
#include "gdef.h"
|
||||
#include "glyf.h"
|
||||
#include "gpos.h"
|
||||
#include "gsub.h"
|
||||
#include "gvar.h"
|
||||
#include "hdmx.h"
|
||||
#include "head.h"
|
||||
#include "hhea.h"
|
||||
#include "hmtx.h"
|
||||
#include "hvar.h"
|
||||
#include "kern.h"
|
||||
#include "loca.h"
|
||||
#include "ltsh.h"
|
||||
#include "math_.h"
|
||||
#include "maxp.h"
|
||||
#include "mvar.h"
|
||||
#include "name.h"
|
||||
#include "os2.h"
|
||||
#include "ots.h"
|
||||
#include "post.h"
|
||||
#include "prep.h"
|
||||
#include "stat.h"
|
||||
#include "vdmx.h"
|
||||
#include "vhea.h"
|
||||
#include "vmtx.h"
|
||||
#include "vorg.h"
|
||||
#include "vvar.h"
|
||||
|
||||
// Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
|
||||
#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
|
||||
#define OTS_FAILURE_MSG_HDR(msg_) OTS_FAILURE_MSG_(header, msg_)
|
||||
#define OTS_WARNING_MSG_HDR(msg_) OTS_WARNING_MSG_(header, msg_)
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
#include "feat.h"
|
||||
#include "glat.h"
|
||||
#include "gloc.h"
|
||||
#include "sile.h"
|
||||
#include "silf.h"
|
||||
#include "sill.h"
|
||||
#endif
|
||||
|
||||
|
||||
struct OpenTypeTable {
|
||||
uint32_t tag;
|
||||
uint32_t chksum;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
uint32_t uncompressed_length;
|
||||
};
|
||||
|
||||
bool CheckTag(uint32_t tag_value) {
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
const uint32_t check = tag_value & 0xff;
|
||||
if (check < 32 || check > 126) {
|
||||
return false; // non-ASCII character found.
|
||||
}
|
||||
tag_value >>= 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
namespace ots {
|
||||
|
||||
struct Arena {
|
||||
public:
|
||||
~Arena() {
|
||||
for (std::vector<uint8_t*>::iterator
|
||||
i = hunks_.begin(); i != hunks_.end(); ++i) {
|
||||
delete[] *i;
|
||||
for (auto& hunk : hunks_) {
|
||||
delete[] hunk;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,80 +85,94 @@ struct Arena {
|
|||
std::vector<uint8_t*> hunks_;
|
||||
};
|
||||
|
||||
bool CheckTag(uint32_t tag_value) {
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
const uint32_t check = tag_value & 0xff;
|
||||
if (check < 32 || check > 126) {
|
||||
return false; // non-ASCII character found.
|
||||
}
|
||||
tag_value >>= 8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace ots
|
||||
|
||||
namespace {
|
||||
|
||||
#define OTS_MSG_TAG_(level,otf_,msg_,tag_) \
|
||||
(OTS_MESSAGE_(level,otf_,"%c%c%c%c: %s", OTS_UNTAG(tag_), msg_), false)
|
||||
|
||||
// Generate a message with or without a table tag, when 'header' is the FontFile pointer
|
||||
#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_MSG_TAG_(0, header, msg_, tag_)
|
||||
#define OTS_FAILURE_MSG_HDR(...) OTS_FAILURE_MSG_(header, __VA_ARGS__)
|
||||
#define OTS_WARNING_MSG_HDR(...) OTS_WARNING_MSG_(header, __VA_ARGS__)
|
||||
|
||||
|
||||
const struct {
|
||||
uint32_t tag;
|
||||
bool (*parse)(ots::Font *font, const uint8_t *data, size_t length);
|
||||
bool (*serialise)(ots::OTSStream *out, ots::Font *font);
|
||||
bool (*should_serialise)(ots::Font *font);
|
||||
void (*reuse)(ots::Font *font, ots::Font *other);
|
||||
bool required;
|
||||
} table_parsers[] = {
|
||||
{ OTS_TAG('m','a','x','p'), ots::ots_maxp_parse, ots::ots_maxp_serialise,
|
||||
ots::ots_maxp_should_serialise, ots::ots_maxp_reuse, true },
|
||||
{ OTS_TAG('h','e','a','d'), ots::ots_head_parse, ots::ots_head_serialise,
|
||||
ots::ots_head_should_serialise, ots::ots_head_reuse, true },
|
||||
{ OTS_TAG('O','S','/','2'), ots::ots_os2_parse, ots::ots_os2_serialise,
|
||||
ots::ots_os2_should_serialise, ots::ots_os2_reuse, true },
|
||||
{ OTS_TAG('c','m','a','p'), ots::ots_cmap_parse, ots::ots_cmap_serialise,
|
||||
ots::ots_cmap_should_serialise, ots::ots_cmap_reuse, true },
|
||||
{ OTS_TAG('h','h','e','a'), ots::ots_hhea_parse, ots::ots_hhea_serialise,
|
||||
ots::ots_hhea_should_serialise, ots::ots_hhea_reuse, true },
|
||||
{ OTS_TAG('h','m','t','x'), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
|
||||
ots::ots_hmtx_should_serialise, ots::ots_hmtx_reuse, true },
|
||||
{ OTS_TAG('n','a','m','e'), ots::ots_name_parse, ots::ots_name_serialise,
|
||||
ots::ots_name_should_serialise, ots::ots_name_reuse, true },
|
||||
{ OTS_TAG('p','o','s','t'), ots::ots_post_parse, ots::ots_post_serialise,
|
||||
ots::ots_post_should_serialise, ots::ots_post_reuse, true },
|
||||
{ OTS_TAG('l','o','c','a'), ots::ots_loca_parse, ots::ots_loca_serialise,
|
||||
ots::ots_loca_should_serialise, ots::ots_loca_reuse, false },
|
||||
{ OTS_TAG('g','l','y','f'), ots::ots_glyf_parse, ots::ots_glyf_serialise,
|
||||
ots::ots_glyf_should_serialise, ots::ots_glyf_reuse, false },
|
||||
{ OTS_TAG('C','F','F',' '), ots::ots_cff_parse, ots::ots_cff_serialise,
|
||||
ots::ots_cff_should_serialise, ots::ots_cff_reuse, false },
|
||||
{ OTS_TAG('V','D','M','X'), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
|
||||
ots::ots_vdmx_should_serialise, ots::ots_vdmx_reuse, false },
|
||||
{ OTS_TAG('h','d','m','x'), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
|
||||
ots::ots_hdmx_should_serialise, ots::ots_hdmx_reuse, false },
|
||||
{ OTS_TAG('g','a','s','p'), ots::ots_gasp_parse, ots::ots_gasp_serialise,
|
||||
ots::ots_gasp_should_serialise, ots::ots_gasp_reuse, false },
|
||||
{ OTS_TAG('c','v','t',' '), ots::ots_cvt_parse, ots::ots_cvt_serialise,
|
||||
ots::ots_cvt_should_serialise, ots::ots_cvt_reuse, false },
|
||||
{ OTS_TAG('f','p','g','m'), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
|
||||
ots::ots_fpgm_should_serialise, ots::ots_fpgm_reuse, false },
|
||||
{ OTS_TAG('p','r','e','p'), ots::ots_prep_parse, ots::ots_prep_serialise,
|
||||
ots::ots_prep_should_serialise, ots::ots_prep_reuse, false },
|
||||
{ OTS_TAG('L','T','S','H'), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
|
||||
ots::ots_ltsh_should_serialise, ots::ots_ltsh_reuse, false },
|
||||
{ OTS_TAG('V','O','R','G'), ots::ots_vorg_parse, ots::ots_vorg_serialise,
|
||||
ots::ots_vorg_should_serialise, ots::ots_vorg_reuse, false },
|
||||
{ OTS_TAG('k','e','r','n'), ots::ots_kern_parse, ots::ots_kern_serialise,
|
||||
ots::ots_kern_should_serialise, ots::ots_kern_reuse, false },
|
||||
} supported_tables[] = {
|
||||
{ OTS_TAG_MAXP, true },
|
||||
{ OTS_TAG_HEAD, true },
|
||||
{ OTS_TAG_OS2, true },
|
||||
{ OTS_TAG_CMAP, true },
|
||||
{ OTS_TAG_HHEA, true },
|
||||
{ OTS_TAG_HMTX, true },
|
||||
{ OTS_TAG_NAME, true },
|
||||
{ OTS_TAG_POST, true },
|
||||
{ OTS_TAG_LOCA, false },
|
||||
{ OTS_TAG_GLYF, false },
|
||||
{ OTS_TAG_CFF, false },
|
||||
{ OTS_TAG_VDMX, false },
|
||||
{ OTS_TAG_HDMX, false },
|
||||
{ OTS_TAG_GASP, false },
|
||||
{ OTS_TAG_CVT, false },
|
||||
{ OTS_TAG_FPGM, false },
|
||||
{ OTS_TAG_PREP, false },
|
||||
{ OTS_TAG_LTSH, false },
|
||||
{ OTS_TAG_VORG, false },
|
||||
{ OTS_TAG_KERN, false },
|
||||
// We need to parse fvar table before other tables that may need to know
|
||||
// the number of variation axes (if any)
|
||||
{ OTS_TAG_FVAR, false },
|
||||
{ OTS_TAG_AVAR, false },
|
||||
{ OTS_TAG_CVAR, false },
|
||||
{ OTS_TAG_GVAR, false },
|
||||
{ OTS_TAG_HVAR, false },
|
||||
{ OTS_TAG_MVAR, false },
|
||||
{ OTS_TAG_STAT, false },
|
||||
{ OTS_TAG_VVAR, false },
|
||||
{ OTS_TAG_CFF2, false },
|
||||
// We need to parse GDEF table in advance of parsing GSUB/GPOS tables
|
||||
// because they could refer GDEF table.
|
||||
{ OTS_TAG('G','D','E','F'), ots::ots_gdef_parse, ots::ots_gdef_serialise,
|
||||
ots::ots_gdef_should_serialise, ots::ots_gdef_reuse, false },
|
||||
{ OTS_TAG('G','P','O','S'), ots::ots_gpos_parse, ots::ots_gpos_serialise,
|
||||
ots::ots_gpos_should_serialise, ots::ots_gpos_reuse, false },
|
||||
{ OTS_TAG('G','S','U','B'), ots::ots_gsub_parse, ots::ots_gsub_serialise,
|
||||
ots::ots_gsub_should_serialise, ots::ots_gsub_reuse, false },
|
||||
{ OTS_TAG('v','h','e','a'), ots::ots_vhea_parse, ots::ots_vhea_serialise,
|
||||
ots::ots_vhea_should_serialise, ots::ots_vhea_reuse, false },
|
||||
{ OTS_TAG('v','m','t','x'), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
|
||||
ots::ots_vmtx_should_serialise, ots::ots_vmtx_reuse, false },
|
||||
{ OTS_TAG('M','A','T','H'), ots::ots_math_parse, ots::ots_math_serialise,
|
||||
ots::ots_math_should_serialise, ots::ots_math_reuse, false },
|
||||
{ 0, NULL, NULL, NULL, NULL, false },
|
||||
{ OTS_TAG_GDEF, false },
|
||||
{ OTS_TAG_GPOS, false },
|
||||
{ OTS_TAG_GSUB, false },
|
||||
{ OTS_TAG_VHEA, false },
|
||||
{ OTS_TAG_VMTX, false },
|
||||
{ OTS_TAG_MATH, false },
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
{ OTS_TAG_GLOC, false },
|
||||
{ OTS_TAG_GLAT, false },
|
||||
{ OTS_TAG_FEAT, false },
|
||||
{ OTS_TAG_SILF, false },
|
||||
{ OTS_TAG_SILE, false },
|
||||
{ OTS_TAG_SILL, false },
|
||||
#endif
|
||||
{ 0, false },
|
||||
};
|
||||
|
||||
bool ProcessGeneric(ots::OpenTypeFile *header,
|
||||
bool ProcessGeneric(ots::FontFile *header,
|
||||
ots::Font *font,
|
||||
uint32_t signature,
|
||||
ots::OTSStream *output,
|
||||
const uint8_t *data, size_t length,
|
||||
const std::vector<OpenTypeTable>& tables,
|
||||
const std::vector<ots::TableEntry>& tables,
|
||||
ots::Buffer& file);
|
||||
|
||||
bool ProcessTTF(ots::OpenTypeFile *header,
|
||||
bool ProcessTTF(ots::FontFile *header,
|
||||
ots::Font *font,
|
||||
ots::OTSStream *output, const uint8_t *data, size_t length,
|
||||
uint32_t offset = 0) {
|
||||
|
@ -202,10 +236,10 @@ bool ProcessTTF(ots::OpenTypeFile *header,
|
|||
}
|
||||
|
||||
// Next up is the list of tables.
|
||||
std::vector<OpenTypeTable> tables;
|
||||
std::vector<ots::TableEntry> tables;
|
||||
|
||||
for (unsigned i = 0; i < font->num_tables; ++i) {
|
||||
OpenTypeTable table;
|
||||
ots::TableEntry table;
|
||||
if (!file.ReadU32(&table.tag) ||
|
||||
!file.ReadU32(&table.chksum) ||
|
||||
!file.ReadU32(&table.offset) ||
|
||||
|
@ -221,7 +255,7 @@ bool ProcessTTF(ots::OpenTypeFile *header,
|
|||
tables, file);
|
||||
}
|
||||
|
||||
bool ProcessTTC(ots::OpenTypeFile *header,
|
||||
bool ProcessTTC(ots::FontFile *header,
|
||||
ots::OTSStream *output,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
|
@ -308,7 +342,7 @@ bool ProcessTTC(ots::OpenTypeFile *header,
|
|||
}
|
||||
}
|
||||
|
||||
bool ProcessWOFF(ots::OpenTypeFile *header,
|
||||
bool ProcessWOFF(ots::FontFile *header,
|
||||
ots::Font *font,
|
||||
ots::OTSStream *output, const uint8_t *data, size_t length) {
|
||||
ots::Buffer file(data, length);
|
||||
|
@ -388,14 +422,14 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||
}
|
||||
|
||||
// Next up is the list of tables.
|
||||
std::vector<OpenTypeTable> tables;
|
||||
std::vector<ots::TableEntry> tables;
|
||||
|
||||
uint32_t first_index = 0;
|
||||
uint32_t last_index = 0;
|
||||
// Size of sfnt header plus size of table records.
|
||||
uint64_t total_sfnt_size = 12 + 16 * font->num_tables;
|
||||
for (unsigned i = 0; i < font->num_tables; ++i) {
|
||||
OpenTypeTable table;
|
||||
ots::TableEntry table;
|
||||
if (!file.ReadU32(&table.tag) ||
|
||||
!file.ReadU32(&table.offset) ||
|
||||
!file.ReadU32(&table.length) ||
|
||||
|
@ -463,13 +497,17 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||
return ProcessGeneric(header, font, woff_tag, output, data, length, tables, file);
|
||||
}
|
||||
|
||||
bool ProcessWOFF2(ots::OpenTypeFile *header,
|
||||
bool ProcessWOFF2(ots::FontFile *header,
|
||||
ots::OTSStream *output,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
uint32_t index) {
|
||||
size_t decompressed_size = woff2::ComputeWOFF2FinalSize(data, length);
|
||||
|
||||
if (decompressed_size < length) {
|
||||
return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is less than compressed size");
|
||||
}
|
||||
|
||||
if (decompressed_size == 0) {
|
||||
return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0");
|
||||
}
|
||||
|
@ -493,16 +531,16 @@ bool ProcessWOFF2(ots::OpenTypeFile *header,
|
|||
}
|
||||
}
|
||||
|
||||
ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
|
||||
ots::TableAction GetTableAction(const ots::FontFile *header, uint32_t tag) {
|
||||
ots::TableAction action = header->context->GetTableAction(tag);
|
||||
|
||||
if (action == ots::TABLE_ACTION_DEFAULT) {
|
||||
action = ots::TABLE_ACTION_DROP;
|
||||
|
||||
for (unsigned i = 0; ; ++i) {
|
||||
if (table_parsers[i].parse == NULL) break;
|
||||
if (supported_tables[i].tag == 0) break;
|
||||
|
||||
if (table_parsers[i].tag == tag) {
|
||||
if (supported_tables[i].tag == tag) {
|
||||
action = ots::TABLE_ACTION_SANITIZE;
|
||||
break;
|
||||
}
|
||||
|
@ -514,14 +552,14 @@ ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
|
|||
}
|
||||
|
||||
bool GetTableData(const uint8_t *data,
|
||||
const OpenTypeTable& table,
|
||||
Arena *arena,
|
||||
const ots::TableEntry& table,
|
||||
ots::Arena &arena,
|
||||
size_t *table_length,
|
||||
const uint8_t **table_data) {
|
||||
if (table.uncompressed_length != table.length) {
|
||||
// Compressed table. Need to uncompress into memory first.
|
||||
*table_length = table.uncompressed_length;
|
||||
*table_data = (*arena).Allocate(*table_length);
|
||||
*table_data = arena.Allocate(*table_length);
|
||||
uLongf dest_len = *table_length;
|
||||
int r = uncompress((Bytef*) *table_data, &dest_len,
|
||||
data + table.offset, table.length);
|
||||
|
@ -537,12 +575,12 @@ bool GetTableData(const uint8_t *data,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ProcessGeneric(ots::OpenTypeFile *header,
|
||||
bool ProcessGeneric(ots::FontFile *header,
|
||||
ots::Font *font,
|
||||
uint32_t signature,
|
||||
ots::OTSStream *output,
|
||||
const uint8_t *data, size_t length,
|
||||
const std::vector<OpenTypeTable>& tables,
|
||||
const std::vector<ots::TableEntry>& tables,
|
||||
ots::Buffer& file) {
|
||||
const size_t data_offset = file.offset();
|
||||
|
||||
|
@ -560,8 +598,8 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
|
|||
}
|
||||
|
||||
// all tag names must be built from printable ASCII characters
|
||||
if (!CheckTag(tables[i].tag)) {
|
||||
return OTS_FAILURE_MSG_TAG("invalid table tag", tables[i].tag);
|
||||
if (!ots::CheckTag(tables[i].tag)) {
|
||||
OTS_WARNING_MSG_HDR("Invalid table tag: 0x%X", tables[i].tag);
|
||||
}
|
||||
|
||||
// tables must be 4-byte aligned
|
||||
|
@ -616,11 +654,6 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
|
|||
return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
|
||||
}
|
||||
|
||||
std::map<uint32_t, OpenTypeTable> table_map;
|
||||
for (unsigned i = 0; i < font->num_tables; ++i) {
|
||||
table_map[tables[i].tag] = tables[i];
|
||||
}
|
||||
|
||||
// check that the tables are not overlapping.
|
||||
std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
|
||||
for (unsigned i = 0; i < font->num_tables; ++i) {
|
||||
|
@ -639,80 +672,66 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
|
|||
}
|
||||
}
|
||||
|
||||
Arena arena;
|
||||
std::map<uint32_t, ots::TableEntry> table_map;
|
||||
for (unsigned i = 0; i < font->num_tables; ++i) {
|
||||
table_map[tables[i].tag] = tables[i];
|
||||
}
|
||||
|
||||
ots::Arena arena;
|
||||
// Parse known tables first as we need to parse them in specific order.
|
||||
for (unsigned i = 0; ; ++i) {
|
||||
if (table_parsers[i].parse == NULL) break;
|
||||
if (supported_tables[i].tag == 0) break;
|
||||
|
||||
uint32_t tag = table_parsers[i].tag;
|
||||
const std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.find(tag);
|
||||
|
||||
ots::TableAction action = GetTableAction(header, tag);
|
||||
if (it == table_map.end()) {
|
||||
if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
|
||||
return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
|
||||
uint32_t tag = supported_tables[i].tag;
|
||||
const auto &it = table_map.find(tag);
|
||||
if (it == table_map.cend()) {
|
||||
if (supported_tables[i].required) {
|
||||
return OTS_FAILURE_MSG_TAG("missing required table", tag);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t input_offset = it->second.offset;
|
||||
const ots::TableMap::const_iterator ot = header->tables.find(input_offset);
|
||||
if (ot == header->tables.end()) {
|
||||
const uint8_t* table_data;
|
||||
size_t table_length;
|
||||
|
||||
if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
|
||||
return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag);
|
||||
} else {
|
||||
if (!font->ParseTable(it->second, data, arena)) {
|
||||
return OTS_FAILURE_MSG_TAG("Failed to parse table", tag);
|
||||
}
|
||||
|
||||
if (action == ots::TABLE_ACTION_SANITIZE &&
|
||||
!table_parsers[i].parse(font, table_data, table_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else if (action == ots::TABLE_ACTION_SANITIZE) {
|
||||
table_parsers[i].reuse(font, ot->second.first);
|
||||
}
|
||||
}
|
||||
|
||||
if (font->cff) {
|
||||
// Then parse any tables left.
|
||||
for (const auto &table_entry : tables) {
|
||||
if (!font->GetTable(table_entry.tag)) {
|
||||
if (!font->ParseTable(table_entry, data, arena)) {
|
||||
return OTS_FAILURE_MSG_TAG("Failed to parse table", table_entry.tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (font->GetTable(OTS_TAG_CFF) || font->GetTable(OTS_TAG_CFF2)) {
|
||||
// font with PostScript glyph
|
||||
if (font->version != OTS_TAG('O','T','T','O')) {
|
||||
return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
|
||||
}
|
||||
if (font->glyf || font->loca) {
|
||||
if (font->GetTable(OTS_TAG_GLYF) || font->GetTable(OTS_TAG_LOCA)) {
|
||||
// mixing outline formats is not recommended
|
||||
return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
|
||||
}
|
||||
} else {
|
||||
if (!font->glyf || !font->loca) {
|
||||
if (!font->GetTable(OTS_TAG_GLYF) || !font->GetTable(OTS_TAG_LOCA)) {
|
||||
// No TrueType glyph found.
|
||||
#define PASSTHRU_TABLE(tag_) (table_map.find(tag_) != table_map.end() && \
|
||||
GetTableAction(header, tag_) == ots::TABLE_ACTION_PASSTHRU)
|
||||
// We don't sanitise bitmap table, but don't reject bitmap-only fonts if
|
||||
// we keep the tables.
|
||||
if (!PASSTHRU_TABLE(OTS_TAG('C','B','D','T')) ||
|
||||
!PASSTHRU_TABLE(OTS_TAG('C','B','L','C'))) {
|
||||
//
|
||||
// We don't sanitize bitmap tables, but don’t reject bitmap-only fonts if
|
||||
// we are asked to pass them thru.
|
||||
// Also don’t reject if we are asked to pass glyf/loca thru.
|
||||
if (!font->GetTable(OTS_TAG('C','B','D','T')) &&
|
||||
!font->GetTable(OTS_TAG('C','B','L','C'))) {
|
||||
return OTS_FAILURE_MSG_HDR("no supported glyph shapes table(s) present");
|
||||
}
|
||||
#undef PASSTHRU_TABLE
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t num_output_tables = 0;
|
||||
for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
|
||||
it != table_map.end(); ++it) {
|
||||
ots::TableAction action = GetTableAction(header, it->first);
|
||||
if (action == ots::TABLE_ACTION_PASSTHRU) {
|
||||
for (const auto &it : table_map) {
|
||||
ots::Table *table = font->GetTable(it.first);
|
||||
if (table != NULL && table->ShouldSerialize())
|
||||
num_output_tables++;
|
||||
} else {
|
||||
for (unsigned i = 0; table_parsers[i].parse != NULL; ++i) {
|
||||
if (table_parsers[i].tag == it->first &&
|
||||
table_parsers[i].should_serialise(font)) {
|
||||
num_output_tables++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t max_pow2 = 0;
|
||||
|
@ -738,85 +757,49 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
|
|||
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||
}
|
||||
|
||||
std::vector<ots::OutputTable> out_tables;
|
||||
std::vector<ots::TableEntry> out_tables;
|
||||
|
||||
size_t head_table_offset = 0;
|
||||
for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
|
||||
it != table_map.end(); ++it) {
|
||||
uint32_t input_offset = it->second.offset;
|
||||
const ots::TableMap::const_iterator ot = header->tables.find(input_offset);
|
||||
if (ot != header->tables.end()) {
|
||||
ots::OutputTable out = ot->second.second;
|
||||
for (const auto &it : table_map) {
|
||||
uint32_t input_offset = it.second.offset;
|
||||
const auto &ot = header->table_entries.find(input_offset);
|
||||
if (ot != header->table_entries.end()) {
|
||||
ots::TableEntry out = ot->second;
|
||||
if (out.tag == OTS_TAG('h','e','a','d')) {
|
||||
head_table_offset = out.offset;
|
||||
}
|
||||
out_tables.push_back(out);
|
||||
} else {
|
||||
ots::OutputTable out;
|
||||
out.tag = it->first;
|
||||
ots::TableEntry out;
|
||||
out.tag = it.first;
|
||||
out.offset = output->Tell();
|
||||
|
||||
if (out.tag == OTS_TAG('h','e','a','d')) {
|
||||
head_table_offset = out.offset;
|
||||
}
|
||||
|
||||
ots::TableAction action = GetTableAction(header, it->first);
|
||||
if (action == ots::TABLE_ACTION_PASSTHRU) {
|
||||
ots::Table *table = font->GetTable(out.tag);
|
||||
if (table != NULL && table->ShouldSerialize()) {
|
||||
output->ResetChecksum();
|
||||
const uint8_t* table_data;
|
||||
size_t table_length;
|
||||
|
||||
if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
|
||||
return OTS_FAILURE_MSG_HDR("Failed to uncompress table");
|
||||
}
|
||||
|
||||
if (!output->Write(table_data, table_length)) {
|
||||
return OTS_FAILURE_MSG_HDR("Failed to serialize table");
|
||||
if (!table->Serialize(output)) {
|
||||
return OTS_FAILURE_MSG_TAG("Failed to serialize table", out.tag);
|
||||
}
|
||||
|
||||
const size_t end_offset = output->Tell();
|
||||
if (end_offset <= out.offset) {
|
||||
// paranoid check. |end_offset| is supposed to be greater than the offset,
|
||||
// as long as the Tell() interface is implemented correctly.
|
||||
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||
return OTS_FAILURE_MSG_TAG("Table is empty or have -ve size", out.tag);
|
||||
}
|
||||
out.length = end_offset - out.offset;
|
||||
|
||||
// align tables to four bytes
|
||||
if (!output->Pad((4 - (end_offset & 3)) % 4)) {
|
||||
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||
return OTS_FAILURE_MSG_TAG("Failed to pad table to 4 bytes", out.tag);
|
||||
}
|
||||
out.chksum = output->chksum();
|
||||
out_tables.push_back(out);
|
||||
header->tables[input_offset] = std::make_pair(font, out);
|
||||
} else {
|
||||
for (unsigned i = 0; table_parsers[i].parse != NULL; ++i) {
|
||||
if (table_parsers[i].tag == it->first &&
|
||||
table_parsers[i].should_serialise(font)) {
|
||||
output->ResetChecksum();
|
||||
if (!table_parsers[i].serialise(output, font)) {
|
||||
return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag);
|
||||
}
|
||||
|
||||
const size_t end_offset = output->Tell();
|
||||
if (end_offset <= out.offset) {
|
||||
// paranoid check. |end_offset| is supposed to be greater than the offset,
|
||||
// as long as the Tell() interface is implemented correctly.
|
||||
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||
}
|
||||
out.length = end_offset - out.offset;
|
||||
|
||||
// align tables to four bytes
|
||||
if (!output->Pad((4 - (end_offset & 3)) % 4)) {
|
||||
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||
}
|
||||
out.chksum = output->chksum();
|
||||
out_tables.push_back(out);
|
||||
header->tables[input_offset] = std::make_pair(font, out);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
header->table_entries[input_offset] = out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -869,6 +852,225 @@ bool ProcessGeneric(ots::OpenTypeFile *header,
|
|||
|
||||
namespace ots {
|
||||
|
||||
FontFile::~FontFile() {
|
||||
for (const auto& it : tables) {
|
||||
delete it.second;
|
||||
}
|
||||
tables.clear();
|
||||
}
|
||||
|
||||
bool Font::ParseTable(const TableEntry& table_entry, const uint8_t* data,
|
||||
Arena &arena) {
|
||||
uint32_t tag = table_entry.tag;
|
||||
TableAction action = GetTableAction(file, tag);
|
||||
if (action == TABLE_ACTION_DROP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &it = file->tables.find(table_entry);
|
||||
if (it != file->tables.end()) {
|
||||
m_tables[tag] = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
Table *table = NULL;
|
||||
bool ret = false;
|
||||
|
||||
if (action == TABLE_ACTION_PASSTHRU) {
|
||||
table = new TablePassthru(this, tag);
|
||||
} else {
|
||||
switch (tag) {
|
||||
case OTS_TAG_AVAR: table = new OpenTypeAVAR(this, tag); break;
|
||||
case OTS_TAG_CFF: table = new OpenTypeCFF(this, tag); break;
|
||||
case OTS_TAG_CFF2: table = new OpenTypeCFF2(this, tag); break;
|
||||
case OTS_TAG_CMAP: table = new OpenTypeCMAP(this, tag); break;
|
||||
case OTS_TAG_CVAR: table = new OpenTypeCVAR(this, tag); break;
|
||||
case OTS_TAG_CVT: table = new OpenTypeCVT(this, tag); break;
|
||||
case OTS_TAG_FPGM: table = new OpenTypeFPGM(this, tag); break;
|
||||
case OTS_TAG_FVAR: table = new OpenTypeFVAR(this, tag); break;
|
||||
case OTS_TAG_GASP: table = new OpenTypeGASP(this, tag); break;
|
||||
case OTS_TAG_GDEF: table = new OpenTypeGDEF(this, tag); break;
|
||||
case OTS_TAG_GLYF: table = new OpenTypeGLYF(this, tag); break;
|
||||
case OTS_TAG_GPOS: table = new OpenTypeGPOS(this, tag); break;
|
||||
case OTS_TAG_GSUB: table = new OpenTypeGSUB(this, tag); break;
|
||||
case OTS_TAG_GVAR: table = new OpenTypeGVAR(this, tag); break;
|
||||
case OTS_TAG_HDMX: table = new OpenTypeHDMX(this, tag); break;
|
||||
case OTS_TAG_HEAD: table = new OpenTypeHEAD(this, tag); break;
|
||||
case OTS_TAG_HHEA: table = new OpenTypeHHEA(this, tag); break;
|
||||
case OTS_TAG_HMTX: table = new OpenTypeHMTX(this, tag); break;
|
||||
case OTS_TAG_HVAR: table = new OpenTypeHVAR(this, tag); break;
|
||||
case OTS_TAG_KERN: table = new OpenTypeKERN(this, tag); break;
|
||||
case OTS_TAG_LOCA: table = new OpenTypeLOCA(this, tag); break;
|
||||
case OTS_TAG_LTSH: table = new OpenTypeLTSH(this, tag); break;
|
||||
case OTS_TAG_MATH: table = new OpenTypeMATH(this, tag); break;
|
||||
case OTS_TAG_MAXP: table = new OpenTypeMAXP(this, tag); break;
|
||||
case OTS_TAG_MVAR: table = new OpenTypeMVAR(this, tag); break;
|
||||
case OTS_TAG_NAME: table = new OpenTypeNAME(this, tag); break;
|
||||
case OTS_TAG_OS2: table = new OpenTypeOS2(this, tag); break;
|
||||
case OTS_TAG_POST: table = new OpenTypePOST(this, tag); break;
|
||||
case OTS_TAG_PREP: table = new OpenTypePREP(this, tag); break;
|
||||
case OTS_TAG_STAT: table = new OpenTypeSTAT(this, tag); break;
|
||||
case OTS_TAG_VDMX: table = new OpenTypeVDMX(this, tag); break;
|
||||
case OTS_TAG_VHEA: table = new OpenTypeVHEA(this, tag); break;
|
||||
case OTS_TAG_VMTX: table = new OpenTypeVMTX(this, tag); break;
|
||||
case OTS_TAG_VORG: table = new OpenTypeVORG(this, tag); break;
|
||||
case OTS_TAG_VVAR: table = new OpenTypeVVAR(this, tag); break;
|
||||
// Graphite tables
|
||||
#ifdef OTS_GRAPHITE
|
||||
case OTS_TAG_FEAT: table = new OpenTypeFEAT(this, tag); break;
|
||||
case OTS_TAG_GLAT: table = new OpenTypeGLAT(this, tag); break;
|
||||
case OTS_TAG_GLOC: table = new OpenTypeGLOC(this, tag); break;
|
||||
case OTS_TAG_SILE: table = new OpenTypeSILE(this, tag); break;
|
||||
case OTS_TAG_SILF: table = new OpenTypeSILF(this, tag); break;
|
||||
case OTS_TAG_SILL: table = new OpenTypeSILL(this, tag); break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (table) {
|
||||
const uint8_t* table_data;
|
||||
size_t table_length;
|
||||
|
||||
ret = GetTableData(data, table_entry, arena, &table_length, &table_data);
|
||||
if (ret) {
|
||||
// FIXME: Parsing some tables will fail if the table is not added to
|
||||
// m_tables first.
|
||||
m_tables[tag] = table;
|
||||
ret = table->Parse(table_data, table_length);
|
||||
if (ret)
|
||||
file->tables[table_entry] = table;
|
||||
else
|
||||
m_tables.erase(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
delete table;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Table* Font::GetTable(uint32_t tag) const {
|
||||
const auto &it = m_tables.find(tag);
|
||||
if (it != m_tables.end())
|
||||
return it->second;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Table* Font::GetTypedTable(uint32_t tag) const {
|
||||
Table* t = GetTable(tag);
|
||||
if (t && t->Type() == tag)
|
||||
return t;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Font::DropGraphite() {
|
||||
file->context->Message(0, "Dropping all Graphite tables");
|
||||
for (const std::pair<uint32_t, Table*> entry : m_tables) {
|
||||
if (entry.first == OTS_TAG_FEAT ||
|
||||
entry.first == OTS_TAG_GLAT ||
|
||||
entry.first == OTS_TAG_GLOC ||
|
||||
entry.first == OTS_TAG_SILE ||
|
||||
entry.first == OTS_TAG_SILF ||
|
||||
entry.first == OTS_TAG_SILL) {
|
||||
entry.second->Drop("Discarding Graphite table");
|
||||
}
|
||||
}
|
||||
dropped_graphite = true;
|
||||
}
|
||||
|
||||
void Font::DropVariations() {
|
||||
file->context->Message(0, "Dropping all Variation tables");
|
||||
for (const std::pair<uint32_t, Table*> entry : m_tables) {
|
||||
if (entry.first == OTS_TAG_AVAR ||
|
||||
entry.first == OTS_TAG_CVAR ||
|
||||
entry.first == OTS_TAG_FVAR ||
|
||||
entry.first == OTS_TAG_GVAR ||
|
||||
entry.first == OTS_TAG_HVAR ||
|
||||
entry.first == OTS_TAG_MVAR ||
|
||||
entry.first == OTS_TAG_STAT ||
|
||||
entry.first == OTS_TAG_VVAR) {
|
||||
entry.second->Drop("Discarding Variations table");
|
||||
}
|
||||
}
|
||||
dropped_variations = true;
|
||||
}
|
||||
|
||||
bool Table::ShouldSerialize() {
|
||||
return m_shouldSerialize;
|
||||
}
|
||||
|
||||
void Table::Message(int level, const char *format, va_list va) {
|
||||
char msg[206] = { OTS_UNTAG(m_tag), ':', ' ' };
|
||||
std::vsnprintf(msg + 6, 200, format, va);
|
||||
m_font->file->context->Message(level, msg);
|
||||
}
|
||||
|
||||
bool Table::Error(const char *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
Message(0, format, va);
|
||||
va_end(va);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Table::Warning(const char *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
Message(1, format, va);
|
||||
va_end(va);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Table::Drop(const char *format, ...) {
|
||||
m_shouldSerialize = false;
|
||||
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
Message(0, format, va);
|
||||
m_font->file->context->Message(0, "Table discarded");
|
||||
va_end(va);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Table::DropGraphite(const char *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
Message(0, format, va);
|
||||
va_end(va);
|
||||
|
||||
m_font->DropGraphite();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Table::DropVariations(const char *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
Message(0, format, va);
|
||||
va_end(va);
|
||||
|
||||
m_font->DropVariations();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TablePassthru::Parse(const uint8_t *data, size_t length) {
|
||||
m_data = data;
|
||||
m_length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TablePassthru::Serialize(OTSStream *out) {
|
||||
if (!out->Write(m_data, m_length)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsValidVersionTag(uint32_t tag) {
|
||||
return tag == 0x000010000 ||
|
||||
// OpenType fonts with CFF data have 'OTTO' tag.
|
||||
|
@ -882,7 +1084,7 @@ bool OTSContext::Process(OTSStream *output,
|
|||
const uint8_t *data,
|
||||
size_t length,
|
||||
uint32_t index) {
|
||||
OpenTypeFile header;
|
||||
FontFile header;
|
||||
Font font(&header);
|
||||
header.context = this;
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_H_
|
||||
#define OTS_H_
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
|
@ -54,13 +58,9 @@ namespace ots {
|
|||
#define OTS_WARNING_MSG_(otf_,...) \
|
||||
OTS_MESSAGE_(1,otf_,__VA_ARGS__)
|
||||
|
||||
// Generate a message with an associated table tag
|
||||
#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
|
||||
(OTS_MESSAGE_(0,otf_,"%c%c%c%c: %s", OTS_UNTAG(tag_), msg_), false)
|
||||
|
||||
// Convenience macros for use in files that only handle a single table tag,
|
||||
// defined as TABLE_NAME at the top of the file; the 'file' variable is
|
||||
// expected to be the current OpenTypeFile pointer.
|
||||
// expected to be the current FontFile pointer.
|
||||
#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)
|
||||
|
||||
#define OTS_WARNING(...) OTS_WARNING_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)
|
||||
|
@ -112,7 +112,7 @@ class Buffer {
|
|||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
|
||||
*value = ntohs(*value);
|
||||
*value = ots_ntohs(*value);
|
||||
offset_ += 2;
|
||||
return true;
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ class Buffer {
|
|||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
|
||||
*value = ntohl(*value);
|
||||
*value = ots_ntohl(*value);
|
||||
offset_ += 4;
|
||||
return true;
|
||||
}
|
||||
|
@ -184,111 +184,175 @@ template<typename T> T Round2(T value) {
|
|||
return (value + 1) & ~1;
|
||||
}
|
||||
|
||||
// Check that a tag consists entirely of printable ASCII characters
|
||||
bool CheckTag(uint32_t tag_value);
|
||||
|
||||
bool IsValidVersionTag(uint32_t tag);
|
||||
|
||||
#define FOR_EACH_TABLE_TYPE \
|
||||
F(cff, CFF) \
|
||||
F(cmap, CMAP) \
|
||||
F(cvt, CVT) \
|
||||
F(fpgm, FPGM) \
|
||||
F(gasp, GASP) \
|
||||
F(gdef, GDEF) \
|
||||
F(glyf, GLYF) \
|
||||
F(gpos, GPOS) \
|
||||
F(gsub, GSUB) \
|
||||
F(hdmx, HDMX) \
|
||||
F(head, HEAD) \
|
||||
F(hhea, HHEA) \
|
||||
F(hmtx, HMTX) \
|
||||
F(kern, KERN) \
|
||||
F(loca, LOCA) \
|
||||
F(ltsh, LTSH) \
|
||||
F(math, MATH) \
|
||||
F(maxp, MAXP) \
|
||||
F(name, NAME) \
|
||||
F(os2, OS2) \
|
||||
F(post, POST) \
|
||||
F(prep, PREP) \
|
||||
F(vdmx, VDMX) \
|
||||
F(vorg, VORG) \
|
||||
F(vhea, VHEA) \
|
||||
F(vmtx, VMTX)
|
||||
#define OTS_TAG_CFF OTS_TAG('C','F','F',' ')
|
||||
#define OTS_TAG_CFF2 OTS_TAG('C','F','F','2')
|
||||
#define OTS_TAG_CMAP OTS_TAG('c','m','a','p')
|
||||
#define OTS_TAG_CVT OTS_TAG('c','v','t',' ')
|
||||
#define OTS_TAG_FEAT OTS_TAG('F','e','a','t')
|
||||
#define OTS_TAG_FPGM OTS_TAG('f','p','g','m')
|
||||
#define OTS_TAG_GASP OTS_TAG('g','a','s','p')
|
||||
#define OTS_TAG_GDEF OTS_TAG('G','D','E','F')
|
||||
#define OTS_TAG_GLAT OTS_TAG('G','l','a','t')
|
||||
#define OTS_TAG_GLOC OTS_TAG('G','l','o','c')
|
||||
#define OTS_TAG_GLYF OTS_TAG('g','l','y','f')
|
||||
#define OTS_TAG_GPOS OTS_TAG('G','P','O','S')
|
||||
#define OTS_TAG_GSUB OTS_TAG('G','S','U','B')
|
||||
#define OTS_TAG_HDMX OTS_TAG('h','d','m','x')
|
||||
#define OTS_TAG_HEAD OTS_TAG('h','e','a','d')
|
||||
#define OTS_TAG_HHEA OTS_TAG('h','h','e','a')
|
||||
#define OTS_TAG_HMTX OTS_TAG('h','m','t','x')
|
||||
#define OTS_TAG_KERN OTS_TAG('k','e','r','n')
|
||||
#define OTS_TAG_LOCA OTS_TAG('l','o','c','a')
|
||||
#define OTS_TAG_LTSH OTS_TAG('L','T','S','H')
|
||||
#define OTS_TAG_MATH OTS_TAG('M','A','T','H')
|
||||
#define OTS_TAG_MAXP OTS_TAG('m','a','x','p')
|
||||
#define OTS_TAG_NAME OTS_TAG('n','a','m','e')
|
||||
#define OTS_TAG_OS2 OTS_TAG('O','S','/','2')
|
||||
#define OTS_TAG_POST OTS_TAG('p','o','s','t')
|
||||
#define OTS_TAG_PREP OTS_TAG('p','r','e','p')
|
||||
#define OTS_TAG_SILE OTS_TAG('S','i','l','e')
|
||||
#define OTS_TAG_SILF OTS_TAG('S','i','l','f')
|
||||
#define OTS_TAG_SILL OTS_TAG('S','i','l','l')
|
||||
#define OTS_TAG_VDMX OTS_TAG('V','D','M','X')
|
||||
#define OTS_TAG_VHEA OTS_TAG('v','h','e','a')
|
||||
#define OTS_TAG_VMTX OTS_TAG('v','m','t','x')
|
||||
#define OTS_TAG_VORG OTS_TAG('V','O','R','G')
|
||||
|
||||
#define F(name, capname) struct OpenType##capname;
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
#define OTS_TAG_AVAR OTS_TAG('a','v','a','r')
|
||||
#define OTS_TAG_CVAR OTS_TAG('c','v','a','r')
|
||||
#define OTS_TAG_FVAR OTS_TAG('f','v','a','r')
|
||||
#define OTS_TAG_GVAR OTS_TAG('g','v','a','r')
|
||||
#define OTS_TAG_HVAR OTS_TAG('H','V','A','R')
|
||||
#define OTS_TAG_MVAR OTS_TAG('M','V','A','R')
|
||||
#define OTS_TAG_VVAR OTS_TAG('V','V','A','R')
|
||||
#define OTS_TAG_STAT OTS_TAG('S','T','A','T')
|
||||
|
||||
struct Font;
|
||||
struct OpenTypeFile;
|
||||
struct FontFile;
|
||||
struct TableEntry;
|
||||
struct Arena;
|
||||
|
||||
#define F(name, capname) \
|
||||
bool ots_##name##_parse(Font *f, const uint8_t *d, size_t l); \
|
||||
bool ots_##name##_should_serialise(Font *f); \
|
||||
bool ots_##name##_serialise(OTSStream *s, Font *f); \
|
||||
void ots_##name##_reuse(Font *f, Font *o);\
|
||||
void ots_##name##_free(Font *f);
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
class Table {
|
||||
public:
|
||||
explicit Table(Font *font, uint32_t tag, uint32_t type)
|
||||
: m_tag(tag),
|
||||
m_type(type),
|
||||
m_font(font),
|
||||
m_shouldSerialize(true) {
|
||||
}
|
||||
|
||||
virtual ~Table() { }
|
||||
|
||||
virtual bool Parse(const uint8_t *data, size_t length) = 0;
|
||||
virtual bool Serialize(OTSStream *out) = 0;
|
||||
virtual bool ShouldSerialize();
|
||||
|
||||
// Return the tag (table type) this Table was parsed as, to support
|
||||
// "poor man's RTTI" so that we know if we can safely down-cast to
|
||||
// a specific Table subclass. The m_type field is initialized to the
|
||||
// appropriate tag when a subclass is constructed, or to zero for
|
||||
// TablePassthru (indicating unparsed data).
|
||||
uint32_t Type() { return m_type; }
|
||||
|
||||
Font* GetFont() { return m_font; }
|
||||
|
||||
bool Error(const char *format, ...);
|
||||
bool Warning(const char *format, ...);
|
||||
bool Drop(const char *format, ...);
|
||||
bool DropGraphite(const char *format, ...);
|
||||
bool DropVariations(const char *format, ...);
|
||||
|
||||
private:
|
||||
void Message(int level, const char *format, va_list va);
|
||||
|
||||
uint32_t m_tag;
|
||||
uint32_t m_type;
|
||||
Font *m_font;
|
||||
bool m_shouldSerialize;
|
||||
};
|
||||
|
||||
class TablePassthru : public Table {
|
||||
public:
|
||||
explicit TablePassthru(Font *font, uint32_t tag)
|
||||
: Table(font, tag, 0),
|
||||
m_data(NULL),
|
||||
m_length(0) {
|
||||
}
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
struct Font {
|
||||
explicit Font(const OpenTypeFile *f)
|
||||
explicit Font(FontFile *f)
|
||||
: file(f),
|
||||
version(0),
|
||||
num_tables(0),
|
||||
search_range(0),
|
||||
entry_selector(0),
|
||||
range_shift(0) {
|
||||
#define F(name, capname) \
|
||||
name = NULL; \
|
||||
name##_reused = false;
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
range_shift(0),
|
||||
dropped_graphite(false),
|
||||
dropped_variations(false) {
|
||||
}
|
||||
|
||||
~Font() {
|
||||
#define F(name, capname) \
|
||||
if (!name##_reused) {\
|
||||
ots_##name##_free(this); \
|
||||
}
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
}
|
||||
bool ParseTable(const TableEntry& tableinfo, const uint8_t* data,
|
||||
Arena &arena);
|
||||
Table* GetTable(uint32_t tag) const;
|
||||
|
||||
const OpenTypeFile *file;
|
||||
// This checks that the returned Table is actually of the correct subclass
|
||||
// for |tag|, so it can safely be downcast to the corresponding OpenTypeXXXX;
|
||||
// if not (i.e. if the table was treated as Passthru), it will return NULL.
|
||||
Table* GetTypedTable(uint32_t tag) const;
|
||||
|
||||
// Drop all Graphite tables and don't parse new ones.
|
||||
void DropGraphite();
|
||||
|
||||
// Drop all Variations tables and don't parse new ones.
|
||||
void DropVariations();
|
||||
|
||||
FontFile *file;
|
||||
|
||||
uint32_t version;
|
||||
uint16_t num_tables;
|
||||
uint16_t search_range;
|
||||
uint16_t entry_selector;
|
||||
uint16_t range_shift;
|
||||
bool dropped_graphite;
|
||||
bool dropped_variations;
|
||||
|
||||
#define F(name, capname) \
|
||||
OpenType##capname *name; \
|
||||
bool name##_reused;
|
||||
FOR_EACH_TABLE_TYPE
|
||||
#undef F
|
||||
private:
|
||||
std::map<uint32_t, Table*> m_tables;
|
||||
};
|
||||
|
||||
struct OutputTable {
|
||||
struct TableEntry {
|
||||
uint32_t tag;
|
||||
size_t offset;
|
||||
size_t length;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
uint32_t uncompressed_length;
|
||||
uint32_t chksum;
|
||||
|
||||
bool operator<(const OutputTable& other) const {
|
||||
bool operator<(const TableEntry& other) const {
|
||||
return tag < other.tag;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<uint32_t, std::pair<Font*, OutputTable> > TableMap;
|
||||
struct FontFile {
|
||||
~FontFile();
|
||||
|
||||
struct OpenTypeFile {
|
||||
OTSContext *context;
|
||||
TableMap tables;
|
||||
std::map<TableEntry, Table*> tables;
|
||||
std::map<uint32_t, TableEntry> table_entries;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef FOR_EACH_TABLE_TYPE
|
||||
|
||||
#endif // OTS_H_
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,74 +9,73 @@
|
|||
// post - PostScript
|
||||
// http://www.microsoft.com/typography/otspec/post.htm
|
||||
|
||||
#define TABLE_NAME "post"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_post_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypePOST::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypePOST *post = new OpenTypePOST;
|
||||
font->post = post;
|
||||
|
||||
if (!table.ReadU32(&post->version) ||
|
||||
!table.ReadU32(&post->italic_angle) ||
|
||||
!table.ReadS16(&post->underline) ||
|
||||
!table.ReadS16(&post->underline_thickness) ||
|
||||
!table.ReadU32(&post->is_fixed_pitch)) {
|
||||
return OTS_FAILURE_MSG("Failed to read post header");
|
||||
if (!table.ReadU32(&this->version)) {
|
||||
return Error("Failed to read table version");
|
||||
}
|
||||
|
||||
if (post->underline_thickness < 0) {
|
||||
post->underline_thickness = 1;
|
||||
}
|
||||
|
||||
if (post->version == 0x00010000) {
|
||||
return true;
|
||||
} else if (post->version == 0x00030000) {
|
||||
return true;
|
||||
} else if (post->version != 0x00020000) {
|
||||
if (this->version != 0x00010000 &&
|
||||
this->version != 0x00020000 &&
|
||||
this->version != 0x00030000) {
|
||||
// 0x00025000 is deprecated. We don't accept it.
|
||||
return OTS_FAILURE_MSG("Bad post version %x", post->version);
|
||||
return Error("Unsupported table version 0x%x", this->version);
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->italic_angle) ||
|
||||
!table.ReadS16(&this->underline) ||
|
||||
!table.ReadS16(&this->underline_thickness) ||
|
||||
!table.ReadU32(&this->is_fixed_pitch) ||
|
||||
// We don't care about the memory usage fields. We'll set all these to
|
||||
// zero when serialising
|
||||
!table.Skip(16)) {
|
||||
return Error("Failed to read table header");
|
||||
}
|
||||
|
||||
if (this->underline_thickness < 0) {
|
||||
this->underline_thickness = 1;
|
||||
}
|
||||
|
||||
if (this->version == 0x00010000 || this->version == 0x00030000) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We have a version 2 table with a list of Pascal strings at the end
|
||||
|
||||
// We don't care about the memory usage fields. We'll set all these to zero
|
||||
// when serialising
|
||||
if (!table.Skip(16)) {
|
||||
return OTS_FAILURE_MSG("Failed to skip memory usage in post table");
|
||||
}
|
||||
|
||||
uint16_t num_glyphs = 0;
|
||||
if (!table.ReadU16(&num_glyphs)) {
|
||||
return OTS_FAILURE_MSG("Failed to read number of glyphs");
|
||||
return Error("Failed to read numberOfGlyphs");
|
||||
}
|
||||
|
||||
if (!font->maxp) {
|
||||
return OTS_FAILURE_MSG("No maxp table required by post table");
|
||||
OpenTypeMAXP* maxp = static_cast<OpenTypeMAXP*>
|
||||
(GetFont()->GetTable(OTS_TAG_MAXP));
|
||||
if (!maxp) {
|
||||
return Error("Missing required maxp table");
|
||||
}
|
||||
|
||||
if (num_glyphs == 0) {
|
||||
if (font->maxp->num_glyphs > 258) {
|
||||
return OTS_FAILURE_MSG("Can't have no glyphs in the post table if there are more than 256 glyphs in the font");
|
||||
if (maxp->num_glyphs > 258) {
|
||||
return Error("Can't have no glyphs in the post table if there are more "
|
||||
"than 258 glyphs in the font");
|
||||
}
|
||||
OTS_WARNING("table version is 1, but no glyf names are found");
|
||||
// workaround for fonts in http://www.fontsquirrel.com/fontface
|
||||
// (e.g., yataghan.ttf).
|
||||
post->version = 0x00010000;
|
||||
return true;
|
||||
this->version = 0x00010000;
|
||||
return Warning("Table version is 1, but no glyph names are found");
|
||||
}
|
||||
|
||||
if (num_glyphs != font->maxp->num_glyphs) {
|
||||
if (num_glyphs != maxp->num_glyphs) {
|
||||
// Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
|
||||
return OTS_FAILURE_MSG("Bad number of glyphs in post table %d", num_glyphs);
|
||||
return Error("Bad number of glyphs: %d", num_glyphs);
|
||||
}
|
||||
|
||||
post->glyph_name_index.resize(num_glyphs);
|
||||
this->glyph_name_index.resize(num_glyphs);
|
||||
for (unsigned i = 0; i < num_glyphs; ++i) {
|
||||
if (!table.ReadU16(&post->glyph_name_index[i])) {
|
||||
return OTS_FAILURE_MSG("Failed to read post information for glyph %d", i);
|
||||
if (!table.ReadU16(&this->glyph_name_index[i])) {
|
||||
return Error("Failed to read glyph name %d", i);
|
||||
}
|
||||
// Note: A strict interpretation of the specification requires name indexes
|
||||
// are less than 32768. This, however, excludes fonts like unifont.ttf
|
||||
|
@ -93,101 +92,85 @@ bool ots_post_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
if (strings == strings_end) break;
|
||||
const unsigned string_length = *strings;
|
||||
if (strings + 1 + string_length > strings_end) {
|
||||
return OTS_FAILURE_MSG("Bad string length %d", string_length);
|
||||
return Error("Bad string length %d", string_length);
|
||||
}
|
||||
if (std::memchr(strings + 1, '\0', string_length)) {
|
||||
return OTS_FAILURE_MSG("Bad string of length %d", string_length);
|
||||
return Error("Bad string of length %d", string_length);
|
||||
}
|
||||
post->names.push_back(
|
||||
this->names.push_back(
|
||||
std::string(reinterpret_cast<const char*>(strings + 1), string_length));
|
||||
strings += 1 + string_length;
|
||||
}
|
||||
const unsigned num_strings = post->names.size();
|
||||
const unsigned num_strings = this->names.size();
|
||||
|
||||
// check that all the references are within bounds
|
||||
for (unsigned i = 0; i < num_glyphs; ++i) {
|
||||
unsigned offset = post->glyph_name_index[i];
|
||||
unsigned offset = this->glyph_name_index[i];
|
||||
if (offset < 258) {
|
||||
continue;
|
||||
}
|
||||
|
||||
offset -= 258;
|
||||
if (offset >= num_strings) {
|
||||
return OTS_FAILURE_MSG("Bad string index %d", offset);
|
||||
return Error("Bad string index %d", offset);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_post_should_serialise(Font *font) {
|
||||
return font->post != NULL;
|
||||
}
|
||||
|
||||
bool ots_post_serialise(OTSStream *out, Font *font) {
|
||||
const OpenTypePOST *post = font->post;
|
||||
|
||||
bool OpenTypePOST::Serialize(OTSStream *out) {
|
||||
// OpenType with CFF glyphs must have v3 post table.
|
||||
if (post && font->cff && post->version != 0x00030000) {
|
||||
return OTS_FAILURE_MSG("Bad post version %x", post->version);
|
||||
if (GetFont()->GetTable(OTS_TAG_CFF) && this->version != 0x00030000) {
|
||||
return Error("Only version supported for fonts with CFF table is 0x00030000"
|
||||
" not 0x%x", this->version);
|
||||
}
|
||||
|
||||
if (!out->WriteU32(post->version) ||
|
||||
!out->WriteU32(post->italic_angle) ||
|
||||
!out->WriteS16(post->underline) ||
|
||||
!out->WriteS16(post->underline_thickness) ||
|
||||
!out->WriteU32(post->is_fixed_pitch) ||
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU32(this->italic_angle) ||
|
||||
!out->WriteS16(this->underline) ||
|
||||
!out->WriteS16(this->underline_thickness) ||
|
||||
!out->WriteU32(this->is_fixed_pitch) ||
|
||||
!out->WriteU32(0) ||
|
||||
!out->WriteU32(0) ||
|
||||
!out->WriteU32(0) ||
|
||||
!out->WriteU32(0)) {
|
||||
return OTS_FAILURE_MSG("Failed to write post header");
|
||||
return Error("Failed to write post header");
|
||||
}
|
||||
|
||||
if (post->version != 0x00020000) {
|
||||
if (this->version != 0x00020000) {
|
||||
return true; // v1.0 and v3.0 does not have glyph names.
|
||||
}
|
||||
|
||||
const uint16_t num_indexes =
|
||||
static_cast<uint16_t>(post->glyph_name_index.size());
|
||||
if (num_indexes != post->glyph_name_index.size() ||
|
||||
static_cast<uint16_t>(this->glyph_name_index.size());
|
||||
if (num_indexes != this->glyph_name_index.size() ||
|
||||
!out->WriteU16(num_indexes)) {
|
||||
return OTS_FAILURE_MSG("Failed to write number of indices");
|
||||
return Error("Failed to write number of indices");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < num_indexes; ++i) {
|
||||
if (!out->WriteU16(post->glyph_name_index[i])) {
|
||||
return OTS_FAILURE_MSG("Failed to write name index %d", i);
|
||||
if (!out->WriteU16(this->glyph_name_index[i])) {
|
||||
return Error("Failed to write name index %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we just have to write out the strings in the correct order
|
||||
for (unsigned i = 0; i < post->names.size(); ++i) {
|
||||
const std::string& s = post->names[i];
|
||||
for (unsigned i = 0; i < this->names.size(); ++i) {
|
||||
const std::string& s = this->names[i];
|
||||
const uint8_t string_length = static_cast<uint8_t>(s.size());
|
||||
if (string_length != s.size() ||
|
||||
!out->Write(&string_length, 1)) {
|
||||
return OTS_FAILURE_MSG("Failed to write string %d", i);
|
||||
return Error("Failed to write string %d", i);
|
||||
}
|
||||
// Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
|
||||
// We allow them.
|
||||
if (string_length > 0 && !out->Write(s.data(), string_length)) {
|
||||
return OTS_FAILURE_MSG("Failed to write string length for string %d", i);
|
||||
return Error("Failed to write string length for string %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_post_reuse(Font *font, Font *other) {
|
||||
font->post = other->post;
|
||||
font->post_reused = true;
|
||||
}
|
||||
|
||||
void ots_post_free(Font *font) {
|
||||
delete font->post;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -13,7 +13,15 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypePOST {
|
||||
class OpenTypePOST : public Table {
|
||||
public:
|
||||
explicit OpenTypePOST(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
|
||||
private:
|
||||
uint32_t version;
|
||||
uint32_t italic_angle;
|
||||
int16_t underline;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -7,53 +7,37 @@
|
|||
// prep - Control Value Program
|
||||
// http://www.microsoft.com/typography/otspec/prep.htm
|
||||
|
||||
#define TABLE_NAME "prep"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_prep_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypePREP::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
|
||||
OpenTypePREP *prep = new OpenTypePREP;
|
||||
font->prep = prep;
|
||||
|
||||
if (length >= 128 * 1024u) {
|
||||
return OTS_FAILURE_MSG("table length %ld > 120K", length); // almost all prep tables are less than 9k bytes.
|
||||
// almost all prep tables are less than 9k bytes.
|
||||
return Error("Table length %ld > 120K", length);
|
||||
}
|
||||
|
||||
if (!table.Skip(length)) {
|
||||
return OTS_FAILURE_MSG("Failed to read table of length %ld", length);
|
||||
return Error("Failed to read table of length %ld", length);
|
||||
}
|
||||
|
||||
prep->data = data;
|
||||
prep->length = length;
|
||||
this->m_data = data;
|
||||
this->m_length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_prep_should_serialise(Font *font) {
|
||||
if (!font->glyf) return false; // this table is not for CFF fonts.
|
||||
return font->prep != NULL;
|
||||
}
|
||||
|
||||
bool ots_prep_serialise(OTSStream *out, Font *font) {
|
||||
const OpenTypePREP *prep = font->prep;
|
||||
|
||||
if (!out->Write(prep->data, prep->length)) {
|
||||
return OTS_FAILURE_MSG("Failed to write table length");
|
||||
bool OpenTypePREP::Serialize(OTSStream *out) {
|
||||
if (!out->Write(this->m_data, this->m_length)) {
|
||||
return Error("Failed to write table length");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_prep_reuse(Font *font, Font *other) {
|
||||
font->prep = other->prep;
|
||||
font->prep_reused = true;
|
||||
}
|
||||
|
||||
void ots_prep_free(Font *font) {
|
||||
delete font->prep;
|
||||
bool OpenTypePREP::ShouldSerialize() {
|
||||
return Table::ShouldSerialize() &&
|
||||
// this table is not for CFF fonts.
|
||||
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,9 +9,18 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypePREP {
|
||||
const uint8_t *data;
|
||||
uint32_t length;
|
||||
class OpenTypePREP : public Table {
|
||||
public:
|
||||
explicit OpenTypePREP(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool ShouldSerialize();
|
||||
|
||||
private:
|
||||
const uint8_t *m_data;
|
||||
uint32_t m_length;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "sile.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeSILE::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
|
||||
return DropGraphite("Failed to read valid version");
|
||||
}
|
||||
if (!table.ReadU32(&this->checksum)) {
|
||||
return DropGraphite("Failed to read checksum");
|
||||
}
|
||||
if (!table.ReadU32(&this->createTime[0]) ||
|
||||
!table.ReadU32(&this->createTime[1])) {
|
||||
return DropGraphite("Failed to read createTime");
|
||||
}
|
||||
if (!table.ReadU32(&this->modifyTime[0]) ||
|
||||
!table.ReadU32(&this->modifyTime[1])) {
|
||||
return DropGraphite("Failed to read modifyTime");
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->fontNameLength)) {
|
||||
return DropGraphite("Failed to read fontNameLength");
|
||||
}
|
||||
//this->fontName.resize(this->fontNameLength);
|
||||
for (unsigned i = 0; i < this->fontNameLength; ++i) {
|
||||
this->fontName.emplace_back();
|
||||
if (!table.ReadU16(&this->fontName[i])) {
|
||||
return DropGraphite("Failed to read fontName[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->fontFileLength)) {
|
||||
return DropGraphite("Failed to read fontFileLength");
|
||||
}
|
||||
//this->baseFile.resize(this->fontFileLength);
|
||||
for (unsigned i = 0; i < this->fontFileLength; ++i) {
|
||||
this->baseFile.emplace_back();
|
||||
if (!table.ReadU16(&this->baseFile[i])) {
|
||||
return DropGraphite("Failed to read baseFile[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILE::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU32(this->checksum) ||
|
||||
!out->WriteU32(this->createTime[0]) ||
|
||||
!out->WriteU32(this->createTime[1]) ||
|
||||
!out->WriteU32(this->modifyTime[0]) ||
|
||||
!out->WriteU32(this->modifyTime[1]) ||
|
||||
!out->WriteU16(this->fontNameLength) ||
|
||||
!SerializeParts(this->fontName, out) ||
|
||||
!out->WriteU16(this->fontFileLength) ||
|
||||
!SerializeParts(this->baseFile, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_SILE_H_
|
||||
#define OTS_SILE_H_
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeSILE : public Table {
|
||||
public:
|
||||
explicit OpenTypeSILE(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
uint32_t version;
|
||||
uint32_t checksum;
|
||||
uint32_t createTime[2];
|
||||
uint32_t modifyTime[2];
|
||||
uint16_t fontNameLength;
|
||||
std::vector<uint16_t> fontName;
|
||||
uint16_t fontFileLength;
|
||||
std::vector<uint16_t> baseFile;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_SILE_H_
|
|
@ -0,0 +1,977 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "silf.h"
|
||||
|
||||
#include "name.h"
|
||||
#include "mozilla/Compression.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
|
||||
bool prevent_decompression) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!table.ReadU32(&this->version)) {
|
||||
return DropGraphite("Failed to read version");
|
||||
}
|
||||
if (this->version >> 16 != 1 &&
|
||||
this->version >> 16 != 2 &&
|
||||
this->version >> 16 != 3 &&
|
||||
this->version >> 16 != 4 &&
|
||||
this->version >> 16 != 5) {
|
||||
return DropGraphite("Unsupported table version: %u", this->version >> 16);
|
||||
}
|
||||
if (this->version >> 16 >= 3 && !table.ReadU32(&this->compHead)) {
|
||||
return DropGraphite("Failed to read compHead");
|
||||
}
|
||||
if (this->version >> 16 >= 5) {
|
||||
switch ((this->compHead & SCHEME) >> 27) {
|
||||
case 0: // uncompressed
|
||||
break;
|
||||
case 1: { // lz4
|
||||
if (prevent_decompression) {
|
||||
return DropGraphite("Illegal nested compression");
|
||||
}
|
||||
size_t decompressed_size = this->compHead & FULL_SIZE;
|
||||
if (decompressed_size < length) {
|
||||
return DropGraphite("Decompressed size is less than compressed size");
|
||||
}
|
||||
if (decompressed_size == 0) {
|
||||
return DropGraphite("Decompressed size is set to 0");
|
||||
}
|
||||
// decompressed table must be <= 30MB
|
||||
if (decompressed_size > 30 * 1024 * 1024) {
|
||||
return DropGraphite("Decompressed size exceeds 30MB: %gMB",
|
||||
decompressed_size / (1024.0 * 1024.0));
|
||||
}
|
||||
std::vector<uint8_t> decompressed(decompressed_size);
|
||||
size_t outputSize = 0;
|
||||
bool ret = mozilla::Compression::LZ4::decompressPartial(
|
||||
reinterpret_cast<const char*>(data + table.offset()),
|
||||
table.remaining(), // input buffer size (input size + padding)
|
||||
reinterpret_cast<char*>(decompressed.data()),
|
||||
decompressed.size(), // target output size
|
||||
&outputSize); // return output size
|
||||
if (!ret || outputSize != decompressed.size()) {
|
||||
return DropGraphite("Decompression failed");
|
||||
}
|
||||
return this->Parse(decompressed.data(), decompressed.size(), true);
|
||||
}
|
||||
default:
|
||||
return DropGraphite("Unknown compression scheme");
|
||||
}
|
||||
}
|
||||
if (!table.ReadU16(&this->numSub)) {
|
||||
return DropGraphite("Failed to read numSub");
|
||||
}
|
||||
if (this->version >> 16 >= 2 && !table.ReadU16(&this->reserved)) {
|
||||
return DropGraphite("Failed to read reserved");
|
||||
}
|
||||
if (this->version >> 16 >= 2 && this->reserved != 0) {
|
||||
Warning("Nonzero reserved");
|
||||
}
|
||||
|
||||
unsigned long last_offset = 0;
|
||||
//this->offset.resize(this->numSub);
|
||||
for (unsigned i = 0; i < this->numSub; ++i) {
|
||||
this->offset.emplace_back();
|
||||
if (!table.ReadU32(&this->offset[i]) || this->offset[i] < last_offset) {
|
||||
return DropGraphite("Failed to read offset[%u]", i);
|
||||
}
|
||||
last_offset = this->offset[i];
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < this->numSub; ++i) {
|
||||
if (table.offset() != this->offset[i]) {
|
||||
return DropGraphite("Offset check failed for tables[%lu]", i);
|
||||
}
|
||||
SILSub subtable(this);
|
||||
if (!subtable.ParsePart(table)) {
|
||||
return DropGraphite("Failed to read tables[%u]", i);
|
||||
}
|
||||
tables.push_back(subtable);
|
||||
}
|
||||
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
(this->version >> 16 >= 3 && !out->WriteU32(this->compHead)) ||
|
||||
!out->WriteU16(this->numSub) ||
|
||||
(this->version >> 16 >= 2 && !out->WriteU16(this->reserved)) ||
|
||||
!SerializeParts(this->offset, out) ||
|
||||
!SerializeParts(this->tables, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
|
||||
size_t init_offset = table.offset();
|
||||
if (parent->version >> 16 >= 3) {
|
||||
if (!table.ReadU32(&this->ruleVersion)) {
|
||||
return parent->Error("SILSub: Failed to read ruleVersion");
|
||||
}
|
||||
if (!table.ReadU16(&this->passOffset)) {
|
||||
return parent->Error("SILSub: Failed to read passOffset");
|
||||
}
|
||||
if (!table.ReadU16(&this->pseudosOffset)) {
|
||||
return parent->Error("SILSub: Failed to read pseudosOffset");
|
||||
}
|
||||
}
|
||||
if (!table.ReadU16(&this->maxGlyphID)) {
|
||||
return parent->Error("SILSub: Failed to read maxGlyphID");
|
||||
}
|
||||
if (!table.ReadS16(&this->extraAscent)) {
|
||||
return parent->Error("SILSub: Failed to read extraAscent");
|
||||
}
|
||||
if (!table.ReadS16(&this->extraDescent)) {
|
||||
return parent->Error("SILSub: Failed to read extraDescent");
|
||||
}
|
||||
if (!table.ReadU8(&this->numPasses)) {
|
||||
return parent->Error("SILSub: Failed to read numPasses");
|
||||
}
|
||||
if (!table.ReadU8(&this->iSubst) || this->iSubst > this->numPasses) {
|
||||
return parent->Error("SILSub: Failed to read valid iSubst");
|
||||
}
|
||||
if (!table.ReadU8(&this->iPos) || this->iPos > this->numPasses) {
|
||||
return parent->Error("SILSub: Failed to read valid iPos");
|
||||
}
|
||||
if (!table.ReadU8(&this->iJust) || this->iJust > this->numPasses) {
|
||||
return parent->Error("SILSub: Failed to read valid iJust");
|
||||
}
|
||||
if (!table.ReadU8(&this->iBidi) ||
|
||||
!(iBidi == 0xFF || this->iBidi <= this->iPos)) {
|
||||
return parent->Error("SILSub: Failed to read valid iBidi");
|
||||
}
|
||||
if (!table.ReadU8(&this->flags)) {
|
||||
return parent->Error("SILSub: Failed to read flags");
|
||||
// checks omitted
|
||||
}
|
||||
if (!table.ReadU8(&this->maxPreContext)) {
|
||||
return parent->Error("SILSub: Failed to read maxPreContext");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxPostContext)) {
|
||||
return parent->Error("SILSub: Failed to read maxPostContext");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrPseudo)) {
|
||||
return parent->Error("SILSub: Failed to read attrPseudo");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrBreakWeight)) {
|
||||
return parent->Error("SILSub: Failed to read attrBreakWeight");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrDirectionality)) {
|
||||
return parent->Error("SILSub: Failed to read attrDirectionality");
|
||||
}
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU8(&this->attrMirroring)) {
|
||||
return parent->Error("SILSub: Failed to read attrMirroring");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrSkipPasses)) {
|
||||
return parent->Error("SILSub: Failed to read attrSkipPasses");
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->numJLevels)) {
|
||||
return parent->Error("SILSub: Failed to read numJLevels");
|
||||
}
|
||||
//this->jLevels.resize(this->numJLevels, parent);
|
||||
for (unsigned i = 0; i < this->numJLevels; ++i) {
|
||||
this->jLevels.emplace_back(parent);
|
||||
if (!this->jLevels[i].ParsePart(table)) {
|
||||
return parent->Error("SILSub: Failed to read jLevels[%u]", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->numLigComp)) {
|
||||
return parent->Error("SILSub: Failed to read numLigComp");
|
||||
}
|
||||
if (!table.ReadU8(&this->numUserDefn)) {
|
||||
return parent->Error("SILSub: Failed to read numUserDefn");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxCompPerLig)) {
|
||||
return parent->Error("SILSub: Failed to read maxCompPerLig");
|
||||
}
|
||||
if (!table.ReadU8(&this->direction)) {
|
||||
return parent->Error("SILSub: Failed to read direction");
|
||||
}
|
||||
if (!table.ReadU8(&this->attCollisions)) {
|
||||
return parent->Error("SILSub: Failed to read attCollisions");
|
||||
}
|
||||
if (parent->version >> 16 < 5 && this->attCollisions != 0) {
|
||||
parent->Warning("SILSub: Nonzero attCollisions (reserved before v5)");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved4)) {
|
||||
return parent->Error("SILSub: Failed to read reserved4");
|
||||
}
|
||||
if (this->reserved4 != 0) {
|
||||
parent->Warning("SILSub: Nonzero reserved4");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved5)) {
|
||||
return parent->Error("SILSub: Failed to read reserved5");
|
||||
}
|
||||
if (this->reserved5 != 0) {
|
||||
parent->Warning("SILSub: Nonzero reserved5");
|
||||
}
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU8(&this->reserved6)) {
|
||||
return parent->Error("SILSub: Failed to read reserved6");
|
||||
}
|
||||
if (this->reserved6 != 0) {
|
||||
parent->Warning("SILSub: Nonzero reserved6");
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->numCritFeatures)) {
|
||||
return parent->Error("SILSub: Failed to read numCritFeatures");
|
||||
}
|
||||
//this->critFeatures.resize(this->numCritFeatures);
|
||||
for (unsigned i = 0; i < this->numCritFeatures; ++i) {
|
||||
this->critFeatures.emplace_back();
|
||||
if (!table.ReadU16(&this->critFeatures[i])) {
|
||||
return parent->Error("SILSub: Failed to read critFeatures[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->reserved7)) {
|
||||
return parent->Error("SILSub: Failed to read reserved7");
|
||||
}
|
||||
if (this->reserved7 != 0) {
|
||||
parent->Warning("SILSub: Nonzero reserved7");
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->numScriptTag)) {
|
||||
return parent->Error("SILSub: Failed to read numScriptTag");
|
||||
}
|
||||
//this->scriptTag.resize(this->numScriptTag);
|
||||
for (unsigned i = 0; i < this->numScriptTag; ++i) {
|
||||
this->scriptTag.emplace_back();
|
||||
if (!table.ReadU32(&this->scriptTag[i])) {
|
||||
return parent->Error("SILSub: Failed to read scriptTag[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU16(&this->lbGID)) {
|
||||
return parent->Error("SILSub: Failed to read lbGID");
|
||||
}
|
||||
if (this->lbGID > this->maxGlyphID) {
|
||||
parent->Warning("SILSub: lbGID %u outside range 0..%u, replaced with 0",
|
||||
this->lbGID, this->maxGlyphID);
|
||||
this->lbGID = 0;
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 3 &&
|
||||
table.offset() != init_offset + this->passOffset) {
|
||||
return parent->Error("SILSub: passOffset check failed");
|
||||
}
|
||||
unsigned long last_oPass = 0;
|
||||
//this->oPasses.resize(static_cast<unsigned>(this->numPasses) + 1);
|
||||
for (unsigned i = 0; i <= this->numPasses; ++i) {
|
||||
this->oPasses.emplace_back();
|
||||
if (!table.ReadU32(&this->oPasses[i]) || this->oPasses[i] < last_oPass) {
|
||||
return false;
|
||||
}
|
||||
last_oPass = this->oPasses[i];
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 3 &&
|
||||
table.offset() != init_offset + this->pseudosOffset) {
|
||||
return parent->Error("SILSub: pseudosOffset check failed");
|
||||
}
|
||||
if (!table.ReadU16(&this->numPseudo)) {
|
||||
return parent->Error("SILSub: Failed to read numPseudo");
|
||||
}
|
||||
|
||||
// The following three fields are deprecated and ignored. We fix them up here
|
||||
// just for internal consistency, but the Graphite engine doesn't care.
|
||||
if (!table.ReadU16(&this->searchPseudo) ||
|
||||
!table.ReadU16(&this->pseudoSelector) ||
|
||||
!table.ReadU16(&this->pseudoShift)) {
|
||||
return parent->Error("SILSub: Failed to read searchPseudo..pseudoShift");
|
||||
}
|
||||
if (this->numPseudo == 0) {
|
||||
if (this->searchPseudo != 0 || this->pseudoSelector != 0 || this->pseudoShift != 0) {
|
||||
this->searchPseudo = this->pseudoSelector = this->pseudoShift = 0;
|
||||
}
|
||||
} else {
|
||||
unsigned floorLog2 = std::floor(std::log2(this->numPseudo));
|
||||
if (this->searchPseudo != 6 * (unsigned)std::pow(2, floorLog2) ||
|
||||
this->pseudoSelector != floorLog2 ||
|
||||
this->pseudoShift != 6 * this->numPseudo - this->searchPseudo) {
|
||||
this->searchPseudo = 6 * (unsigned)std::pow(2, floorLog2);
|
||||
this->pseudoSelector = floorLog2;
|
||||
this->pseudoShift = 6 * this->numPseudo - this->searchPseudo;
|
||||
}
|
||||
}
|
||||
|
||||
//this->pMaps.resize(this->numPseudo, parent);
|
||||
for (unsigned i = 0; i < numPseudo; i++) {
|
||||
this->pMaps.emplace_back(parent);
|
||||
if (!this->pMaps[i].ParsePart(table)) {
|
||||
return parent->Error("SILSub: Failed to read pMaps[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->classes.ParsePart(table)) {
|
||||
return parent->Error("SILSub: Failed to read classes");
|
||||
}
|
||||
|
||||
//this->passes.resize(this->numPasses, parent);
|
||||
for (unsigned i = 0; i < this->numPasses; ++i) {
|
||||
this->passes.emplace_back(parent);
|
||||
if (table.offset() != init_offset + this->oPasses[i]) {
|
||||
return parent->Error("SILSub: Offset check failed for passes[%u]", i);
|
||||
}
|
||||
if (!this->passes[i].ParsePart(table, init_offset, this->oPasses[i+1])) {
|
||||
return parent->Error("SILSub: Failed to read passes[%u]", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::SerializePart(OTSStream* out) const {
|
||||
if ((parent->version >> 16 >= 3 &&
|
||||
(!out->WriteU32(this->ruleVersion) ||
|
||||
!out->WriteU16(this->passOffset) ||
|
||||
!out->WriteU16(this->pseudosOffset))) ||
|
||||
!out->WriteU16(this->maxGlyphID) ||
|
||||
!out->WriteS16(this->extraAscent) ||
|
||||
!out->WriteS16(this->extraDescent) ||
|
||||
!out->WriteU8(this->numPasses) ||
|
||||
!out->WriteU8(this->iSubst) ||
|
||||
!out->WriteU8(this->iPos) ||
|
||||
!out->WriteU8(this->iJust) ||
|
||||
!out->WriteU8(this->iBidi) ||
|
||||
!out->WriteU8(this->flags) ||
|
||||
!out->WriteU8(this->maxPreContext) ||
|
||||
!out->WriteU8(this->maxPostContext) ||
|
||||
!out->WriteU8(this->attrPseudo) ||
|
||||
!out->WriteU8(this->attrBreakWeight) ||
|
||||
!out->WriteU8(this->attrDirectionality) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU8(this->attrMirroring) ||
|
||||
!out->WriteU8(this->attrSkipPasses) ||
|
||||
!out->WriteU8(this->numJLevels) ||
|
||||
!SerializeParts(this->jLevels, out))) ||
|
||||
!out->WriteU16(this->numLigComp) ||
|
||||
!out->WriteU8(this->numUserDefn) ||
|
||||
!out->WriteU8(this->maxCompPerLig) ||
|
||||
!out->WriteU8(this->direction) ||
|
||||
!out->WriteU8(this->attCollisions) ||
|
||||
!out->WriteU8(this->reserved4) ||
|
||||
!out->WriteU8(this->reserved5) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU8(this->reserved6) ||
|
||||
!out->WriteU8(this->numCritFeatures) ||
|
||||
!SerializeParts(this->critFeatures, out) ||
|
||||
!out->WriteU8(this->reserved7))) ||
|
||||
!out->WriteU8(this->numScriptTag) ||
|
||||
!SerializeParts(this->scriptTag, out) ||
|
||||
!out->WriteU16(this->lbGID) ||
|
||||
!SerializeParts(this->oPasses, out) ||
|
||||
!out->WriteU16(this->numPseudo) ||
|
||||
!out->WriteU16(this->searchPseudo) ||
|
||||
!out->WriteU16(this->pseudoSelector) ||
|
||||
!out->WriteU16(this->pseudoShift) ||
|
||||
!SerializeParts(this->pMaps, out) ||
|
||||
!this->classes.SerializePart(out) ||
|
||||
!SerializeParts(this->passes, out)) {
|
||||
return parent->Error("SILSub: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
JustificationLevel::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->attrStretch)) {
|
||||
return parent->Error("JustificationLevel: Failed to read attrStretch");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrShrink)) {
|
||||
return parent->Error("JustificationLevel: Failed to read attrShrink");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrStep)) {
|
||||
return parent->Error("JustificationLevel: Failed to read attrStep");
|
||||
}
|
||||
if (!table.ReadU8(&this->attrWeight)) {
|
||||
return parent->Error("JustificationLevel: Failed to read attrWeight");
|
||||
}
|
||||
if (!table.ReadU8(&this->runto)) {
|
||||
return parent->Error("JustificationLevel: Failed to read runto");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved)) {
|
||||
return parent->Error("JustificationLevel: Failed to read reserved");
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
parent->Warning("JustificationLevel: Nonzero reserved");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved2)) {
|
||||
return parent->Error("JustificationLevel: Failed to read reserved2");
|
||||
}
|
||||
if (this->reserved2 != 0) {
|
||||
parent->Warning("JustificationLevel: Nonzero reserved2");
|
||||
}
|
||||
if (!table.ReadU8(&this->reserved3)) {
|
||||
return parent->Error("JustificationLevel: Failed to read reserved3");
|
||||
}
|
||||
if (this->reserved3 != 0) {
|
||||
parent->Warning("JustificationLevel: Nonzero reserved3");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
JustificationLevel::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->attrStretch) ||
|
||||
!out->WriteU8(this->attrShrink) ||
|
||||
!out->WriteU8(this->attrStep) ||
|
||||
!out->WriteU8(this->attrWeight) ||
|
||||
!out->WriteU8(this->runto) ||
|
||||
!out->WriteU8(this->reserved) ||
|
||||
!out->WriteU8(this->reserved2) ||
|
||||
!out->WriteU8(this->reserved3)) {
|
||||
return parent->Error("JustificationLevel: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
PseudoMap::ParsePart(Buffer& table) {
|
||||
if (parent->version >> 16 >= 2 && !table.ReadU32(&this->unicode)) {
|
||||
return parent->Error("PseudoMap: Failed to read unicode");
|
||||
}
|
||||
if (parent->version >> 16 == 1) {
|
||||
uint16_t unicode;
|
||||
if (!table.ReadU16(&unicode)) {
|
||||
return parent->Error("PseudoMap: Failed to read unicode");
|
||||
}
|
||||
this->unicode = unicode;
|
||||
}
|
||||
if (!table.ReadU16(&this->nPseudo)) {
|
||||
return parent->Error("PseudoMap: Failed to read nPseudo");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
PseudoMap::SerializePart(OTSStream* out) const {
|
||||
if ((parent->version >> 16 >= 2 && !out->WriteU32(this->unicode)) ||
|
||||
(parent->version >> 16 == 1 &&
|
||||
!out->WriteU16(static_cast<uint16_t>(this->unicode))) ||
|
||||
!out->WriteU16(this->nPseudo)) {
|
||||
return parent->Error("PseudoMap: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
ClassMap::ParsePart(Buffer& table) {
|
||||
size_t init_offset = table.offset();
|
||||
if (!table.ReadU16(&this->numClass)) {
|
||||
return parent->Error("ClassMap: Failed to read numClass");
|
||||
}
|
||||
if (!table.ReadU16(&this->numLinear) || this->numLinear > this->numClass) {
|
||||
return parent->Error("ClassMap: Failed to read valid numLinear");
|
||||
}
|
||||
|
||||
//this->oClass.resize(static_cast<unsigned long>(this->numClass) + 1);
|
||||
if (parent->version >> 16 >= 4) {
|
||||
unsigned long last_oClass = 0;
|
||||
for (unsigned long i = 0; i <= this->numClass; ++i) {
|
||||
this->oClass.emplace_back();
|
||||
if (!table.ReadU32(&this->oClass[i]) || this->oClass[i] < last_oClass) {
|
||||
return parent->Error("ClassMap: Failed to read oClass[%lu]", i);
|
||||
}
|
||||
last_oClass = this->oClass[i];
|
||||
}
|
||||
}
|
||||
if (parent->version >> 16 < 4) {
|
||||
unsigned last_oClass = 0;
|
||||
for (unsigned long i = 0; i <= this->numClass; ++i) {
|
||||
uint16_t offset;
|
||||
if (!table.ReadU16(&offset) || offset < last_oClass) {
|
||||
return parent->Error("ClassMap: Failed to read oClass[%lu]", i);
|
||||
}
|
||||
last_oClass = offset;
|
||||
this->oClass.push_back(static_cast<uint32_t>(offset));
|
||||
}
|
||||
}
|
||||
|
||||
if (table.offset() - init_offset > this->oClass[this->numLinear]) {
|
||||
return parent->Error("ClassMap: Failed to calculate length of glyphs");
|
||||
}
|
||||
unsigned long glyphs_len = (this->oClass[this->numLinear] -
|
||||
(table.offset() - init_offset))/2;
|
||||
//this->glyphs.resize(glyphs_len);
|
||||
for (unsigned long i = 0; i < glyphs_len; ++i) {
|
||||
this->glyphs.emplace_back();
|
||||
if (!table.ReadU16(&this->glyphs[i])) {
|
||||
return parent->Error("ClassMap: Failed to read glyphs[%lu]", i);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned lookups_len = this->numClass - this->numLinear;
|
||||
// this->numLinear <= this->numClass
|
||||
//this->lookups.resize(lookups_len, parent);
|
||||
for (unsigned i = 0; i < lookups_len; ++i) {
|
||||
this->lookups.emplace_back(parent);
|
||||
if (table.offset() != init_offset + oClass[this->numLinear + i]) {
|
||||
return parent->Error("ClassMap: Offset check failed for lookups[%u]", i);
|
||||
}
|
||||
if (!this->lookups[i].ParsePart(table)) {
|
||||
return parent->Error("ClassMap: Failed to read lookups[%u]", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
ClassMap::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->numClass) ||
|
||||
!out->WriteU16(this->numLinear) ||
|
||||
(parent->version >> 16 >= 4 && !SerializeParts(this->oClass, out)) ||
|
||||
(parent->version >> 16 < 4 &&
|
||||
![&] {
|
||||
for (uint32_t offset : this->oClass) {
|
||||
if (!out->WriteU16(static_cast<uint16_t>(offset))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}()) ||
|
||||
!SerializeParts(this->glyphs, out) ||
|
||||
!SerializeParts(this->lookups, out)) {
|
||||
return parent->Error("ClassMap: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ClassMap::
|
||||
LookupClass::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU16(&this->numIDs)) {
|
||||
return parent->Error("LookupClass: Failed to read numIDs");
|
||||
}
|
||||
if (!table.ReadU16(&this->searchRange) ||
|
||||
!table.ReadU16(&this->entrySelector) ||
|
||||
!table.ReadU16(&this->rangeShift)) {
|
||||
return parent->Error("LookupClass: Failed to read searchRange..rangeShift");
|
||||
}
|
||||
if (this->numIDs == 0) {
|
||||
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
|
||||
parent->Warning("LookupClass: Correcting binary-search header for zero-length LookupPair list");
|
||||
this->searchRange = this->entrySelector = this->rangeShift = 0;
|
||||
}
|
||||
} else {
|
||||
unsigned floorLog2 = std::floor(std::log2(this->numIDs));
|
||||
if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
|
||||
this->entrySelector != floorLog2 ||
|
||||
this->rangeShift != this->numIDs - this->searchRange) {
|
||||
parent->Warning("LookupClass: Correcting binary-search header for LookupPair list");
|
||||
this->searchRange = (unsigned)std::pow(2, floorLog2);
|
||||
this->entrySelector = floorLog2;
|
||||
this->rangeShift = this->numIDs - this->searchRange;
|
||||
}
|
||||
}
|
||||
|
||||
//this->lookups.resize(this->numIDs, parent);
|
||||
for (unsigned i = 0; i < numIDs; ++i) {
|
||||
this->lookups.emplace_back(parent);
|
||||
if (!this->lookups[i].ParsePart(table)) {
|
||||
return parent->Error("LookupClass: Failed to read lookups[%u]", i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ClassMap::
|
||||
LookupClass::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->numIDs) ||
|
||||
!out->WriteU16(this->searchRange) ||
|
||||
!out->WriteU16(this->entrySelector) ||
|
||||
!out->WriteU16(this->rangeShift) ||
|
||||
!SerializeParts(this->lookups, out)) {
|
||||
return parent->Error("LookupClass: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ClassMap::LookupClass::
|
||||
LookupPair::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU16(&this->glyphId)) {
|
||||
return parent->Error("LookupPair: Failed to read glyphId");
|
||||
}
|
||||
if (!table.ReadU16(&this->index)) {
|
||||
return parent->Error("LookupPair: Failed to read index");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::ClassMap::LookupClass::
|
||||
LookupPair::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->glyphId) ||
|
||||
!out->WriteU16(this->index)) {
|
||||
return parent->Error("LookupPair: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
||||
const size_t next_pass_offset) {
|
||||
size_t init_offset = table.offset();
|
||||
if (!table.ReadU8(&this->flags)) {
|
||||
return parent->Error("SILPass: Failed to read flags");
|
||||
// checks omitted
|
||||
}
|
||||
if (!table.ReadU8(&this->maxRuleLoop)) {
|
||||
return parent->Error("SILPass: Failed to read valid maxRuleLoop");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxRuleContext)) {
|
||||
return parent->Error("SILPass: Failed to read maxRuleContext");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxBackup)) {
|
||||
return parent->Error("SILPass: Failed to read maxBackup");
|
||||
}
|
||||
if (!table.ReadU16(&this->numRules)) {
|
||||
return parent->Error("SILPass: Failed to read numRules");
|
||||
}
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU16(&this->fsmOffset)) {
|
||||
return parent->Error("SILPass: Failed to read fsmOffset");
|
||||
}
|
||||
if (!table.ReadU32(&this->pcCode) ||
|
||||
(parent->version >= 3 && this->pcCode < this->fsmOffset)) {
|
||||
return parent->Error("SILPass: Failed to read pcCode");
|
||||
}
|
||||
}
|
||||
if (!table.ReadU32(&this->rcCode) ||
|
||||
(parent->version >> 16 >= 2 && this->rcCode < this->pcCode)) {
|
||||
return parent->Error("SILPass: Failed to read valid rcCode");
|
||||
}
|
||||
if (!table.ReadU32(&this->aCode) || this->aCode < this->rcCode) {
|
||||
return parent->Error("SILPass: Failed to read valid aCode");
|
||||
}
|
||||
if (!table.ReadU32(&this->oDebug) ||
|
||||
(this->oDebug && this->oDebug < this->aCode)) {
|
||||
return parent->Error("SILPass: Failed to read valid oDebug");
|
||||
}
|
||||
if (parent->version >> 16 >= 3 &&
|
||||
table.offset() != init_offset + this->fsmOffset) {
|
||||
return parent->Error("SILPass: fsmOffset check failed");
|
||||
}
|
||||
if (!table.ReadU16(&this->numRows) ||
|
||||
(this->oDebug && this->numRows < this->numRules)) {
|
||||
return parent->Error("SILPass: Failed to read valid numRows");
|
||||
}
|
||||
if (!table.ReadU16(&this->numTransitional)) {
|
||||
return parent->Error("SILPass: Failed to read numTransitional");
|
||||
}
|
||||
if (!table.ReadU16(&this->numSuccess)) {
|
||||
return parent->Error("SILPass: Failed to read numSuccess");
|
||||
}
|
||||
if (!table.ReadU16(&this->numColumns)) {
|
||||
return parent->Error("SILPass: Failed to read numColumns");
|
||||
}
|
||||
if (!table.ReadU16(&this->numRange)) {
|
||||
return parent->Error("SILPass: Failed to read numRange");
|
||||
}
|
||||
|
||||
// The following three fields are deprecated and ignored. We fix them up here
|
||||
// just for internal consistency, but the Graphite engine doesn't care.
|
||||
if (!table.ReadU16(&this->searchRange) ||
|
||||
!table.ReadU16(&this->entrySelector) ||
|
||||
!table.ReadU16(&this->rangeShift)) {
|
||||
return parent->Error("SILPass: Failed to read searchRange..rangeShift");
|
||||
}
|
||||
if (this->numRange == 0) {
|
||||
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
|
||||
this->searchRange = this->entrySelector = this->rangeShift = 0;
|
||||
}
|
||||
} else {
|
||||
unsigned floorLog2 = std::floor(std::log2(this->numRange));
|
||||
if (this->searchRange != 6 * (unsigned)std::pow(2, floorLog2) ||
|
||||
this->entrySelector != floorLog2 ||
|
||||
this->rangeShift != 6 * this->numRange - this->searchRange) {
|
||||
this->searchRange = 6 * (unsigned)std::pow(2, floorLog2);
|
||||
this->entrySelector = floorLog2;
|
||||
this->rangeShift = 6 * this->numRange - this->searchRange;
|
||||
}
|
||||
}
|
||||
|
||||
//this->ranges.resize(this->numRange, parent);
|
||||
for (unsigned i = 0 ; i < this->numRange; ++i) {
|
||||
this->ranges.emplace_back(parent);
|
||||
if (!this->ranges[i].ParsePart(table)) {
|
||||
return parent->Error("SILPass: Failed to read ranges[%u]", i);
|
||||
}
|
||||
}
|
||||
unsigned ruleMap_len = 0; // maximum value in oRuleMap
|
||||
//this->oRuleMap.resize(static_cast<unsigned long>(this->numSuccess) + 1);
|
||||
for (unsigned long i = 0; i <= this->numSuccess; ++i) {
|
||||
this->oRuleMap.emplace_back();
|
||||
if (!table.ReadU16(&this->oRuleMap[i])) {
|
||||
return parent->Error("SILPass: Failed to read oRuleMap[%u]", i);
|
||||
}
|
||||
if (oRuleMap[i] > ruleMap_len) {
|
||||
ruleMap_len = oRuleMap[i];
|
||||
}
|
||||
}
|
||||
|
||||
//this->ruleMap.resize(ruleMap_len);
|
||||
for (unsigned i = 0; i < ruleMap_len; ++i) {
|
||||
this->ruleMap.emplace_back();
|
||||
if (!table.ReadU16(&this->ruleMap[i])) {
|
||||
return parent->Error("SILPass: Failed to read ruleMap[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!table.ReadU8(&this->minRulePreContext)) {
|
||||
return parent->Error("SILPass: Failed to read minRulePreContext");
|
||||
}
|
||||
if (!table.ReadU8(&this->maxRulePreContext) ||
|
||||
this->maxRulePreContext < this->minRulePreContext) {
|
||||
return parent->Error("SILPass: Failed to read valid maxRulePreContext");
|
||||
}
|
||||
|
||||
unsigned startStates_len = this->maxRulePreContext - this->minRulePreContext
|
||||
+ 1;
|
||||
// this->minRulePreContext <= this->maxRulePreContext
|
||||
//this->startStates.resize(startStates_len);
|
||||
for (unsigned i = 0; i < startStates_len; ++i) {
|
||||
this->startStates.emplace_back();
|
||||
if (!table.ReadS16(&this->startStates[i])) {
|
||||
return parent->Error("SILPass: Failed to read startStates[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
//this->ruleSortKeys.resize(this->numRules);
|
||||
for (unsigned i = 0; i < this->numRules; ++i) {
|
||||
this->ruleSortKeys.emplace_back();
|
||||
if (!table.ReadU16(&this->ruleSortKeys[i])) {
|
||||
return parent->Error("SILPass: Failed to read ruleSortKeys[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
//this->rulePreContext.resize(this->numRules);
|
||||
for (unsigned i = 0; i < this->numRules; ++i) {
|
||||
this->rulePreContext.emplace_back();
|
||||
if (!table.ReadU8(&this->rulePreContext[i])) {
|
||||
return parent->Error("SILPass: Failed to read rulePreContext[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU8(&this->collisionThreshold)) {
|
||||
return parent->Error("SILPass: Failed to read collisionThreshold");
|
||||
}
|
||||
if (!table.ReadU16(&this->pConstraint)) {
|
||||
return parent->Error("SILPass: Failed to read pConstraint");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long ruleConstraints_len = this->aCode - this->rcCode;
|
||||
// this->rcCode <= this->aCode
|
||||
//this->oConstraints.resize(static_cast<unsigned long>(this->numRules) + 1);
|
||||
for (unsigned long i = 0; i <= this->numRules; ++i) {
|
||||
this->oConstraints.emplace_back();
|
||||
if (!table.ReadU16(&this->oConstraints[i]) ||
|
||||
this->oConstraints[i] > ruleConstraints_len) {
|
||||
return parent->Error("SILPass: Failed to read valid oConstraints[%lu]",
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->oDebug && this->aCode > next_pass_offset) {
|
||||
return parent->Error("SILPass: Failed to calculate length of actions");
|
||||
}
|
||||
unsigned long actions_len = this->oDebug ? this->oDebug - this->aCode :
|
||||
next_pass_offset - this->aCode;
|
||||
// if this->oDebug, then this->aCode <= this->oDebug
|
||||
//this->oActions.resize(static_cast<unsigned long>(this->numRules) + 1);
|
||||
for (unsigned long i = 0; i <= this->numRules; ++i) {
|
||||
this->oActions.emplace_back();
|
||||
if (!table.ReadU16(&this->oActions[i]) ||
|
||||
(this->oActions[i] > actions_len)) {
|
||||
return parent->Error("SILPass: Failed to read valid oActions[%lu]", i);
|
||||
}
|
||||
}
|
||||
|
||||
//this->stateTrans.resize(this->numTransitional);
|
||||
for (unsigned i = 0; i < this->numTransitional; ++i) {
|
||||
this->stateTrans.emplace_back();
|
||||
//this->stateTrans[i].resize(this->numColumns);
|
||||
for (unsigned j = 0; j < this->numColumns; ++j) {
|
||||
this->stateTrans[i].emplace_back();
|
||||
if (!table.ReadU16(&stateTrans[i][j])) {
|
||||
return parent->Error("SILPass: Failed to read stateTrans[%u][%u]",
|
||||
i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->version >> 16 >= 2) {
|
||||
if (!table.ReadU8(&this->reserved2)) {
|
||||
return parent->Error("SILPass: Failed to read reserved2");
|
||||
}
|
||||
if (this->reserved2 != 0) {
|
||||
parent->Warning("SILPass: Nonzero reserved2");
|
||||
}
|
||||
|
||||
if (table.offset() != SILSub_init_offset + this->pcCode) {
|
||||
return parent->Error("SILPass: pcCode check failed");
|
||||
}
|
||||
//this->passConstraints.resize(this->pConstraint);
|
||||
for (unsigned i = 0; i < this->pConstraint; ++i) {
|
||||
this->passConstraints.emplace_back();
|
||||
if (!table.ReadU8(&this->passConstraints[i])) {
|
||||
return parent->Error("SILPass: Failed to read passConstraints[%u]", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table.offset() != SILSub_init_offset + this->rcCode) {
|
||||
return parent->Error("SILPass: rcCode check failed");
|
||||
}
|
||||
//this->ruleConstraints.resize(ruleConstraints_len); // calculated above
|
||||
for (unsigned long i = 0; i < ruleConstraints_len; ++i) {
|
||||
this->ruleConstraints.emplace_back();
|
||||
if (!table.ReadU8(&this->ruleConstraints[i])) {
|
||||
return parent->Error("SILPass: Failed to read ruleConstraints[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (table.offset() != SILSub_init_offset + this->aCode) {
|
||||
return parent->Error("SILPass: aCode check failed");
|
||||
}
|
||||
//this->actions.resize(actions_len); // calculated above
|
||||
for (unsigned long i = 0; i < actions_len; ++i) {
|
||||
this->actions.emplace_back();
|
||||
if (!table.ReadU8(&this->actions[i])) {
|
||||
return parent->Error("SILPass: Failed to read actions[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->oDebug) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
parent->GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
if (!name) {
|
||||
return parent->Error("SILPass: Required name table is missing");
|
||||
}
|
||||
|
||||
if (table.offset() != SILSub_init_offset + this->oDebug) {
|
||||
return parent->Error("SILPass: oDebug check failed");
|
||||
}
|
||||
//this->dActions.resize(this->numRules);
|
||||
for (unsigned i = 0; i < this->numRules; ++i) {
|
||||
this->dActions.emplace_back();
|
||||
if (!table.ReadU16(&this->dActions[i]) ||
|
||||
!name->IsValidNameId(this->dActions[i])) {
|
||||
return parent->Error("SILPass: Failed to read valid dActions[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned dStates_len = this->numRows - this->numRules;
|
||||
// this->numRules <= this->numRows
|
||||
//this->dStates.resize(dStates_len);
|
||||
for (unsigned i = 0; i < dStates_len; ++i) {
|
||||
this->dStates.emplace_back();
|
||||
if (!table.ReadU16(&this->dStates[i]) ||
|
||||
!name->IsValidNameId(this->dStates[i])) {
|
||||
return parent->Error("SILPass: Failed to read valid dStates[%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
//this->dCols.resize(this->numRules);
|
||||
for (unsigned i = 0; i < this->numRules; ++i) {
|
||||
this->dCols.emplace_back();
|
||||
if (!table.ReadU16(&this->dCols[i]) ||
|
||||
!name->IsValidNameId(this->dCols[i])) {
|
||||
return parent->Error("SILPass: Failed to read valid dCols[%u]");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::
|
||||
SILPass::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->flags) ||
|
||||
!out->WriteU8(this->maxRuleLoop) ||
|
||||
!out->WriteU8(this->maxRuleContext) ||
|
||||
!out->WriteU8(this->maxBackup) ||
|
||||
!out->WriteU16(this->numRules) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU16(this->fsmOffset) ||
|
||||
!out->WriteU32(this->pcCode))) ||
|
||||
!out->WriteU32(this->rcCode) ||
|
||||
!out->WriteU32(this->aCode) ||
|
||||
!out->WriteU32(this->oDebug) ||
|
||||
!out->WriteU16(this->numRows) ||
|
||||
!out->WriteU16(this->numTransitional) ||
|
||||
!out->WriteU16(this->numSuccess) ||
|
||||
!out->WriteU16(this->numColumns) ||
|
||||
!out->WriteU16(this->numRange) ||
|
||||
!out->WriteU16(this->searchRange) ||
|
||||
!out->WriteU16(this->entrySelector) ||
|
||||
!out->WriteU16(this->rangeShift) ||
|
||||
!SerializeParts(this->ranges, out) ||
|
||||
!SerializeParts(this->oRuleMap, out) ||
|
||||
!SerializeParts(this->ruleMap, out) ||
|
||||
!out->WriteU8(this->minRulePreContext) ||
|
||||
!out->WriteU8(this->maxRulePreContext) ||
|
||||
!SerializeParts(this->startStates, out) ||
|
||||
!SerializeParts(this->ruleSortKeys, out) ||
|
||||
!SerializeParts(this->rulePreContext, out) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU8(this->collisionThreshold) ||
|
||||
!out->WriteU16(this->pConstraint))) ||
|
||||
!SerializeParts(this->oConstraints, out) ||
|
||||
!SerializeParts(this->oActions, out) ||
|
||||
!SerializeParts(this->stateTrans, out) ||
|
||||
(parent->version >> 16 >= 2 &&
|
||||
(!out->WriteU8(this->reserved2) ||
|
||||
!SerializeParts(this->passConstraints, out))) ||
|
||||
!SerializeParts(this->ruleConstraints, out) ||
|
||||
!SerializeParts(this->actions, out) ||
|
||||
!SerializeParts(this->dActions, out) ||
|
||||
!SerializeParts(this->dStates, out) ||
|
||||
!SerializeParts(this->dCols, out)) {
|
||||
return parent->Error("SILPass: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::SILPass::
|
||||
PassRange::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU16(&this->firstId)) {
|
||||
return parent->Error("PassRange: Failed to read firstId");
|
||||
}
|
||||
if (!table.ReadU16(&this->lastId)) {
|
||||
return parent->Error("PassRange: Failed to read lastId");
|
||||
}
|
||||
if (!table.ReadU16(&this->colId)) {
|
||||
return parent->Error("PassRange: Failed to read colId");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILF::SILSub::SILPass::
|
||||
PassRange::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU16(this->firstId) ||
|
||||
!out->WriteU16(this->lastId) ||
|
||||
!out->WriteU16(this->colId)) {
|
||||
return parent->Error("PassRange: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_SILF_H_
|
||||
#define OTS_SILF_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeSILF : public Table {
|
||||
public:
|
||||
explicit OpenTypeSILF(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length) {
|
||||
return this->Parse(data, length, false);
|
||||
}
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
bool Parse(const uint8_t* data, size_t length, bool prevent_decompression);
|
||||
struct SILSub : public TablePart<OpenTypeSILF> {
|
||||
explicit SILSub(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent), classes(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct JustificationLevel : public TablePart<OpenTypeSILF> {
|
||||
explicit JustificationLevel(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint8_t attrStretch;
|
||||
uint8_t attrShrink;
|
||||
uint8_t attrStep;
|
||||
uint8_t attrWeight;
|
||||
uint8_t runto;
|
||||
uint8_t reserved;
|
||||
uint8_t reserved2;
|
||||
uint8_t reserved3;
|
||||
};
|
||||
struct PseudoMap : public TablePart<OpenTypeSILF> {
|
||||
explicit PseudoMap(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint32_t unicode;
|
||||
uint16_t nPseudo;
|
||||
};
|
||||
struct ClassMap : public TablePart<OpenTypeSILF> {
|
||||
explicit ClassMap(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct LookupClass : public TablePart<OpenTypeSILF> {
|
||||
explicit LookupClass(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct LookupPair : public TablePart<OpenTypeSILF> {
|
||||
explicit LookupPair(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint16_t glyphId;
|
||||
uint16_t index;
|
||||
};
|
||||
uint16_t numIDs;
|
||||
uint16_t searchRange;
|
||||
uint16_t entrySelector;
|
||||
uint16_t rangeShift;
|
||||
std::vector<LookupPair> lookups;
|
||||
};
|
||||
uint16_t numClass;
|
||||
uint16_t numLinear;
|
||||
std::vector<uint32_t> oClass; // uint16_t before v4
|
||||
std::vector<uint16_t> glyphs;
|
||||
std::vector<LookupClass> lookups;
|
||||
};
|
||||
struct SILPass : public TablePart<OpenTypeSILF> {
|
||||
explicit SILPass(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table) { return false; }
|
||||
bool ParsePart(Buffer& table, const size_t SILSub_init_offset,
|
||||
const size_t next_pass_offset);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
struct PassRange : public TablePart<OpenTypeSILF> {
|
||||
explicit PassRange(OpenTypeSILF* parent)
|
||||
: TablePart<OpenTypeSILF>(parent) { }
|
||||
bool ParsePart(Buffer& table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint16_t firstId;
|
||||
uint16_t lastId;
|
||||
uint16_t colId;
|
||||
};
|
||||
uint8_t flags;
|
||||
uint8_t maxRuleLoop;
|
||||
uint8_t maxRuleContext;
|
||||
uint8_t maxBackup;
|
||||
uint16_t numRules;
|
||||
uint16_t fsmOffset;
|
||||
uint32_t pcCode;
|
||||
uint32_t rcCode;
|
||||
uint32_t aCode;
|
||||
uint32_t oDebug;
|
||||
uint16_t numRows;
|
||||
uint16_t numTransitional;
|
||||
uint16_t numSuccess;
|
||||
uint16_t numColumns;
|
||||
uint16_t numRange;
|
||||
uint16_t searchRange;
|
||||
uint16_t entrySelector;
|
||||
uint16_t rangeShift;
|
||||
std::vector<PassRange> ranges;
|
||||
std::vector<uint16_t> oRuleMap;
|
||||
std::vector<uint16_t> ruleMap;
|
||||
uint8_t minRulePreContext;
|
||||
uint8_t maxRulePreContext;
|
||||
std::vector<int16_t> startStates;
|
||||
std::vector<uint16_t> ruleSortKeys;
|
||||
std::vector<uint8_t> rulePreContext;
|
||||
uint8_t collisionThreshold; // reserved before v5
|
||||
uint16_t pConstraint;
|
||||
std::vector<uint16_t> oConstraints;
|
||||
std::vector<uint16_t> oActions;
|
||||
std::vector<std::vector<uint16_t>> stateTrans;
|
||||
uint8_t reserved2;
|
||||
std::vector<uint8_t> passConstraints;
|
||||
std::vector<uint8_t> ruleConstraints;
|
||||
std::vector<uint8_t> actions;
|
||||
std::vector<uint16_t> dActions;
|
||||
std::vector<uint16_t> dStates;
|
||||
std::vector<uint16_t> dCols;
|
||||
};
|
||||
uint32_t ruleVersion;
|
||||
uint16_t passOffset;
|
||||
uint16_t pseudosOffset;
|
||||
uint16_t maxGlyphID;
|
||||
int16_t extraAscent;
|
||||
int16_t extraDescent;
|
||||
uint8_t numPasses;
|
||||
uint8_t iSubst;
|
||||
uint8_t iPos;
|
||||
uint8_t iJust;
|
||||
uint8_t iBidi;
|
||||
uint8_t flags;
|
||||
uint8_t maxPreContext;
|
||||
uint8_t maxPostContext;
|
||||
uint8_t attrPseudo;
|
||||
uint8_t attrBreakWeight;
|
||||
uint8_t attrDirectionality;
|
||||
uint8_t attrMirroring; // reserved before v4
|
||||
uint8_t attrSkipPasses; // reserved2 before v4
|
||||
uint8_t numJLevels;
|
||||
std::vector<JustificationLevel> jLevels;
|
||||
uint16_t numLigComp;
|
||||
uint8_t numUserDefn;
|
||||
uint8_t maxCompPerLig;
|
||||
uint8_t direction;
|
||||
uint8_t attCollisions; // reserved3 before v5
|
||||
uint8_t reserved4;
|
||||
uint8_t reserved5;
|
||||
uint8_t reserved6;
|
||||
uint8_t numCritFeatures;
|
||||
std::vector<uint16_t> critFeatures;
|
||||
uint8_t reserved7;
|
||||
uint8_t numScriptTag;
|
||||
std::vector<uint32_t> scriptTag;
|
||||
uint16_t lbGID;
|
||||
std::vector<uint32_t> oPasses;
|
||||
uint16_t numPseudo;
|
||||
uint16_t searchPseudo;
|
||||
uint16_t pseudoSelector;
|
||||
uint16_t pseudoShift;
|
||||
std::vector<PseudoMap> pMaps;
|
||||
ClassMap classes;
|
||||
std::vector<SILPass> passes;
|
||||
};
|
||||
uint32_t version;
|
||||
uint32_t compHead; // compression header
|
||||
static const uint32_t SCHEME = 0xF8000000;
|
||||
static const uint32_t FULL_SIZE = 0x07FFFFFF;
|
||||
static const uint32_t COMPILER_VERSION = 0x07FFFFFF;
|
||||
uint16_t numSub;
|
||||
uint16_t reserved;
|
||||
std::vector<uint32_t> offset;
|
||||
std::vector<SILSub> tables;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_SILF_H_
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "sill.h"
|
||||
|
||||
#include "feat.h"
|
||||
#include <cmath>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool OpenTypeSILL::Parse(const uint8_t* data, size_t length) {
|
||||
if (GetFont()->dropped_graphite) {
|
||||
return Drop("Skipping Graphite table");
|
||||
}
|
||||
Buffer table(data, length);
|
||||
|
||||
if (!table.ReadU32(&this->version) || this->version >> 16 != 1) {
|
||||
return Drop("Failed to read valid version");
|
||||
}
|
||||
if (!table.ReadU16(&this->numLangs)) {
|
||||
return Drop("Failed to read numLangs");
|
||||
}
|
||||
|
||||
// The following three fields are deprecated and ignored. We fix them up here
|
||||
// just for internal consistency, but the Graphite engine doesn't care.
|
||||
if (!table.ReadU16(&this->searchRange) ||
|
||||
!table.ReadU16(&this->entrySelector) ||
|
||||
!table.ReadU16(&this->rangeShift)) {
|
||||
return Drop("Failed to read searchRange..rangeShift");
|
||||
}
|
||||
if (this->numLangs == 0) {
|
||||
if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
|
||||
this->searchRange = this->entrySelector = this->rangeShift = 0;
|
||||
}
|
||||
} else {
|
||||
unsigned floorLog2 = std::floor(std::log2(this->numLangs));
|
||||
if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
|
||||
this->entrySelector != floorLog2 ||
|
||||
this->rangeShift != this->numLangs - this->searchRange) {
|
||||
this->searchRange = (unsigned)std::pow(2, floorLog2);
|
||||
this->entrySelector = floorLog2;
|
||||
this->rangeShift = this->numLangs - this->searchRange;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<size_t> unverified;
|
||||
//this->entries.resize(static_cast<unsigned long>(this->numLangs) + 1, this);
|
||||
for (unsigned long i = 0; i <= this->numLangs; ++i) {
|
||||
this->entries.emplace_back(this);
|
||||
LanguageEntry& entry = this->entries[i];
|
||||
if (!entry.ParsePart(table)) {
|
||||
return Drop("Failed to read entries[%u]", i);
|
||||
}
|
||||
for (unsigned j = 0; j < entry.numSettings; ++j) {
|
||||
size_t offset = entry.offset + j * 8;
|
||||
if (offset < entry.offset || offset > length) {
|
||||
return DropGraphite("Invalid LangFeatureSetting offset %zu/%zu",
|
||||
offset, length);
|
||||
}
|
||||
unverified.insert(offset);
|
||||
// need to verify that this LanguageEntry points to valid
|
||||
// LangFeatureSetting
|
||||
}
|
||||
}
|
||||
|
||||
while (table.remaining()) {
|
||||
unverified.erase(table.offset());
|
||||
LangFeatureSetting setting(this);
|
||||
if (!setting.ParsePart(table)) {
|
||||
return Drop("Failed to read a LangFeatureSetting");
|
||||
}
|
||||
settings.push_back(setting);
|
||||
}
|
||||
|
||||
if (!unverified.empty()) {
|
||||
return Drop("%zu incorrect offsets into settings", unverified.size());
|
||||
}
|
||||
if (table.remaining()) {
|
||||
return Warning("%zu bytes unparsed", table.remaining());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::Serialize(OTSStream* out) {
|
||||
if (!out->WriteU32(this->version) ||
|
||||
!out->WriteU16(this->numLangs) ||
|
||||
!out->WriteU16(this->searchRange) ||
|
||||
!out->WriteU16(this->entrySelector) ||
|
||||
!out->WriteU16(this->rangeShift) ||
|
||||
!SerializeParts(this->entries, out) ||
|
||||
!SerializeParts(this->settings, out)) {
|
||||
return Error("Failed to write table");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::LanguageEntry::ParsePart(Buffer& table) {
|
||||
if (!table.ReadU8(&this->langcode[0]) ||
|
||||
!table.ReadU8(&this->langcode[1]) ||
|
||||
!table.ReadU8(&this->langcode[2]) ||
|
||||
!table.ReadU8(&this->langcode[3])) {
|
||||
return parent->Error("LanguageEntry: Failed to read langcode");
|
||||
}
|
||||
if (!table.ReadU16(&this->numSettings)) {
|
||||
return parent->Error("LanguageEntry: Failed to read numSettings");
|
||||
}
|
||||
if (!table.ReadU16(&this->offset)) {
|
||||
return parent->Error("LanguageEntry: Failed to read offset");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::LanguageEntry::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU8(this->langcode[0]) ||
|
||||
!out->WriteU8(this->langcode[1]) ||
|
||||
!out->WriteU8(this->langcode[2]) ||
|
||||
!out->WriteU8(this->langcode[3]) ||
|
||||
!out->WriteU16(this->numSettings) ||
|
||||
!out->WriteU16(this->offset)) {
|
||||
return parent->Error("LanguageEntry: Failed to write");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::LangFeatureSetting::ParsePart(Buffer& table) {
|
||||
OpenTypeFEAT* feat = static_cast<OpenTypeFEAT*>(
|
||||
parent->GetFont()->GetTypedTable(OTS_TAG_FEAT));
|
||||
if (!feat) {
|
||||
return parent->Error("FeatureDefn: Required Feat table is missing");
|
||||
}
|
||||
|
||||
if (!table.ReadU32(&this->featureId) ||
|
||||
!feat->IsValidFeatureId(this->featureId)) {
|
||||
return parent->Error("LangFeatureSetting: Failed to read valid featureId");
|
||||
}
|
||||
if (!table.ReadS16(&this->value)) {
|
||||
return parent->Error("LangFeatureSetting: Failed to read value");
|
||||
}
|
||||
if (!table.ReadU16(&this->reserved)) {
|
||||
return parent->Error("LangFeatureSetting: Failed to read reserved");
|
||||
}
|
||||
if (this->reserved != 0) {
|
||||
parent->Warning("LangFeatureSetting: Nonzero reserved");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSILL::LangFeatureSetting::SerializePart(OTSStream* out) const {
|
||||
if (!out->WriteU32(this->featureId) ||
|
||||
!out->WriteS16(this->value) ||
|
||||
!out->WriteU16(this->reserved)) {
|
||||
return parent->Error("LangFeatureSetting: Failed to read reserved");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_SILL_H_
|
||||
#define OTS_SILL_H_
|
||||
|
||||
#include "ots.h"
|
||||
#include "graphite.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ots {
|
||||
|
||||
class OpenTypeSILL : public Table {
|
||||
public:
|
||||
explicit OpenTypeSILL(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
struct LanguageEntry : public TablePart<OpenTypeSILL> {
|
||||
explicit LanguageEntry(OpenTypeSILL* parent)
|
||||
: TablePart<OpenTypeSILL>(parent) { }
|
||||
bool ParsePart(Buffer &table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint8_t langcode[4];
|
||||
uint16_t numSettings;
|
||||
uint16_t offset;
|
||||
};
|
||||
struct LangFeatureSetting : public TablePart<OpenTypeSILL> {
|
||||
explicit LangFeatureSetting(OpenTypeSILL* parent)
|
||||
: TablePart<OpenTypeSILL>(parent) { }
|
||||
bool ParsePart(Buffer &table);
|
||||
bool SerializePart(OTSStream* out) const;
|
||||
uint32_t featureId;
|
||||
int16_t value;
|
||||
uint16_t reserved;
|
||||
};
|
||||
uint32_t version;
|
||||
uint16_t numLangs;
|
||||
uint16_t searchRange;
|
||||
uint16_t entrySelector;
|
||||
uint16_t rangeShift;
|
||||
std::vector<LanguageEntry> entries;
|
||||
std::vector<LangFeatureSetting> settings;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_SILL_H_
|
|
@ -0,0 +1,347 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "stat.h"
|
||||
#include "name.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeSTAT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool OpenTypeSTAT::ValidateNameId(uint16_t nameid, bool allowPredefined) {
|
||||
OpenTypeNAME* name = static_cast<OpenTypeNAME*>(
|
||||
GetFont()->GetTypedTable(OTS_TAG_NAME));
|
||||
|
||||
if (!name || !name->IsValidNameId(nameid)) {
|
||||
Drop("Invalid nameID: %d", nameid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!allowPredefined && nameid < 26) {
|
||||
Warning("nameID out of range: %d", nameid);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((nameid >= 26 && nameid <= 255) || nameid >= 32768) {
|
||||
Warning("nameID out of range: %d", nameid);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSTAT::Parse(const uint8_t* data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
if (!table.ReadU16(&this->majorVersion) ||
|
||||
!table.ReadU16(&this->minorVersion) ||
|
||||
!table.ReadU16(&this->designAxisSize) ||
|
||||
!table.ReadU16(&this->designAxisCount) ||
|
||||
!table.ReadU32(&this->designAxesOffset) ||
|
||||
!table.ReadU16(&this->axisValueCount) ||
|
||||
!table.ReadU32(&this->offsetToAxisValueOffsets) ||
|
||||
!(this->minorVersion < 1 || table.ReadU16(&this->elidedFallbackNameID))) {
|
||||
return Drop("Failed to read table header");
|
||||
}
|
||||
if (this->majorVersion != 1) {
|
||||
return Drop("Unknown table version");
|
||||
}
|
||||
if (this->minorVersion > 2) {
|
||||
Warning("Unknown minor version, downgrading to 2");
|
||||
this->minorVersion = 2;
|
||||
}
|
||||
|
||||
if (this->designAxisSize < sizeof(AxisRecord)) {
|
||||
return Drop("Invalid designAxisSize");
|
||||
}
|
||||
|
||||
size_t headerEnd = table.offset();
|
||||
|
||||
if (this->designAxisCount == 0) {
|
||||
if (this->designAxesOffset != 0) {
|
||||
Warning("Unexpected non-zero designAxesOffset");
|
||||
this->designAxesOffset = 0;
|
||||
}
|
||||
} else {
|
||||
if (this->designAxesOffset < headerEnd ||
|
||||
size_t(this->designAxesOffset) +
|
||||
size_t(this->designAxisCount) * size_t(this->designAxisSize) > length) {
|
||||
return Drop("Invalid designAxesOffset");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->designAxisCount; i++) {
|
||||
table.set_offset(this->designAxesOffset + i * this->designAxisSize);
|
||||
this->designAxes.emplace_back();
|
||||
auto& axis = this->designAxes[i];
|
||||
if (!table.ReadU32(&axis.axisTag) ||
|
||||
!table.ReadU16(&axis.axisNameID) ||
|
||||
!table.ReadU16(&axis.axisOrdering)) {
|
||||
return Drop("Failed to read design axis");
|
||||
}
|
||||
if (!CheckTag(axis.axisTag)) {
|
||||
return Drop("Bad design axis tag");
|
||||
}
|
||||
if (!ValidateNameId(axis.axisNameID, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// - check that all axes defined in fvar are covered by STAT
|
||||
// - check that axisOrdering values are not duplicated (warn only)
|
||||
|
||||
if (this->axisValueCount == 0) {
|
||||
if (this->offsetToAxisValueOffsets != 0) {
|
||||
Warning("Unexpected non-zero offsetToAxisValueOffsets");
|
||||
this->offsetToAxisValueOffsets = 0;
|
||||
}
|
||||
} else {
|
||||
if (this->offsetToAxisValueOffsets < headerEnd ||
|
||||
size_t(this->offsetToAxisValueOffsets) +
|
||||
size_t(this->axisValueCount) * sizeof(uint16_t) > length) {
|
||||
return Drop("Invalid offsetToAxisValueOffsets");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < this->axisValueCount; i++) {
|
||||
table.set_offset(this->offsetToAxisValueOffsets + i * sizeof(uint16_t));
|
||||
uint16_t axisValueOffset;
|
||||
if (!table.ReadU16(&axisValueOffset)) {
|
||||
return Drop("Failed to read axis value offset");
|
||||
}
|
||||
if (this->offsetToAxisValueOffsets + axisValueOffset > length) {
|
||||
return Drop("Invalid axis value offset");
|
||||
}
|
||||
table.set_offset(this->offsetToAxisValueOffsets + axisValueOffset);
|
||||
uint16_t format;
|
||||
if (!table.ReadU16(&format)) {
|
||||
return Drop("Failed to read axis value format");
|
||||
}
|
||||
this->axisValues.emplace_back(format);
|
||||
auto& axisValue = axisValues[i];
|
||||
switch (format) {
|
||||
case 1:
|
||||
if (!table.ReadU16(&axisValue.format1.axisIndex) ||
|
||||
!table.ReadU16(&axisValue.format1.flags) ||
|
||||
!table.ReadU16(&axisValue.format1.valueNameID) ||
|
||||
!table.ReadS32(&axisValue.format1.value)) {
|
||||
return Drop("Failed to read axis value (format 1)");
|
||||
}
|
||||
if (axisValue.format1.axisIndex >= this->designAxisCount) {
|
||||
return Drop("Axis index out of range");
|
||||
}
|
||||
if ((axisValue.format1.flags & 0xFFFCu) != 0) {
|
||||
Warning("Unexpected axis value flags");
|
||||
axisValue.format1.flags &= ~0xFFFCu;
|
||||
}
|
||||
if (!ValidateNameId(axisValue.format1.valueNameID)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!table.ReadU16(&axisValue.format2.axisIndex) ||
|
||||
!table.ReadU16(&axisValue.format2.flags) ||
|
||||
!table.ReadU16(&axisValue.format2.valueNameID) ||
|
||||
!table.ReadS32(&axisValue.format2.nominalValue) ||
|
||||
!table.ReadS32(&axisValue.format2.rangeMinValue) ||
|
||||
!table.ReadS32(&axisValue.format2.rangeMaxValue)) {
|
||||
return Drop("Failed to read axis value (format 2)");
|
||||
}
|
||||
if (axisValue.format2.axisIndex >= this->designAxisCount) {
|
||||
return Drop("Axis index out of range");
|
||||
}
|
||||
if ((axisValue.format2.flags & 0xFFFCu) != 0) {
|
||||
Warning("Unexpected axis value flags");
|
||||
axisValue.format1.flags &= ~0xFFFCu;
|
||||
}
|
||||
if (!ValidateNameId(axisValue.format2.valueNameID)) {
|
||||
return true;
|
||||
}
|
||||
if (!(axisValue.format2.rangeMinValue <= axisValue.format2.nominalValue &&
|
||||
axisValue.format2.nominalValue <= axisValue.format2.rangeMaxValue)) {
|
||||
Warning("Bad axis value range or nominal value");
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!table.ReadU16(&axisValue.format3.axisIndex) ||
|
||||
!table.ReadU16(&axisValue.format3.flags) ||
|
||||
!table.ReadU16(&axisValue.format3.valueNameID) ||
|
||||
!table.ReadS32(&axisValue.format3.value) ||
|
||||
!table.ReadS32(&axisValue.format3.linkedValue)) {
|
||||
return Drop("Failed to read axis value (format 3)");
|
||||
}
|
||||
if (axisValue.format3.axisIndex >= this->designAxisCount) {
|
||||
return Drop("Axis index out of range");
|
||||
}
|
||||
if ((axisValue.format3.flags & 0xFFFCu) != 0) {
|
||||
Warning("Unexpected axis value flags");
|
||||
axisValue.format3.flags &= ~0xFFFCu;
|
||||
}
|
||||
if (!ValidateNameId(axisValue.format3.valueNameID)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (this->minorVersion < 2) {
|
||||
Warning("Invalid table version for format 4 axis values - updating");
|
||||
this->minorVersion = 2;
|
||||
}
|
||||
if (!table.ReadU16(&axisValue.format4.axisCount) ||
|
||||
!table.ReadU16(&axisValue.format4.flags) ||
|
||||
!table.ReadU16(&axisValue.format4.valueNameID)) {
|
||||
return Drop("Failed to read axis value (format 4)");
|
||||
}
|
||||
if (axisValue.format4.axisCount > this->designAxisCount) {
|
||||
return Drop("Axis count out of range");
|
||||
}
|
||||
if ((axisValue.format4.flags & 0xFFFCu) != 0) {
|
||||
Warning("Unexpected axis value flags");
|
||||
axisValue.format4.flags &= ~0xFFFCu;
|
||||
}
|
||||
if (!ValidateNameId(axisValue.format4.valueNameID)) {
|
||||
return true;
|
||||
}
|
||||
for (unsigned j = 0; j < axisValue.format4.axisCount; j++) {
|
||||
axisValue.format4.axisValues.emplace_back();
|
||||
auto& v = axisValue.format4.axisValues[j];
|
||||
if (!table.ReadU16(&v.axisIndex) ||
|
||||
!table.ReadS32(&v.value)) {
|
||||
return Drop("Failed to read axis value");
|
||||
}
|
||||
if (v.axisIndex >= this->designAxisCount) {
|
||||
return Drop("Axis index out of range");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return Drop("Unknown axis value format");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenTypeSTAT::Serialize(OTSStream* out) {
|
||||
off_t tableStart = out->Tell();
|
||||
|
||||
size_t headerSize = 5 * sizeof(uint16_t) + 2 * sizeof(uint32_t);
|
||||
if (this->minorVersion >= 1) {
|
||||
headerSize += sizeof(uint16_t);
|
||||
}
|
||||
|
||||
if (this->designAxisCount == 0) {
|
||||
this->designAxesOffset = 0;
|
||||
} else {
|
||||
this->designAxesOffset = headerSize;
|
||||
}
|
||||
|
||||
this->designAxisSize = sizeof(AxisRecord);
|
||||
|
||||
if (this->axisValueCount == 0) {
|
||||
this->offsetToAxisValueOffsets = 0;
|
||||
} else {
|
||||
if (this->designAxesOffset == 0) {
|
||||
this->offsetToAxisValueOffsets = headerSize;
|
||||
} else {
|
||||
this->offsetToAxisValueOffsets = this->designAxesOffset + this->designAxisCount * this->designAxisSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (!out->WriteU16(this->majorVersion) ||
|
||||
!out->WriteU16(this->minorVersion) ||
|
||||
!out->WriteU16(this->designAxisSize) ||
|
||||
!out->WriteU16(this->designAxisCount) ||
|
||||
!out->WriteU32(this->designAxesOffset) ||
|
||||
!out->WriteU16(this->axisValueCount) ||
|
||||
!out->WriteU32(this->offsetToAxisValueOffsets) ||
|
||||
!(this->minorVersion < 1 || out->WriteU16(this->elidedFallbackNameID))) {
|
||||
return Error("Failed to write table header");
|
||||
}
|
||||
|
||||
if (this->designAxisCount > 0) {
|
||||
if (out->Tell() - tableStart != this->designAxesOffset) {
|
||||
return Error("Error computing designAxesOffset");
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < this->designAxisCount; i++) {
|
||||
const auto& axis = this->designAxes[i];
|
||||
if (!out->WriteU32(axis.axisTag) ||
|
||||
!out->WriteU16(axis.axisNameID) ||
|
||||
!out->WriteU16(axis.axisOrdering)) {
|
||||
return Error("Failed to write design axis");
|
||||
}
|
||||
}
|
||||
|
||||
if (this->axisValueCount > 0) {
|
||||
if (out->Tell() - tableStart != this->offsetToAxisValueOffsets) {
|
||||
return Error("Error computing offsetToAxisValueOffsets");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t axisValueOffset = this->axisValueCount * sizeof(uint16_t);
|
||||
for (unsigned i = 0; i < this->axisValueCount; i++) {
|
||||
const auto& value = this->axisValues[i];
|
||||
if (!out->WriteU16(axisValueOffset)) {
|
||||
return Error("Failed to write axis value offset");
|
||||
}
|
||||
axisValueOffset += value.Length();
|
||||
}
|
||||
for (unsigned i = 0; i < this->axisValueCount; i++) {
|
||||
const auto& value = this->axisValues[i];
|
||||
if (!out->WriteU16(value.format)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
switch (value.format) {
|
||||
case 1:
|
||||
if (!out->WriteU16(value.format1.axisIndex) ||
|
||||
!out->WriteU16(value.format1.flags) ||
|
||||
!out->WriteU16(value.format1.valueNameID) ||
|
||||
!out->WriteS32(value.format1.value)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!out->WriteU16(value.format2.axisIndex) ||
|
||||
!out->WriteU16(value.format2.flags) ||
|
||||
!out->WriteU16(value.format2.valueNameID) ||
|
||||
!out->WriteS32(value.format2.nominalValue) ||
|
||||
!out->WriteS32(value.format2.rangeMinValue) ||
|
||||
!out->WriteS32(value.format2.rangeMaxValue)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!out->WriteU16(value.format3.axisIndex) ||
|
||||
!out->WriteU16(value.format3.flags) ||
|
||||
!out->WriteU16(value.format3.valueNameID) ||
|
||||
!out->WriteS32(value.format3.value) ||
|
||||
!out->WriteS32(value.format3.linkedValue)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (!out->WriteU16(value.format4.axisCount) ||
|
||||
!out->WriteU16(value.format4.flags) ||
|
||||
!out->WriteU16(value.format4.valueNameID)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
for (unsigned j = 0; j < value.format4.axisValues.size(); j++) {
|
||||
if (!out->WriteU16(value.format4.axisValues[j].axisIndex) ||
|
||||
!out->WriteS32(value.format4.axisValues[j].value)) {
|
||||
return Error("Failed to write axis value");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return Error("Bad value format");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_STAT_H_
|
||||
#define OTS_STAT_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OpenTypeSTAT Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class OpenTypeSTAT : public Table {
|
||||
public:
|
||||
explicit OpenTypeSTAT(Font* font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t* data, size_t length);
|
||||
bool Serialize(OTSStream* out);
|
||||
|
||||
private:
|
||||
bool ValidateNameId(uint16_t nameid, bool allowPredefined = true);
|
||||
|
||||
uint16_t majorVersion;
|
||||
uint16_t minorVersion;
|
||||
uint16_t designAxisSize;
|
||||
uint16_t designAxisCount;
|
||||
uint32_t designAxesOffset;
|
||||
uint16_t axisValueCount;
|
||||
uint32_t offsetToAxisValueOffsets;
|
||||
uint16_t elidedFallbackNameID;
|
||||
|
||||
struct AxisRecord {
|
||||
uint32_t axisTag;
|
||||
uint16_t axisNameID;
|
||||
uint16_t axisOrdering;
|
||||
};
|
||||
std::vector<AxisRecord> designAxes;
|
||||
|
||||
typedef int32_t Fixed; /* 16.16 fixed-point value */
|
||||
|
||||
struct AxisValueFormat1 {
|
||||
uint16_t axisIndex;
|
||||
uint16_t flags;
|
||||
uint16_t valueNameID;
|
||||
Fixed value;
|
||||
static size_t Length() {
|
||||
return 3 * sizeof(uint16_t) + sizeof(Fixed);
|
||||
}
|
||||
};
|
||||
|
||||
struct AxisValueFormat2 {
|
||||
uint16_t axisIndex;
|
||||
uint16_t flags;
|
||||
uint16_t valueNameID;
|
||||
Fixed nominalValue;
|
||||
Fixed rangeMinValue;
|
||||
Fixed rangeMaxValue;
|
||||
static size_t Length() {
|
||||
return 3 * sizeof(uint16_t) + 3 * sizeof(Fixed);
|
||||
}
|
||||
};
|
||||
|
||||
struct AxisValueFormat3 {
|
||||
uint16_t axisIndex;
|
||||
uint16_t flags;
|
||||
uint16_t valueNameID;
|
||||
Fixed value;
|
||||
Fixed linkedValue;
|
||||
static size_t Length() {
|
||||
return 3 * sizeof(uint16_t) + 2 * sizeof(Fixed);
|
||||
}
|
||||
};
|
||||
|
||||
struct AxisValueFormat4 {
|
||||
uint16_t axisCount;
|
||||
uint16_t flags;
|
||||
uint16_t valueNameID;
|
||||
struct AxisValue {
|
||||
uint16_t axisIndex;
|
||||
Fixed value;
|
||||
};
|
||||
std::vector<AxisValue> axisValues;
|
||||
size_t Length() const {
|
||||
return 3 * sizeof(uint16_t) + axisValues.size() * (sizeof(uint16_t) + sizeof(Fixed));
|
||||
}
|
||||
};
|
||||
|
||||
struct AxisValueRecord {
|
||||
uint16_t format;
|
||||
union {
|
||||
AxisValueFormat1 format1;
|
||||
AxisValueFormat2 format2;
|
||||
AxisValueFormat3 format3;
|
||||
AxisValueFormat4 format4;
|
||||
};
|
||||
explicit AxisValueRecord(uint16_t format_)
|
||||
: format(format_)
|
||||
{
|
||||
if (format == 4) {
|
||||
new (&this->format4) AxisValueFormat4();
|
||||
}
|
||||
}
|
||||
AxisValueRecord(const AxisValueRecord& other_)
|
||||
: format(other_.format)
|
||||
{
|
||||
switch (format) {
|
||||
case 1:
|
||||
format1 = other_.format1;
|
||||
break;
|
||||
case 2:
|
||||
format2 = other_.format2;
|
||||
break;
|
||||
case 3:
|
||||
format3 = other_.format3;
|
||||
break;
|
||||
case 4:
|
||||
new (&this->format4) AxisValueFormat4();
|
||||
format4 = other_.format4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
~AxisValueRecord() {
|
||||
if (format == 4) {
|
||||
this->format4.~AxisValueFormat4();
|
||||
}
|
||||
}
|
||||
uint32_t Length() const {
|
||||
switch (format) {
|
||||
case 1:
|
||||
return sizeof(uint16_t) + format1.Length();
|
||||
case 2:
|
||||
return sizeof(uint16_t) + format2.Length();
|
||||
case 3:
|
||||
return sizeof(uint16_t) + format3.Length();
|
||||
case 4:
|
||||
return sizeof(uint16_t) + format4.Length();
|
||||
default:
|
||||
// can't happen
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<AxisValueRecord> axisValues;
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_STAT_H_
|
|
@ -0,0 +1,261 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "layout.h"
|
||||
|
||||
#include "fvar.h"
|
||||
|
||||
// OpenType Variations Common Table Formats
|
||||
|
||||
#define TABLE_NAME "Variations" // XXX: use individual table names
|
||||
|
||||
namespace {
|
||||
|
||||
bool ParseVariationRegionList(const ots::Font* font, const uint8_t* data, const size_t length,
|
||||
uint16_t* regionCount) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
uint16_t axisCount;
|
||||
|
||||
if (!subtable.ReadU16(&axisCount) ||
|
||||
!subtable.ReadU16(regionCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation region list header");
|
||||
}
|
||||
|
||||
if (*regionCount == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ots::OpenTypeFVAR* fvar =
|
||||
static_cast<ots::OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
|
||||
if (!fvar) {
|
||||
return OTS_FAILURE_MSG("Required fvar table is missing");
|
||||
}
|
||||
if (axisCount != fvar->AxisCount()) {
|
||||
return OTS_FAILURE_MSG("Axis count mismatch");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < *regionCount; i++) {
|
||||
for (unsigned j = 0; j < axisCount; j++) {
|
||||
int16_t startCoord, peakCoord, endCoord;
|
||||
if (!subtable.ReadS16(&startCoord) ||
|
||||
!subtable.ReadS16(&peakCoord) ||
|
||||
!subtable.ReadS16(&endCoord)) {
|
||||
return OTS_FAILURE_MSG("Failed to read region axis coordinates");
|
||||
}
|
||||
if (startCoord > peakCoord || peakCoord > endCoord) {
|
||||
return OTS_FAILURE_MSG("Region axis coordinates out of order");
|
||||
}
|
||||
if (startCoord < -0x4000 || endCoord > 0x4000) {
|
||||
return OTS_FAILURE_MSG("Region axis coordinate out of range");
|
||||
}
|
||||
if ((peakCoord < 0 && endCoord > 0) ||
|
||||
(peakCoord > 0 && startCoord < 0)) {
|
||||
return OTS_FAILURE_MSG("Invalid region axis coordinates");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ParseVariationDataSubtable(const ots::Font* font, const uint8_t* data, const size_t length,
|
||||
const uint16_t regionCount,
|
||||
uint16_t* regionIndexCount) {
|
||||
ots::Buffer subtable(data, length);
|
||||
|
||||
uint16_t itemCount;
|
||||
uint16_t shortDeltaCount;
|
||||
|
||||
if (!subtable.ReadU16(&itemCount) ||
|
||||
!subtable.ReadU16(&shortDeltaCount) ||
|
||||
!subtable.ReadU16(regionIndexCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation data subtable header");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < *regionIndexCount; i++) {
|
||||
uint16_t regionIndex;
|
||||
if (!subtable.ReadU16(®ionIndex) || regionIndex >= regionCount) {
|
||||
return OTS_FAILURE_MSG("Bad region index");
|
||||
}
|
||||
}
|
||||
|
||||
if (!subtable.Skip(size_t(itemCount) * (size_t(shortDeltaCount) + size_t(*regionIndexCount)))) {
|
||||
return OTS_FAILURE_MSG("Failed to read delta data");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool
|
||||
ParseItemVariationStore(const Font* font,
|
||||
const uint8_t* data, const size_t length,
|
||||
std::vector<uint16_t>* regionIndexCounts) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t format;
|
||||
uint32_t variationRegionListOffset;
|
||||
uint16_t itemVariationDataCount;
|
||||
|
||||
if (!subtable.ReadU16(&format) ||
|
||||
!subtable.ReadU32(&variationRegionListOffset) ||
|
||||
!subtable.ReadU16(&itemVariationDataCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read item variation store header");
|
||||
}
|
||||
|
||||
if (format != 1) {
|
||||
return OTS_FAILURE_MSG("Unknown item variation store format");
|
||||
}
|
||||
|
||||
if (variationRegionListOffset < subtable.offset() + 4 * itemVariationDataCount ||
|
||||
variationRegionListOffset > length) {
|
||||
return OTS_FAILURE_MSG("Invalid variation region list offset");
|
||||
}
|
||||
|
||||
uint16_t regionCount;
|
||||
if (!ParseVariationRegionList(font,
|
||||
data + variationRegionListOffset,
|
||||
length - variationRegionListOffset,
|
||||
®ionCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse variation region list");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < itemVariationDataCount; i++) {
|
||||
uint32_t offset;
|
||||
if (!subtable.ReadU32(&offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation data subtable offset");
|
||||
}
|
||||
if (offset >= length) {
|
||||
return OTS_FAILURE_MSG("Bad offset to variation data subtable");
|
||||
}
|
||||
uint16_t regionIndexCount = 0;
|
||||
if (!ParseVariationDataSubtable(font, data + offset, length - offset,
|
||||
regionCount,
|
||||
®ionIndexCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse variation data subtable");
|
||||
}
|
||||
if (regionIndexCounts) {
|
||||
regionIndexCounts->push_back(regionIndexCount);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t entryFormat;
|
||||
uint16_t mapCount;
|
||||
|
||||
if (!subtable.ReadU16(&entryFormat) ||
|
||||
!subtable.ReadU16(&mapCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read delta set index map header");
|
||||
}
|
||||
|
||||
const uint16_t MAP_ENTRY_SIZE_MASK = 0x0030;
|
||||
|
||||
const uint16_t entrySize = (((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1);
|
||||
if (!subtable.Skip(entrySize * mapCount)) {
|
||||
return OTS_FAILURE_MSG("Failed to read delta set index map data");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseVariationData(const Font* font, const uint8_t* data, size_t length,
|
||||
size_t axisCount, size_t sharedTupleCount) {
|
||||
Buffer subtable(data, length);
|
||||
|
||||
uint16_t tupleVariationCount;
|
||||
uint16_t dataOffset;
|
||||
if (!subtable.ReadU16(&tupleVariationCount) ||
|
||||
!subtable.ReadU16(&dataOffset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read variation data header");
|
||||
}
|
||||
|
||||
if (dataOffset > length) {
|
||||
return OTS_FAILURE_MSG("Invalid serialized data offset");
|
||||
}
|
||||
|
||||
tupleVariationCount &= 0x0FFF; // mask off flags
|
||||
|
||||
const uint16_t EMBEDDED_PEAK_TUPLE = 0x8000;
|
||||
const uint16_t INTERMEDIATE_REGION = 0x4000;
|
||||
const uint16_t TUPLE_INDEX_MASK = 0x0FFF;
|
||||
|
||||
for (unsigned i = 0; i < tupleVariationCount; i++) {
|
||||
uint16_t variationDataSize;
|
||||
uint16_t tupleIndex;
|
||||
|
||||
if (!subtable.ReadU16(&variationDataSize) ||
|
||||
!subtable.ReadU16(&tupleIndex)) {
|
||||
return OTS_FAILURE_MSG("Failed to read tuple variation header");
|
||||
}
|
||||
|
||||
if (tupleIndex & EMBEDDED_PEAK_TUPLE) {
|
||||
for (unsigned axis = 0; axis < axisCount; axis++) {
|
||||
int16_t coordinate;
|
||||
if (!subtable.ReadS16(&coordinate)) {
|
||||
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
|
||||
}
|
||||
if (coordinate < -0x4000 || coordinate > 0x4000) {
|
||||
return OTS_FAILURE_MSG("Invalid tuple coordinate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tupleIndex & INTERMEDIATE_REGION) {
|
||||
std::vector<int16_t> startTuple(axisCount);
|
||||
for (unsigned axis = 0; axis < axisCount; axis++) {
|
||||
int16_t coordinate;
|
||||
if (!subtable.ReadS16(&coordinate)) {
|
||||
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
|
||||
}
|
||||
if (coordinate < -0x4000 || coordinate > 0x4000) {
|
||||
return OTS_FAILURE_MSG("Invalid tuple coordinate");
|
||||
}
|
||||
startTuple.push_back(coordinate);
|
||||
}
|
||||
|
||||
std::vector<int16_t> endTuple(axisCount);
|
||||
for (unsigned axis = 0; axis < axisCount; axis++) {
|
||||
int16_t coordinate;
|
||||
if (!subtable.ReadS16(&coordinate)) {
|
||||
return OTS_FAILURE_MSG("Failed to read tuple coordinate");
|
||||
}
|
||||
if (coordinate < -0x4000 || coordinate > 0x4000) {
|
||||
return OTS_FAILURE_MSG("Invalid tuple coordinate");
|
||||
}
|
||||
endTuple.push_back(coordinate);
|
||||
}
|
||||
|
||||
for (unsigned axis = 0; axis < axisCount; axis++) {
|
||||
if (startTuple[axis] > endTuple[axis]) {
|
||||
return OTS_FAILURE_MSG("Invalid intermediate range");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(tupleIndex & EMBEDDED_PEAK_TUPLE)) {
|
||||
tupleIndex &= TUPLE_INDEX_MASK;
|
||||
if (tupleIndex >= sharedTupleCount) {
|
||||
return OTS_FAILURE_MSG("Tuple index out of range");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: we don't attempt to interpret the serialized data block
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) 2018 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef OTS_VARIATIONS_H_
|
||||
#define OTS_VARIATIONS_H_
|
||||
|
||||
#include <vector>
|
||||
#include "ots.h"
|
||||
|
||||
// Utility functions for OpenType variations common table formats.
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ParseItemVariationStore(const Font* font,
|
||||
const uint8_t* data, const size_t length,
|
||||
std::vector<uint16_t>* out_region_index_count = NULL);
|
||||
|
||||
bool ParseDeltaSetIndexMap(const Font* font, const uint8_t* data, const size_t length);
|
||||
|
||||
bool ParseVariationData(const Font* font, const uint8_t* data, size_t length,
|
||||
size_t axisCount, size_t sharedTupleCount);
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#endif // OTS_VARIATIONS_H_
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -7,91 +7,75 @@
|
|||
// VDMX - Vertical Device Metrics
|
||||
// http://www.microsoft.com/typography/otspec/vdmx.htm
|
||||
|
||||
#define TABLE_NAME "VDMX"
|
||||
|
||||
#define DROP_THIS_TABLE(...) \
|
||||
do { \
|
||||
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
|
||||
OTS_FAILURE_MSG("Table discarded"); \
|
||||
delete font->vdmx; \
|
||||
font->vdmx = 0; \
|
||||
} while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_vdmx_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeVDMX::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
font->vdmx = new OpenTypeVDMX;
|
||||
OpenTypeVDMX * const vdmx = font->vdmx;
|
||||
|
||||
if (!table.ReadU16(&vdmx->version) ||
|
||||
!table.ReadU16(&vdmx->num_recs) ||
|
||||
!table.ReadU16(&vdmx->num_ratios)) {
|
||||
return OTS_FAILURE_MSG("Failed to read table header");
|
||||
if (!table.ReadU16(&this->version) ||
|
||||
!table.ReadU16(&this->num_recs) ||
|
||||
!table.ReadU16(&this->num_ratios)) {
|
||||
return Error("Failed to read table header");
|
||||
}
|
||||
|
||||
if (vdmx->version > 1) {
|
||||
DROP_THIS_TABLE("bad version: %u", vdmx->version);
|
||||
return true; // continue transcoding
|
||||
if (this->version > 1) {
|
||||
return Drop("Unsupported table version: %u", this->version);
|
||||
}
|
||||
|
||||
vdmx->rat_ranges.reserve(vdmx->num_ratios);
|
||||
for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
|
||||
this->rat_ranges.reserve(this->num_ratios);
|
||||
for (unsigned i = 0; i < this->num_ratios; ++i) {
|
||||
OpenTypeVDMXRatioRecord rec;
|
||||
|
||||
if (!table.ReadU8(&rec.charset) ||
|
||||
!table.ReadU8(&rec.x_ratio) ||
|
||||
!table.ReadU8(&rec.y_start_ratio) ||
|
||||
!table.ReadU8(&rec.y_end_ratio)) {
|
||||
return OTS_FAILURE_MSG("Failed to read ratio header %d", i);
|
||||
return Error("Failed to read RatioRange record %d", i);
|
||||
}
|
||||
|
||||
if (rec.charset > 1) {
|
||||
DROP_THIS_TABLE("bad charset: %u", rec.charset);
|
||||
return true;
|
||||
return Drop("Unsupported character set: %u", rec.charset);
|
||||
}
|
||||
|
||||
if (rec.y_start_ratio > rec.y_end_ratio) {
|
||||
DROP_THIS_TABLE("bad y ratio");
|
||||
return true;
|
||||
return Drop("Bad y ratio");
|
||||
}
|
||||
|
||||
// All values set to zero signal the default grouping to use;
|
||||
// if present, this must be the last Ratio group in the table.
|
||||
if ((i < vdmx->num_ratios - 1u) &&
|
||||
if ((i < this->num_ratios - 1u) &&
|
||||
(rec.x_ratio == 0) &&
|
||||
(rec.y_start_ratio == 0) &&
|
||||
(rec.y_end_ratio == 0)) {
|
||||
// workaround for fonts which have 2 or more {0, 0, 0} terminators.
|
||||
DROP_THIS_TABLE("superfluous terminator found");
|
||||
return true;
|
||||
return Drop("Superfluous terminator found");
|
||||
}
|
||||
|
||||
vdmx->rat_ranges.push_back(rec);
|
||||
this->rat_ranges.push_back(rec);
|
||||
}
|
||||
|
||||
vdmx->offsets.reserve(vdmx->num_ratios);
|
||||
this->offsets.reserve(this->num_ratios);
|
||||
const size_t current_offset = table.offset();
|
||||
// current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
|
||||
for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
|
||||
for (unsigned i = 0; i < this->num_ratios; ++i) {
|
||||
uint16_t offset;
|
||||
if (!table.ReadU16(&offset)) {
|
||||
return OTS_FAILURE_MSG("Failed to read ratio offset %d", i);
|
||||
return Error("Failed to read ratio offset %d", i);
|
||||
}
|
||||
if (current_offset + offset >= length) { // thus doesn't overflow.
|
||||
return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset, i);
|
||||
return Error("Bad ratio offset %d for ration %d", offset, i);
|
||||
}
|
||||
|
||||
vdmx->offsets.push_back(offset);
|
||||
this->offsets.push_back(offset);
|
||||
}
|
||||
|
||||
vdmx->groups.reserve(vdmx->num_recs);
|
||||
for (unsigned i = 0; i < vdmx->num_recs; ++i) {
|
||||
this->groups.reserve(this->num_recs);
|
||||
for (unsigned i = 0; i < this->num_recs; ++i) {
|
||||
OpenTypeVDMXGroup group;
|
||||
if (!table.ReadU16(&group.recs) ||
|
||||
!table.ReadU8(&group.startsz) ||
|
||||
!table.ReadU8(&group.endsz)) {
|
||||
return OTS_FAILURE_MSG("Failed to read record header %d", i);
|
||||
return Error("Failed to read record header %d", i);
|
||||
}
|
||||
group.entries.reserve(group.recs);
|
||||
for (unsigned j = 0; j < group.recs; ++j) {
|
||||
|
@ -99,71 +83,68 @@ bool ots_vdmx_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
if (!table.ReadU16(&vt.y_pel_height) ||
|
||||
!table.ReadS16(&vt.y_max) ||
|
||||
!table.ReadS16(&vt.y_min)) {
|
||||
return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i, j);
|
||||
return Error("Failed to read reacord %d group %d", i, j);
|
||||
}
|
||||
if (vt.y_max < vt.y_min) {
|
||||
DROP_THIS_TABLE("bad y min/max");
|
||||
return true;
|
||||
return Drop("bad y min/max");
|
||||
}
|
||||
|
||||
// This table must appear in sorted order (sorted by yPelHeight),
|
||||
// but need not be continuous.
|
||||
if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
|
||||
DROP_THIS_TABLE("the table is not sorted");
|
||||
return true;
|
||||
return Drop("The table is not sorted");
|
||||
}
|
||||
|
||||
group.entries.push_back(vt);
|
||||
}
|
||||
vdmx->groups.push_back(group);
|
||||
this->groups.push_back(group);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_vdmx_should_serialise(Font *font) {
|
||||
if (!font->glyf) return false; // this table is not for CFF fonts.
|
||||
return font->vdmx != NULL;
|
||||
bool OpenTypeVDMX::ShouldSerialize() {
|
||||
return Table::ShouldSerialize() &&
|
||||
// this table is not for CFF fonts.
|
||||
GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
|
||||
}
|
||||
|
||||
bool ots_vdmx_serialise(OTSStream *out, Font *font) {
|
||||
OpenTypeVDMX * const vdmx = font->vdmx;
|
||||
|
||||
if (!out->WriteU16(vdmx->version) ||
|
||||
!out->WriteU16(vdmx->num_recs) ||
|
||||
!out->WriteU16(vdmx->num_ratios)) {
|
||||
return OTS_FAILURE_MSG("Failed to write table header");
|
||||
bool OpenTypeVDMX::Serialize(OTSStream *out) {
|
||||
if (!out->WriteU16(this->version) ||
|
||||
!out->WriteU16(this->num_recs) ||
|
||||
!out->WriteU16(this->num_ratios)) {
|
||||
return Error("Failed to write table header");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
|
||||
const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
|
||||
for (unsigned i = 0; i < this->rat_ranges.size(); ++i) {
|
||||
const OpenTypeVDMXRatioRecord& rec = this->rat_ranges[i];
|
||||
if (!out->Write(&rec.charset, 1) ||
|
||||
!out->Write(&rec.x_ratio, 1) ||
|
||||
!out->Write(&rec.y_start_ratio, 1) ||
|
||||
!out->Write(&rec.y_end_ratio, 1)) {
|
||||
return OTS_FAILURE_MSG("Failed to write ratio %d", i);
|
||||
return Error("Failed to write RatioRange record %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
|
||||
if (!out->WriteU16(vdmx->offsets[i])) {
|
||||
return OTS_FAILURE_MSG("Failed to write ratio offset %d", i);
|
||||
for (unsigned i = 0; i < this->offsets.size(); ++i) {
|
||||
if (!out->WriteU16(this->offsets[i])) {
|
||||
return Error("Failed to write ratio offset %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
|
||||
const OpenTypeVDMXGroup& group = vdmx->groups[i];
|
||||
for (unsigned i = 0; i < this->groups.size(); ++i) {
|
||||
const OpenTypeVDMXGroup& group = this->groups[i];
|
||||
if (!out->WriteU16(group.recs) ||
|
||||
!out->Write(&group.startsz, 1) ||
|
||||
!out->Write(&group.endsz, 1)) {
|
||||
return OTS_FAILURE_MSG("Failed to write group %d", i);
|
||||
return Error("Failed to write group %d", i);
|
||||
}
|
||||
for (unsigned j = 0; j < group.entries.size(); ++j) {
|
||||
const OpenTypeVDMXVTable& vt = group.entries[j];
|
||||
if (!out->WriteU16(vt.y_pel_height) ||
|
||||
!out->WriteS16(vt.y_max) ||
|
||||
!out->WriteS16(vt.y_min)) {
|
||||
return OTS_FAILURE_MSG("Failed to write group %d entry %d", i, j);
|
||||
return Error("Failed to write group %d entry %d", i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,16 +152,4 @@ bool ots_vdmx_serialise(OTSStream *out, Font *font) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void ots_vdmx_reuse(Font *font, Font *other) {
|
||||
font->vdmx = other->vdmx;
|
||||
font->vdmx_reused = true;
|
||||
}
|
||||
|
||||
void ots_vdmx_free(Font *font) {
|
||||
delete font->vdmx;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
#undef DROP_THIS_TABLE
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -31,7 +31,16 @@ struct OpenTypeVDMXGroup {
|
|||
std::vector<OpenTypeVDMXVTable> entries;
|
||||
};
|
||||
|
||||
struct OpenTypeVDMX {
|
||||
class OpenTypeVDMX : public Table {
|
||||
public:
|
||||
explicit OpenTypeVDMX(Font *font, uint32_t tag)
|
||||
: Table(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool ShouldSerialize();
|
||||
|
||||
private:
|
||||
uint16_t version;
|
||||
uint16_t num_recs;
|
||||
uint16_t num_ratios;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -10,51 +10,30 @@
|
|||
// vhea - Vertical Header Table
|
||||
// http://www.microsoft.com/typography/otspec/vhea.htm
|
||||
|
||||
#define TABLE_NAME "vhea"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_vhea_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeVHEA::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeVHEA *vhea = new OpenTypeVHEA;
|
||||
font->vhea = vhea;
|
||||
|
||||
if (!table.ReadU32(&vhea->header.version)) {
|
||||
return OTS_FAILURE_MSG("Failed to read version");
|
||||
if (!table.ReadU32(&this->version)) {
|
||||
return Error("Failed to read version");
|
||||
}
|
||||
if (vhea->header.version != 0x00010000 &&
|
||||
vhea->header.version != 0x00011000) {
|
||||
return OTS_FAILURE_MSG("Bad vhea version %x", vhea->header.version);
|
||||
if (this->version != 0x00010000 &&
|
||||
this->version != 0x00011000) {
|
||||
return Error("Unsupported table version: 0x%x", this->version);
|
||||
}
|
||||
|
||||
if (!ParseMetricsHeader(font, &table, &vhea->header)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse metrics in vhea");
|
||||
}
|
||||
|
||||
return true;
|
||||
return OpenTypeMetricsHeader::Parse(data, length);
|
||||
}
|
||||
|
||||
bool ots_vhea_should_serialise(Font *font) {
|
||||
// vhea should'nt serialise when vmtx doesn't exist.
|
||||
return font->vhea != NULL && font->vmtx != NULL;
|
||||
bool OpenTypeVHEA::Serialize(OTSStream *out) {
|
||||
return OpenTypeMetricsHeader::Serialize(out);
|
||||
}
|
||||
|
||||
bool ots_vhea_serialise(OTSStream *out, Font *font) {
|
||||
if (!SerialiseMetricsHeader(font, out, &font->vhea->header)) {
|
||||
return OTS_FAILURE_MSG("Failed to write vhea metrics");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_vhea_reuse(Font *font, Font *other) {
|
||||
font->vhea = other->vhea;
|
||||
font->vhea_reused = true;
|
||||
}
|
||||
|
||||
void ots_vhea_free(Font *font) {
|
||||
delete font->vhea;
|
||||
bool OpenTypeVHEA::ShouldSerialize() {
|
||||
return OpenTypeMetricsHeader::ShouldSerialize() &&
|
||||
// vhea shouldn't serialise when vmtx doesn't exist.
|
||||
GetFont()->GetTable(OTS_TAG_VMTX) != NULL;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -10,8 +10,14 @@
|
|||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeVHEA {
|
||||
OpenTypeMetricsHeader header;
|
||||
class OpenTypeVHEA : public OpenTypeMetricsHeader {
|
||||
public:
|
||||
explicit OpenTypeVHEA(Font *font, uint32_t tag)
|
||||
: OpenTypeMetricsHeader(font, tag, tag) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool ShouldSerialize();
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -10,48 +10,20 @@
|
|||
// vmtx - Vertical Metrics Table
|
||||
// http://www.microsoft.com/typography/otspec/vmtx.htm
|
||||
|
||||
#define TABLE_NAME "vmtx"
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_vmtx_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
OpenTypeVMTX *vmtx = new OpenTypeVMTX;
|
||||
font->vmtx = vmtx;
|
||||
|
||||
if (!font->vhea || !font->maxp) {
|
||||
return OTS_FAILURE_MSG("vhea or maxp table missing as needed by vmtx");
|
||||
}
|
||||
|
||||
if (!ParseMetricsTable(font, &table, font->maxp->num_glyphs,
|
||||
&font->vhea->header, &vmtx->metrics)) {
|
||||
return OTS_FAILURE_MSG("Failed to parse vmtx metrics");
|
||||
}
|
||||
|
||||
return true;
|
||||
bool OpenTypeVMTX::Parse(const uint8_t *data, size_t length) {
|
||||
return OpenTypeMetricsTable::Parse(data, length);
|
||||
}
|
||||
|
||||
bool ots_vmtx_should_serialise(Font *font) {
|
||||
// vmtx should serialise when vhea is preserved.
|
||||
return font->vmtx != NULL && font->vhea != NULL;
|
||||
bool OpenTypeVMTX::Serialize(OTSStream *out) {
|
||||
return OpenTypeMetricsTable::Serialize(out);
|
||||
}
|
||||
|
||||
bool ots_vmtx_serialise(OTSStream *out, Font *font) {
|
||||
if (!SerialiseMetricsTable(font, out, &font->vmtx->metrics)) {
|
||||
return OTS_FAILURE_MSG("Failed to write vmtx metrics");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_vmtx_reuse(Font *font, Font *other) {
|
||||
font->vmtx = other->vmtx;
|
||||
font->vmtx_reused = true;
|
||||
}
|
||||
|
||||
void ots_vmtx_free(Font *font) {
|
||||
delete font->vmtx;
|
||||
bool OpenTypeVMTX::ShouldSerialize() {
|
||||
return OpenTypeMetricsTable::ShouldSerialize() &&
|
||||
// vmtx should serialise when vhea is preserved.
|
||||
GetFont()->GetTable(OTS_TAG_VHEA) != NULL;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -6,12 +6,19 @@
|
|||
#define OTS_VMTX_H_
|
||||
|
||||
#include "metrics.h"
|
||||
#include "vhea.h"
|
||||
#include "ots.h"
|
||||
|
||||
namespace ots {
|
||||
|
||||
struct OpenTypeVMTX {
|
||||
OpenTypeMetricsTable metrics;
|
||||
struct OpenTypeVMTX : public OpenTypeMetricsTable {
|
||||
public:
|
||||
explicit OpenTypeVMTX(Font *font, uint32_t tag)
|
||||
: OpenTypeMetricsTable(font, tag, tag, OTS_TAG_VHEA) { }
|
||||
|
||||
bool Parse(const uint8_t *data, size_t length);
|
||||
bool Serialize(OTSStream *out);
|
||||
bool ShouldSerialize();
|
||||
};
|
||||
|
||||
} // namespace ots
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
@ -9,37 +9,23 @@
|
|||
// VORG - Vertical Origin Table
|
||||
// http://www.microsoft.com/typography/otspec/vorg.htm
|
||||
|
||||
#define TABLE_NAME "VORG"
|
||||
|
||||
#define DROP_THIS_TABLE(...) \
|
||||
do { \
|
||||
OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
|
||||
OTS_FAILURE_MSG("Table discarded"); \
|
||||
delete font->vorg; \
|
||||
font->vorg = 0; \
|
||||
} while (0)
|
||||
|
||||
namespace ots {
|
||||
|
||||
bool ots_vorg_parse(Font *font, const uint8_t *data, size_t length) {
|
||||
bool OpenTypeVORG::Parse(const uint8_t *data, size_t length) {
|
||||
Buffer table(data, length);
|
||||
font->vorg = new OpenTypeVORG;
|
||||
OpenTypeVORG * const vorg = font->vorg;
|
||||
|
||||
uint16_t num_recs;
|
||||
if (!table.ReadU16(&vorg->major_version) ||
|
||||
!table.ReadU16(&vorg->minor_version) ||
|
||||
!table.ReadS16(&vorg->default_vert_origin_y) ||
|
||||
if (!table.ReadU16(&this->major_version) ||
|
||||
!table.ReadU16(&this->minor_version) ||
|
||||
!table.ReadS16(&this->default_vert_origin_y) ||
|
||||
!table.ReadU16(&num_recs)) {
|
||||
return OTS_FAILURE_MSG("Failed to read header");
|
||||
return Error("Failed to read header");
|
||||
}
|
||||
if (vorg->major_version != 1) {
|
||||
DROP_THIS_TABLE("bad major version: %u", vorg->major_version);
|
||||
return true;
|
||||
if (this->major_version != 1) {
|
||||
return Drop("Unsupported majorVersion: %u", this->major_version);
|
||||
}
|
||||
if (vorg->minor_version != 0) {
|
||||
DROP_THIS_TABLE("bad minor version: %u", vorg->minor_version);
|
||||
return true;
|
||||
if (this->minor_version != 0) {
|
||||
return Drop("Unsupported minorVersion: %u", this->minor_version);
|
||||
}
|
||||
|
||||
// num_recs might be zero (e.g., DFHSMinchoPro5-W3-Demo.otf).
|
||||
|
@ -48,64 +34,50 @@ bool ots_vorg_parse(Font *font, const uint8_t *data, size_t length) {
|
|||
}
|
||||
|
||||
uint16_t last_glyph_index = 0;
|
||||
vorg->metrics.reserve(num_recs);
|
||||
this->metrics.reserve(num_recs);
|
||||
for (unsigned i = 0; i < num_recs; ++i) {
|
||||
OpenTypeVORGMetrics rec;
|
||||
|
||||
if (!table.ReadU16(&rec.glyph_index) ||
|
||||
!table.ReadS16(&rec.vert_origin_y)) {
|
||||
return OTS_FAILURE_MSG("Failed to read record %d", i);
|
||||
return Error("Failed to read record %d", i);
|
||||
}
|
||||
if ((i != 0) && (rec.glyph_index <= last_glyph_index)) {
|
||||
DROP_THIS_TABLE("the table is not sorted");
|
||||
return true;
|
||||
return Drop("The table is not sorted");
|
||||
}
|
||||
last_glyph_index = rec.glyph_index;
|
||||
|
||||
vorg->metrics.push_back(rec);
|
||||
this->metrics.push_back(rec);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ots_vorg_should_serialise(Font *font) {
|
||||
if (!font->cff) return false; // this table is not for fonts with TT glyphs.
|
||||
return font->vorg != NULL;
|
||||
}
|
||||
|
||||
bool ots_vorg_serialise(OTSStream *out, Font *font) {
|
||||
OpenTypeVORG * const vorg = font->vorg;
|
||||
|
||||
const uint16_t num_metrics = static_cast<uint16_t>(vorg->metrics.size());
|
||||
if (num_metrics != vorg->metrics.size() ||
|
||||
!out->WriteU16(vorg->major_version) ||
|
||||
!out->WriteU16(vorg->minor_version) ||
|
||||
!out->WriteS16(vorg->default_vert_origin_y) ||
|
||||
bool OpenTypeVORG::Serialize(OTSStream *out) {
|
||||
const uint16_t num_metrics = static_cast<uint16_t>(this->metrics.size());
|
||||
if (num_metrics != this->metrics.size() ||
|
||||
!out->WriteU16(this->major_version) ||
|
||||
!out->WriteU16(this->minor_version) ||
|
||||
!out->WriteS16(this->default_vert_origin_y) ||
|
||||
!out->WriteU16(num_metrics)) {
|
||||
return OTS_FAILURE_MSG("Failed to write table header");
|
||||
return Error("Failed to write table header");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < num_metrics; ++i) {
|
||||
const OpenTypeVORGMetrics& rec = vorg->metrics[i];
|
||||
const OpenTypeVORGMetrics& rec = this->metrics[i];
|
||||
if (!out->WriteU16(rec.glyph_index) ||
|
||||
!out->WriteS16(rec.vert_origin_y)) {
|
||||
return OTS_FAILURE_MSG("Failed to write record %d", i);
|
||||
return Error("Failed to write record %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ots_vorg_reuse(Font *font, Font *other) {
|
||||
font->vorg = other->vorg;
|
||||
font->vorg_reused = true;
|
||||
}
|
||||
|
||||
void ots_vorg_free(Font *font) {
|
||||
delete font->vorg;
|
||||
bool OpenTypeVORG::ShouldSerialize() {
|
||||
return Table::ShouldSerialize() &&
|
||||
// this table is not for fonts with TT glyphs.
|
||||
GetFont()->GetTable(OTS_TAG_CFF) != NULL;
|
||||
}
|
||||
|
||||
} // namespace ots
|
||||
|
||||
#undef TABLE_NAME
|
||||
#undef DROP_THIS_TABLE
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue