author | Ryan VanderMeulen <ryanvm@gmail.com> |
Fri, 31 Jan 2014 16:56:48 -0500 | |
changeset 182342 | 3c2673cd1e3c8b97e0a59162b38eaca396075983 |
parent 182341 | 3b1911f967084dce2cfc8095535014373d0f6a66 (current diff) |
parent 182290 | 8ded1c0760cc86b2f6cf1236a2a186415b8e87a2 (diff) |
child 182343 | 4c86e7c1f546598713e5e1565b033562b4ed2be8 |
push id | 3343 |
push user | ffxbld |
push date | Mon, 17 Mar 2014 21:55:32 +0000 |
treeherder | mozilla-beta@2f7d3415f79f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 29.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
gfx/ots/ots-fix-sparc64.patch | file | annotate | diff | comparison | revisions | |
gfx/ots/ots-fix-vc10.patch | file | annotate | diff | comparison | revisions | |
gfx/ots/ots-graphite.patch | file | annotate | diff | comparison | revisions | |
gfx/ots/src/graphite.cc | file | annotate | diff | comparison | revisions | |
gfx/ots/src/graphite.h | file | annotate | diff | comparison | revisions | |
gfx/ots/src/svg.cc | file | annotate | diff | comparison | revisions | |
gfx/ots/src/svg.h | file | annotate | diff | comparison | revisions |
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2830,18 +2830,18 @@ nsDOMWindowUtils::AreDialogsEnabled(bool static nsIDOMBlob* GetXPConnectNative(JSContext* aCx, JSObject* aObj) { nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface( nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, aObj)); return blob; } static nsresult -GetFileOrBlob(const nsAString& aName, const JS::Value& aBlobParts, - const JS::Value& aParameters, JSContext* aCx, +GetFileOrBlob(const nsAString& aName, JS::Handle<JS::Value> aBlobParts, + JS::Handle<JS::Value> aParameters, JSContext* aCx, uint8_t aOptionalArgCount, nsISupports** aResult) { if (!nsContentUtils::IsCallerChrome()) { return NS_ERROR_DOM_SECURITY_ERR; } nsresult rv; @@ -2853,19 +2853,22 @@ GetFileOrBlob(const nsAString& aName, co else { rv = nsDOMMultipartFile::NewFile(aName, getter_AddRefs(file)); } NS_ENSURE_SUCCESS(rv, rv); nsDOMMultipartFile* domFile = static_cast<nsDOMMultipartFile*>(static_cast<nsIDOMFile*>(file.get())); - JS::Value args[2] = { aBlobParts, aParameters }; - - rv = domFile->InitBlob(aCx, aOptionalArgCount, args, GetXPConnectNative); + JS::AutoValueVector args(aCx); + MOZ_ALWAYS_TRUE(args.resize(2)); + args[0] = aBlobParts; + args[1] = aParameters; + + rv = domFile->InitBlob(aCx, aOptionalArgCount, args.begin(), GetXPConnectNative); NS_ENSURE_SUCCESS(rv, rv); file.forget(aResult); return NS_OK; } NS_IMETHODIMP nsDOMWindowUtils::GetFile(const nsAString& aName, JS::Handle<JS::Value> aBlobParts,
--- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -904,17 +904,17 @@ CompositorOGL::CreateFBOWithTexture(cons bool isFormatCompatibleWithRGBA = gl()->IsGLES2() ? (format == LOCAL_GL_RGBA) : true; if (isFormatCompatibleWithRGBA) { mGLContext->fCopyTexImage2D(mFBOTextureTarget, 0, LOCAL_GL_RGBA, - aRect.x, aRect.y, + aRect.x, FlipY(aRect.y + aRect.height), aRect.width, aRect.height, 0); } else { // Curses, incompatible formats. Take a slow path. // RGBA size_t bufferSize = aRect.width * aRect.height * 4; nsAutoArrayPtr<uint8_t> buf(new uint8_t[bufferSize]);
--- a/gfx/ots/README.mozilla +++ b/gfx/ots/README.mozilla @@ -1,14 +1,10 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/. -Current revision: r95 +Our reference repository is https://github.com/khaledhosny/ots/. + +Current revision: d7d831edd171054c7974f5e0dec2fc19bf869574 Applied local patches: - ots-fix-vc10.patch - workaround for VS10 STL wrappers (bug 602558) - - ots-fix-sparc64.patch - fix alignment error on sparc64 (bug 643137) - - ots-graphite.patch - preserve Graphite layout tables (bug 631479) - ots-visibility.patch - make Process function externally visible for Windows DLL (bug 711079) -Patches from https://bugzilla.mozilla.org/show_bug.cgi?id=670901. + ots-woff2 - disable WOFF2 support (bug 941019)
--- a/gfx/ots/include/opentype-sanitiser.h +++ b/gfx/ots/include/opentype-sanitiser.h @@ -39,17 +39,17 @@ typedef unsigned __int64 uint64_t; #define ntohs(x) _byteswap_ushort (x) #define htonl(x) _byteswap_ulong (x) #define htons(x) _byteswap_ushort (x) #else #include <arpa/inet.h> #include <stdint.h> #endif -#include <algorithm> // for std::min +#include <algorithm> #include <cassert> #include <cstddef> #include <cstring> namespace ots { // ----------------------------------------------------------------------------- // This is an interface for an abstract stream class which is used for writing @@ -76,19 +76,19 @@ class OTSStream { std::min(length, static_cast<size_t>(4) - chksum_buffer_offset_); std::memcpy(chksum_buffer_ + chksum_buffer_offset_, data, l); chksum_buffer_offset_ += l; offset += l; length -= l; } if (chksum_buffer_offset_ == 4) { - uint32_t chksum; - std::memcpy(&chksum, chksum_buffer_, 4); - chksum_ += ntohl(chksum); + uint32_t tmp; + std::memcpy(&tmp, chksum_buffer_, 4); + chksum_ += ntohl(tmp); chksum_buffer_offset_ = 0; } while (length >= 4) { uint32_t tmp; std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset, sizeof(uint32_t)); chksum_ += ntohl(tmp); @@ -194,37 +194,58 @@ class OTSStream { } protected: uint32_t chksum_; uint8_t chksum_buffer_[4]; unsigned chksum_buffer_offset_; }; -#ifdef MOZ_OTS_REPORT_ERRORS -// Signature of the function to be provided by the client in order to report errors. -// The return type is a boolean so that it can be used within an expression, -// but the actual value is ignored. (Suggested convention is to always return 'false'.) -typedef bool (*MessageFunc)(void *user_data, const char *format, ...); -#endif - // ----------------------------------------------------------------------------- // 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. // input: the OpenType file // length: the size, in bytes, of |input| -// preserve_graphite_tables: whether to preserve Graphite Layout tables // ----------------------------------------------------------------------------- -bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length, -#ifdef MOZ_OTS_REPORT_ERRORS - MessageFunc message_func, void *user_data, +bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length); + +// Signature of the function to be provided by the client in order to report errors. +// The return type is a boolean so that it can be used within an expression, +// but the actual value is ignored. (Suggested convention is to always return 'false'.) +#ifdef __GCC__ +#define MSGFUNC_FMT_ATTR __attribute__((format(printf, 2, 3))) +#else +#define MSGFUNC_FMT_ATTR #endif - bool preserve_graphite_tables = false); +typedef bool (*MessageFunc)(void *user_data, const char *format, ...) MSGFUNC_FMT_ATTR; + +// Set a callback function that will be called when OTS is reporting an error. +void OTS_API SetMessageCallback(MessageFunc func, void *user_data); + +enum TableAction { + TABLE_ACTION_DEFAULT, // Use OTS's default action for that table + TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it + TABLE_ACTION_PASSTHRU, // Serialize the table unchanged + TABLE_ACTION_DROP // Drop the table +}; + +// Signature of the function to be provided by the client to decide what action +// to do for a given table. +typedef TableAction (*TableActionFunc)(uint32_t tag, void *user_data); + +// Set a callback function that will be called when OTS needs to decide what to +// do for a font table. +void OTS_API SetTableActionCallback(TableActionFunc func, void *user_data); // Force to disable debug output even when the library is compiled with // -DOTS_DEBUG. void DisableDebugOutput(); +#ifdef MOZ_OTS_WOFF2 +// Enable WOFF2 support(experimental). +void EnableWOFF2(); +#endif + } // namespace ots #endif // OPENTYPE_SANITISER_H_
--- a/gfx/ots/include/ots-memory-stream.h +++ b/gfx/ots/include/ots-memory-stream.h @@ -66,17 +66,17 @@ class ExpandingMemoryStream : public OTS if (length_ == limit_) return false; size_t new_length = (length_ + 1) * 2; if (new_length < length_) return false; if (new_length > limit_) new_length = limit_; uint8_t* new_buf = new uint8_t[new_length]; - memcpy(new_buf, ptr_, length_); + std::memcpy(new_buf, ptr_, length_); length_ = new_length; delete[] static_cast<uint8_t*>(ptr_); ptr_ = new_buf; return WriteRaw(data, length); } std::memcpy(static_cast<char*>(ptr_) + off_, data, length); off_ += length; return true;
deleted file mode 100644 --- a/gfx/ots/ots-fix-sparc64.patch +++ /dev/null @@ -1,26 +0,0 @@ -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 -@@ -83,18 +83,20 @@ class OTSStream { - if (chksum_buffer_offset_ == 4) { - uint32_t chksum; - std::memcpy(&chksum, chksum_buffer_, 4); - chksum_ += ntohl(chksum); - chksum_buffer_offset_ = 0; - } - - while (length >= 4) { -- chksum_ += ntohl(*reinterpret_cast<const uint32_t*>( -- reinterpret_cast<const uint8_t*>(data) + offset)); -+ uint32_t tmp; -+ std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset, -+ sizeof(uint32_t)); -+ chksum_ += ntohl(tmp); - length -= 4; - offset += 4; - } - - if (length) { - if (chksum_buffer_offset_ != 0) return false; // not reached - if (length > 4) return false; // not reached - std::memcpy(chksum_buffer_,
deleted file mode 100644 --- a/gfx/ots/ots-fix-vc10.patch +++ /dev/null @@ -1,60 +0,0 @@ -diff --git a/gfx/ots/src/gasp.h b/gfx/ots/src/gasp.h ---- a/gfx/ots/src/gasp.h -+++ b/gfx/ots/src/gasp.h -@@ -1,15 +1,16 @@ - // 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. - - #ifndef OTS_GASP_H_ - #define OTS_GASP_H_ - -+#include <new> - #include <utility> // std::pair - #include <vector> - - #include "ots.h" - - namespace ots { - - struct OpenTypeGASP { -diff --git a/gfx/ots/src/glyf.h b/gfx/ots/src/glyf.h ---- a/gfx/ots/src/glyf.h -+++ b/gfx/ots/src/glyf.h -@@ -1,15 +1,16 @@ - // 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. - - #ifndef OTS_GLYF_H_ - #define OTS_GLYF_H_ - -+#include <new> - #include <utility> // std::pair - #include <vector> - - #include "ots.h" - - namespace ots { - - struct OpenTypeGLYF { -diff --git a/gfx/ots/src/metrics.h b/gfx/ots/src/metrics.h ---- a/gfx/ots/src/metrics.h -+++ b/gfx/ots/src/metrics.h -@@ -1,15 +1,16 @@ - // Copyright (c) 2011 The Chromium Authors. All rights reserved. - // Use of this source code is governed by a BSD-style license that can be - // found in the LICENSE file. - - #ifndef OTS_METRICS_H_ - #define OTS_METRICS_H_ - -+#include <new> - #include <utility> // std::pair - #include <vector> - - #include "ots.h" - - namespace ots { - - struct OpenTypeMetricsHeader {
deleted file mode 100644 --- a/gfx/ots/ots-graphite.patch +++ /dev/null @@ -1,131 +0,0 @@ -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 -@@ -176,18 +176,20 @@ class OTSStream { - - // ----------------------------------------------------------------------------- - // 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. - // input: the OpenType file - // length: the size, in bytes, of |input| -+// preserve_graphite_tables: whether to preserve Graphite Layout tables - // ----------------------------------------------------------------------------- --bool Process(OTSStream *output, const uint8_t *input, size_t length); -+bool Process(OTSStream *output, const uint8_t *input, size_t length, -+ bool preserve_graphite_tables = false); - - // Force to disable debug output even when the library is compiled with - // -DOTS_DEBUG. - void DisableDebugOutput(); - - } // namespace ots - - #endif // OPENTYPE_SANITISER_H_ -diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc ---- a/gfx/ots/src/ots.cc -+++ b/gfx/ots/src/ots.cc -@@ -138,16 +138,27 @@ const struct { - { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise, - ots::ots_gpos_should_serialise, ots::ots_gpos_free, false }, - { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise, - ots::ots_gsub_should_serialise, ots::ots_gsub_free, false }, - { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise, - ots::ots_vhea_should_serialise, ots::ots_vhea_free, false }, - { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise, - ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false }, -+ // SILGraphite layout tables - not actually parsed, just copied -+ { "Silf", ots::ots_silf_parse, ots::ots_silf_serialise, -+ ots::ots_silf_should_serialise, ots::ots_silf_free, false }, -+ { "Sill", ots::ots_sill_parse, ots::ots_sill_serialise, -+ ots::ots_sill_should_serialise, ots::ots_sill_free, false }, -+ { "Gloc", ots::ots_gloc_parse, ots::ots_gloc_serialise, -+ ots::ots_gloc_should_serialise, ots::ots_gloc_free, false }, -+ { "Glat", ots::ots_glat_parse, ots::ots_glat_serialise, -+ ots::ots_glat_should_serialise, ots::ots_glat_free, false }, -+ { "Feat", ots::ots_feat_parse, ots::ots_feat_serialise, -+ ots::ots_feat_should_serialise, ots::ots_feat_free, false }, - // TODO(bashi): Support mort, base, and jstf tables. - { 0, NULL, NULL, NULL, NULL, false }, - }; - - bool IsValidVersionTag(uint32_t tag) { - return tag == Tag("\x00\x01\x00\x00") || - // OpenType fonts with CFF data have 'OTTO' tag. - tag == Tag("OTTO") || -@@ -581,22 +592,25 @@ bool ProcessGeneric(ots::OpenTypeFile *h - } // namespace - - namespace ots { - - void DisableDebugOutput() { - g_debug_output = false; - } - --bool Process(OTSStream *output, const uint8_t *data, size_t length) { -+bool Process(OTSStream *output, const uint8_t *data, size_t length, -+ bool preserveGraphite) { - OpenTypeFile header; - if (length < 4) { - return OTS_FAILURE(); - } - -+ header.preserve_graphite = preserveGraphite; -+ - bool result; - if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') { - result = ProcessWOFF(&header, output, data, length); - } else { - result = ProcessTTF(&header, output, data, length); - } - - for (unsigned i = 0; ; ++i) { -diff --git a/gfx/ots/src/ots.h b/gfx/ots/src/ots.h ---- a/gfx/ots/src/ots.h -+++ b/gfx/ots/src/ots.h -@@ -178,17 +178,22 @@ class Buffer { - 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) -+ F(vmtx, VMTX) \ -+ F(silf, SILF) \ -+ F(sill, SILL) \ -+ F(glat, GLAT) \ -+ F(gloc, GLOC) \ -+ F(feat, FEAT) - - #define F(name, capname) struct OpenType##capname; - FOR_EACH_TABLE_TYPE - #undef F - - struct OpenTypeFile { - OpenTypeFile() { - #define F(name, capname) name = NULL; -@@ -197,16 +202,20 @@ struct OpenTypeFile { - } - - uint32_t version; - uint16_t num_tables; - uint16_t search_range; - uint16_t entry_selector; - uint16_t range_shift; - -+ // This is used to tell the relevant parsers whether to preserve the -+ // Graphite layout tables (currently _without_ any checking) -+ bool preserve_graphite; -+ - #define F(name, capname) OpenType##capname *name; - FOR_EACH_TABLE_TYPE - #undef F - }; - - #define F(name, capname) \ - bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \ - bool ots_##name##_should_serialise(OpenTypeFile *f); \
--- a/gfx/ots/ots-visibility.patch +++ b/gfx/ots/ots-visibility.patch @@ -32,29 +32,57 @@ diff --git a/gfx/ots/include/opentype-sa #if defined(_WIN32) #include <stdlib.h> typedef signed char int8_t; typedef unsigned char uint8_t; typedef short int16_t; typedef unsigned short uint16_t; typedef int int32_t; typedef unsigned int uint32_t; -@@ -178,18 +198,18 @@ class OTSStream { +@@ -182,45 +202,45 @@ class OTSStream { + // ----------------------------------------------------------------------------- // 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. // input: the OpenType file // length: the size, in bytes, of |input| - // preserve_graphite_tables: whether to preserve Graphite Layout tables // ----------------------------------------------------------------------------- --bool Process(OTSStream *output, const uint8_t *input, size_t length, -- bool preserve_graphite_tables = false); -+bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length, -+ bool preserve_graphite_tables = false); +-bool Process(OTSStream *output, const uint8_t *input, size_t length); ++bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length); + + // Signature of the function to be provided by the client in order to report errors. + // The return type is a boolean so that it can be used within an expression, + // but the actual value is ignored. (Suggested convention is to always return 'false'.) + #ifdef __GCC__ + #define MSGFUNC_FMT_ATTR __attribute__((format(printf, 2, 3))) + #else + #define MSGFUNC_FMT_ATTR + #endif + typedef bool (*MessageFunc)(void *user_data, const char *format, ...) MSGFUNC_FMT_ATTR; + + // Set a callback function that will be called when OTS is reporting an error. +-void SetMessageCallback(MessageFunc func, void *user_data); ++void OTS_API SetMessageCallback(MessageFunc func, void *user_data); + + enum TableAction { + TABLE_ACTION_DEFAULT, // Use OTS's default action for that table + TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it + TABLE_ACTION_PASSTHRU, // Serialize the table unchanged + TABLE_ACTION_DROP // Drop the table + }; + + // Signature of the function to be provided by the client to decide what action + // to do for a given table. + typedef TableAction (*TableActionFunc)(uint32_t tag, void *user_data); + + // Set a callback function that will be called when OTS needs to decide what to + // do for a font table. +-void SetTableActionCallback(TableActionFunc func, void *user_data); ++void OTS_API SetTableActionCallback(TableActionFunc func, void *user_data); // Force to disable debug output even when the library is compiled with // -DOTS_DEBUG. void DisableDebugOutput(); - } // namespace ots + // Enable WOFF2 support(experimental). + void EnableWOFF2(); - #endif // OPENTYPE_SANITISER_H_
new file mode 100644 --- /dev/null +++ b/gfx/ots/ots-woff2.patch @@ -0,0 +1,134 @@ +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 +@@ -236,14 +236,16 @@ typedef TableAction (*TableActionFunc)(u + // Set a callback function that will be called when OTS needs to decide what to + // do for a font table. + void OTS_API SetTableActionCallback(TableActionFunc func, void *user_data); + + // Force to disable debug output even when the library is compiled with + // -DOTS_DEBUG. + void DisableDebugOutput(); + ++#ifdef MOZ_OTS_WOFF2 + // Enable WOFF2 support(experimental). + void EnableWOFF2(); ++#endif + + } // namespace ots + + #endif // OPENTYPE_SANITISER_H_ +diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc +--- a/gfx/ots/src/ots.cc ++++ b/gfx/ots/src/ots.cc +@@ -9,25 +9,29 @@ + + #include <algorithm> + #include <cstdlib> + #include <cstring> + #include <limits> + #include <map> + #include <vector> + ++#ifdef MOZ_OTS_WOFF2 + #include "woff2.h" ++#endif + + // The OpenType Font File + // http://www.microsoft.com/typography/otspec/cmap.htm + + namespace { + + bool g_debug_output = true; ++#ifdef MOZ_OTS_WOFF2 + bool g_enable_woff2 = false; ++#endif + + ots::MessageFunc g_message_func = NULL; + void *g_message_user_data = NULL; + + ots::TableActionFunc g_table_action_func = NULL; + void *g_table_action_user_data = NULL; + + // Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer +@@ -395,16 +399,17 @@ bool ProcessWOFF(ots::OpenTypeFile *head + } + if (block_end != ots::Round4(length)) { + return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)"); + } + + return ProcessGeneric(header, woff_tag, output, data, length, tables, file); + } + ++#ifdef MOZ_OTS_WOFF2 + bool ProcessWOFF2(ots::OpenTypeFile *header, + ots::OTSStream *output, const uint8_t *data, size_t length) { + size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length); + if (decompressed_size == 0) { + return OTS_FAILURE(); + } + // decompressed font must be <= 30MB + if (decompressed_size > 30 * 1024 * 1024) { +@@ -413,16 +418,17 @@ bool ProcessWOFF2(ots::OpenTypeFile *hea + + std::vector<uint8_t> decompressed_buffer(decompressed_size); + if (!ots::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size, + data, length)) { + return OTS_FAILURE(); + } + return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size); + } ++#endif + + ots::TableAction GetTableAction(uint32_t tag) { + ots::TableAction action = ots::TABLE_ACTION_DEFAULT; + + if (g_table_action_func != NULL) { + action = g_table_action_func(htonl(tag), g_table_action_user_data); + } + +@@ -795,19 +801,21 @@ bool IsValidVersionTag(uint32_t tag) { + tag == Tag("true") || + tag == Tag("typ1"); + } + + void DisableDebugOutput() { + g_debug_output = false; + } + ++#ifdef MOZ_OTS_WOFF2 + void EnableWOFF2() { + g_enable_woff2 = true; + } ++#endif + + void SetMessageCallback(MessageFunc func, void *user_data) { + g_message_func = func; + g_message_user_data = user_data; + } + + void SetTableActionCallback(TableActionFunc func, void *user_data) { + g_table_action_func = func; +@@ -822,20 +830,22 @@ bool Process(OTSStream *output, const ui + + if (length < 4) { + return OTS_FAILURE_MSG_(&header, "file less than 4 bytes"); + } + + bool result; + if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') { + result = ProcessWOFF(&header, output, data, length); ++#ifdef MOZ_OTS_WOFF2 + } else if (g_enable_woff2 && + data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && + data[3] == '2') { + result = ProcessWOFF2(&header, output, data, length); ++#endif + } else { + result = ProcessTTF(&header, output, data, length); + } + + for (unsigned i = 0; ; ++i) { + if (table_parsers[i].parse == NULL) break; + table_parsers[i].free(&header); + }
--- a/gfx/ots/src/cff.cc +++ b/gfx/ots/src/cff.cc @@ -1,24 +1,26 @@ // Copyright (c) 2012 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. #include "cff.h" #include <cstring> -#include <utility> // std::pair +#include <utility> #include <vector> #include "cff_type2_charstring.h" // CFF - PostScript font program (Compact Font Format) table // http://www.microsoft.com/typography/otspec/cff.htm // http://www.microsoft.com/typography/otspec/cffspec.htm +#define TABLE_NAME "CFF" + namespace { enum DICT_OPERAND_TYPE { DICT_OPERAND_INTEGER, DICT_OPERAND_REAL, DICT_OPERATOR, }; @@ -410,32 +412,32 @@ bool ParsePrivateDictData( } if (operands.back().first >= 1024 * 1024 * 1024) { return OTS_FAILURE(); } if (operands.back().first + offset >= table_length) { return OTS_FAILURE(); } // parse "16. Local Subrs INDEX" - ots::Buffer table(data, table_length); - table.set_offset(operands.back().first + offset); + ots::Buffer cff_table(data, table_length); + cff_table.set_offset(operands.back().first + offset); ots::CFFIndex *local_subrs_index = NULL; if (type == DICT_DATA_FDARRAY) { if (out_cff->local_subrs_per_font.empty()) { return OTS_FAILURE(); // not reached. } local_subrs_index = out_cff->local_subrs_per_font.back(); } else { // type == DICT_DATA_TOPLEVEL if (out_cff->local_subrs) { return OTS_FAILURE(); // two or more local_subrs? } local_subrs_index = new ots::CFFIndex; out_cff->local_subrs = local_subrs_index; } - if (!ParseIndex(&table, local_subrs_index)) { + if (!ParseIndex(&cff_table, local_subrs_index)) { return OTS_FAILURE(); } break; } // boolean case (12U << 8) + 14: // ForceBold if (operands.size() != 1) { @@ -597,20 +599,20 @@ bool ParseDictData(const uint8_t *data, if (operands.back().first <= 1) { break; // predefined encoding, "Standard" or "Expert", is used. } if (!CheckOffset(operands.back(), table_length)) { return OTS_FAILURE(); } // parse sub dictionary INDEX. - ots::Buffer table(data, table_length); - table.set_offset(operands.back().first); + ots::Buffer cff_table(data, table_length); + cff_table.set_offset(operands.back().first); uint8_t format = 0; - if (!table.ReadU8(&format)) { + if (!cff_table.ReadU8(&format)) { return OTS_FAILURE(); } if (format & 0x80) { // supplemental encoding is not supported at the moment. return OTS_FAILURE(); } // TODO(yusukes): support & parse supplemental encoding tables. break; @@ -622,20 +624,20 @@ bool ParseDictData(const uint8_t *data, } if (operands.size() != 1) { return OTS_FAILURE(); } if (!CheckOffset(operands.back(), table_length)) { return OTS_FAILURE(); } // parse "14. CharStrings INDEX" - ots::Buffer table(data, table_length); - table.set_offset(operands.back().first); + ots::Buffer cff_table(data, table_length); + cff_table.set_offset(operands.back().first); ots::CFFIndex *charstring_index = out_cff->char_strings_array.back(); - if (!ParseIndex(&table, charstring_index)) { + if (!ParseIndex(&cff_table, charstring_index)) { return OTS_FAILURE(); } if (charstring_index->count < 2) { return OTS_FAILURE(); } if (glyphs) { return OTS_FAILURE(); // multiple charstring tables? } @@ -650,20 +652,20 @@ bool ParseDictData(const uint8_t *data, if (operands.size() != 1) { return OTS_FAILURE(); } if (!CheckOffset(operands.back(), table_length)) { return OTS_FAILURE(); } // parse sub dictionary INDEX. - ots::Buffer table(data, table_length); - table.set_offset(operands.back().first); + ots::Buffer cff_table(data, table_length); + cff_table.set_offset(operands.back().first); ots::CFFIndex sub_dict_index; - if (!ParseIndex(&table, &sub_dict_index)) { + if (!ParseIndex(&cff_table, &sub_dict_index)) { return OTS_FAILURE(); } if (!ParseDictData(data, table_length, sub_dict_index, sid_max, DICT_DATA_FDARRAY, out_cff)) { return OTS_FAILURE(); } if (out_cff->font_dict_length != 0) { @@ -680,44 +682,44 @@ bool ParseDictData(const uint8_t *data, if (operands.size() != 1) { return OTS_FAILURE(); } if (!CheckOffset(operands.back(), table_length)) { return OTS_FAILURE(); } // parse FDSelect data structure - ots::Buffer table(data, table_length); - table.set_offset(operands.back().first); + ots::Buffer cff_table(data, table_length); + cff_table.set_offset(operands.back().first); uint8_t format = 0; - if (!table.ReadU8(&format)) { + if (!cff_table.ReadU8(&format)) { return OTS_FAILURE(); } if (format == 0) { for (size_t j = 0; j < glyphs; ++j) { uint8_t fd_index = 0; - if (!table.ReadU8(&fd_index)) { + if (!cff_table.ReadU8(&fd_index)) { return OTS_FAILURE(); } (out_cff->fd_select)[j] = fd_index; } } else if (format == 3) { uint16_t n_ranges = 0; - if (!table.ReadU16(&n_ranges)) { + if (!cff_table.ReadU16(&n_ranges)) { return OTS_FAILURE(); } if (n_ranges == 0) { return OTS_FAILURE(); } uint16_t last_gid = 0; uint8_t fd_index = 0; for (unsigned j = 0; j < n_ranges; ++j) { uint16_t first = 0; // GID - if (!table.ReadU16(&first)) { + if (!cff_table.ReadU16(&first)) { return OTS_FAILURE(); } // Sanity checks. if ((j == 0) && (first != 0)) { return OTS_FAILURE(); } if ((j != 0) && (last_gid >= first)) { @@ -729,24 +731,24 @@ bool ParseDictData(const uint8_t *data, for (uint16_t k = last_gid; k < first; ++k) { if (!out_cff->fd_select.insert( std::make_pair(k, fd_index)).second) { return OTS_FAILURE(); } } } - if (!table.ReadU8(&fd_index)) { + if (!cff_table.ReadU8(&fd_index)) { return OTS_FAILURE(); } last_gid = first; // TODO(yusukes): check GID? } uint16_t sentinel = 0; - if (!table.ReadU16(&sentinel)) { + if (!cff_table.ReadU16(&sentinel)) { return OTS_FAILURE(); } if (last_gid >= sentinel) { return OTS_FAILURE(); } for (uint16_t k = last_gid; k < sentinel; ++k) { if (!out_cff->fd_select.insert( std::make_pair(k, fd_index)).second) { @@ -823,58 +825,58 @@ bool ParseDictData(const uint8_t *data, if (font_format == FORMAT_UNKNOWN) { font_format = FORMAT_OTHER; } } // parse "13. Charsets" if (charset_offset) { - ots::Buffer table(data, table_length); - table.set_offset(charset_offset); + ots::Buffer cff_table(data, table_length); + cff_table.set_offset(charset_offset); uint8_t format = 0; - if (!table.ReadU8(&format)) { + if (!cff_table.ReadU8(&format)) { return OTS_FAILURE(); } switch (format) { case 0: for (unsigned j = 1 /* .notdef is omitted */; j < glyphs; ++j) { uint16_t sid = 0; - if (!table.ReadU16(&sid)) { + if (!cff_table.ReadU16(&sid)) { return OTS_FAILURE(); } if (!have_ros && (sid > sid_max)) { return OTS_FAILURE(); } // TODO(yusukes): check CIDs when have_ros is true. } break; case 1: case 2: { uint32_t total = 1; // .notdef is omitted. while (total < glyphs) { uint16_t sid = 0; - if (!table.ReadU16(&sid)) { + if (!cff_table.ReadU16(&sid)) { return OTS_FAILURE(); } if (!have_ros && (sid > sid_max)) { return OTS_FAILURE(); } // TODO(yusukes): check CIDs when have_ros is true. if (format == 1) { uint8_t left = 0; - if (!table.ReadU8(&left)) { + if (!cff_table.ReadU8(&left)) { return OTS_FAILURE(); } total += (left + 1); } else { uint16_t left = 0; - if (!table.ReadU16(&left)) { + if (!cff_table.ReadU16(&left)) { return OTS_FAILURE(); } total += (left + 1); } } break; }
--- a/gfx/ots/src/cff_type2_charstring.cc +++ b/gfx/ots/src/cff_type2_charstring.cc @@ -33,16 +33,17 @@ bool ExecuteType2CharString(size_t call_ 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); +#ifdef DUMP_T2CHARSTRING // Converts |op| to a string and returns it. const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) { switch (op) { case ots::kHStem: return "HStem"; case ots::kVStem: return "VStem"; case ots::kVMoveTo: @@ -82,16 +83,18 @@ const char *Type2CharStringOperatorToStr case ots::kHHCurveTo: return "HHCurveTo"; case ots::kCallGSubr: return "CallGSubr"; case ots::kVHCurveTo: return "VHCurveTo"; case ots::kHVCurveTo: return "HVCurveTo"; + case ots::kDotSection: + return "DotSection"; case ots::kAnd: return "And"; case ots::kOr: return "Or"; case ots::kNot: return "Not"; case ots::kAbs: return "Abs"; @@ -134,16 +137,17 @@ const char *Type2CharStringOperatorToStr case ots::kHFlex1: return "HFlex1"; case ots::kFlex1: return "Flex1"; } return "UNKNOWN"; } +#endif // 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) { uint8_t v = 0; @@ -519,16 +523,23 @@ bool ExecuteType2CharStringOperator(int3 // {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? successful = true; } while (!argument_stack->empty()) argument_stack->pop(); return successful ? true : OTS_FAILURE(); } + case ots::kDotSection: + // Deprecated operator but harmless, we probably should drop it some how. + if (stack_size != 0) { + return OTS_FAILURE(); + } + return true; + case ots::kAnd: case ots::kOr: case ots::kEq: case ots::kAdd: case ots::kSub: if (stack_size < 2) { return OTS_FAILURE(); } @@ -733,29 +744,31 @@ bool ExecuteType2CharString(size_t call_ int32_t operator_or_operand = 0; bool is_operator = false; if (!ReadNextNumberFromType2CharString(char_string, &operator_or_operand, &is_operator)) { return OTS_FAILURE(); } +#ifdef DUMP_T2CHARSTRING /* You can dump all operators and operands (except mask bytes for hintmask and cntrmask) by the following code: + */ if (!is_operator) { std::fprintf(stderr, "#%d# ", operator_or_operand); } else { std::fprintf(stderr, "#%s#\n", Type2CharStringOperatorToString( - Type2CharStringOperator(operator_or_operand)), - operator_or_operand); + ots::Type2CharStringOperator(operator_or_operand)) + ); } - */ +#endif if (!is_operator) { argument_stack->push(operator_or_operand); if (argument_stack->size() > kMaxArgumentStack) { return OTS_FAILURE(); } continue; }
--- a/gfx/ots/src/cff_type2_charstring.h +++ b/gfx/ots/src/cff_type2_charstring.h @@ -62,16 +62,17 @@ enum Type2CharStringOperator { kVStemHm = 23, kRCurveLine = 24, kRLineCurve = 25, kVVCurveTo = 26, kHHCurveTo = 27, kCallGSubr = 29, kVHCurveTo = 30, kHVCurveTo = 31, + kDotSection = 12 << 8, kAnd = (12 << 8) + 3, kOr = (12 << 8) + 4, kNot = (12 << 8) + 5, kAbs = (12 << 8) + 9, kAdd = (12 << 8) + 10, kSub = (12 << 8) + 11, kDiv = (12 << 8) + 12, kNeg = (12 << 8) + 14, @@ -86,15 +87,14 @@ enum Type2CharStringOperator { kDup = (12 << 8) + 27, kExch = (12 << 8) + 28, kIndex = (12 << 8) + 29, kRoll = (12 << 8) + 30, kHFlex = (12 << 8) + 34, kFlex = (12 << 8) + 35, kHFlex1 = (12 << 8) + 36, kFlex1 = (12 << 8) + 37, - // Operators that are obsoleted or undocumented, such as 'blend', will be - // rejected. + // Operators that are undocumented, such as 'blend', will be rejected. }; } // namespace ots #endif // OTS_CFF_TYPE2_CHARSTRING_H_
--- a/gfx/ots/src/cmap.cc +++ b/gfx/ots/src/cmap.cc @@ -8,26 +8,29 @@ #include <set> #include <utility> #include <vector> #include "maxp.h" #include "os2.h" // cmap - Character To Glyph Index Mapping Table -// http://www.microsoft.com/opentype/otspec/cmap.htm +// http://www.microsoft.com/typography/otspec/cmap.htm + +#define TABLE_NAME "cmap" namespace { struct CMAPSubtableHeader { uint16_t platform; uint16_t encoding; uint32_t offset; uint16_t format; uint32_t length; + uint32_t language; }; struct Subtable314Range { uint16_t start_range; uint16_t end_range; int16_t id_delta; uint16_t id_range_offset; uint32_t id_range_offset_offset; @@ -62,112 +65,112 @@ bool ParseFormat4(ots::OpenTypeFile *fil const uint8_t *data, size_t length, uint16_t num_glyphs) { ots::Buffer subtable(data, length); // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the // whole thing and recompacting it, we validate it and include it verbatim // in the output. if (!file->os2) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Required OS/2 table missing"); } if (!subtable.Skip(4)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable"); } uint16_t language = 0; if (!subtable.ReadU16(&language)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read language"); } if (language) { // Platform ID 3 (windows) subtables should have language '0'. - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Languages should be 0 (%d)", language); } uint16_t segcountx2, search_range, entry_selector, range_shift; segcountx2 = search_range = entry_selector = range_shift = 0; if (!subtable.ReadU16(&segcountx2) || !subtable.ReadU16(&search_range) || !subtable.ReadU16(&entry_selector) || !subtable.ReadU16(&range_shift)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read subcmap structure"); } if (segcountx2 & 1 || search_range & 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount); } // log2segcount is the maximal x s.t. 2^x < segcount unsigned log2segcount = 0; while (1u << (log2segcount + 1) <= segcount) { log2segcount++; } const uint16_t expected_search_range = 2 * 1u << log2segcount; if (expected_search_range != search_range) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range); } if (entry_selector != log2segcount) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Failed to read segment %d", i); } } uint16_t padding; if (!subtable.ReadU16(&padding)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding"); } if (padding) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Failed to read segment range offset %d", i); } if (ranges[i].id_range_offset & 1) { // Some font generators seem to put 65535 on id_range_offset // for 0xFFFF-0xFFFF range. // (e.g., many fonts in http://www.princexml.com/fonts/) if (i == segcount - 1u) { OTS_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(); + return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset); } } } // ranges must be ascending order, based on the end_code. Ranges may not // overlap. for (unsigned i = 1; i < segcount; ++i) { if ((i == segcount - 1u) && @@ -179,20 +182,20 @@ bool ParseFormat4(ots::OpenTypeFile *fil // We'll accept them as an exception. OTS_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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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 (file->os2->first_char_index != 0xFFFF && ranges[i].start_range != 0xFFFF && file->os2->first_char_index > ranges[i].start_range) { file->os2->first_char_index = ranges[i].start_range; @@ -200,48 +203,49 @@ bool ParseFormat4(ots::OpenTypeFile *fil if (file->os2->last_char_index != 0xFFFF && ranges[i].end_range != 0xFFFF && file->os2->last_char_index < ranges[i].end_range) { file->os2->last_char_index = ranges[i].end_range; } } // The last range must end at 0xffff - if (ranges[segcount - 1].end_range != 0xffff) { - return OTS_FAILURE(); + 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)", + ranges[segcount - 1].start_range, ranges[segcount - 1].end_range); } // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of // each code-point defined in the table and make sure that they are all valid // glyphs and that we don't access anything out-of-bounds. for (unsigned i = 0; i < segcount; ++i) { for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) { const uint16_t code_point = cp; if (ranges[i].id_range_offset == 0) { // 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(); + return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1); } } else { const uint16_t range_delta = code_point - ranges[i].start_range; // this might seem odd, but it's true. The offset is relative to the // location of the offset value itself. const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset + ranges[i].id_range_offset + 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(); + return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length); } uint16_t glyph; - memcpy(&glyph, data + glyph_id_offset, 2); + std::memcpy(&glyph, data + glyph_id_offset, 2); glyph = ntohs(glyph); if (glyph >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1); } } } } // We accept the table. // TODO(yusukes): transcode the subtable. if (platform == 3 && encoding == 0) { @@ -249,324 +253,329 @@ bool ParseFormat4(ots::OpenTypeFile *fil file->cmap->subtable_3_0_4_length = length; } else if (platform == 3 && encoding == 1) { file->cmap->subtable_3_1_4_data = data; file->cmap->subtable_3_1_4_length = length; } else if (platform == 0 && encoding == 3) { file->cmap->subtable_0_3_4_data = data; file->cmap->subtable_0_3_4_length = length; } else { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding); } return true; } bool Parse31012(ots::OpenTypeFile *file, 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(); + return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable"); } uint32_t language = 0; if (!subtable.ReadU32(&language)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("can't read format 12 subtable language"); } if (language) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language); } uint32_t num_groups = 0; if (!subtable.ReadU32(&num_groups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("can't read number of format 12 subtable groups"); } if (num_groups == 0 || num_groups > kMaxCMAPGroups) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad format 12 subtable group count %d", num_groups); } std::vector<ots::OpenTypeCMAPSubtableRange> &groups = file->cmap->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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)", + groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id); } // [0xD800, 0xDFFF] are surrogate code points. if (groups[i].start_range >= 0xD800 && groups[i].start_range <= 0xDFFF) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("format 12 subtable out of range group startCharCode (0x%4X)", groups[i].start_range); } if (groups[i].end_range >= 0xD800 && groups[i].end_range <= 0xDFFF) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("format 12 subtable out of range group endCharCode (0x%4X)", groups[i].end_range); } if (groups[i].start_range < 0xD800 && groups[i].end_range > 0xDFFF) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad format 12 subtable group startCharCode (0x%4X) or endCharCode (0x%4X)", + groups[i].start_range, groups[i].end_range); } // We assert that the glyph value is within range. Because of the range // limits, above, we don't need to worry about overflow. if (groups[i].end_range < groups[i].start_range) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)", + groups[i].start_range, groups[i-1].end_range); } } return true; } bool Parse31013(ots::OpenTypeFile *file, 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(); + return OTS_FAILURE_MSG("Bad cmap subtable length"); } uint16_t language = 0; if (!subtable.ReadU16(&language)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read cmap subtable language"); } if (language) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language); } uint32_t num_groups = 0; if (!subtable.ReadU32(&num_groups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable"); } // We limit the number of groups in the same way as in 3.10.12 tables. See // the comment there in if (num_groups == 0 || num_groups > kMaxCMAPGroups) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad number of groups (%d) in a cmap subtable", num_groups); } std::vector<ots::OpenTypeCMAPSubtableRange> &groups = file->cmap->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(); + return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable"); } // We conservatively limit all of the values to protect some parsers from // overflows if (groups[i].start_range > kUnicodeUpperLimit || groups[i].end_range > kUnicodeUpperLimit || groups[i].start_glyph_id > 0xFFFF) { - return OTS_FAILURE(); + 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); } if (groups[i].start_glyph_id >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range); } } return true; } bool Parse0514(ots::OpenTypeFile *file, const uint8_t *data, size_t length, uint16_t num_glyphs) { // Unicode Variation Selector table ots::Buffer subtable(data, length); // Format 14 tables are simple. We parse these and fully serialise them // later. // Skip format (USHORT) and length (ULONG) if (!subtable.Skip(6)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read start of cmap subtable"); } uint32_t num_records = 0; if (!subtable.ReadU32(&num_records)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read number of records in cmap subtable"); } if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad number of records (%d) in cmap subtable", num_records); } std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records = file->cmap->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(); + return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i); } // Checks the value of variation selector if (!((records[i].var_selector >= kMongolianVSStart && records[i].var_selector <= kMongolianVSEnd) || (records[i].var_selector >= kVSStart && records[i].var_selector <= kVSEnd) || (records[i].var_selector >= kIVSStart && records[i].var_selector <= kIVSEnd))) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i); } if (records[i].default_offset && records[i].default_offset >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i); } } for (unsigned i = 0; i < num_records; ++i) { // Checks default UVS table if (records[i].default_offset) { subtable.set_offset(records[i].default_offset); uint32_t num_ranges = 0; if (!subtable.ReadU32(&num_ranges)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i); } if (!num_ranges || num_ranges > kMaxCMAPGroups) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("number of ranges too high (%d > %d) in record %d", num_ranges, kMaxCMAPGroups, i); } uint32_t last_unicode_value = 0; std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges = records[i].ranges; ranges.resize(num_ranges); for (unsigned j = 0; j < num_ranges; ++j) { if (!subtable.ReadU24(&ranges[j].unicode_value) || !subtable.ReadU8(&ranges[j].additional_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i); } const uint32_t check_value = ranges[j].unicode_value + ranges[j].additional_count; if (ranges[j].unicode_value == 0 || ranges[j].unicode_value > kUnicodeUpperLimit || check_value > kUVSUpperLimit || (last_unicode_value && ranges[j].unicode_value <= last_unicode_value)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i); } last_unicode_value = check_value; } } // Checks non default UVS table if (records[i].non_default_offset) { subtable.set_offset(records[i].non_default_offset); uint32_t num_mappings = 0; if (!subtable.ReadU32(&num_mappings)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i); } if (!num_mappings || num_mappings > kMaxCMAPGroups) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Number of mappings too high (%d) in variation selector record %d", num_mappings, i); } uint32_t last_unicode_value = 0; std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings = records[i].mappings; mappings.resize(num_mappings); for (unsigned j = 0; j < num_mappings; ++j) { if (!subtable.ReadU24(&mappings[j].unicode_value) || !subtable.ReadU16(&mappings[j].glyph_id)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + 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); } last_unicode_value = mappings[j].unicode_value; } } } if (subtable.offset() != length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length); } file->cmap->subtable_0_5_14_length = subtable.offset(); return true; } bool Parse100(ots::OpenTypeFile *file, const uint8_t *data, size_t length) { // Mac Roman table ots::Buffer subtable(data, length); if (!subtable.Skip(4)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad cmap subtable"); } uint16_t language = 0; if (!subtable.ReadU16(&language)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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); } file->cmap->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(); + return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i); } file->cmap->subtable_1_0_0.push_back(glyph_id); } return true; } } // namespace @@ -576,113 +585,131 @@ namespace ots { bool ots_cmap_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); file->cmap = new OpenTypeCMAP; uint16_t version = 0; uint16_t num_tables = 0; if (!table.ReadU16(&version) || !table.ReadU16(&num_tables)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read structure of cmap"); } if (version != 0) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Non zero cmap version (%d)", version); } if (!num_tables) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("No subtables in cmap!"); } std::vector<CMAPSubtableHeader> subtable_headers; // read the subtable headers subtable_headers.reserve(num_tables); for (unsigned i = 0; i < num_tables; ++i) { CMAPSubtableHeader subt; if (!table.ReadU16(&subt.platform) || !table.ReadU16(&subt.encoding) || !table.ReadU32(&subt.offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i); } subtable_headers.push_back(subt); } const size_t data_offset = table.offset(); // make sure that all the offsets are valid. - uint32_t last_id = 0; for (unsigned i = 0; i < num_tables; ++i) { if (subtable_headers[i].offset > 1024 * 1024 * 1024) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i); } if (subtable_headers[i].offset < data_offset || subtable_headers[i].offset >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i); } - - // check if the table is sorted first by platform ID, then by encoding ID. - uint32_t current_id - = (subtable_headers[i].platform << 16) + subtable_headers[i].encoding; - if ((i != 0) && (last_id >= current_id)) { - return OTS_FAILURE(); - } - last_id = current_id; } // the format of the table is the first couple of bytes in the table. The // length of the table is stored in a format-specific way. 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(); + return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i); } uint16_t len = 0; + uint16_t lang = 0; switch (subtable_headers[i].format) { case 0: case 4: if (!table.ReadU16(&len)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i); + } + if (!table.ReadU16(&lang)) { + return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i); } subtable_headers[i].length = len; + subtable_headers[i].language = lang; break; case 12: case 13: if (!table.Skip(2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i); } if (!table.ReadU32(&subtable_headers[i].length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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); } break; case 14: if (!table.ReadU32(&subtable_headers[i].length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i); } + subtable_headers[i].language = 0; break; default: subtable_headers[i].length = 0; + subtable_headers[i].language = 0; break; } } + // check if the table is sorted first by platform ID, then by encoding ID. + uint32_t last_id = 0; + for (unsigned i = 0; i < num_tables; ++i) { + uint32_t current_id + = (subtable_headers[i].platform << 24) + + (subtable_headers[i].encoding << 16) + + subtable_headers[i].language; + if ((i != 0) && (last_id >= current_id)) { + return OTS_FAILURE_MSG("subtable %d with platform ID %d, encoding ID %d, language ID %d " + "following subtable with platform ID %d, encoding ID %d, language ID %d", + i, + (uint8_t)(current_id >> 24), (uint8_t)(current_id >> 16), (uint8_t)(current_id), + (uint8_t)(last_id >> 24), (uint8_t)(last_id >> 16), (uint8_t)(last_id)); + } + last_id = current_id; + } + // Now, verify that all the lengths are sane for (unsigned i = 0; i < num_tables; ++i) { if (!subtable_headers[i].length) continue; if (subtable_headers[i].length > 1024 * 1024 * 1024) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length); } } // check that the cmap subtables are not overlapping. std::set<std::pair<uint32_t, uint32_t> > uniq_checker; std::vector<std::pair<uint32_t, uint8_t> > overlap_checker; for (unsigned i = 0; i < num_tables; ++i) { const uint32_t end_byte @@ -700,24 +727,24 @@ bool ots_cmap_parse(OpenTypeFile *file, overlap_checker.push_back( std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */)); } std::sort(overlap_checker.begin(), overlap_checker.end()); int overlap_count = 0; for (unsigned i = 0; i < overlap_checker.size(); ++i) { overlap_count += (overlap_checker[i].second ? 1 : -1); if (overlap_count > 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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 (!file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap."); } const uint16_t num_glyphs = file->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 // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables: // Platform ID Encoding ID Format // 0 0 4 (Unicode Default) @@ -747,37 +774,37 @@ bool ots_cmap_parse(OpenTypeFile *file, if ((subtable_headers[i].encoding == 0) && (subtable_headers[i].format == 4)) { // parse and output the 0-0-4 table as 3-1-4 table. Sometimes the 0-0-4 // 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(file, 3, 1, data + subtable_headers[i].offset, subtable_headers[i].length, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(file, 0, 3, data + subtable_headers[i].offset, subtable_headers[i].length, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i); } } else if ((subtable_headers[i].encoding == 3) && (subtable_headers[i].format == 12)) { // parse and output the 0-3-12 table as 3-10-12 table. if (!Parse31012(file, data + subtable_headers[i].offset, subtable_headers[i].length, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i); } } else if ((subtable_headers[i].encoding == 5) && (subtable_headers[i].format == 14)) { if (!Parse0514(file, data + subtable_headers[i].offset, subtable_headers[i].length, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i); } } } else if (subtable_headers[i].platform == 1) { // Mac platform if ((subtable_headers[i].encoding == 0) && (subtable_headers[i].format == 0)) { // parse and output the 1-0-0 table.
--- a/gfx/ots/src/cvt.cc +++ b/gfx/ots/src/cvt.cc @@ -1,35 +1,37 @@ // 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. #include "cvt.h" // cvt - Control Value Table -// http://www.microsoft.com/opentype/otspec/cvt.htm +// http://www.microsoft.com/typography/otspec/cvt.htm + +#define TABLE_NAME "cvt" namespace ots { bool ots_cvt_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeCVT *cvt = new OpenTypeCVT; file->cvt = cvt; if (length >= 128 * 1024u) { - return OTS_FAILURE(); // almost all cvt tables are less than 4k bytes. + return OTS_FAILURE_MSG("Length (%d) > 120K"); // almost all cvt tables are less than 4k bytes. } if (length % 2 != 0) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Uneven cvt length (%d)", length); } if (!table.Skip(length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Length too high"); } cvt->data = data; cvt->length = length; return true; } bool ots_cvt_should_serialise(OpenTypeFile *file) { @@ -38,17 +40,17 @@ bool ots_cvt_should_serialise(OpenTypeFi } return g_transcode_hints && file->cvt; } bool ots_cvt_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeCVT *cvt = file->cvt; if (!out->Write(cvt->data, cvt->length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write CVT table"); } return true; } void ots_cvt_free(OpenTypeFile *file) { delete file->cvt; }
--- a/gfx/ots/src/fpgm.cc +++ b/gfx/ots/src/fpgm.cc @@ -1,48 +1,50 @@ // 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. #include "fpgm.h" // fpgm - Font Program -// http://www.microsoft.com/opentype/otspec/fpgm.htm +// http://www.microsoft.com/typography/otspec/fpgm.htm + +#define TABLE_NAME "fpgm" namespace ots { bool ots_fpgm_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeFPGM *fpgm = new OpenTypeFPGM; file->fpgm = fpgm; if (length >= 128 * 1024u) { - return OTS_FAILURE(); // almost all fpgm tables are less than 5k bytes. + return OTS_FAILURE_MSG("length (%ld) > 120", length); // almost all fpgm tables are less than 5k bytes. } if (!table.Skip(length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad fpgm length"); } fpgm->data = data; fpgm->length = length; return true; } bool ots_fpgm_should_serialise(OpenTypeFile *file) { if (!file->glyf) return false; // this table is not for CFF fonts. return g_transcode_hints && file->fpgm; } bool ots_fpgm_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeFPGM *fpgm = file->fpgm; if (!out->Write(fpgm->data, fpgm->length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write fpgm"); } return true; } void ots_fpgm_free(OpenTypeFile *file) { delete file->fpgm; }
--- a/gfx/ots/src/gasp.cc +++ b/gfx/ots/src/gasp.cc @@ -1,32 +1,38 @@ // 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. #include "gasp.h" // gasp - Grid-fitting And Scan-conversion Procedure -// http://www.microsoft.com/opentype/otspec/gasp.htm +// http://www.microsoft.com/typography/otspec/gasp.htm + +#define TABLE_NAME "gasp" #define DROP_THIS_TABLE \ - do { delete file->gasp; file->gasp = 0; } while (0) + do { \ + delete file->gasp; \ + file->gasp = 0; \ + OTS_FAILURE_MSG("Table discarded"); \ + } while (0) namespace ots { bool ots_gasp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeGASP *gasp = new OpenTypeGASP; file->gasp = gasp; uint16_t num_ranges = 0; if (!table.ReadU16(&gasp->version) || !table.ReadU16(&num_ranges)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read table header"); } if (gasp->version > 1) { // Lots of Linux fonts have bad version numbers... OTS_WARNING("bad version: %u", gasp->version); DROP_THIS_TABLE; return true; } @@ -38,17 +44,17 @@ bool ots_gasp_parse(OpenTypeFile *file, } gasp->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(); + return OTS_FAILURE_MSG("Failed to read subrange %d", i); } if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) { // The records in the gaspRange[] array must be sorted in order of // increasing rangeMaxPPEM value. OTS_WARNING("ranges are not sorted"); DROP_THIS_TABLE; return true; } @@ -81,23 +87,23 @@ bool ots_gasp_should_serialise(OpenTypeF return file->gasp != NULL; } bool ots_gasp_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeGASP *gasp = file->gasp; if (!out->WriteU16(gasp->version) || !out->WriteU16(gasp->gasp_ranges.size())) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("failed to write gasp header"); } for (unsigned i = 0; i < gasp->gasp_ranges.size(); ++i) { if (!out->WriteU16(gasp->gasp_ranges[i].first) || !out->WriteU16(gasp->gasp_ranges[i].second)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write gasp subtable %d", i); } } return true; } void ots_gasp_free(OpenTypeFile *file) { delete file->gasp;
--- a/gfx/ots/src/gasp.h +++ b/gfx/ots/src/gasp.h @@ -1,17 +1,17 @@ // 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. #ifndef OTS_GASP_H_ #define OTS_GASP_H_ #include <new> -#include <utility> // std::pair +#include <utility> #include <vector> #include "ots.h" namespace ots { struct OpenTypeGASP { uint16_t version;
--- a/gfx/ots/src/gdef.cc +++ b/gfx/ots/src/gdef.cc @@ -25,391 +25,359 @@ const uint16_t kMaxClassDefValue = 0xFFF 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; bool ParseGlyphClassDefTable(ots::OpenTypeFile *file, const uint8_t *data, size_t length, const uint16_t num_glyphs) { - return ots::ParseClassDefTable(data, length, num_glyphs, + return ots::ParseClassDefTable(file, data, length, num_glyphs, kMaxGlyphClassDefValue); } bool ParseAttachListTable(ots::OpenTypeFile *file, const uint8_t *data, size_t length, const uint16_t num_glyphs) { 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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Bad glyph count in gdef"); } if (offset_coverage == 0 || offset_coverage >= length || offset_coverage < attach_points_end) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage); } if (glyph_count > num_glyphs) { - OTS_WARNING("bad glyph count: %u", glyph_count); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Can't read attachment point %d", i); } if (attach_points[i] >= length || attach_points[i] < attach_points_end) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad attachment point %d of %d", i, attach_points[i]); } } // Parse coverage table - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage table"); } // Parse attach point table for (unsigned i = 0; i < attach_points.size(); ++i) { subtable.set_offset(attach_points[i]); uint16_t point_count = 0; if (!subtable.ReadU16(&point_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read point count %d", i); } if (point_count == 0) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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) { - OTS_WARNING("bad contour indeces: %u >= %u", + return OTS_FAILURE_MSG("bad contour indeces: %u >= %u", last_point_index, point_index); - return OTS_FAILURE(); } last_point_index = point_index; } } return true; } bool ParseLigCaretListTable(ots::OpenTypeFile *file, const uint8_t *data, size_t length, const uint16_t num_glyphs) { 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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Bad caret structure"); } if (offset_coverage == 0 || offset_coverage >= length || offset_coverage < lig_glyphs_end) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad caret coverate offset %d", offset_coverage); } if (lig_glyph_count > num_glyphs) { - OTS_WARNING("bad ligature glyph count: %u", lig_glyph_count); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Can't read ligature glyph location %d", i); } if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i); } } // Parse coverage table - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't parse caret coverage table"); } // Parse ligature glyph table for (unsigned i = 0; i < lig_glyphs.size(); ++i) { subtable.set_offset(lig_glyphs[i]); uint16_t caret_count = 0; if (!subtable.ReadU16(&caret_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read caret count for glyph %d", i); } if (caret_count == 0) { - OTS_WARNING("bad caret value count: %u", caret_count); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad caret value count: %u", caret_count); } - std::vector<uint16_t> caret_values; - caret_values.resize(caret_count); - uint16_t last_offset_caret = 0; - unsigned caret_values_end = 2 * static_cast<unsigned>(caret_count) + 2; + std::vector<uint16_t> caret_value_offsets; + caret_value_offsets.resize(caret_count); + unsigned caret_value_offsets_end = 2 * static_cast<unsigned>(caret_count) + 2; for (unsigned j = 0; j < caret_count; ++j) { - if (!subtable.ReadU16(&caret_values[j])) { - return OTS_FAILURE(); + if (!subtable.ReadU16(&caret_value_offsets[j])) { + return OTS_FAILURE_MSG("Can't read caret offset %d for glyph %d", j, i); } - if (caret_values[j] >= length || caret_values[j] < caret_values_end) { - return OTS_FAILURE(); + 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); } - // Caret offsets are in increasing coordinate order - if (last_offset_caret != 0 && last_offset_caret >= caret_values[j]) { - OTS_WARNING("offset isn't in increasing coordinate order: %u >= %u", - last_offset_caret, caret_values[j]); - return OTS_FAILURE(); - } - last_offset_caret = caret_values[j]; } // Parse caret values table for (unsigned j = 0; j < caret_count; ++j) { - subtable.set_offset(lig_glyphs[i] + caret_values[j]); + subtable.set_offset(lig_glyphs[i] + caret_value_offsets[j]); uint16_t caret_format = 0; if (!subtable.ReadU16(&caret_format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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) { - OTS_WARNING("bad caret value format: %u", caret_format); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Bad caret value table structure %d in glyph %d", j, i); } } } return true; } bool ParseMarkAttachClassDefTable(ots::OpenTypeFile *file, const uint8_t *data, size_t length, const uint16_t num_glyphs) { - return ots::ParseClassDefTable(data, length, num_glyphs, kMaxClassDefValue); + return ots::ParseClassDefTable(file, data, length, num_glyphs, kMaxClassDefValue); } bool ParseMarkGlyphSetsDefTable(ots::OpenTypeFile *file, const uint8_t *data, size_t length, const uint16_t num_glyphs) { 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(); + return OTS_FAILURE_MSG("Can' read mark glyph table structure"); } if (format != 1) { - OTS_WARNING("bad mark glyph set table format: %u", format); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Can't read covrage location for mark set %d", i); } if (offset_coverage >= length || offset_coverage < mark_sets_end) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage location %d for mark set %d", offset_coverage, i); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table for mark set %d", i); } } file->gdef->num_mark_glyph_sets = mark_set_count; return true; } } // namespace -#define DROP_THIS_TABLE \ +#define DROP_THIS_TABLE(msg_) \ do { \ file->gdef->data = 0; \ file->gdef->length = 0; \ - OTS_FAILURE_MSG("OpenType layout data discarded"); \ + OTS_FAILURE_MSG(msg_ ", table discarded"); \ } while (0) namespace ots { bool ots_gdef_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { // Grab the number of glyphs in the file from the maxp table to check // GlyphIDs in GDEF table. if (!file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("No maxp table in font, needed by GDEF"); } const uint16_t num_glyphs = file->maxp->num_glyphs; Buffer table(data, length); OpenTypeGDEF *gdef = new OpenTypeGDEF; file->gdef = gdef; uint32_t version = 0; if (!table.ReadU32(&version)) { - OTS_WARNING("incomplete GDEF table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Incomplete table"); return true; } if (version < 0x00010000 || version == 0x00010001) { - OTS_WARNING("bad GDEF version"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Bad version"); return true; } if (version >= 0x00010002) { gdef->version_2 = true; } uint16_t offset_glyph_class_def = 0; uint16_t offset_attach_list = 0; uint16_t offset_lig_caret_list = 0; uint16_t offset_mark_attach_class_def = 0; if (!table.ReadU16(&offset_glyph_class_def) || !table.ReadU16(&offset_attach_list) || !table.ReadU16(&offset_lig_caret_list) || !table.ReadU16(&offset_mark_attach_class_def)) { - OTS_WARNING("incomplete GDEF table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Incomplete table"); return true; } uint16_t offset_mark_glyph_sets_def = 0; if (gdef->version_2) { if (!table.ReadU16(&offset_mark_glyph_sets_def)) { - OTS_WARNING("incomplete GDEF table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Incomplete table"); return true; } } - unsigned gdef_header_end = 8; + unsigned gdef_header_end = 4 + 4 * 2; if (gdef->version_2) gdef_header_end += 2; // Parse subtables if (offset_glyph_class_def) { if (offset_glyph_class_def >= length || offset_glyph_class_def < gdef_header_end) { - OTS_WARNING("invalid offset to glyph classes"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Invalid offset to glyph classes"); return true; } if (!ParseGlyphClassDefTable(file, data + offset_glyph_class_def, length - offset_glyph_class_def, num_glyphs)) { - OTS_WARNING("invalid glyph classes"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Invalid glyph classes"); return true; } gdef->has_glyph_class_def = true; } if (offset_attach_list) { if (offset_attach_list >= length || offset_attach_list < gdef_header_end) { - OTS_WARNING("invalid offset to attachment list"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Invalid offset to attachment list"); return true; } if (!ParseAttachListTable(file, data + offset_attach_list, length - offset_attach_list, num_glyphs)) { - OTS_WARNING("invalid attachment list"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Invalid attachment list"); return true; } } if (offset_lig_caret_list) { if (offset_lig_caret_list >= length || offset_lig_caret_list < gdef_header_end) { - OTS_WARNING("invalid offset to lig-caret list"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Invalid offset to ligature caret list"); return true; } if (!ParseLigCaretListTable(file, data + offset_lig_caret_list, length - offset_lig_caret_list, num_glyphs)) { - OTS_WARNING("invalid ligature caret list"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Invalid ligature caret list"); return true; } } if (offset_mark_attach_class_def) { if (offset_mark_attach_class_def >= length || offset_mark_attach_class_def < gdef_header_end) { - OTS_WARNING("invalid offset to mark attachment list"); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Invalid offset to mark attachment list"); } if (!ParseMarkAttachClassDefTable(file, data + offset_mark_attach_class_def, length - offset_mark_attach_class_def, num_glyphs)) { - OTS_WARNING("invalid mark attachment list"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Invalid mark attachment list"); return true; } 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) { - OTS_WARNING("invalid offset to mark glyph sets"); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("invalid offset to mark glyph sets"); } if (!ParseMarkGlyphSetsDefTable(file, data + offset_mark_glyph_sets_def, length - offset_mark_glyph_sets_def, num_glyphs)) { - OTS_WARNING("invalid mark glyph sets"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Invalid mark glyph sets"); return true; } gdef->has_mark_glyph_sets_def = true; } gdef->data = data; gdef->length = length; return true; } bool ots_gdef_should_serialise(OpenTypeFile *file) { - const bool needed_tables_dropped = - (file->gsub && file->gsub->data == NULL) || - (file->gpos && file->gpos->data == NULL); - return file->gdef != NULL && file->gdef->data != NULL && - !needed_tables_dropped; + return file->gdef != NULL && file->gdef->data != NULL; } bool ots_gdef_serialise(OTSStream *out, OpenTypeFile *file) { if (!out->Write(file->gdef->data, file->gdef->length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write GDEF table"); } return true; } void ots_gdef_free(OpenTypeFile *file) { delete file->gdef; }
--- a/gfx/ots/src/glyf.cc +++ b/gfx/ots/src/glyf.cc @@ -7,29 +7,32 @@ #include <algorithm> #include <limits> #include "head.h" #include "loca.h" #include "maxp.h" // glyf - Glyph Data -// http://www.microsoft.com/opentype/otspec/glyf.htm +// http://www.microsoft.com/typography/otspec/glyf.htm + +#define TABLE_NAME "glyf" namespace { -bool ParseFlagsForSimpleGlyph(ots::Buffer *table, +bool ParseFlagsForSimpleGlyph(ots::OpenTypeFile *file, + 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) { uint8_t flag = 0; if (!table->ReadU8(&flag)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read flag"); } uint32_t delta = 0; if (flag & (1u << 1)) { // x-Short ++delta; } else if (!(flag & (1u << 4))) { delta += 2; } @@ -37,81 +40,81 @@ bool ParseFlagsForSimpleGlyph(ots::Buffe if (flag & (1u << 2)) { // y-Short ++delta; } else if (!(flag & (1u << 5))) { delta += 2; } if (flag & (1u << 3)) { // repeat if (*flags_count_logical + 1 >= num_flags) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags); } uint8_t repeat = 0; if (!table->ReadU8(&repeat)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read repeat value"); } if (repeat == 0) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Zero repeat"); } delta += (delta * repeat); *flags_count_logical += repeat; if (*flags_count_logical >= num_flags) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags); } ++(*flags_count_physical); } if ((flag & (1u << 6)) || (flag & (1u << 7))) { // reserved flags - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad flag value (%d)", flag); } *xy_coordinates_length += delta; if (gly_length < *xy_coordinates_length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length); } return true; } bool ParseSimpleGlyph(ots::OpenTypeFile *file, 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 = file->glyf; // 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(); + return OTS_FAILURE_MSG("Can't read contour index %d", i); } if (tmp_index == 0xffffu) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad contour index %d", i); } // check if the indices are monotonically increasing if (i && (tmp_index + 1 <= num_flags)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Can't read bytecode length"); } if ((file->maxp->version_1) && (file->maxp->max_size_glyf_instructions < bytecode_length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length); } const uint32_t gly_header_length = 10 + num_contours * 2 + 2; if (gly_length < (gly_header_length + bytecode_length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length); } if (ots::g_transcode_hints) { glyf->iov.push_back(std::make_pair( data + gly_offset, static_cast<size_t>(gly_header_length + bytecode_length))); } else { // enqueue two vectors: the glyph data up to the bytecode length, then @@ -119,44 +122,45 @@ bool ParseSimpleGlyph(ots::OpenTypeFile glyf->iov.push_back(std::make_pair( data + gly_offset, static_cast<size_t>(gly_header_length - 2))); glyf->iov.push_back(std::make_pair((const uint8_t*) "\x00\x00", static_cast<size_t>(2))); } if (!table->Skip(bytecode_length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't skip 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(table, + if (!ParseFlagsForSimpleGlyph(file, + table, gly_length, num_flags, &flags_count_logical, &flags_count_physical, &xy_coordinates_length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical); } } if (gly_length < (gly_header_length + bytecode_length + flags_count_physical + xy_coordinates_length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Glyph too short %d", gly_length); } if (gly_length - (gly_header_length + bytecode_length + flags_count_physical + xy_coordinates_length) > 3) { // We allow 0-3 bytes difference since gly_length is 4-bytes aligned, // zero-padded length. - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length); } 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; @@ -170,88 +174,88 @@ bool ParseSimpleGlyph(ots::OpenTypeFile } // namespace namespace ots { bool ots_glyf_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); if (!file->maxp || !file->loca || !file->head) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table"); } OpenTypeGLYF *glyf = new OpenTypeGLYF; file->glyf = glyf; const unsigned num_glyphs = file->maxp->num_glyphs; std::vector<uint32_t> &offsets = file->loca->offsets; if (offsets.size() != num_glyphs + 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1); } std::vector<uint32_t> resulting_offsets(num_glyphs + 1); uint32_t current_offset = 0; for (unsigned i = 0; i < num_glyphs; ++i) { const unsigned gly_offset = offsets[i]; // The LOCA parser checks that these values are monotonic const unsigned gly_length = offsets[i + 1] - offsets[i]; if (!gly_length) { // this glyph has no outline (e.g. the space charactor) resulting_offsets[i] = current_offset; continue; } if (gly_offset >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length); } if (gly_offset + gly_length > length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length); } table.set_offset(gly_offset); 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(); + return OTS_FAILURE_MSG("Can't read glyph %d header", i); } if (num_contours <= -2) { // -2, -3, -4, ... are reserved for future use. - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i); } // workaround for fonts in http://www.princexml.com/fonts/ if ((xmin == 32767) && (xmax == -32767) && (ymin == 32767) && (ymax == -32767)) { OTS_WARNING("bad xmin/xmax/ymin/ymax values"); xmin = xmax = ymin = ymax = 0; } if (xmin > xmax || ymin > ymax) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(file, data, &table, num_contours, gly_offset, gly_length, &new_size)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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; } @@ -286,17 +290,17 @@ bool ots_glyf_should_serialise(OpenTypeF return file->glyf != NULL; } bool ots_glyf_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeGLYF *glyf = file->glyf; for (unsigned i = 0; i < glyf->iov.size(); ++i) { if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Falied to write glyph %d", i); } } return true; } void ots_glyf_free(OpenTypeFile *file) { delete file->glyf;
--- a/gfx/ots/src/glyf.h +++ b/gfx/ots/src/glyf.h @@ -1,17 +1,17 @@ // 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. #ifndef OTS_GLYF_H_ #define OTS_GLYF_H_ #include <new> -#include <utility> // std::pair +#include <utility> #include <vector> #include "ots.h" namespace ots { struct OpenTypeGLYF { std::vector<std::pair<const uint8_t*, size_t> > iov;
--- a/gfx/ots/src/gpos.cc +++ b/gfx/ots/src/gpos.cc @@ -2,18 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "gpos.h" #include <limits> #include <vector> -#include "gdef.h" -#include "gsub.h" #include "layout.h" #include "maxp.h" // GPOS - The Glyph Positioning Table // http://www.microsoft.com/typography/otspec/gpos.htm #define TABLE_NAME "GPOS" @@ -66,140 +64,142 @@ const ots::LookupSubtableParser::TypePar {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment}, {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment}, {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment}, {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning}, {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning}, {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning} }; -// TODO(bashi): Port Chromium's arraysize macro and use it instead of sizeof(). const ots::LookupSubtableParser kGposLookupSubtableParser = { - sizeof(kGposTypeParsers) / sizeof(kGposTypeParsers[0]), + arraysize(kGposTypeParsers), GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers }; // Shared Tables: ValueRecord, Anchor Table, and MarkArray -bool ParseValueRecord(ots::Buffer* subtable, const uint8_t *data, +bool ParseValueRecord(const ots::OpenTypeFile *file, + ots::Buffer* subtable, const uint8_t *data, const size_t length, const uint16_t value_format) { // Check existence of adjustment fields. for (unsigned i = 0; i < 4; ++i) { if ((value_format >> i) & 0x1) { // Just read the field since these fileds could take an arbitrary values. if (!subtable->Skip(2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read value reacord component"); } } } // Check existence of offsets to device table. for (unsigned i = 0; i < 4; ++i) { if ((value_format >> (i + 4)) & 0x1) { uint16_t offset = 0; if (!subtable->ReadU16(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read value record offset"); } if (offset) { // TODO(bashi): Is it possible that device tables locate before // this record? No fonts contain such offset AKAIF. if (offset >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Value record offset too high %d >= %ld", offset, length); } - if (!ots::ParseDeviceTable(data + offset, length - offset)) { - return OTS_FAILURE(); + if (!ots::ParseDeviceTable(file, data + offset, length - offset)) { + return OTS_FAILURE_MSG("Failed to parse device table in value record"); } } } } return true; } -bool ParseAnchorTable(const uint8_t *data, const size_t length) { +bool ParseAnchorTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length) { ots::Buffer subtable(data, length); uint16_t format = 0; // Read format and skip 2 2-byte fields that could be arbitrary values. if (!subtable.ReadU16(&format) || !subtable.Skip(4)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Faled to read anchor table"); } if (format == 0 || format > kMaxAnchorFormat) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad Anchor table format %d", format); } // Format 2 and 3 has additional fields. if (format == 2) { // Format 2 provides an index to a glyph contour point, which will take // arbitrary value. uint16_t anchor_point = 0; if (!subtable.ReadU16(&anchor_point)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read anchor point in format 2 Anchor Table"); } } else if (format == 3) { uint16_t offset_x_device = 0; uint16_t offset_y_device = 0; if (!subtable.ReadU16(&offset_x_device) || !subtable.ReadU16(&offset_y_device)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read device table offsets in format 3 anchor table"); } const unsigned format_end = static_cast<unsigned>(10); if (offset_x_device) { if (offset_x_device < format_end || offset_x_device >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad x device table offset %d", offset_x_device); } - if (!ots::ParseDeviceTable(data + offset_x_device, + if (!ots::ParseDeviceTable(file, data + offset_x_device, length - offset_x_device)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse device table in anchor table"); } } if (offset_y_device) { if (offset_y_device < format_end || offset_y_device >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad y device table offset %d", offset_y_device); } - if (!ots::ParseDeviceTable(data + offset_y_device, + if (!ots::ParseDeviceTable(file, data + offset_y_device, length - offset_y_device)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse device table in anchor table"); } } } return true; } -bool ParseMarkArrayTable(const uint8_t *data, const size_t length, +bool ParseMarkArrayTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t class_count) { ots::Buffer subtable(data, length); uint16_t mark_count = 0; if (!subtable.ReadU16(&mark_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read mark table length"); } // MarkRecord consists of 4-bytes. const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2; if (mark_records_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad mark table length"); } for (unsigned i = 0; i < mark_count; ++i) { uint16_t class_value = 0; uint16_t offset_mark_anchor = 0; if (!subtable.ReadU16(&class_value) || !subtable.ReadU16(&offset_mark_anchor)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read mark table %d", i); } // |class_value| may take arbitrary values including 0 here so we don't // check the value. if (offset_mark_anchor < mark_records_end || offset_mark_anchor >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad mark anchor offset %d for mark table %d", offset_mark_anchor, i); } - if (!ParseAnchorTable(data + offset_mark_anchor, + if (!ParseAnchorTable(file, data + offset_mark_anchor, length - offset_mark_anchor)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i); } } return true; } // Lookup Type 1: // Single Adjustment Positioning Subtable @@ -208,170 +208,173 @@ bool ParseSingleAdjustment(const ots::Op ots::Buffer subtable(data, length); uint16_t format = 0; uint16_t offset_coverage = 0; uint16_t value_format = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&value_format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read single adjustment information"); } if (format == 1) { // Format 1 exactly one value record. - if (!ParseValueRecord(&subtable, data, length, value_format)) { - return OTS_FAILURE(); + if (!ParseValueRecord(file, &subtable, data, length, value_format)) { + return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table"); } } else if (format == 2) { uint16_t value_count = 0; if (!subtable.ReadU16(&value_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table"); } for (unsigned i = 0; i < value_count; ++i) { - if (!ParseValueRecord(&subtable, data, length, value_format)) { - return OTS_FAILURE(); + if (!ParseValueRecord(file, &subtable, data, length, value_format)) { + return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i); } } } else { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad format %d in single adjustment table", format); } if (offset_coverage < subtable.offset() || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d in single adjustment table", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, file->maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table"); } return true; } -bool ParsePairSetTable(const uint8_t *data, const size_t length, +bool ParsePairSetTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t value_format1, const uint16_t value_format2, const uint16_t num_glyphs) { ots::Buffer subtable(data, length); uint16_t value_count = 0; if (!subtable.ReadU16(&value_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pair set table structure"); } for (unsigned i = 0; i < value_count; ++i) { // Check pair value record. uint16_t glyph_id = 0; if (!subtable.ReadU16(&glyph_id)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i); } if (glyph_id >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs); } - if (!ParseValueRecord(&subtable, data, length, value_format1)) { - return OTS_FAILURE(); + if (!ParseValueRecord(file, &subtable, data, length, value_format1)) { + return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table"); } - if (!ParseValueRecord(&subtable, data, length, value_format2)) { - return OTS_FAILURE(); + if (!ParseValueRecord(file, &subtable, data, length, value_format2)) { + return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table"); } } return true; } -bool ParsePairPosFormat1(const uint8_t *data, const size_t length, +bool ParsePairPosFormat1(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t value_format1, const uint16_t value_format2, const uint16_t num_glyphs) { ots::Buffer subtable(data, length); // Skip 8 bytes that are already read before. if (!subtable.Skip(8)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pair pos table structure"); } uint16_t pair_set_count = 0; if (!subtable.ReadU16(&pair_set_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pair pos set count"); } const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10; if (pair_pos_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad pair set length %d", pair_pos_end); } for (unsigned i = 0; i < pair_set_count; ++i) { uint16_t pair_set_offset = 0; if (!subtable.ReadU16(&pair_set_offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pair set offset for pair set %d", i); } if (pair_set_offset < pair_pos_end || pair_set_offset >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad pair set offset %d for pair set %d", pair_set_offset, i); } // Check pair set tables - if (!ParsePairSetTable(data + pair_set_offset, length - pair_set_offset, + if (!ParsePairSetTable(file, data + pair_set_offset, length - pair_set_offset, value_format1, value_format2, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse pair set table %d", i); } } return true; } -bool ParsePairPosFormat2(const uint8_t *data, const size_t length, +bool ParsePairPosFormat2(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t value_format1, const uint16_t value_format2, const uint16_t num_glyphs) { ots::Buffer subtable(data, length); // Skip 8 bytes that are already read before. if (!subtable.Skip(8)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pair pos format 2 structure"); } uint16_t offset_class_def1 = 0; uint16_t offset_class_def2 = 0; uint16_t class1_count = 0; uint16_t class2_count = 0; if (!subtable.ReadU16(&offset_class_def1) || !subtable.ReadU16(&offset_class_def2) || !subtable.ReadU16(&class1_count) || !subtable.ReadU16(&class2_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pair pos format 2 data"); } // 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(&subtable, data, length, + if (value_format1 && !ParseValueRecord(file, &subtable, data, length, value_format1)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i); } - if (value_format2 && !ParseValueRecord(&subtable, data, length, + if (value_format2 && !ParseValueRecord(file, &subtable, data, length, value_format2)) { - return OTS_FAILURE(); + 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(); + return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2); } - if (!ots::ParseClassDefTable(data + offset_class_def1, + if (!ots::ParseClassDefTable(file, data + offset_class_def1, length - offset_class_def1, num_glyphs, kMaxClassDefValue)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse class definition table 1"); } - if (!ots::ParseClassDefTable(data + offset_class_def2, + if (!ots::ParseClassDefTable(file, data + offset_class_def2, length - offset_class_def2, num_glyphs, kMaxClassDefValue)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse class definition table 2"); } return true; } // Lookup Type 2: // Pair Adjustment Positioning Subtable bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data, @@ -381,40 +384,40 @@ bool ParsePairAdjustment(const ots::Open uint16_t format = 0; uint16_t offset_coverage = 0; uint16_t value_format1 = 0; uint16_t value_format2 = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&value_format1) || !subtable.ReadU16(&value_format2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pair adjustment structure"); } if (format == 1) { - if (!ParsePairPosFormat1(data, length, value_format1, value_format2, + if (!ParsePairPosFormat1(file, data, length, value_format1, value_format2, file->maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse pair pos format 1"); } } else if (format == 2) { - if (!ParsePairPosFormat2(data, length, value_format1, value_format2, + if (!ParsePairPosFormat2(file, data, length, value_format1, value_format2, file->maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse pair format 2"); } } else { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad pos pair format %d", format); } if (offset_coverage < subtable.offset() || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad pair pos offset coverage %d", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, file->maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table"); } return true; } // Lookup Type 3 // Cursive Attachment Positioning Subtable bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data, @@ -422,125 +425,127 @@ bool ParseCursiveAttachment(const ots::O ots::Buffer subtable(data, length); uint16_t format = 0; uint16_t offset_coverage = 0; uint16_t entry_exit_count = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&entry_exit_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read cursive attachment structure"); } if (format != 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad cursive attachment format %d", format); } // Check entry exit records. const unsigned entry_exit_records_end = 2 * static_cast<unsigned>(entry_exit_count) + 6; if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad entry exit record end %d", entry_exit_records_end); } for (unsigned i = 0; i < entry_exit_count; ++i) { uint16_t offset_entry_anchor = 0; uint16_t offset_exit_anchor = 0; if (!subtable.ReadU16(&offset_entry_anchor) || !subtable.ReadU16(&offset_exit_anchor)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read entry exit record %d", i); } // These offsets could be NULL. if (offset_entry_anchor) { if (offset_entry_anchor < entry_exit_records_end || offset_entry_anchor >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i); } - if (!ParseAnchorTable(data + offset_entry_anchor, + if (!ParseAnchorTable(file, data + offset_entry_anchor, length - offset_entry_anchor)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse entry anchor table in entry exit record %d", i); } } if (offset_exit_anchor) { if (offset_exit_anchor < entry_exit_records_end || offset_exit_anchor >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i); } - if (!ParseAnchorTable(data + offset_exit_anchor, + if (!ParseAnchorTable(file, data + offset_exit_anchor, length - offset_exit_anchor)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse exit anchor table in entry exit record %d", i); } } } if (offset_coverage < subtable.offset() || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset in cursive attachment %d", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, file->maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment"); } return true; } -bool ParseAnchorArrayTable(const uint8_t *data, const size_t length, +bool ParseAnchorArrayTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t class_count) { ots::Buffer subtable(data, length); uint16_t record_count = 0; if (!subtable.ReadU16(&record_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read anchor array length"); } const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) * static_cast<unsigned>(class_count) + 2; if (anchor_array_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of anchor array %d", anchor_array_end); } for (unsigned i = 0; i < record_count; ++i) { for (unsigned j = 0; j < class_count; ++j) { uint16_t offset_record = 0; if (!subtable.ReadU16(&offset_record)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read anchor array record offset for class %d and record %d", j, i); } // |offset_record| could be NULL. if (offset_record) { if (offset_record < anchor_array_end || offset_record >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad record offset %d in class %d, record %d", offset_record, j, i); } - if (!ParseAnchorTable(data + offset_record, + if (!ParseAnchorTable(file, data + offset_record, length - offset_record)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse anchor table for class %d, record %d", j, i); } } } } return true; } -bool ParseLigatureArrayTable(const uint8_t *data, const size_t length, +bool ParseLigatureArrayTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t class_count) { ots::Buffer subtable(data, length); uint16_t ligature_count = 0; if (!subtable.ReadU16(&ligature_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read ligature count"); } for (unsigned i = 0; i < ligature_count; ++i) { uint16_t offset_ligature_attach = 0; if (!subtable.ReadU16(&offset_ligature_attach)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read ligature offset %d", i); } if (offset_ligature_attach < 2 || offset_ligature_attach >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad ligature attachment offset %d in ligature %d", offset_ligature_attach, i); } - if (!ParseAnchorArrayTable(data + offset_ligature_attach, + if (!ParseAnchorArrayTable(file, data + offset_ligature_attach, length - offset_ligature_attach, class_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse anchor table for ligature %d", i); } } return true; } // Common parser for Lookup Type 4, 5 and 6. bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file, const uint8_t *data, const size_t length, @@ -554,71 +559,71 @@ bool ParseMarkToAttachmentSubtables(cons uint16_t offset_mark_array = 0; uint16_t offset_type_specific_array = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage1) || !subtable.ReadU16(&offset_coverage2) || !subtable.ReadU16(&class_count) || !subtable.ReadU16(&offset_mark_array) || !subtable.ReadU16(&offset_type_specific_array)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read mark attachment subtable header"); } if (format != 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad mark attachment subtable format %d", format); } const unsigned header_end = static_cast<unsigned>(subtable.offset()); if (header_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end); } if (offset_coverage1 < header_end || offset_coverage1 >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1); } - if (!ots::ParseCoverageTable(data + offset_coverage1, + if (!ots::ParseCoverageTable(file, data + offset_coverage1, length - offset_coverage1, file->maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse converge 1 table"); } if (offset_coverage2 < header_end || offset_coverage2 >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2); } - if (!ots::ParseCoverageTable(data + offset_coverage2, + if (!ots::ParseCoverageTable(file, data + offset_coverage2, length - offset_coverage2, file->maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table 2"); } if (offset_mark_array < header_end || offset_mark_array >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array); } - if (!ParseMarkArrayTable(data + offset_mark_array, + if (!ParseMarkArrayTable(file, data + offset_mark_array, length - offset_mark_array, class_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse mark array"); } if (offset_type_specific_array < header_end || offset_type_specific_array >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad type specific array offset %d", offset_type_specific_array); } if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT || type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) { - if (!ParseAnchorArrayTable(data + offset_type_specific_array, + if (!ParseAnchorArrayTable(file, data + offset_type_specific_array, length - offset_type_specific_array, class_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse anchor array"); } } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) { - if (!ParseLigatureArrayTable(data + offset_type_specific_array, + if (!ParseLigatureArrayTable(file, data + offset_type_specific_array, length - offset_type_specific_array, class_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse ligature array"); } } else { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad attachment type %d", type); } return true; } // Lookup Type 4: // MarkToBase Attachment Positioning Subtable bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file, @@ -642,44 +647,44 @@ bool ParseMarkToMarkAttachment(const ots return ParseMarkToAttachmentSubtables(file, data, length, GPOS_TYPE_MARK_TO_MARK_ATTACHMENT); } // Lookup Type 7: // Contextual Positioning Subtables bool ParseContextPositioning(const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { - return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs, + return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs, file->gpos->num_lookups); } // Lookup Type 8: // Chaining Contexual Positioning Subtable bool ParseChainedContextPositioning(const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { - return ots::ParseChainingContextSubtable(data, length, + return ots::ParseChainingContextSubtable(file, data, length, file->maxp->num_glyphs, file->gpos->num_lookups); } // Lookup Type 9: // Extension Positioning bool ParseExtensionPositioning(const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { return ots::ParseExtensionSubtable(file, data, length, &kGposLookupSubtableParser); } } // namespace -#define DROP_THIS_TABLE \ +#define DROP_THIS_TABLE(msg_) \ do { \ file->gpos->data = 0; \ file->gpos->length = 0; \ - OTS_FAILURE_MSG("OpenType layout data discarded"); \ + OTS_FAILURE_MSG(msg_ ", table discarded"); \ } while (0) 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 @@ -723,94 +728,84 @@ namespace ots { // Padauk.ttf // // # Contour point indexes aren't sorted // Arial Unicode.ttf bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { // Parsing GPOS table requires num_glyphs which is contained in maxp table. if (!file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("missing maxp table needed in GPOS"); } Buffer table(data, length); OpenTypeGPOS *gpos = new OpenTypeGPOS; file->gpos = gpos; uint32_t version = 0; uint16_t offset_script_list = 0; uint16_t offset_feature_list = 0; uint16_t offset_lookup_list = 0; if (!table.ReadU32(&version) || !table.ReadU16(&offset_script_list) || !table.ReadU16(&offset_feature_list) || !table.ReadU16(&offset_lookup_list)) { - OTS_WARNING("incomplete GPOS table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Incomplete table"); return true; } if (version != 0x00010000) { - OTS_WARNING("bad GPOS version"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Bad version"); return true; } if ((offset_script_list < kGposHeaderSize || offset_script_list >= length) || (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) || (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length)) { - OTS_WARNING("bad offset in GPOS header"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Bad offset in table header"); return true; } if (!ParseLookupListTable(file, data + offset_lookup_list, length - offset_lookup_list, &kGposLookupSubtableParser, &gpos->num_lookups)) { - OTS_WARNING("failed to parse lookup list table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Failed to parse lookup list table"); return true; } uint16_t num_features = 0; - if (!ParseFeatureListTable(data + offset_feature_list, + if (!ParseFeatureListTable(file, data + offset_feature_list, length - offset_feature_list, gpos->num_lookups, &num_features)) { - OTS_WARNING("failed to parse feature list table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Failed to parse feature list table"); return true; } - if (!ParseScriptListTable(data + offset_script_list, + if (!ParseScriptListTable(file, data + offset_script_list, length - offset_script_list, num_features)) { - OTS_WARNING("failed to parse script list table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Failed to parse script list table"); return true; } gpos->data = data; gpos->length = length; return true; } bool ots_gpos_should_serialise(OpenTypeFile *file) { - const bool needed_tables_dropped = - (file->gdef && file->gdef->data == NULL) || - (file->gsub && file->gsub->data == NULL); - return file->gpos != NULL && file->gpos->data != NULL && - !needed_tables_dropped; + return file->gpos != NULL && file->gpos->data != NULL; } bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) { if (!out->Write(file->gpos->data, file->gpos->length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write GPOS table"); } return true; } void ots_gpos_free(OpenTypeFile *file) { delete file->gpos; }
deleted file mode 100644 --- a/gfx/ots/src/graphite.cc +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2010 Mozilla Foundation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "graphite.h" - -// Support for preserving (NOT sanitizing) the Graphite tables - -namespace ots { - -// 'Silf' -bool ots_silf_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { - Buffer table(data, length); - - OpenTypeSILF *silf = new OpenTypeSILF; - file->silf = silf; - - if (!table.Skip(length)) { - return OTS_FAILURE(); - } - - silf->data = data; - silf->length = length; - return true; -} - -bool ots_silf_should_serialise(OpenTypeFile *file) { - return file->preserve_graphite && file->silf; -} - -bool ots_silf_serialise(OTSStream *out, OpenTypeFile *file) { - const OpenTypeSILF *silf = file->silf; - - if (!out->Write(silf->data, silf->length)) { - return OTS_FAILURE(); - } - - return true; -} - -void ots_silf_free(OpenTypeFile *file) { - delete file->silf; -} - -// 'Sill' -bool ots_sill_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { - Buffer table(data, length); - - OpenTypeSILL *sill = new OpenTypeSILL; - file->sill = sill; - - if (!table.Skip(length)) { - return OTS_FAILURE(); - } - - sill->data = data; - sill->length = length; - return true; -} - -bool ots_sill_should_serialise(OpenTypeFile *file) { - return file->preserve_graphite && file->sill; -} - -bool ots_sill_serialise(OTSStream *out, OpenTypeFile *file) { - const OpenTypeSILL *sill = file->sill; - - if (!out->Write(sill->data, sill->length)) { - return OTS_FAILURE(); - } - - return true; -} - -void ots_sill_free(OpenTypeFile *file) { - delete file->sill; -} - -// 'Gloc' -bool ots_gloc_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { - Buffer table(data, length); - - OpenTypeGLOC *gloc = new OpenTypeGLOC; - file->gloc = gloc; - - if (!table.Skip(length)) { - return OTS_FAILURE(); - } - - gloc->data = data; - gloc->length = length; - return true; -} - -bool ots_gloc_should_serialise(OpenTypeFile *file) { - return file->preserve_graphite && file->gloc; -} - -bool ots_gloc_serialise(OTSStream *out, OpenTypeFile *file) { - const OpenTypeGLOC *gloc = file->gloc; - - if (!out->Write(gloc->data, gloc->length)) { - return OTS_FAILURE(); - } - - return true; -} - -void ots_gloc_free(OpenTypeFile *file) { - delete file->gloc; -} - -// 'Glat' -bool ots_glat_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { - Buffer table(data, length); - - OpenTypeGLAT *glat = new OpenTypeGLAT; - file->glat = glat; - - if (!table.Skip(length)) { - return OTS_FAILURE(); - } - - glat->data = data; - glat->length = length; - return true; -} - -bool ots_glat_should_serialise(OpenTypeFile *file) { - return file->preserve_graphite && file->glat; -} - -bool ots_glat_serialise(OTSStream *out, OpenTypeFile *file) { - const OpenTypeGLAT *glat = file->glat; - - if (!out->Write(glat->data, glat->length)) { - return OTS_FAILURE(); - } - - return true; -} - -void ots_glat_free(OpenTypeFile *file) { - delete file->glat; -} - -// 'Feat' -bool ots_feat_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { - Buffer table(data, length); - - OpenTypeFEAT *feat = new OpenTypeFEAT; - file->feat = feat; - - if (!table.Skip(length)) { - return OTS_FAILURE(); - } - - feat->data = data; - feat->length = length; - return true; -} - -bool ots_feat_should_serialise(OpenTypeFile *file) { - return file->preserve_graphite && file->feat; -} - -bool ots_feat_serialise(OTSStream *out, OpenTypeFile *file) { - const OpenTypeFEAT *feat = file->feat; - - if (!out->Write(feat->data, feat->length)) { - return OTS_FAILURE(); - } - - return true; -} - -void ots_feat_free(OpenTypeFile *file) { - delete file->feat; -} - -} // namespace ots
deleted file mode 100644 --- a/gfx/ots/src/graphite.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2010 Mozilla Foundation. 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 "ots.h" - -namespace ots { - -struct OpenTypeSILF { - const uint8_t *data; - uint32_t length; -}; - -struct OpenTypeSILL { - const uint8_t *data; - uint32_t length; -}; - -struct OpenTypeGLOC { - const uint8_t *data; - uint32_t length; -}; - -struct OpenTypeGLAT { - const uint8_t *data; - uint32_t length; -}; - -struct OpenTypeFEAT { - const uint8_t *data; - uint32_t length; -}; - -} // namespace ots - -#endif // OTS_GRAPHITE_H_
--- a/gfx/ots/src/gsub.cc +++ b/gfx/ots/src/gsub.cc @@ -2,30 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "gsub.h" #include <limits> #include <vector> -#include "gdef.h" -#include "gpos.h" #include "layout.h" #include "maxp.h" // GSUB - The Glyph Substitution Table // http://www.microsoft.com/typography/otspec/gsub.htm #define TABLE_NAME "GSUB" namespace { // The GSUB header size -const size_t kGsubHeaderSize = 8; +const size_t kGsubHeaderSize = 4 + 3 * 2; enum GSUB_TYPE { GSUB_TYPE_SINGLE = 1, GSUB_TYPE_MULTIPLE = 2, GSUB_TYPE_ALTERNATE = 3, GSUB_TYPE_LIGATURE = 4, GSUB_TYPE_CONTEXT = 5, GSUB_TYPE_CHANGING_CONTEXT = 6, @@ -60,98 +58,97 @@ const ots::LookupSubtableParser::TypePar {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution}, {GSUB_TYPE_CONTEXT, ParseContextSubstitution}, {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution}, {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution}, {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE, ParseReverseChainingContextSingleSubstitution} }; -// TODO(bashi): Port Chromium's arraysize macro and use it instead of sizeof(). const ots::LookupSubtableParser kGsubLookupSubtableParser = { - sizeof(kGsubTypeParsers) / sizeof(kGsubTypeParsers[0]), + arraysize(kGsubTypeParsers), GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers }; // Lookup Type 1: // Single Substitution Subtable bool ParseSingleSubstitution(const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { ots::Buffer subtable(data, length); uint16_t format = 0; uint16_t offset_coverage = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read single subst table header"); } const uint16_t num_glyphs = file->maxp->num_glyphs; if (format == 1) { // Parse SingleSubstFormat1 int16_t delta_glyph_id = 0; if (!subtable.ReadS16(&delta_glyph_id)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table"); } if (std::abs(delta_glyph_id) >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id); } } else if (format == 2) { // Parse SingleSubstFormat2 uint16_t glyph_count = 0; if (!subtable.ReadU16(&glyph_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph cound in format 2 single subst table"); } if (glyph_count > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs); } for (unsigned i = 0; i < glyph_count; ++i) { uint16_t substitute = 0; if (!subtable.ReadU16(&substitute)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read substitution %d in format 2 single subst table", i); } if (substitute >= num_glyphs) { - OTS_WARNING("too large substitute: %u", substitute); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("too large substitute: %u", substitute); } } } else { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad single subst table format %d", format); } if (offset_coverage < subtable.offset() || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %x", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table"); } return true; } -bool ParseSequenceTable(const uint8_t *data, const size_t length, +bool ParseSequenceTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs) { ots::Buffer subtable(data, length); uint16_t glyph_count = 0; if (!subtable.ReadU16(&glyph_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph count in sequence table"); } if (glyph_count > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs); } for (unsigned i = 0; i < glyph_count; ++i) { uint16_t substitute = 0; if (!subtable.ReadU16(&substitute)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failedt o read substitution %d in sequence table", i); } if (substitute >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad subsitution (%d) %d > %d", i, substitute, num_glyphs); } } return true; } // Lookup Type 2: // Multiple Substitution Subtable @@ -161,73 +158,73 @@ bool ParseMutipleSubstitution(const ots: uint16_t format = 0; uint16_t offset_coverage = 0; uint16_t sequence_count = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&sequence_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read header of multiple subst table"); } if (format != 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad multiple subst table format %d", format); } const uint16_t num_glyphs = file->maxp->num_glyphs; const unsigned sequence_end = static_cast<unsigned>(6) + sequence_count * 2; if (sequence_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad segence end %d, in multiple subst", sequence_end); } for (unsigned i = 0; i < sequence_count; ++i) { uint16_t offset_sequence = 0; if (!subtable.ReadU16(&offset_sequence)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read sequence offset for sequence %d", i); } if (offset_sequence < sequence_end || offset_sequence >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad sequence offset %d for sequence %d", offset_sequence, i); } - if (!ParseSequenceTable(data + offset_sequence, length - offset_sequence, + if (!ParseSequenceTable(file, data + offset_sequence, length - offset_sequence, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse sequence table %d", i); } } if (offset_coverage < sequence_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table"); } return true; } -bool ParseAlternateSetTable(const uint8_t *data, const size_t length, +bool ParseAlternateSetTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs) { ots::Buffer subtable(data, length); uint16_t glyph_count = 0; if (!subtable.ReadU16(&glyph_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read alternate set header"); } if (glyph_count > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs); } for (unsigned i = 0; i < glyph_count; ++i) { uint16_t alternate = 0; if (!subtable.ReadU16(&alternate)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read alternate %d", i); } if (alternate >= num_glyphs) { - OTS_WARNING("too large alternate: %u", alternate); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Too large alternate: %u", alternate); } } return true; } // Lookup Type 3: // Alternate Substitution Subtable bool ParseAlternateSubstitution(const ots::OpenTypeFile *file, @@ -236,113 +233,114 @@ bool ParseAlternateSubstitution(const ot uint16_t format = 0; uint16_t offset_coverage = 0; uint16_t alternate_set_count = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&alternate_set_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read alternate subst header"); } if (format != 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad alternate subst table format %d", format); } const uint16_t num_glyphs = file->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()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end); } for (unsigned i = 0; i < alternate_set_count; ++i) { uint16_t offset_alternate_set = 0; if (!subtable.ReadU16(&offset_alternate_set)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read alternate set offset for set %d", i); } if (offset_alternate_set < alternate_set_end || offset_alternate_set >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad alternate set offset %d for set %d", offset_alternate_set, i); } - if (!ParseAlternateSetTable(data + offset_alternate_set, + if (!ParseAlternateSetTable(file, data + offset_alternate_set, length - offset_alternate_set, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse alternate set"); } } if (offset_coverage < alternate_set_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table"); } return true; } -bool ParseLigatureTable(const uint8_t *data, const size_t length, +bool ParseLigatureTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs) { ots::Buffer subtable(data, length); uint16_t lig_glyph = 0; uint16_t comp_count = 0; if (!subtable.ReadU16(&lig_glyph) || !subtable.ReadU16(&comp_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read ligatuer table header"); } if (lig_glyph >= num_glyphs) { - OTS_WARNING("too large lig_glyph: %u", lig_glyph); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph); } if (comp_count == 0 || comp_count > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad component count of %d", comp_count); } for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) { uint16_t component = 0; if (!subtable.ReadU16(&component)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read ligature component %d", i); } if (component >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component); } } return true; } -bool ParseLigatureSetTable(const uint8_t *data, const size_t length, +bool ParseLigatureSetTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs) { ots::Buffer subtable(data, length); uint16_t ligature_count = 0; if (!subtable.ReadU16(&ligature_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read ligature count in ligature set"); } const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2; if (ligature_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end); } for (unsigned i = 0; i < ligature_count; ++i) { uint16_t offset_ligature = 0; if (!subtable.ReadU16(&offset_ligature)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read ligature offset %d", i); } if (offset_ligature < ligature_end || offset_ligature >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i); } - if (!ParseLigatureTable(data + offset_ligature, length - offset_ligature, + if (!ParseLigatureTable(file, data + offset_ligature, length - offset_ligature, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse ligature %d", i); } } return true; } // Lookup Type 4: // Ligature Substitution Subtable @@ -352,69 +350,69 @@ bool ParseLigatureSubstitution(const ots uint16_t format = 0; uint16_t offset_coverage = 0; uint16_t lig_set_count = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&lig_set_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read ligature substitution header"); } if (format != 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format); } const uint16_t num_glyphs = file->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()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end); } for (unsigned i = 0; i < lig_set_count; ++i) { uint16_t offset_ligature_set = 0; if (!subtable.ReadU16(&offset_ligature_set)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read ligature set offset %d", i); } if (offset_ligature_set < ligature_set_end || offset_ligature_set >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad ligature set offset %d for set %d", offset_ligature_set, i); } - if (!ParseLigatureSetTable(data + offset_ligature_set, + if (!ParseLigatureSetTable(file, data + offset_ligature_set, length - offset_ligature_set, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse ligature set %d", i); } } if (offset_coverage < ligature_set_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table"); } return true; } // Lookup Type 5: // Contextual Substitution Subtable bool ParseContextSubstitution(const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { - return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs, + return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs, file->gsub->num_lookups); } // Lookup Type 6: // Chaining Contextual Substitution Subtable bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { - return ots::ParseChainingContextSubtable(data, length, + return ots::ParseChainingContextSubtable(file, data, length, file->maxp->num_glyphs, file->gsub->num_lookups); } // Lookup Type 7: // Extension Substition bool ParseExtensionSubstitution(const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { @@ -428,118 +426,118 @@ bool ParseReverseChainingContextSingleSu const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { ots::Buffer subtable(data, length); uint16_t format = 0; uint16_t offset_coverage = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&offset_coverage)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read reverse chaining header"); } const uint16_t num_glyphs = file->maxp->num_glyphs; uint16_t backtrack_glyph_count = 0; if (!subtable.ReadU16(&backtrack_glyph_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table"); } if (backtrack_glyph_count > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad backtrack glyph count of %d", backtrack_glyph_count); } std::vector<uint16_t> offsets_backtrack; offsets_backtrack.reserve(backtrack_glyph_count); for (unsigned i = 0; i < backtrack_glyph_count; ++i) { uint16_t offset = 0; if (!subtable.ReadU16(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read backtrack offset %d", i); } offsets_backtrack.push_back(offset); } uint16_t lookahead_glyph_count = 0; if (!subtable.ReadU16(&lookahead_glyph_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read look ahead glyph count"); } if (lookahead_glyph_count > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad look ahead glyph count %d", lookahead_glyph_count); } std::vector<uint16_t> offsets_lookahead; offsets_lookahead.reserve(lookahead_glyph_count); for (unsigned i = 0; i < lookahead_glyph_count; ++i) { uint16_t offset = 0; if (!subtable.ReadU16(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read look ahead offset %d", i); } offsets_lookahead.push_back(offset); } uint16_t glyph_count = 0; if (!subtable.ReadU16(&glyph_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read glyph count in reverse chaining table"); } if (glyph_count > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph count of %d", glyph_count); } for (unsigned i = 0; i < glyph_count; ++i) { uint16_t substitute = 0; if (!subtable.ReadU16(&substitute)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read substitution %d reverse chaining table", i); } if (substitute >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i); } } const unsigned substitute_end = static_cast<unsigned>(10) + (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2; if (substitute_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad substitute end offset in reverse chaining table"); } if (offset_coverage < substitute_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d in reverse chaining table", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table in reverse chaining table"); } for (unsigned i = 0; i < backtrack_glyph_count; ++i) { if (offsets_backtrack[i] < substitute_end || offsets_backtrack[i] >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i); } - if (!ots::ParseCoverageTable(data + offsets_backtrack[i], + if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i], length - offsets_backtrack[i], num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table for backtrack %d in reverse chaining table", i); } } for (unsigned i = 0; i < lookahead_glyph_count; ++i) { if (offsets_lookahead[i] < substitute_end || offsets_lookahead[i] >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i); } - if (!ots::ParseCoverageTable(data + offsets_lookahead[i], + if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i], length - offsets_lookahead[i], num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in reverse chaining table", i); } } return true; } } // namespace -#define DROP_THIS_TABLE \ +#define DROP_THIS_TABLE(msg_) \ do { \ file->gsub->data = 0; \ file->gsub->length = 0; \ - OTS_FAILURE_MSG("OpenType layout data discarded"); \ + OTS_FAILURE_MSG(msg_ ", table discarded"); \ } while (0) 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) @@ -587,94 +585,84 @@ namespace ots { // KacstQurn.ttf // KacstDigital.ttf // KacstBook.ttf // KacstFarsi.ttf bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { // Parsing gsub table requires |file->maxp->num_glyphs| if (!file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB"); } Buffer table(data, length); OpenTypeGSUB *gsub = new OpenTypeGSUB; file->gsub = gsub; uint32_t version = 0; uint16_t offset_script_list = 0; uint16_t offset_feature_list = 0; uint16_t offset_lookup_list = 0; if (!table.ReadU32(&version) || !table.ReadU16(&offset_script_list) || !table.ReadU16(&offset_feature_list) || !table.ReadU16(&offset_lookup_list)) { - OTS_WARNING("incomplete GSUB table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Incomplete table"); return true; } if (version != 0x00010000) { - OTS_WARNING("bad GSUB version"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Bad version"); return true; } if ((offset_script_list < kGsubHeaderSize || offset_script_list >= length) || (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) || (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length)) { - OTS_WARNING("bad offset in GSUB header"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Bad offset in table header"); return true; } if (!ParseLookupListTable(file, data + offset_lookup_list, length - offset_lookup_list, &kGsubLookupSubtableParser, &gsub->num_lookups)) { - OTS_WARNING("failed to parse lookup list table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Failed to parse lookup list table"); return true; } uint16_t num_features = 0; - if (!ParseFeatureListTable(data + offset_feature_list, + if (!ParseFeatureListTable(file, data + offset_feature_list, length - offset_feature_list, gsub->num_lookups, &num_features)) { - OTS_WARNING("failed to parse feature list table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Failed to parse feature list table"); return true; } - if (!ParseScriptListTable(data + offset_script_list, + if (!ParseScriptListTable(file, data + offset_script_list, length - offset_script_list, num_features)) { - OTS_WARNING("failed to parse script list table"); - DROP_THIS_TABLE; + DROP_THIS_TABLE("Failed to parse script list table"); return true; } gsub->data = data; gsub->length = length; return true; } bool ots_gsub_should_serialise(OpenTypeFile *file) { - const bool needed_tables_dropped = - (file->gdef && file->gdef->data == NULL) || - (file->gpos && file->gpos->data == NULL); - return file->gsub != NULL && file->gsub->data != NULL - && !needed_tables_dropped; + return file->gsub != NULL && file->gsub->data != NULL; } bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) { if (!out->Write(file->gsub->data, file->gsub->length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write GSUB table"); } return true; } void ots_gsub_free(OpenTypeFile *file) { delete file->gsub; }
--- a/gfx/ots/src/hdmx.cc +++ b/gfx/ots/src/hdmx.cc @@ -2,45 +2,51 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "hdmx.h" #include "head.h" #include "maxp.h" // hdmx - Horizontal Device Metrics -// http://www.microsoft.com/opentype/otspec/hdmx.htm +// http://www.microsoft.com/typography/otspec/hdmx.htm + +#define TABLE_NAME "hdmx" #define DROP_THIS_TABLE \ - do { delete file->hdmx; file->hdmx = 0; } while (0) + do { \ + delete file->hdmx; \ + file->hdmx = 0; \ + OTS_FAILURE_MSG("Table discarded"); \ + } while (0) namespace ots { bool ots_hdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); file->hdmx = new OpenTypeHDMX; OpenTypeHDMX * const hdmx = file->hdmx; if (!file->head || !file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Missing maxp or head tables in font, needed by hdmx"); } if ((file->head->flags & 0x14) == 0) { // http://www.microsoft.com/typography/otspec/recom.htm OTS_WARNING("the table should not be present when bit 2 and 4 of the " "head->flags are not set"); DROP_THIS_TABLE; return true; } int16_t num_recs; if (!table.ReadU16(&hdmx->version) || !table.ReadS16(&num_recs) || !table.ReadS32(&hdmx->size_device_record)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read hdmx header"); } if (hdmx->version != 0) { OTS_WARNING("bad version: %u", hdmx->version); DROP_THIS_TABLE; return true; } if (num_recs <= 0) { OTS_WARNING("bad num_recs: %d", num_recs); @@ -51,48 +57,48 @@ bool ots_hdmx_parse(OpenTypeFile *file, if (hdmx->size_device_record < actual_size_device_record) { OTS_WARNING("bad hdmx->size_device_record: %d", hdmx->size_device_record); DROP_THIS_TABLE; return true; } hdmx->pad_len = hdmx->size_device_record - actual_size_device_record; if (hdmx->pad_len > 3) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad padding %d", hdmx->pad_len); } uint8_t last_pixel_size = 0; hdmx->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(); + return OTS_FAILURE_MSG("Failed to read hdmx record %d", i); } if ((i != 0) && (rec.pixel_size <= last_pixel_size)) { OTS_WARNING("records are not sorted"); DROP_THIS_TABLE; return true; } last_pixel_size = rec.pixel_size; rec.widths.reserve(file->maxp->num_glyphs); for (unsigned j = 0; j < file->maxp->num_glyphs; ++j) { uint8_t width; if (!table.ReadU8(&width)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph width %d in record %d", j, i); } rec.widths.push_back(width); } if ((hdmx->pad_len > 0) && !table.Skip(hdmx->pad_len)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip padding %d", hdmx->pad_len); } hdmx->records.push_back(rec); } return true; } @@ -103,29 +109,29 @@ bool ots_hdmx_should_serialise(OpenTypeF } bool ots_hdmx_serialise(OTSStream *out, OpenTypeFile *file) { OpenTypeHDMX * const hdmx = file->hdmx; if (!out->WriteU16(hdmx->version) || !out->WriteS16(hdmx->records.size()) || !out->WriteS32(hdmx->size_device_record)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write hdmx header"); } for (unsigned i = 0; i < hdmx->records.size(); ++i) { const OpenTypeHDMXDeviceRecord& rec = hdmx->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(); + return OTS_FAILURE_MSG("Failed to write hdmx record %d", i); } if ((hdmx->pad_len > 0) && !out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write hdmx padding of length %d", hdmx->pad_len); } } return true; } void ots_hdmx_free(OpenTypeFile *file) { delete file->hdmx;
--- a/gfx/ots/src/head.cc +++ b/gfx/ots/src/head.cc @@ -2,118 +2,120 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "head.h" #include <cstring> // head - Font Header -// http://www.microsoft.com/opentype/otspec/head.htm +// http://www.microsoft.com/typography/otspec/head.htm + +#define TABLE_NAME "head" namespace ots { bool ots_head_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); file->head = new OpenTypeHEAD; uint32_t version = 0; if (!table.ReadU32(&version) || !table.ReadU32(&file->head->revision)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read head header"); } if (version >> 16 != 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad head table version of %d", version); } // Skip the checksum adjustment if (!table.Skip(4)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read checksum"); } uint32_t magic; if (!table.ReadTag(&magic) || std::memcmp(&magic, "\x5F\x0F\x3C\xF5", 4)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read font magic number"); } if (!table.ReadU16(&file->head->flags)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read head flags"); } // We allow bits 0..4, 11..13 file->head->flags &= 0x381f; if (!table.ReadU16(&file->head->ppem)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pixels per em"); } // ppem must be in range if (file->head->ppem < 16 || file->head->ppem > 16384) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad ppm of %d", file->head->ppem); } // 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 ((file->head->ppem - 1) & file->head->ppem) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("ppm not a power of two: %d", file->head->ppem); } #endif if (!table.ReadR64(&file->head->created) || !table.ReadR64(&file->head->modified)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't read font dates"); } if (!table.ReadS16(&file->head->xmin) || !table.ReadS16(&file->head->ymin) || !table.ReadS16(&file->head->xmax) || !table.ReadS16(&file->head->ymax)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read font bounding box"); } if (file->head->xmin > file->head->xmax) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad x dimension in the font bounding box (%d, %d)", file->head->xmin, file->head->xmax); } if (file->head->ymin > file->head->ymax) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad y dimension in the font bounding box (%d, %d)", file->head->ymin, file->head->ymax); } if (!table.ReadU16(&file->head->mac_style)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read font style"); } // We allow bits 0..6 file->head->mac_style &= 0x7f; if (!table.ReadU16(&file->head->min_ppem)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read font minimum ppm"); } // We don't care about the font direction hint if (!table.Skip(2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip font direction hint"); } if (!table.ReadS16(&file->head->index_to_loc_format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read index to loc format"); } if (file->head->index_to_loc_format < 0 || file->head->index_to_loc_format > 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad index to loc format %d", file->head->index_to_loc_format); } int16_t glyph_data_format; if (!table.ReadS16(&glyph_data_format) || glyph_data_format) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph data format"); } return true; } bool ots_head_should_serialise(OpenTypeFile *file) { return file->head != NULL; } @@ -131,17 +133,17 @@ bool ots_head_serialise(OTSStream *out, !out->WriteS16(file->head->ymin) || !out->WriteS16(file->head->xmax) || !out->WriteS16(file->head->ymax) || !out->WriteU16(file->head->mac_style) || !out->WriteU16(file->head->min_ppem) || !out->WriteS16(2) || !out->WriteS16(file->head->index_to_loc_format) || !out->WriteS16(0)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write head table"); } return true; } void ots_head_free(OpenTypeFile *file) { delete file->head; }
--- a/gfx/ots/src/hhea.cc +++ b/gfx/ots/src/hhea.cc @@ -3,46 +3,48 @@ // found in the LICENSE file. #include "hhea.h" #include "head.h" #include "maxp.h" // hhea - Horizontal Header -// http://www.microsoft.com/opentype/otspec/hhea.htm +// http://www.microsoft.com/typography/otspec/hhea.htm + +#define TABLE_NAME "hhea" namespace ots { bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeHHEA *hhea = new OpenTypeHHEA; file->hhea = hhea; if (!table.ReadU32(&hhea->header.version)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read hhea version"); } if (hhea->header.version >> 16 != 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad hhea version of %d", hhea->header.version); } if (!ParseMetricsHeader(file, &table, &hhea->header)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse horizontal metrics"); } return true; } bool ots_hhea_should_serialise(OpenTypeFile *file) { return file->hhea != NULL; } bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) { - if (!SerialiseMetricsHeader(out, &file->hhea->header)) { - return OTS_FAILURE(); + if (!SerialiseMetricsHeader(file, out, &file->hhea->header)) { + return OTS_FAILURE_MSG("Failed to serialise horizontal metrics"); } return true; } void ots_hhea_free(OpenTypeFile *file) { delete file->hhea; }
--- a/gfx/ots/src/hmtx.cc +++ b/gfx/ots/src/hmtx.cc @@ -3,44 +3,46 @@ // found in the LICENSE file. #include "hmtx.h" #include "hhea.h" #include "maxp.h" // hmtx - Horizontal Metrics -// http://www.microsoft.com/opentype/otspec/hmtx.htm +// http://www.microsoft.com/typography/otspec/hmtx.htm + +#define TABLE_NAME "hmtx" namespace ots { bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeHMTX *hmtx = new OpenTypeHMTX; file->hmtx = hmtx; if (!file->hhea || !file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Missing hhea or maxp tables in font, needed by hmtx"); } - if (!ParseMetricsTable(&table, file->maxp->num_glyphs, + if (!ParseMetricsTable(file, &table, file->maxp->num_glyphs, &file->hhea->header, &hmtx->metrics)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse hmtx metrics"); } return true; } bool ots_hmtx_should_serialise(OpenTypeFile *file) { return file->hmtx != NULL; } bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) { - if (!SerialiseMetricsTable(out, &file->hmtx->metrics)) { - return OTS_FAILURE(); + if (!SerialiseMetricsTable(file, out, &file->hmtx->metrics)) { + return OTS_FAILURE_MSG("Failed to serialise htmx metrics"); } return true; } void ots_hmtx_free(OpenTypeFile *file) { delete file->hmtx; }
--- a/gfx/ots/src/kern.cc +++ b/gfx/ots/src/kern.cc @@ -1,32 +1,38 @@ // 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. #include "kern.h" // kern - Kerning -// http://www.microsoft.com/opentype/otspec/kern.htm +// http://www.microsoft.com/typography/otspec/kern.htm + +#define TABLE_NAME "kern" #define DROP_THIS_TABLE \ - do { delete file->kern; file->kern = 0; } while (0) + do { \ + delete file->kern; \ + file->kern = 0; \ + OTS_FAILURE_MSG("Table discarded"); \ + } while (0) namespace ots { bool ots_kern_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeKERN *kern = new OpenTypeKERN; file->kern = kern; uint16_t num_tables = 0; if (!table.ReadU16(&kern->version) || !table.ReadU16(&num_tables)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read kern header"); } if (kern->version > 0) { DROP_THIS_TABLE; return true; } if (num_tables == 0) { @@ -37,31 +43,31 @@ bool ots_kern_parse(OpenTypeFile *file, kern->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(); + return OTS_FAILURE_MSG("Failed to read kern subtable %d header", i); } if (subtable.version > 0) { OTS_WARNING("Bad subtable version: %d", subtable.version); continue; } const size_t current_offset = table.offset(); if (current_offset - 4 + sub_length > length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad kern subtable %d offset %ld", i, current_offset); } if (!table.ReadU16(&subtable.coverage)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Cailed to read kern subtable %d coverage", i); } if (!(subtable.coverage & 0x1)) { OTS_WARNING( "We don't support vertical data as the renderer doesn't support it."); continue; } if (subtable.coverage & 0xF0) { @@ -76,17 +82,17 @@ bool ots_kern_parse(OpenTypeFile *file, } // Parse the format 0 field. uint16_t num_pairs = 0; if (!table.ReadU16(&num_pairs) || !table.ReadU16(&subtable.search_range) || !table.ReadU16(&subtable.entry_selector) || !table.ReadU16(&subtable.range_shift)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read kern subtable %d format 0 fields", i); } if (!num_pairs) { OTS_WARNING("Zero length subtable is found."); DROP_THIS_TABLE; return true; } @@ -104,34 +110,34 @@ bool ots_kern_parse(OpenTypeFile *file, ++max_pow2; } const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize; if (subtable.search_range != expected_search_range) { OTS_WARNING("bad search range"); subtable.search_range = expected_search_range; } if (subtable.entry_selector != max_pow2) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad subtable %d entry selector %d", i, subtable.entry_selector); } const uint32_t expected_range_shift = kFormat0PairSize * num_pairs - subtable.search_range; if (subtable.range_shift != expected_range_shift) { OTS_WARNING("bad range shift"); subtable.range_shift = expected_range_shift; } // Read kerning pairs. subtable.pairs.reserve(num_pairs); uint32_t last_pair = 0; for (unsigned j = 0; j < num_pairs; ++j) { OpenTypeKERNFormat0Pair kerning_pair; if (!table.ReadU16(&kerning_pair.left) || !table.ReadU16(&kerning_pair.right) || !table.ReadS16(&kerning_pair.value)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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) { OTS_WARNING("Kerning pairs are not sorted."); // Many free fonts don't follow this rule, so we don't call OTS_FAILURE // in order to support these fonts. DROP_THIS_TABLE; @@ -158,35 +164,35 @@ bool ots_kern_should_serialise(OpenTypeF return file->kern != NULL; } bool ots_kern_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeKERN *kern = file->kern; if (!out->WriteU16(kern->version) || !out->WriteU16(kern->subtables.size())) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't write kern table header"); } for (unsigned i = 0; i < kern->subtables.size(); ++i) { const uint16_t length = 14 + (6 * kern->subtables[i].pairs.size()); if (!out->WriteU16(kern->subtables[i].version) || !out->WriteU16(length) || !out->WriteU16(kern->subtables[i].coverage) || !out->WriteU16(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(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Failed to write kern pair %d for subtable %d", j, i); } } } return true; } void ots_kern_free(OpenTypeFile *file) {
--- a/gfx/ots/src/layout.cc +++ b/gfx/ots/src/layout.cc @@ -7,16 +7,18 @@ #include <limits> #include <vector> #include "gdef.h" // OpenType Layout Common Table Formats // http://www.microsoft.com/typography/otspec/chapter2.htm +#define TABLE_NAME "Layout" // XXX: use individual table names + namespace { // The 'DFLT' tag of script table. const uint32_t kScriptTableTagDflt = 0x44464c54; // The value which represents there is no required feature index. const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF; // The lookup flag bit which indicates existence of MarkFilteringSet. const uint16_t kUseMarkFilteringSetBit = 0x0010; @@ -39,1444 +41,1472 @@ struct LangSysRecord { uint16_t offset; }; struct FeatureRecord { uint32_t tag; uint16_t offset; }; -bool ParseLangSysTable(ots::Buffer *subtable, const uint32_t tag, +bool ParseLangSysTable(const ots::OpenTypeFile *file, + ots::Buffer *subtable, const uint32_t tag, const uint16_t num_features) { uint16_t offset_lookup_order = 0; uint16_t req_feature_index = 0; uint16_t feature_count = 0; if (!subtable->ReadU16(&offset_lookup_order) || !subtable->ReadU16(&req_feature_index) || !subtable->ReadU16(&feature_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read langsys header for table %4s", (char *)&tag); } // |offset_lookup_order| is reserved and should be NULL. if (offset_lookup_order != 0) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookup offset order %d in table %4s", offset_lookup_order, (char *)&tag); } if (req_feature_index != kNoRequiredFeatureIndexDefined && req_feature_index >= num_features) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad required features index %d in table %4s", req_feature_index, (char *)&tag); } if (feature_count > num_features) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad feature count %d in table %4s", feature_count, (char *)&tag); } for (unsigned i = 0; i < feature_count; ++i) { uint16_t feature_index = 0; if (!subtable->ReadU16(&feature_index)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read feature index %d in table %4s", i, (char *)&tag); } if (feature_index >= num_features) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad feature index %d for feature %d in table %4s", feature_index, i, (char *)&tag); } } return true; } -bool ParseScriptTable(const uint8_t *data, const size_t length, +bool ParseScriptTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint32_t tag, const uint16_t num_features) { ots::Buffer subtable(data, length); uint16_t offset_default_lang_sys = 0; uint16_t lang_sys_count = 0; if (!subtable.ReadU16(&offset_default_lang_sys) || !subtable.ReadU16(&lang_sys_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read script header for table %4s", (char *)&tag); } // The spec requires a script table for 'DFLT' tag must contain non-NULL // |offset_default_lang_sys| and |lang_sys_count| == 0 if (tag == kScriptTableTagDflt && (offset_default_lang_sys == 0 || lang_sys_count != 0)) { - OTS_WARNING("DFLT table doesn't satisfy the spec."); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("DFLT table doesn't satisfy the spec. in table %4s", (char *)&tag); } const unsigned lang_sys_record_end = 6 * static_cast<unsigned>(lang_sys_count) + 4; if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of langsys record %d in table %4s", lang_sys_record_end, (char *)&tag); } std::vector<LangSysRecord> lang_sys_records; lang_sys_records.resize(lang_sys_count); uint32_t last_tag = 0; for (unsigned i = 0; i < lang_sys_count; ++i) { if (!subtable.ReadU32(&lang_sys_records[i].tag) || !subtable.ReadU16(&lang_sys_records[i].offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read langsys record header %d for table %4s", i, (char *)&tag); } // The record array must store the records alphabetically by tag if (last_tag != 0 && last_tag > lang_sys_records[i].tag) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad last tag %d for langsys record %d in table %4s", last_tag, i, (char *)&tag); } if (lang_sys_records[i].offset < lang_sys_record_end || lang_sys_records[i].offset >= length) { - OTS_WARNING("bad offset to lang sys table: %x", + return OTS_FAILURE_MSG("bad offset to lang sys table: %x", lang_sys_records[i].offset); - return OTS_FAILURE(); } last_tag = lang_sys_records[i].tag; } // Check lang sys tables for (unsigned i = 0; i < lang_sys_count; ++i) { subtable.set_offset(lang_sys_records[i].offset); - if (!ParseLangSysTable(&subtable, lang_sys_records[i].tag, num_features)) { - return OTS_FAILURE(); + if (!ParseLangSysTable(file, &subtable, lang_sys_records[i].tag, num_features)) { + return OTS_FAILURE_MSG("Failed to parse langsys table %d (%4s) in table %4s", i, (char *)&lang_sys_records[i].tag, (char *)&tag); } } return true; } -bool ParseFeatureTable(const uint8_t *data, const size_t length, +bool ParseFeatureTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t offset_feature_params = 0; uint16_t lookup_count = 0; if (!subtable.ReadU16(&offset_feature_params) || !subtable.ReadU16(&lookup_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read feature table header"); } const unsigned feature_table_end = 2 * static_cast<unsigned>(lookup_count) + 4; if (feature_table_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of feature table %d", feature_table_end); } // |offset_feature_params| is generally set to NULL. if (offset_feature_params != 0 && (offset_feature_params < feature_table_end || offset_feature_params >= length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Badd feature parames offset %d", offset_feature_params); } for (unsigned i = 0; i < lookup_count; ++i) { uint16_t lookup_index = 0; if (!subtable.ReadU16(&lookup_index)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookup index for lookup %d", i); } // lookup index starts with 0. if (lookup_index >= num_lookups) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookup index %d for lookup %d", lookup_index, i); } } return true; } bool ParseLookupTable(ots::OpenTypeFile *file, const uint8_t *data, const size_t length, const ots::LookupSubtableParser* parser) { ots::Buffer subtable(data, length); uint16_t lookup_type = 0; uint16_t lookup_flag = 0; uint16_t subtable_count = 0; if (!subtable.ReadU16(&lookup_type) || !subtable.ReadU16(&lookup_flag) || !subtable.ReadU16(&subtable_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookup table header"); } if (lookup_type == 0 || lookup_type > parser->num_types) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type); } // Check lookup flags. if ((lookup_flag & kGdefRequiredFlags) && (!file->gdef || !file->gdef->has_glyph_class_def)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookup flags %d", lookup_flag); } if ((lookup_flag & kMarkAttachmentTypeMask) && (!file->gdef || !file->gdef->has_mark_attachment_class_def)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("lookup flag asks for mark attachment that is bad %d", lookup_flag); } bool use_mark_filtering_set = false; if (lookup_flag & kUseMarkFilteringSetBit) { if (!file->gdef || !file->gdef->has_mark_glyph_sets_def) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("lookup flag asks for mark filtering that is bad %d", lookup_flag); } use_mark_filtering_set = true; } std::vector<uint16_t> subtables; subtables.reserve(subtable_count); // If the |kUseMarkFilteringSetBit| of |lookup_flag| is set, // extra 2 bytes will follow after subtable offset array. const unsigned lookup_table_end = 2 * static_cast<unsigned>(subtable_count) + (use_mark_filtering_set ? 8 : 6); if (lookup_table_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of lookup %d", lookup_table_end); } for (unsigned i = 0; i < subtable_count; ++i) { uint16_t offset_subtable = 0; if (!subtable.ReadU16(&offset_subtable)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read subtable offset %d", i); } if (offset_subtable < lookup_table_end || offset_subtable >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad subtable offset %d for subtable %d", offset_subtable, i); } subtables.push_back(offset_subtable); } if (subtables.size() != subtable_count) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad subtable size %ld", subtables.size()); } if (use_mark_filtering_set) { uint16_t mark_filtering_set = 0; if (!subtable.ReadU16(&mark_filtering_set)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read mark filtering set"); } if (file->gdef->num_mark_glyph_sets == 0 || mark_filtering_set >= file->gdef->num_mark_glyph_sets) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set); } } // Parse lookup subtables for this lookup type. for (unsigned i = 0; i < subtable_count; ++i) { if (!parser->Parse(file, data + subtables[i], length - subtables[i], lookup_type)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse subtable %d", i); } } return true; } -bool ParseClassDefFormat1(const uint8_t *data, size_t length, +bool ParseClassDefFormat1(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, const uint16_t num_glyphs, const uint16_t num_classes) { ots::Buffer subtable(data, length); // Skip format field. if (!subtable.Skip(2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip class definition header"); } uint16_t start_glyph = 0; if (!subtable.ReadU16(&start_glyph)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read starting glyph of class definition"); } if (start_glyph > num_glyphs) { OTS_WARNING("bad start glyph ID: %u", start_glyph); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad starting glyph %d in class definition", start_glyph); } uint16_t glyph_count = 0; if (!subtable.ReadU16(&glyph_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph count in class definition"); } if (glyph_count > num_glyphs) { - OTS_WARNING("bad glyph count: %u", glyph_count); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count); } for (unsigned i = 0; i < glyph_count; ++i) { uint16_t class_value = 0; if (!subtable.ReadU16(&class_value)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read class value for glyph %d in class definition", i); } if (class_value > num_classes) { OTS_WARNING("bad class value: %u", class_value); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad class value %d for glyph %d in class definition", class_value, i); } } return true; } -bool ParseClassDefFormat2(const uint8_t *data, size_t length, +bool ParseClassDefFormat2(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, const uint16_t num_glyphs, const uint16_t num_classes) { ots::Buffer subtable(data, length); // Skip format field. if (!subtable.Skip(2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip format of class defintion header"); } uint16_t range_count = 0; if (!subtable.ReadU16(&range_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read range count in class definition"); } if (range_count > num_glyphs) { - OTS_WARNING("bad range count: %u", range_count); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad range count: %u", range_count); } uint16_t last_end = 0; for (unsigned i = 0; i < range_count; ++i) { uint16_t start = 0; uint16_t end = 0; uint16_t class_value = 0; if (!subtable.ReadU16(&start) || !subtable.ReadU16(&end) || !subtable.ReadU16(&class_value)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read class definition reange %d", i); } if (start > end || (last_end && start <= last_end)) { - OTS_WARNING("glyph range is overlapping."); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i); } if (class_value > num_classes) { - OTS_WARNING("bad class value: %u", class_value); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad class value: %u", class_value); } last_end = end; } return true; } -bool ParseCoverageFormat1(const uint8_t *data, size_t length, - const uint16_t num_glyphs) { +bool ParseCoverageFormat1(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, + const uint16_t num_glyphs, + const uint16_t expected_num_glyphs) { ots::Buffer subtable(data, length); // Skip format field. if (!subtable.Skip(2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip coverage format"); } uint16_t glyph_count = 0; if (!subtable.ReadU16(&glyph_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph count in coverage"); } if (glyph_count > num_glyphs) { - OTS_WARNING("bad glyph count: %u", glyph_count); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count); } for (unsigned i = 0; i < glyph_count; ++i) { uint16_t glyph = 0; if (!subtable.ReadU16(&glyph)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph %d in coverage", i); } if (glyph > num_glyphs) { - OTS_WARNING("bad glyph ID: %u", glyph); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad glyph ID: %u", glyph); } } + if (expected_num_glyphs && expected_num_glyphs != glyph_count) { + return OTS_FAILURE_MSG("unexpected number of glyphs: %u", glyph_count); + } + return true; } -bool ParseCoverageFormat2(const uint8_t *data, size_t length, - const uint16_t num_glyphs) { +bool ParseCoverageFormat2(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, + const uint16_t num_glyphs, + const uint16_t expected_num_glyphs) { ots::Buffer subtable(data, length); // Skip format field. if (!subtable.Skip(2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip format of coverage type 2"); } uint16_t range_count = 0; if (!subtable.ReadU16(&range_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read range count in coverage"); } if (range_count > num_glyphs) { - OTS_WARNING("bad range count: %u", range_count); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad range count: %u", range_count); } uint16_t last_end = 0; uint16_t last_start_coverage_index = 0; for (unsigned i = 0; i < range_count; ++i) { uint16_t start = 0; uint16_t end = 0; uint16_t start_coverage_index = 0; if (!subtable.ReadU16(&start) || !subtable.ReadU16(&end) || !subtable.ReadU16(&start_coverage_index)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read range %d in coverage", i); } // Some of the Adobe Pro fonts have ranges that overlap by one element: the // start of one range is equal to the end of the previous range. Therefore // the < in the following condition should be <= were it not for this. // See crbug.com/134135. if (start > end || (last_end && start < last_end)) { - OTS_WARNING("glyph range is overlapping."); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("glyph range is overlapping."); } if (start_coverage_index != last_start_coverage_index) { - OTS_WARNING("bad start coverage index."); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad start coverage index."); } last_end = end; last_start_coverage_index += end - start + 1; } + if (expected_num_glyphs && + expected_num_glyphs != last_start_coverage_index) { + return OTS_FAILURE_MSG("unexpected number of glyphs: %u", last_start_coverage_index); + } + return true; } // Parsers for Contextual subtables in GSUB/GPOS tables. -bool ParseLookupRecord(ots::Buffer *subtable, const uint16_t num_glyphs, +bool ParseLookupRecord(const ots::OpenTypeFile *file, + ots::Buffer *subtable, const uint16_t num_glyphs, const uint16_t num_lookups) { uint16_t sequence_index = 0; uint16_t lookup_list_index = 0; if (!subtable->ReadU16(&sequence_index) || !subtable->ReadU16(&lookup_list_index)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read header for lookup record"); } if (sequence_index >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad sequence index %d in lookup record", sequence_index); } if (lookup_list_index >= num_lookups) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookup list index %d in lookup record", lookup_list_index); } return true; } -bool ParseRuleSubtable(const uint8_t *data, const size_t length, +bool ParseRuleSubtable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t glyph_count = 0; uint16_t lookup_count = 0; if (!subtable.ReadU16(&glyph_count) || !subtable.ReadU16(&lookup_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read rule subtable header"); } if (glyph_count == 0 || glyph_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph count %d in rule subtable", glyph_count); } for (unsigned i = 0; i < glyph_count - static_cast<unsigned>(1); ++i) { uint16_t glyph_id = 0; if (!subtable.ReadU16(&glyph_id)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read glyph %d", i); } if (glyph_id > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph %d for entry %d", glyph_id, i); } } for (unsigned i = 0; i < lookup_count; ++i) { - if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse lookup record %d", i); } } return true; } -bool ParseRuleSetTable(const uint8_t *data, const size_t length, +bool ParseRuleSetTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t rule_count = 0; if (!subtable.ReadU16(&rule_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read rule count in rule set"); } const unsigned rule_end = 2 * static_cast<unsigned>(rule_count) + 2; if (rule_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of rule %d in rule set", rule_end); } for (unsigned i = 0; i < rule_count; ++i) { uint16_t offset_rule = 0; if (!subtable.ReadU16(&offset_rule)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read rule offset for rule set %d", i); } if (offset_rule < rule_end || offset_rule >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad rule offset %d in set %d", offset_rule, i); } - if (!ParseRuleSubtable(data + offset_rule, length - offset_rule, + if (!ParseRuleSubtable(file, data + offset_rule, length - offset_rule, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse rule set %d", i); } } return true; } -bool ParseContextFormat1(const uint8_t *data, const size_t length, +bool ParseContextFormat1(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t offset_coverage = 0; uint16_t rule_set_count = 0; // Skip format field. if (!subtable.Skip(2) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&rule_set_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read header of context format 1"); } const unsigned rule_set_end = static_cast<unsigned>(6) + rule_set_count * 2; if (rule_set_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of rule set %d of context format 1", rule_set_end); } if (offset_coverage < rule_set_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d in context format 1", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table in context format 1"); } for (unsigned i = 0; i < rule_set_count; ++i) { uint16_t offset_rule = 0; if (!subtable.ReadU16(&offset_rule)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read rule offset %d in context format 1", i); } if (offset_rule < rule_set_end || offset_rule >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad rule offset %d in rule %d in context format 1", offset_rule, i); } - if (!ParseRuleSetTable(data + offset_rule, length - offset_rule, + if (!ParseRuleSetTable(file, data + offset_rule, length - offset_rule, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse rule set %d in context format 1", i); } } return true; } -bool ParseClassRuleTable(const uint8_t *data, const size_t length, +bool ParseClassRuleTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t glyph_count = 0; uint16_t lookup_count = 0; if (!subtable.ReadU16(&glyph_count) || !subtable.ReadU16(&lookup_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read header of class rule table"); } if (glyph_count == 0 || glyph_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph count %d in class rule table", glyph_count); } // ClassRule table contains an array of classes. Each value of classes // could take arbitrary values including zero so we don't check these value. const unsigned num_classes = glyph_count - static_cast<unsigned>(1); if (!subtable.Skip(2 * num_classes)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip classes in class rule table"); } for (unsigned i = 0; i < lookup_count; ++i) { - if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse lookup record %d in class rule table", i); } } return true; } -bool ParseClassSetTable(const uint8_t *data, const size_t length, +bool ParseClassSetTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t class_rule_count = 0; if (!subtable.ReadU16(&class_rule_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read class rule count in class set table"); } const unsigned class_rule_end = 2 * static_cast<unsigned>(class_rule_count) + 2; if (class_rule_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad class rule end %d in class set table", class_rule_end); } for (unsigned i = 0; i < class_rule_count; ++i) { uint16_t offset_class_rule = 0; if (!subtable.ReadU16(&offset_class_rule)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read class rule offset %d in class set table", i); } if (offset_class_rule < class_rule_end || offset_class_rule >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad class rule offset %d in class %d", offset_class_rule, i); } - if (!ParseClassRuleTable(data + offset_class_rule, + if (!ParseClassRuleTable(file, data + offset_class_rule, length - offset_class_rule, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse class rule table %d", i); } } return true; } -bool ParseContextFormat2(const uint8_t *data, const size_t length, - const uint16_t num_glyphs, - const uint16_t num_lookups) { +bool ParseContextFormat2(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, + const uint16_t num_glyphs, + const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t offset_coverage = 0; uint16_t offset_class_def = 0; uint16_t class_set_cnt = 0; // Skip format field. if (!subtable.Skip(2) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&offset_class_def) || !subtable.ReadU16(&class_set_cnt)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read header for context format 2"); } const unsigned class_set_end = 2 * static_cast<unsigned>(class_set_cnt) + 8; if (class_set_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of class set %d for context format 2", class_set_end); } if (offset_coverage < class_set_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d in context format 2", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table in context format 2"); } if (offset_class_def < class_set_end || offset_class_def >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad class definition offset %d in context format 2", offset_class_def); } - if (!ots::ParseClassDefTable(data + offset_class_def, + if (!ots::ParseClassDefTable(file, data + offset_class_def, length - offset_class_def, num_glyphs, kMaxClassDefValue)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2"); } for (unsigned i = 0; i < class_set_cnt; ++i) { uint16_t offset_class_rule = 0; if (!subtable.ReadU16(&offset_class_rule)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read class rule offset %d in context format 2", i); } if (offset_class_rule) { if (offset_class_rule < class_set_end || offset_class_rule >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad class rule offset %d for rule %d in context format 2", offset_class_rule, i); } - if (!ParseClassSetTable(data + offset_class_rule, + if (!ParseClassSetTable(file, data + offset_class_rule, length - offset_class_rule, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse class set %d in context format 2", i); } } } return true; } -bool ParseContextFormat3(const uint8_t *data, const size_t length, +bool ParseContextFormat3(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t glyph_count = 0; uint16_t lookup_count = 0; // Skip format field. if (!subtable.Skip(2) || !subtable.ReadU16(&glyph_count) || !subtable.ReadU16(&lookup_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read header in context format 3"); } if (glyph_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph count %d in context format 3", glyph_count); } const unsigned lookup_record_end = 2 * static_cast<unsigned>(glyph_count) + 4 * static_cast<unsigned>(lookup_count) + 6; if (lookup_record_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of lookup %d in context format 3", lookup_record_end); } for (unsigned i = 0; i < glyph_count; ++i) { uint16_t offset_coverage = 0; if (!subtable.ReadU16(&offset_coverage)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read coverage offset %d in conxtext format 3", i); } if (offset_coverage < lookup_record_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d for glyph %d in context format 3", offset_coverage, i); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table for glyph %d in context format 3", i); } } for (unsigned i = 0; i < lookup_count; ++i) { - if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse lookup record %d in context format 3", i); } } return true; } // Parsers for Chaning Contextual subtables in GSUB/GPOS tables. -bool ParseChainRuleSubtable(const uint8_t *data, const size_t length, +bool ParseChainRuleSubtable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t backtrack_count = 0; if (!subtable.ReadU16(&backtrack_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read backtrack count in chain rule subtable"); } if (backtrack_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad backtrack count %d in chain rule subtable", backtrack_count); } for (unsigned i = 0; i < backtrack_count; ++i) { uint16_t glyph_id = 0; if (!subtable.ReadU16(&glyph_id)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read backtrack glyph %d in chain rule subtable", i); } if (glyph_id > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph id %d for bactrack glyph %d in chain rule subtable", glyph_id, i); } } uint16_t input_count = 0; if (!subtable.ReadU16(&input_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read input count in chain rule subtable"); } if (input_count == 0 || input_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad input count %d in chain rule subtable", input_count); } for (unsigned i = 0; i < input_count - static_cast<unsigned>(1); ++i) { uint16_t glyph_id = 0; if (!subtable.ReadU16(&glyph_id)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read input glyph %d in chain rule subtable", i); } if (glyph_id > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph id %d for input glyph %d in chain rule subtable", glyph_id, i); } } uint16_t lookahead_count = 0; if (!subtable.ReadU16(&lookahead_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookahead count in chain rule subtable"); } if (lookahead_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookahead count %d in chain rule subtable", lookahead_count); } for (unsigned i = 0; i < lookahead_count; ++i) { uint16_t glyph_id = 0; if (!subtable.ReadU16(&glyph_id)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookahead glyph %d in chain rule subtable", i); } if (glyph_id > num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad glyph id %d for lookadhead glyph %d in chain rule subtable", glyph_id, i); } } uint16_t lookup_count = 0; if (!subtable.ReadU16(&lookup_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookup count in chain rule subtable"); } for (unsigned i = 0; i < lookup_count; ++i) { - if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain rule subtable", i); } } return true; } -bool ParseChainRuleSetTable(const uint8_t *data, const size_t length, +bool ParseChainRuleSetTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t chain_rule_count = 0; if (!subtable.ReadU16(&chain_rule_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read rule count in chain rule set"); } const unsigned chain_rule_end = 2 * static_cast<unsigned>(chain_rule_count) + 2; if (chain_rule_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of chain rule %d in chain rule set", chain_rule_end); } for (unsigned i = 0; i < chain_rule_count; ++i) { uint16_t offset_chain_rule = 0; if (!subtable.ReadU16(&offset_chain_rule)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain rule set", i); } if (offset_chain_rule < chain_rule_end || offset_chain_rule >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad chain rule offset %d for chain rule %d in chain rule set", offset_chain_rule, i); } - if (!ParseChainRuleSubtable(data + offset_chain_rule, + if (!ParseChainRuleSubtable(file, data + offset_chain_rule, length - offset_chain_rule, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse chain rule %d in chain rule set", i); } } return true; } -bool ParseChainContextFormat1(const uint8_t *data, const size_t length, +bool ParseChainContextFormat1(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t offset_coverage = 0; uint16_t chain_rule_set_count = 0; // Skip format field. if (!subtable.Skip(2) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&chain_rule_set_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read header of chain context format 1"); } const unsigned chain_rule_set_end = 2 * static_cast<unsigned>(chain_rule_set_count) + 6; if (chain_rule_set_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad chain rule end %d in chain context format 1", chain_rule_set_end); } if (offset_coverage < chain_rule_set_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 1", chain_rule_set_end); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table for chain context format 1"); } for (unsigned i = 0; i < chain_rule_set_count; ++i) { uint16_t offset_chain_rule_set = 0; if (!subtable.ReadU16(&offset_chain_rule_set)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain context format 1", i); } if (offset_chain_rule_set < chain_rule_set_end || offset_chain_rule_set >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad chain rule set offset %d for chain rule set %d in chain context format 1", offset_chain_rule_set, i); } - if (!ParseChainRuleSetTable(data + offset_chain_rule_set, + if (!ParseChainRuleSetTable(file, data + offset_chain_rule_set, length - offset_chain_rule_set, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse chain rule set %d in chain context format 1", i); } } return true; } -bool ParseChainClassRuleSubtable(const uint8_t *data, const size_t length, +bool ParseChainClassRuleSubtable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); // In this subtable, we don't check the value of classes for now since // these could take arbitrary values. uint16_t backtrack_count = 0; if (!subtable.ReadU16(&backtrack_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read backtrack count in chain class rule subtable"); } if (backtrack_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad backtrack count %d in chain class rule subtable", backtrack_count); } if (!subtable.Skip(2 * backtrack_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip backtrack offsets in chain class rule subtable"); } uint16_t input_count = 0; if (!subtable.ReadU16(&input_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read input count in chain class rule subtable"); } if (input_count == 0 || input_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad input count %d in chain class rule subtable", input_count); } if (!subtable.Skip(2 * (input_count - 1))) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip input offsets in chain class rule subtable"); } uint16_t lookahead_count = 0; if (!subtable.ReadU16(&lookahead_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookahead count in chain class rule subtable"); } if (lookahead_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookahead count %d in chain class rule subtable", lookahead_count); } if (!subtable.Skip(2 * lookahead_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip lookahead offsets in chain class rule subtable"); } uint16_t lookup_count = 0; if (!subtable.ReadU16(&lookup_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookup count in chain class rule subtable"); } for (unsigned i = 0; i < lookup_count; ++i) { - if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain class rule subtable", i); } } return true; } -bool ParseChainClassSetTable(const uint8_t *data, const size_t length, +bool ParseChainClassSetTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t chain_class_rule_count = 0; if (!subtable.ReadU16(&chain_class_rule_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read rule count in chain class set"); } const unsigned chain_class_rule_end = 2 * static_cast<unsigned>(chain_class_rule_count) + 2; if (chain_class_rule_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of chain class set %d in chain class set", chain_class_rule_end); } for (unsigned i = 0; i < chain_class_rule_count; ++i) { uint16_t offset_chain_class_rule = 0; if (!subtable.ReadU16(&offset_chain_class_rule)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read chain class rule offset %d in chain class set", i); } if (offset_chain_class_rule < chain_class_rule_end || offset_chain_class_rule >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad chain class rule offset %d for chain class %d in chain class set", offset_chain_class_rule, i); } - if (!ParseChainClassRuleSubtable(data + offset_chain_class_rule, + if (!ParseChainClassRuleSubtable(file, data + offset_chain_class_rule, length - offset_chain_class_rule, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse chain class rule %d in chain class set", i); } } return true; } -bool ParseChainContextFormat2(const uint8_t *data, const size_t length, +bool ParseChainContextFormat2(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t offset_coverage = 0; uint16_t offset_backtrack_class_def = 0; uint16_t offset_input_class_def = 0; uint16_t offset_lookahead_class_def = 0; uint16_t chain_class_set_count = 0; // Skip format field. if (!subtable.Skip(2) || !subtable.ReadU16(&offset_coverage) || !subtable.ReadU16(&offset_backtrack_class_def) || !subtable.ReadU16(&offset_input_class_def) || !subtable.ReadU16(&offset_lookahead_class_def) || !subtable.ReadU16(&chain_class_set_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read header of chain context format 2"); } const unsigned chain_class_set_end = 2 * static_cast<unsigned>(chain_class_set_count) + 12; if (chain_class_set_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad chain class set end %d in chain context format 2", chain_class_set_end); } if (offset_coverage < chain_class_set_end || offset_coverage >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 2", offset_coverage); } - if (!ots::ParseCoverageTable(data + offset_coverage, + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse coverage table in chain context format 2"); } // Classes for backtrack/lookahead sequences might not be defined. if (offset_backtrack_class_def) { if (offset_backtrack_class_def < chain_class_set_end || offset_backtrack_class_def >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad backtrack class offset %d in chain context format 2", offset_backtrack_class_def); } - if (!ots::ParseClassDefTable(data + offset_backtrack_class_def, + if (!ots::ParseClassDefTable(file, data + offset_backtrack_class_def, length - offset_backtrack_class_def, num_glyphs, kMaxClassDefValue)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2"); } } if (offset_input_class_def < chain_class_set_end || offset_input_class_def >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad input class defn offset %d in chain context format 2", offset_input_class_def); } - if (!ots::ParseClassDefTable(data + offset_input_class_def, + if (!ots::ParseClassDefTable(file, data + offset_input_class_def, length - offset_input_class_def, num_glyphs, kMaxClassDefValue)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2"); } if (offset_lookahead_class_def) { if (offset_lookahead_class_def < chain_class_set_end || offset_lookahead_class_def >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookahead class defn offset %d in chain context format 2", offset_lookahead_class_def); } - if (!ots::ParseClassDefTable(data + offset_lookahead_class_def, + if (!ots::ParseClassDefTable(file, data + offset_lookahead_class_def, length - offset_lookahead_class_def, num_glyphs, kMaxClassDefValue)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2"); } } for (unsigned i = 0; i < chain_class_set_count; ++i) { uint16_t offset_chain_class_set = 0; if (!subtable.ReadU16(&offset_chain_class_set)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read chain class set offset %d", i); } // |offset_chain_class_set| could be NULL. if (offset_chain_class_set) { if (offset_chain_class_set < chain_class_set_end || offset_chain_class_set >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad chain set class offset %d for chain set %d in chain context format 2", offset_chain_class_set, i); } - if (!ParseChainClassSetTable(data + offset_chain_class_set, + if (!ParseChainClassSetTable(file, data + offset_chain_class_set, length - offset_chain_class_set, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse chain class set table %d in chain context format 2", i); } } } return true; } -bool ParseChainContextFormat3(const uint8_t *data, const size_t length, +bool ParseChainContextFormat3(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { ots::Buffer subtable(data, length); uint16_t backtrack_count = 0; // Skip format field. if (!subtable.Skip(2) || !subtable.ReadU16(&backtrack_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read backtrack count in chain context format 3"); } if (backtrack_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad backtrack count %d in chain context format 3", backtrack_count); } std::vector<uint16_t> offsets_backtrack; offsets_backtrack.reserve(backtrack_count); for (unsigned i = 0; i < backtrack_count; ++i) { uint16_t offset = 0; if (!subtable.ReadU16(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read backtrack offset %d in chain context format 3", i); } offsets_backtrack.push_back(offset); } if (offsets_backtrack.size() != backtrack_count) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad backtrack offsets size %ld in chain context format 3", offsets_backtrack.size()); } uint16_t input_count = 0; if (!subtable.ReadU16(&input_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read input count in chain context format 3"); } if (input_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad input count %d in chain context format 3", input_count); } std::vector<uint16_t> offsets_input; offsets_input.reserve(input_count); for (unsigned i = 0; i < input_count; ++i) { uint16_t offset = 0; if (!subtable.ReadU16(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read input offset %d in chain context format 3", i); } offsets_input.push_back(offset); } if (offsets_input.size() != input_count) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad input offsets size %ld in chain context format 3", offsets_input.size()); } uint16_t lookahead_count = 0; if (!subtable.ReadU16(&lookahead_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed ot read lookahead count in chain context format 3"); } if (lookahead_count >= num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookahead count %d in chain context format 3", lookahead_count); } std::vector<uint16_t> offsets_lookahead; offsets_lookahead.reserve(lookahead_count); for (unsigned i = 0; i < lookahead_count; ++i) { uint16_t offset = 0; if (!subtable.ReadU16(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookahead offset %d in chain context format 3", i); } offsets_lookahead.push_back(offset); } if (offsets_lookahead.size() != lookahead_count) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookahead offsets size %ld in chain context format 3", offsets_lookahead.size()); } uint16_t lookup_count = 0; if (!subtable.ReadU16(&lookup_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookup count in chain context format 3"); } for (unsigned i = 0; i < lookup_count; ++i) { - if (!ParseLookupRecord(&subtable, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse lookup %d in chain context format 3", i); } } const unsigned lookup_record_end = 2 * (static_cast<unsigned>(backtrack_count) + static_cast<unsigned>(input_count) + static_cast<unsigned>(lookahead_count)) + 4 * static_cast<unsigned>(lookup_count) + 10; if (lookup_record_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of lookup record %d in chain context format 3", lookup_record_end); } for (unsigned i = 0; i < backtrack_count; ++i) { if (offsets_backtrack[i] < lookup_record_end || offsets_backtrack[i] >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad backtrack offset of %d for backtrack %d in chain context format 3", offsets_backtrack[i], i); } - if (!ots::ParseCoverageTable(data + offsets_backtrack[i], + if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i], length - offsets_backtrack[i], num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse backtrack coverage %d in chain context format 3", i); } } for (unsigned i = 0; i < input_count; ++i) { if (offsets_input[i] < lookup_record_end || offsets_input[i] >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad input offset %d for input %d in chain context format 3", offsets_input[i], i); } - if (!ots::ParseCoverageTable(data + offsets_input[i], + if (!ots::ParseCoverageTable(file, data + offsets_input[i], length - offsets_input[i], num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse input coverage table %d in chain context format 3", i); } } for (unsigned i = 0; i < lookahead_count; ++i) { if (offsets_lookahead[i] < lookup_record_end || offsets_lookahead[i] >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookadhead offset %d for lookahead %d in chain context format 3", offsets_lookahead[i], i); } - if (!ots::ParseCoverageTable(data + offsets_lookahead[i], + if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i], length - offsets_lookahead[i], num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in chain context format 3", i); } } return true; } } // namespace namespace ots { bool LookupSubtableParser::Parse(const OpenTypeFile *file, const uint8_t *data, const size_t length, const uint16_t lookup_type) const { for (unsigned i = 0; i < num_types; ++i) { if (parsers[i].type == lookup_type && parsers[i].parse) { if (!parsers[i].parse(file, data, length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse lookup subtable %d", i); } return true; } } - return OTS_FAILURE(); + return OTS_FAILURE_MSG("No lookup subtables to parse"); } // Parsing ScriptListTable requires number of features so we need to // parse FeatureListTable before calling this function. -bool ParseScriptListTable(const uint8_t *data, const size_t length, +bool ParseScriptListTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_features) { Buffer subtable(data, length); uint16_t script_count = 0; if (!subtable.ReadU16(&script_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read script count in script list table"); } const unsigned script_record_end = 6 * static_cast<unsigned>(script_count) + 2; if (script_record_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of script record %d in script list table", script_record_end); } std::vector<ScriptRecord> script_list; script_list.reserve(script_count); uint32_t last_tag = 0; for (unsigned i = 0; i < script_count; ++i) { ScriptRecord record; if (!subtable.ReadU32(&record.tag) || !subtable.ReadU16(&record.offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read script record %d in script list table", i); } // Script tags should be arranged alphabetically by tag if (last_tag != 0 && last_tag > record.tag) { // Several fonts don't arrange tags alphabetically. // It seems that the order of tags might not be a security issue // so we just warn it. OTS_WARNING("tags aren't arranged alphabetically."); } last_tag = record.tag; if (record.offset < script_record_end || record.offset >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad record offset %d for script %4s entry %d in script list table", record.offset, (char *)&record.tag, i); } script_list.push_back(record); } if (script_list.size() != script_count) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad script list size %ld in script list table", script_list.size()); } // Check script records. for (unsigned i = 0; i < script_count; ++i) { - if (!ParseScriptTable(data + script_list[i].offset, + if (!ParseScriptTable(file, data + script_list[i].offset, length - script_list[i].offset, script_list[i].tag, num_features)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse script table %d", i); } } return true; } // Parsing FeatureListTable requires number of lookups so we need to parse // LookupListTable before calling this function. -bool ParseFeatureListTable(const uint8_t *data, const size_t length, +bool ParseFeatureListTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_lookups, uint16_t* num_features) { Buffer subtable(data, length); uint16_t feature_count = 0; if (!subtable.ReadU16(&feature_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read feature count"); } std::vector<FeatureRecord> feature_records; feature_records.resize(feature_count); const unsigned feature_record_end = 6 * static_cast<unsigned>(feature_count) + 2; if (feature_record_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of feature record %d", feature_record_end); } uint32_t last_tag = 0; for (unsigned i = 0; i < feature_count; ++i) { if (!subtable.ReadU32(&feature_records[i].tag) || !subtable.ReadU16(&feature_records[i].offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read feature header %d", i); } // Feature record array should be arranged alphabetically by tag if (last_tag != 0 && last_tag > feature_records[i].tag) { // Several fonts don't arrange tags alphabetically. // It seems that the order of tags might not be a security issue // so we just warn it. OTS_WARNING("tags aren't arranged alphabetically."); } last_tag = feature_records[i].tag; if (feature_records[i].offset < feature_record_end || feature_records[i].offset >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad feature offset %d for feature %d %4s", feature_records[i].offset, i, (char *)&feature_records[i].tag); } } for (unsigned i = 0; i < feature_count; ++i) { - if (!ParseFeatureTable(data + feature_records[i].offset, + if (!ParseFeatureTable(file, data + feature_records[i].offset, length - feature_records[i].offset, num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse feature table %d", i); } } *num_features = feature_count; return true; } // For parsing GPOS/GSUB tables, this function should be called at first to // obtain the number of lookups because parsing FeatureTableList requires // the number. bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data, const size_t length, const LookupSubtableParser* parser, uint16_t *num_lookups) { Buffer subtable(data, length); if (!subtable.ReadU16(num_lookups)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read number of lookups"); } std::vector<uint16_t> lookups; lookups.reserve(*num_lookups); const unsigned lookup_end = 2 * static_cast<unsigned>(*num_lookups) + 2; if (lookup_end > std::numeric_limits<uint16_t>::max()) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad end of lookups %d", lookup_end); } for (unsigned i = 0; i < *num_lookups; ++i) { uint16_t offset = 0; if (!subtable.ReadU16(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read lookup offset %d", i); } if (offset < lookup_end || offset >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookup offset %d for lookup %d", offset, i); } lookups.push_back(offset); } if (lookups.size() != *num_lookups) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookup offsets list size %ld", lookups.size()); } for (unsigned i = 0; i < *num_lookups; ++i) { if (!ParseLookupTable(file, data + lookups[i], length - lookups[i], parser)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse lookup %d", i); } } return true; } -bool ParseClassDefTable(const uint8_t *data, size_t length, +bool ParseClassDefTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, const uint16_t num_glyphs, const uint16_t num_classes) { Buffer subtable(data, length); uint16_t format = 0; if (!subtable.ReadU16(&format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read class defn format"); } if (format == 1) { - return ParseClassDefFormat1(data, length, num_glyphs, num_classes); + return ParseClassDefFormat1(file, data, length, num_glyphs, num_classes); } else if (format == 2) { - return ParseClassDefFormat2(data, length, num_glyphs, num_classes); + return ParseClassDefFormat2(file, data, length, num_glyphs, num_classes); } - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad class defn format %d", format); } -bool ParseCoverageTable(const uint8_t *data, size_t length, - const uint16_t num_glyphs) { +bool ParseCoverageTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, + const uint16_t num_glyphs, + const uint16_t expected_num_glyphs) { Buffer subtable(data, length); uint16_t format = 0; if (!subtable.ReadU16(&format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read coverage table format"); } if (format == 1) { - return ParseCoverageFormat1(data, length, num_glyphs); + return ParseCoverageFormat1(file, data, length, num_glyphs, expected_num_glyphs); } else if (format == 2) { - return ParseCoverageFormat2(data, length, num_glyphs); + return ParseCoverageFormat2(file, data, length, num_glyphs, expected_num_glyphs); } - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad coverage table format %d", format); } -bool ParseDeviceTable(const uint8_t *data, size_t length) { +bool ParseDeviceTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length) { Buffer subtable(data, length); uint16_t start_size = 0; uint16_t end_size = 0; uint16_t delta_format = 0; if (!subtable.ReadU16(&start_size) || !subtable.ReadU16(&end_size) || !subtable.ReadU16(&delta_format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read device table header"); } if (start_size > end_size) { - OTS_WARNING("bad size range: %u > %u", start_size, end_size); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad size range: %u > %u", start_size, end_size); } if (delta_format == 0 || delta_format > kMaxDeltaFormatType) { - OTS_WARNING("bad delta format: %u", delta_format); - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad delta format: %u", delta_format); } // The number of delta values per uint16. The device table should contain // at least |num_units| * 2 bytes compressed data. const unsigned num_units = (end_size - start_size) / (1 << (4 - delta_format)) + 1; // Just skip |num_units| * 2 bytes since the compressed data could take // arbitrary values. if (!subtable.Skip(num_units * 2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip data in device table"); } return true; } -bool ParseContextSubtable(const uint8_t *data, const size_t length, +bool ParseContextSubtable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { Buffer subtable(data, length); uint16_t format = 0; if (!subtable.ReadU16(&format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read context subtable format"); } if (format == 1) { - if (!ParseContextFormat1(data, length, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseContextFormat1(file, data, length, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse context format 1 subtable"); } } else if (format == 2) { - if (!ParseContextFormat2(data, length, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseContextFormat2(file, data, length, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse context format 2 subtable"); } } else if (format == 3) { - if (!ParseContextFormat3(data, length, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseContextFormat3(file, data, length, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse context format 3 subtable"); } } else { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad context subtable format %d", format); } return true; } -bool ParseChainingContextSubtable(const uint8_t *data, const size_t length, +bool ParseChainingContextSubtable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups) { Buffer subtable(data, length); uint16_t format = 0; if (!subtable.ReadU16(&format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read chaining context subtable format"); } if (format == 1) { - if (!ParseChainContextFormat1(data, length, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseChainContextFormat1(file, data, length, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse chaining context format 1 subtable"); } } else if (format == 2) { - if (!ParseChainContextFormat2(data, length, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseChainContextFormat2(file, data, length, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse chaining context format 2 subtable"); } } else if (format == 3) { - if (!ParseChainContextFormat3(data, length, num_glyphs, num_lookups)) { - return OTS_FAILURE(); + if (!ParseChainContextFormat3(file, data, length, num_glyphs, num_lookups)) { + return OTS_FAILURE_MSG("Failed to parse chaining context format 3 subtable"); } } else { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad chaining context subtable format %d", format); } return true; } bool ParseExtensionSubtable(const OpenTypeFile *file, const uint8_t *data, const size_t length, const LookupSubtableParser* parser) { Buffer subtable(data, length); uint16_t format = 0; uint16_t lookup_type = 0; uint32_t offset_extension = 0; if (!subtable.ReadU16(&format) || !subtable.ReadU16(&lookup_type) || !subtable.ReadU32(&offset_extension)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read extension table header"); } if (format != 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad extension table format %d", format); } // |lookup_type| should be other than |parser->extension_type|. if (lookup_type < 1 || lookup_type > parser->num_types || lookup_type == parser->extension_type) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad lookup type %d in extension table", lookup_type); } const unsigned format_end = static_cast<unsigned>(8); if (offset_extension < format_end || offset_extension >= length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad extension offset %d", offset_extension); } // Parse the extension subtable of |lookup_type|. if (!parser->Parse(file, data + offset_extension, length - offset_extension, lookup_type)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse lookup from extension lookup"); } return true; } } // namespace ots
--- a/gfx/ots/src/layout.h +++ b/gfx/ots/src/layout.h @@ -22,44 +22,52 @@ struct LookupSubtableParser { size_t num_types; uint16_t extension_type; const TypeParser *parsers; bool Parse(const OpenTypeFile *file, const uint8_t *data, const size_t length, const uint16_t lookup_type) const; }; -bool ParseScriptListTable(const uint8_t *data, const size_t length, +bool ParseScriptListTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_features); -bool ParseFeatureListTable(const uint8_t *data, const size_t length, +bool ParseFeatureListTable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_lookups, uint16_t *num_features); bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data, const size_t length, const LookupSubtableParser* parser, uint16_t* num_lookups); -bool ParseClassDefTable(const uint8_t *data, size_t length, +bool ParseClassDefTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, const uint16_t num_glyphs, const uint16_t num_classes); -bool ParseCoverageTable(const uint8_t *data, size_t length, - const uint16_t num_glyphs); +bool ParseCoverageTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, + const uint16_t num_glyphs, + const uint16_t expected_num_glyphs = 0); -bool ParseDeviceTable(const uint8_t *data, size_t length); +bool ParseDeviceTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length); // Parser for 'Contextual' subtable shared by GSUB/GPOS tables. -bool ParseContextSubtable(const uint8_t *data, const size_t length, +bool ParseContextSubtable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups); // Parser for 'Chaining Contextual' subtable shared by GSUB/GPOS tables. -bool ParseChainingContextSubtable(const uint8_t *data, const size_t length, +bool ParseChainingContextSubtable(const ots::OpenTypeFile *file, + const uint8_t *data, const size_t length, const uint16_t num_glyphs, const uint16_t num_lookups); bool ParseExtensionSubtable(const OpenTypeFile *file, const uint8_t *data, const size_t length, const LookupSubtableParser* parser); } // namespace ots
--- a/gfx/ots/src/loca.cc +++ b/gfx/ots/src/loca.cc @@ -3,61 +3,63 @@ // found in the LICENSE file. #include "loca.h" #include "head.h" #include "maxp.h" // loca - Index to Location -// http://www.microsoft.com/opentype/otspec/loca.htm +// http://www.microsoft.com/typography/otspec/loca.htm + +#define TABLE_NAME "loca" namespace ots { bool ots_loca_parse(OpenTypeFile *file, 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; file->loca = loca; if (!file->maxp || !file->head) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("maxp or head tables missing from font, needed by loca"); } const unsigned num_glyphs = file->maxp->num_glyphs; unsigned last_offset = 0; loca->offsets.resize(num_glyphs + 1); // maxp->num_glyphs is uint16_t, thus the addition never overflows. if (file->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(); + return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i); } if (offset < last_offset) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i); } last_offset = offset; loca->offsets[i] = offset * 2; } } else { for (unsigned i = 0; i <= num_glyphs; ++i) { uint32_t offset = 0; if (!table.ReadU32(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i); } if (offset < last_offset) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i); } last_offset = offset; loca->offsets[i] = offset; } } return true; } @@ -66,29 +68,29 @@ bool ots_loca_should_serialise(OpenTypeF return file->loca != NULL; } bool ots_loca_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeLOCA *loca = file->loca; const OpenTypeHEAD *head = file->head; if (!head) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Missing head table in font needed by loca"); } if (head->index_to_loc_format == 0) { for (unsigned i = 0; i < loca->offsets.size(); ++i) { if (!out->WriteU16(loca->offsets[i] >> 1)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i); } } } return true; } void ots_loca_free(OpenTypeFile *file) {
--- a/gfx/ots/src/ltsh.cc +++ b/gfx/ots/src/ltsh.cc @@ -4,35 +4,41 @@ #include "ltsh.h" #include "maxp.h" // LTSH - Linear Threshold // http://www.microsoft.com/typography/otspec/ltsh.htm +#define TABLE_NAME "LTSH" + #define DROP_THIS_TABLE \ - do { delete file->ltsh; file->ltsh = 0; } while (0) + do { \ + delete file->ltsh; \ + file->ltsh = 0; \ + OTS_FAILURE_MSG("Table discarded"); \ + } while (0) namespace ots { bool ots_ltsh_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); if (!file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Missing maxp table from font needed by ltsh"); } OpenTypeLTSH *ltsh = new OpenTypeLTSH; file->ltsh = ltsh; uint16_t num_glyphs = 0; if (!table.ReadU16(<sh->version) || !table.ReadU16(&num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read ltsh header"); } if (ltsh->version != 0) { OTS_WARNING("bad version: %u", ltsh->version); DROP_THIS_TABLE; return true; } @@ -41,17 +47,17 @@ bool ots_ltsh_parse(OpenTypeFile *file, DROP_THIS_TABLE; return true; } ltsh->ypels.reserve(num_glyphs); for (unsigned i = 0; i < num_glyphs; ++i) { uint8_t pel = 0; if (!table.ReadU8(&pel)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read pixels for glyph %d", i); } ltsh->ypels.push_back(pel); } return true; } bool ots_ltsh_should_serialise(OpenTypeFile *file) { @@ -59,21 +65,21 @@ bool ots_ltsh_should_serialise(OpenTypeF return file->ltsh != NULL; } bool ots_ltsh_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeLTSH *ltsh = file->ltsh; if (!out->WriteU16(ltsh->version) || !out->WriteU16(ltsh->ypels.size())) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write pels size"); } for (unsigned i = 0; i < ltsh->ypels.size(); ++i) { if (!out->Write(&(ltsh->ypels[i]), 1)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write pixel size for glyph %d", i); } } return true; } void ots_ltsh_free(OpenTypeFile *file) { delete file->ltsh;
new file mode 100644 --- /dev/null +++ b/gfx/ots/src/math.cc @@ -0,0 +1,608 @@ +// Copyright (c) 2014 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. + +// We use an underscore to avoid confusion with the standard math.h library. +#include "math_.h" + +#include <limits> +#include <vector> + +#include "layout.h" +#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" + +namespace { + +// The size of MATH header. +// Version +// MathConstants +// MathGlyphInfo +// MathVariants +const unsigned kMathHeaderSize = 4 + 3 * 2; + +// The size of the MathGlyphInfo header. +// MathItalicsCorrectionInfo +// MathTopAccentAttachment +// ExtendedShapeCoverage +// MathKernInfo +const unsigned kMathGlyphInfoHeaderSize = 4 * 2; + +// The size of the MathValueRecord. +// Value +// DeviceTable +const unsigned kMathValueRecordSize = 2 * 2; + +// The size of the GlyphPartRecord. +// glyph +// StartConnectorLength +// EndConnectorLength +// FullAdvance +// PartFlags +const unsigned kGlyphPartRecordSize = 5 * 2; + +// Shared Table: MathValueRecord + +bool ParseMathValueRecord(const ots::OpenTypeFile *file, + ots::Buffer* subtable, const uint8_t *data, + const size_t length) { + // Check the Value field. + if (!subtable->Skip(2)) { + return OTS_FAILURE(); + } + + // Check the offset to device table. + uint16_t offset = 0; + if (!subtable->ReadU16(&offset)) { + return OTS_FAILURE(); + } + if (offset) { + if (offset >= length) { + return OTS_FAILURE(); + } + if (!ots::ParseDeviceTable(file, data + offset, length - offset)) { + return OTS_FAILURE(); + } + } + + return true; +} + +bool ParseMathConstantsTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length) { + ots::Buffer subtable(data, length); + + // Part 1: int16 or uint16 constants. + // ScriptPercentScaleDown + // ScriptScriptPercentScaleDown + // DelimitedSubFormulaMinHeight + // DisplayOperatorMinHeight + if (!subtable.Skip(4 * 2)) { + return OTS_FAILURE(); + } + + // Part 2: MathValueRecord constants. + // MathLeading + // AxisHeight + // AccentBaseHeight + // FlattenedAccentBaseHeight + // SubscriptShiftDown + // SubscriptTopMax + // SubscriptBaselineDropMin + // SuperscriptShiftUp + // SuperscriptShiftUpCramped + // SuperscriptBottomMin + // + // SuperscriptBaselineDropMax + // SubSuperscriptGapMin + // SuperscriptBottomMaxWithSubscript + // SpaceAfterScript + // UpperLimitGapMin + // UpperLimitBaselineRiseMin + // LowerLimitGapMin + // LowerLimitBaselineDropMin + // StackTopShiftUp + // StackTopDisplayStyleShiftUp + // + // StackBottomShiftDown + // StackBottomDisplayStyleShiftDown + // StackGapMin + // StackDisplayStyleGapMin + // StretchStackTopShiftUp + // StretchStackBottomShiftDown + // StretchStackGapAboveMin + // StretchStackGapBelowMin + // FractionNumeratorShiftUp + // FractionNumeratorDisplayStyleShiftUp + // + // FractionDenominatorShiftDown + // FractionDenominatorDisplayStyleShiftDown + // FractionNumeratorGapMin + // FractionNumDisplayStyleGapMin + // FractionRuleThickness + // FractionDenominatorGapMin + // FractionDenomDisplayStyleGapMin + // SkewedFractionHorizontalGap + // SkewedFractionVerticalGap + // OverbarVerticalGap + // + // OverbarRuleThickness + // OverbarExtraAscender + // UnderbarVerticalGap + // UnderbarRuleThickness + // UnderbarExtraDescender + // RadicalVerticalGap + // RadicalDisplayStyleVerticalGap + // RadicalRuleThickness + // RadicalExtraAscender + // RadicalKernBeforeDegree + // + // RadicalKernAfterDegree + for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) { + if (!ParseMathValueRecord(file, &subtable, data, length)) { + return OTS_FAILURE(); + } + } + + // Part 3: uint16 constant + // RadicalDegreeBottomRaisePercent + if (!subtable.Skip(2)) { + return OTS_FAILURE(); + } + + return true; +} + +bool ParseMathValueRecordSequenceForGlyphs(const ots::OpenTypeFile *file, + 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; + if (!subtable->ReadU16(&offset_coverage) || + !subtable->ReadU16(&sequence_count)) { + return OTS_FAILURE(); + } + + const unsigned sequence_end = static_cast<unsigned>(2 * 2) + + sequence_count * kMathValueRecordSize; + if (sequence_end > std::numeric_limits<uint16_t>::max()) { + return OTS_FAILURE(); + } + + // Check coverage table. + if (offset_coverage < sequence_end || offset_coverage >= length) { + return OTS_FAILURE(); + } + if (!ots::ParseCoverageTable(file, data + offset_coverage, + length - offset_coverage, + num_glyphs, sequence_count)) { + return OTS_FAILURE(); + } + + // Check sequence. + for (unsigned i = 0; i < sequence_count; ++i) { + if (!ParseMathValueRecord(file, subtable, data, length)) { + return OTS_FAILURE(); + } + } + + return true; +} + +bool ParseMathItalicsCorrectionInfoTable(const ots::OpenTypeFile *file, + const uint8_t *data, + size_t length, + const uint16_t num_glyphs) { + ots::Buffer subtable(data, length); + return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length, + num_glyphs); +} + +bool ParseMathTopAccentAttachmentTable(const ots::OpenTypeFile *file, + const uint8_t *data, + size_t length, + const uint16_t num_glyphs) { + ots::Buffer subtable(data, length); + return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length, + num_glyphs); +} + +bool ParseMathKernTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length) { + ots::Buffer subtable(data, length); + + // Check the Height count. + uint16_t height_count = 0; + if (!subtable.ReadU16(&height_count)) { + return OTS_FAILURE(); + } + + // Check the Correction Heights. + for (unsigned i = 0; i < height_count; ++i) { + if (!ParseMathValueRecord(file, &subtable, data, length)) { + return OTS_FAILURE(); + } + } + + // Check the Kern Values. + for (unsigned i = 0; i <= height_count; ++i) { + if (!ParseMathValueRecord(file, &subtable, data, length)) { + return OTS_FAILURE(); + } + } + + return true; +} + +bool ParseMathKernInfoTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, + const uint16_t num_glyphs) { + ots::Buffer subtable(data, length); + + // Check the header. + uint16_t offset_coverage = 0; + uint16_t sequence_count = 0; + if (!subtable.ReadU16(&offset_coverage) || + !subtable.ReadU16(&sequence_count)) { + return OTS_FAILURE(); + } + + const unsigned sequence_end = static_cast<unsigned>(2 * 2) + + sequence_count * 4 * 2; + if (sequence_end > std::numeric_limits<uint16_t>::max()) { + return OTS_FAILURE(); + } + + // Check coverage table. + if (offset_coverage < sequence_end || offset_coverage >= length) { + return OTS_FAILURE(); + } + if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage, + num_glyphs, sequence_count)) { + return OTS_FAILURE(); + } + + // Check sequence of MathKernInfoRecord + for (unsigned i = 0; i < sequence_count; ++i) { + // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern. + for (unsigned j = 0; j < 4; ++j) { + uint16_t offset_math_kern = 0; + if (!subtable.ReadU16(&offset_math_kern)) { + return OTS_FAILURE(); + } + if (offset_math_kern) { + if (offset_math_kern < sequence_end || offset_math_kern >= length || + !ParseMathKernTable(file, data + offset_math_kern, + length - offset_math_kern)) { + return OTS_FAILURE(); + } + } + } + } + + return true; +} + +bool ParseMathGlyphInfoTable(const ots::OpenTypeFile *file, + const uint8_t *data, size_t length, + const uint16_t num_glyphs) { + ots::Buffer subtable(data, length); + + // Check Header. + uint16_t offset_math_italics_correction_info = 0; + uint16_t offset_math_top_accent_attachment = 0; + uint16_t offset_extended_shaped_coverage = 0; + uint16_t offset_math_kern_info = 0; + if (!subtable.ReadU16(&offset_math_italics_correction_info) || + !subtable.ReadU16(&offset_math_top_accent_attachment) || + !subtable.ReadU16(&offset_extended_shaped_coverage) || + !subtable.ReadU16(&offset_math_kern_info)) { + return OTS_FAILURE(); + } + + // Check subtables. + // The specification does not say whether the offsets for + // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may + // be NULL, but that's the case in some fonts (e.g STIX) so we accept that. + if (offset_math_italics_correction_info) { + if (offset_math_italics_correction_info >= length || + offset_math_italics_correction_info < kMathGlyphInfoHeaderSize || + !ParseMathItalicsCorrectionInfoTable( + file, data + offset_math_italics_correction_info, + length - offset_math_italics_correction_info, + num_glyphs)) { + return OTS_FAILURE(); + } + } + if (offset_math_top_accent_attachment) { + if (offset_math_top_accent_attachment >= length || + offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize || + !ParseMathTopAccentAttachmentTable(file, data + + offset_math_top_accent_attachment, + length - + offset_math_top_accent_attachment, + num_glyphs)) { + return OTS_FAILURE(); + } + } + if (offset_extended_shaped_coverage) { + if (offset_extended_shaped_coverage >= length || + offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize || + !ots::ParseCoverageTable(file, data + offset_extended_shaped_coverage, + length - offset_extended_shaped_coverage, + num_glyphs)) { + return OTS_FAILURE(); + } + } + if (offset_math_kern_info) { + if (offset_math_kern_info >= length || + offset_math_kern_info < kMathGlyphInfoHeaderSize || + !ParseMathKernInfoTable(file, data + offset_math_kern_info, + length - offset_math_kern_info, num_glyphs)) { + return OTS_FAILURE(); + } + } + + return true; +} + +bool ParseGlyphAssemblyTable(const ots::OpenTypeFile *file, + 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(file, &subtable, data, length) || + !subtable.ReadU16(&part_count)) { + return OTS_FAILURE(); + } + + const unsigned sequence_end = kMathValueRecordSize + + static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize; + if (sequence_end > std::numeric_limits<uint16_t>::max()) { + return OTS_FAILURE(); + } + + // Check the sequence of GlyphPartRecord. + for (unsigned i = 0; i < part_count; ++i) { + uint16_t glyph = 0; + uint16_t part_flags = 0; + if (!subtable.ReadU16(&glyph) || + !subtable.Skip(2 * 3) || + !subtable.ReadU16(&part_flags)) { + return OTS_FAILURE(); + } + if (glyph >= num_glyphs) { + OTS_WARNING("bad glyph ID: %u", glyph); + return OTS_FAILURE(); + } + if (part_flags & ~0x00000001) { + OTS_WARNING("unknown part flag: %u", part_flags); + return OTS_FAILURE(); + } + } + + return true; +} + +bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile *file, + const uint8_t *data, + size_t length, const uint16_t num_glyphs) { + ots::Buffer subtable(data, length); + + // Check the header. + uint16_t offset_glyph_assembly = 0; + uint16_t variant_count = 0; + if (!subtable.ReadU16(&offset_glyph_assembly) || + !subtable.ReadU16(&variant_count)) { + return OTS_FAILURE(); + } + + const unsigned sequence_end = static_cast<unsigned>(2 * 2) + + variant_count * 2 * 2; + if (sequence_end > std::numeric_limits<uint16_t>::max()) { + return OTS_FAILURE(); + } + + // Check the GlyphAssembly offset. + if (offset_glyph_assembly) { + if (offset_glyph_assembly >= length || + offset_glyph_assembly < sequence_end) { + return OTS_FAILURE(); + } + if (!ParseGlyphAssemblyTable(file, data + offset_glyph_assembly, + length - offset_glyph_assembly, num_glyphs)) { + return OTS_FAILURE(); + } + } + + // Check the sequence of MathGlyphVariantRecord. + for (unsigned i = 0; i < variant_count; ++i) { + uint16_t glyph = 0; + if (!subtable.ReadU16(&glyph) || + !subtable.Skip(2)) { + return OTS_FAILURE(); + } + if (glyph >= num_glyphs) { + OTS_WARNING("bad glyph ID: %u", glyph); + return OTS_FAILURE(); + } + } + + return true; +} + +bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile *file, + 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) { + // Check coverage table. + if (offset_coverage < sequence_end || offset_coverage >= length) { + return OTS_FAILURE(); + } + if (!ots::ParseCoverageTable(file, data + offset_coverage, + length - offset_coverage, + num_glyphs, glyph_count)) { + return OTS_FAILURE(); + } + + // Check sequence of MathGlyphConstruction. + for (unsigned i = 0; i < glyph_count; ++i) { + uint16_t offset_glyph_construction = 0; + if (!subtable->ReadU16(&offset_glyph_construction)) { + return OTS_FAILURE(); + } + if (offset_glyph_construction < sequence_end || + offset_glyph_construction >= length || + !ParseMathGlyphConstructionTable(file, data + offset_glyph_construction, + length - offset_glyph_construction, + num_glyphs)) { + return OTS_FAILURE(); + } + } + + return true; +} + +bool ParseMathVariantsTable(const ots::OpenTypeFile *file, + const uint8_t *data, + size_t length, const uint16_t num_glyphs) { + ots::Buffer subtable(data, length); + + // Check the header. + uint16_t offset_vert_glyph_coverage = 0; + uint16_t offset_horiz_glyph_coverage = 0; + uint16_t vert_glyph_count = 0; + uint16_t horiz_glyph_count = 0; + if (!subtable.Skip(2) || // MinConnectorOverlap + !subtable.ReadU16(&offset_vert_glyph_coverage) || + !subtable.ReadU16(&offset_horiz_glyph_coverage) || + !subtable.ReadU16(&vert_glyph_count) || + !subtable.ReadU16(&horiz_glyph_count)) { + return OTS_FAILURE(); + } + + const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 + + horiz_glyph_count * 2; + if (sequence_end > std::numeric_limits<uint16_t>::max()) { + return OTS_FAILURE(); + } + + if (!ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs, + offset_vert_glyph_coverage, + vert_glyph_count, + sequence_end) || + !ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs, + offset_horiz_glyph_coverage, + horiz_glyph_count, + sequence_end)) { + return OTS_FAILURE(); + } + + return true; +} + +} // namespace + +#define DROP_THIS_TABLE \ + do { file->math->data = 0; file->math->length = 0; } while (0) + +namespace ots { + +bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { + // Grab the number of glyphs in the file from the maxp table to check + // GlyphIDs in MATH table. + if (!file->maxp) { + return OTS_FAILURE(); + } + const uint16_t num_glyphs = file->maxp->num_glyphs; + + Buffer table(data, length); + + OpenTypeMATH* math = new OpenTypeMATH; + file->math = math; + + uint32_t version = 0; + if (!table.ReadU32(&version)) { + return OTS_FAILURE(); + } + if (version != 0x00010000) { + OTS_WARNING("bad MATH version"); + DROP_THIS_TABLE; + return true; + } + + uint16_t offset_math_constants = 0; + uint16_t offset_math_glyph_info = 0; + uint16_t offset_math_variants = 0; + if (!table.ReadU16(&offset_math_constants) || + !table.ReadU16(&offset_math_glyph_info) || + !table.ReadU16(&offset_math_variants)) { + return OTS_FAILURE(); + } + + if (offset_math_constants >= length || + offset_math_constants < kMathHeaderSize || + offset_math_glyph_info >= length || + offset_math_glyph_info < kMathHeaderSize || + offset_math_variants >= length || + offset_math_variants < kMathHeaderSize) { + OTS_WARNING("bad offset in MATH header"); + DROP_THIS_TABLE; + return true; + } + + if (!ParseMathConstantsTable(file, data + offset_math_constants, + length - offset_math_constants)) { + DROP_THIS_TABLE; + return true; + } + if (!ParseMathGlyphInfoTable(file, data + offset_math_glyph_info, + length - offset_math_glyph_info, num_glyphs)) { + DROP_THIS_TABLE; + return true; + } + if (!ParseMathVariantsTable(file, data + offset_math_variants, + length - offset_math_variants, num_glyphs)) { + DROP_THIS_TABLE; + return true; + } + + math->data = data; + math->length = length; + return true; +} + +bool ots_math_should_serialise(OpenTypeFile *file) { + return file->math != NULL && file->math->data != NULL; +} + +bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) { + if (!out->Write(file->math->data, file->math->length)) { + return OTS_FAILURE(); + } + + return true; +} + +void ots_math_free(OpenTypeFile *file) { + delete file->math; +} + +} // namespace ots +
new file mode 100644 --- /dev/null +++ b/gfx/ots/src/math_.h @@ -0,0 +1,25 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef OTS_MATH_H_ +#define OTS_MATH_H_ + +#include "ots.h" + +namespace ots { + +struct OpenTypeMATH { + OpenTypeMATH() + : data(NULL), + length(0) { + } + + const uint8_t *data; + size_t length; +}; + +} // namespace ots + +#endif +
--- a/gfx/ots/src/maxp.cc +++ b/gfx/ots/src/maxp.cc @@ -1,40 +1,42 @@ // 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. #include "maxp.h" // maxp - Maximum Profile -// http://www.microsoft.com/opentype/otspec/maxp.htm +// http://www.microsoft.com/typography/otspec/maxp.htm + +#define TABLE_NAME "maxp" namespace ots { bool ots_maxp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeMAXP *maxp = new OpenTypeMAXP; file->maxp = maxp; uint32_t version = 0; if (!table.ReadU32(&version)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read version of maxp table"); } if (version >> 16 > 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad maxp version %d", version); } if (!table.ReadU16(&maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read number of glyphs from maxp table"); } if (!maxp->num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad number of glyphs 0 in maxp table"); } 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) || @@ -42,31 +44,31 @@ bool ots_maxp_parse(OpenTypeFile *file, !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(); + return OTS_FAILURE_MSG("Failed to read maxp table"); } if (maxp->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) { // workaround for Ecolier-*.ttf fonts. OTS_WARNING("bad max_zones: %u", maxp->max_zones); maxp->max_zones = 2; } if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad max zones %d in maxp", maxp->max_zones); } } else { maxp->version_1 = false; } return true; } @@ -74,53 +76,53 @@ bool ots_maxp_should_serialise(OpenTypeF return file->maxp != NULL; } bool ots_maxp_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeMAXP *maxp = file->maxp; if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) || !out->WriteU16(maxp->num_glyphs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write maxp version or number of glyphs"); } if (!maxp->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(); + return OTS_FAILURE_MSG("Failed to write maxp"); } if (g_transcode_hints) { 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(); + return OTS_FAILURE_MSG("Failed to write more maxp"); } } else { if (!out->WriteU16(1) || // max zones !out->WriteU16(0) || // max twilight points !out->WriteU16(0) || // max storage !out->WriteU16(0) || // max function defs !out->WriteU16(0) || // max instruction defs !out->WriteU16(0) || // max stack elements !out->WriteU16(0)) { // max instruction byte count - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write more maxp"); } } if (!out->WriteU16(maxp->max_c_components) || !out->WriteU16(maxp->max_c_depth)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write yet more maxp"); } return true; } void ots_maxp_free(OpenTypeFile *file) { delete file->maxp; }
--- a/gfx/ots/src/metrics.cc +++ b/gfx/ots/src/metrics.cc @@ -3,128 +3,132 @@ // found in the LICENSE file. #include "metrics.h" #include "head.h" #include "maxp.h" // OpenType horizontal and vertical common header format -// http://www.microsoft.com/opentype/otspec/hhea.htm -// http://www.microsoft.com/opentype/otspec/vhea.htm +// 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(OpenTypeFile *file, 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(); + return OTS_FAILURE_MSG("Failed to read metrics header"); } 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 (!file->head) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Missing head font table"); } // if the font is non-slanted, caret_offset should be zero. if (!(file->head->mac_style & 2) && (header->caret_offset != 0)) { OTS_WARNING("bad caret offset: %d", header->caret_offset); header->caret_offset = 0; } // skip the reserved bytes if (!table->Skip(8)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to skip reserverd bytes"); } int16_t data_format; if (!table->ReadS16(&data_format)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read data format"); } if (data_format) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad data format %d", data_format); } if (!table->ReadU16(&header->num_metrics)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read number of metrics"); } if (!file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Missing maxp font table"); } if (header->num_metrics > file->maxp->num_glyphs) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad number of metrics %d", header->num_metrics); } return true; } -bool SerialiseMetricsHeader(OTSStream *out, +bool SerialiseMetricsHeader(const ots::OpenTypeFile *file, + 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) || !out->WriteR64(0) || // reserved !out->WriteS16(0) || // metric data format !out->WriteU16(header->num_metrics)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write metrics"); } return true; } -bool ParseMetricsTable(Buffer *table, +bool ParseMetricsTable(const ots::OpenTypeFile *file, + Buffer *table, const uint16_t num_glyphs, const OpenTypeMetricsHeader *header, OpenTypeMetricsTable *metrics) { // |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(); + return OTS_FAILURE_MSG("Bad number of metrics %d", num_metrics); } if (!num_metrics) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("No metrics!"); } const unsigned num_sbs = num_glyphs - num_metrics; metrics->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(); + return OTS_FAILURE_MSG("Failed to read metric %d", i); } // Since so many fonts don't have proper value on |adv| and |sb|, // we should not call ots_failure() here. For example, about 20% of fonts // in http://www.princexml.com/fonts/ (200+ fonts) fails these tests. if (adv > header->adv_width_max) { OTS_WARNING("bad adv: %u > %u", adv, header->adv_width_max); adv = header->adv_width_max; @@ -138,44 +142,45 @@ bool ParseMetricsTable(Buffer *table, metrics->entries.push_back(std::make_pair(adv, sb)); } metrics->sbs.reserve(num_sbs); for (unsigned i = 0; i < num_sbs; ++i) { int16_t sb; if (!table->ReadS16(&sb)) { // Some Japanese fonts (e.g., mona.ttf) fail this test. - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read side bearing %d", i + num_metrics); } if (sb < header->min_sb1) { // The same as above. Three fonts in http://www.fontsquirrel.com/fontface // (e.g., Notice2Std.otf) have weird lsb values. OTS_WARNING("bad lsb: %d < %d", sb, header->min_sb1); sb = header->min_sb1; } metrics->sbs.push_back(sb); } return true; } -bool SerialiseMetricsTable(OTSStream *out, +bool SerialiseMetricsTable(const ots::OpenTypeFile *file, + 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(); + return OTS_FAILURE_MSG("Failed to write metric %d", i); } } for (unsigned i = 0; i < metrics->sbs.size(); ++i) { if (!out->WriteS16(metrics->sbs[i])) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write side bearing %ld", i + metrics->entries.size()); } } return true; } } // namespace ots
--- a/gfx/ots/src/metrics.h +++ b/gfx/ots/src/metrics.h @@ -1,17 +1,17 @@ // Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef OTS_METRICS_H_ #define OTS_METRICS_H_ #include <new> -#include <utility> // std::pair +#include <utility> #include <vector> #include "ots.h" namespace ots { struct OpenTypeMetricsHeader { uint32_t version; @@ -30,22 +30,25 @@ struct OpenTypeMetricsHeader { struct OpenTypeMetricsTable { std::vector<std::pair<uint16_t, int16_t> > entries; std::vector<int16_t> sbs; }; bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table, OpenTypeMetricsHeader *header); -bool SerialiseMetricsHeader(OTSStream *out, +bool SerialiseMetricsHeader(const ots::OpenTypeFile *file, + OTSStream *out, const OpenTypeMetricsHeader *header); -bool ParseMetricsTable(Buffer *table, +bool ParseMetricsTable(const ots::OpenTypeFile *file, + Buffer *table, const uint16_t num_glyphs, const OpenTypeMetricsHeader *header, OpenTypeMetricsTable *metrics); -bool SerialiseMetricsTable(OTSStream *out, +bool SerialiseMetricsTable(const ots::OpenTypeFile *file, + OTSStream *out, const OpenTypeMetricsTable *metrics); } // namespace ots #endif // OTS_METRICS_H_
--- a/gfx/ots/src/moz.build +++ b/gfx/ots/src/moz.build @@ -13,52 +13,50 @@ SOURCES += [ # don't unify sources that use a (file-specific) DROP_THIS_TABLE macro 'gasp.cc', 'gdef.cc', 'gpos.cc', 'gsub.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', - 'graphite.cc', 'head.cc', 'hhea.cc', 'hmtx.cc', 'layout.cc', 'loca.cc', 'maxp.cc', 'metrics.cc', 'name.cc', 'os2.cc', 'ots.cc', 'post.cc', 'prep.cc', - 'svg.cc', 'vhea.cc', 'vmtx.cc', ] MSVC_ENABLE_PGO = True if CONFIG['GKMEDIAS_SHARED_LIBRARY']: NO_VISIBILITY_FLAGS = True FINAL_LIBRARY = 'gkmedias' DEFINES['PACKAGE_VERSION'] = '"moz"' DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"' DEFINES['NOMINMAX'] = True -DEFINES['MOZ_OTS_REPORT_ERRORS'] = True if CONFIG['OS_TARGET'] == 'WINNT': DEFINES['OTS_DLL'] = True DEFINES['OTS_DLL_EXPORTS'] = True
--- a/gfx/ots/src/name.cc +++ b/gfx/ots/src/name.cc @@ -5,17 +5,19 @@ #include "name.h" #include <algorithm> #include <cstring> #include "cff.h" // name - Naming Table -// http://www.microsoft.com/opentype/otspec/name.htm +// http://www.microsoft.com/typography/otspec/name.htm + +#define TABLE_NAME "name" namespace { bool ValidInPsName(char c) { return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c)); } bool CheckPsNameAscii(const std::string& name) { @@ -59,27 +61,27 @@ namespace ots { bool ots_name_parse(OpenTypeFile* file, const uint8_t* data, size_t length) { Buffer table(data, length); OpenTypeNAME* name = new OpenTypeNAME; file->name = name; uint16_t format = 0; if (!table.ReadU16(&format) || format > 1) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format); } uint16_t count = 0; if (!table.ReadU16(&count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read name count"); } uint16_t string_offset = 0; if (!table.ReadU16(&string_offset) || string_offset > length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read strings offset"); } const char* string_base = reinterpret_cast<const char*>(data) + string_offset; NameRecord prev_record; bool sort_required = false; // Read all the names, discarding any with invalid IDs, @@ -90,17 +92,17 @@ bool ots_name_parse(OpenTypeFile* file, NameRecord rec; uint16_t name_length, name_offset; if (!table.ReadU16(&rec.platform_id) || !table.ReadU16(&rec.encoding_id) || !table.ReadU16(&rec.language_id) || !table.ReadU16(&rec.name_id) || !table.ReadU16(&name_length) || !table.ReadU16(&name_offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read name entry %d", i); } // check platform & encoding, discard names with unknown values switch (rec.platform_id) { case 0: // Unicode if (rec.encoding_id > 6) { continue; } break; @@ -161,38 +163,38 @@ bool ots_name_parse(OpenTypeFile* file, name->names.push_back(rec); prev_record = rec; } if (format == 1) { // extended name table format with language tags uint16_t lang_tag_count; if (!table.ReadU16(&lang_tag_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read language tag count"); } 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(); + return OTS_FAILURE_MSG("Faile to read tag length or offset"); } const unsigned tag_end = static_cast<unsigned>(string_offset) + tag_offset + tag_length; if (tag_end > length) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i); } std::string tag(string_base + tag_offset, tag_length); name->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(); + return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset); } // check existence of required name strings (synthesize if necessary) // [0 - copyright - skip] // 1 - family // 2 - subfamily // [3 - unique ID - skip] // 4 - full name @@ -275,56 +277,56 @@ bool ots_name_serialise(OTSStream* out, size_t string_offset = 6 + name_count * 12; if (name->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(); + return OTS_FAILURE_MSG("Bad string offset %ld", string_offset); } if (!out->WriteU16(format) || !out->WriteU16(name_count) || !out->WriteU16(string_offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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; if (!out->WriteU16(rec.platform_id) || !out->WriteU16(rec.encoding_id) || !out->WriteU16(rec.language_id) || !out->WriteU16(rec.name_id) || !out->WriteU16(rec.text.size()) || !out->WriteU16(string_data.size()) ) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Faile to write name entry"); } string_data.append(rec.text); } if (format == 1) { if (!out->WriteU16(lang_tag_count)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Faile to write language tag count"); } for (std::vector<std::string>::const_iterator tag_iter = name->lang_tags.begin(); tag_iter != name->lang_tags.end(); tag_iter++) { if (!out->WriteU16(tag_iter->size()) || !out->WriteU16(string_data.size())) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write string"); } string_data.append(*tag_iter); } } if (!out->Write(string_data.data(), string_data.size())) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Faile to write string data"); } return true; } void ots_name_free(OpenTypeFile* file) { delete file->name; }
--- a/gfx/ots/src/name.h +++ b/gfx/ots/src/name.h @@ -13,22 +13,22 @@ #include "ots.h" namespace ots { struct NameRecord { NameRecord() { } - NameRecord(uint16_t platform_id, uint16_t encoding_id, - uint16_t language_id, uint16_t name_id) - : platform_id(platform_id), - encoding_id(encoding_id), - language_id(language_id), - name_id(name_id) { + NameRecord(uint16_t platformID, uint16_t encodingID, + uint16_t languageID, uint16_t nameID) + : platform_id(platformID), + encoding_id(encodingID), + language_id(languageID), + name_id(nameID) { } uint16_t platform_id; uint16_t encoding_id; uint16_t language_id; uint16_t name_id; std::string text;
--- a/gfx/ots/src/os2.cc +++ b/gfx/ots/src/os2.cc @@ -2,17 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "os2.h" #include "head.h" // OS/2 - OS/2 and Windows Metrics -// http://www.microsoft.com/opentype/otspec/os2.htm +// http://www.microsoft.com/typography/otspec/os2.htm + +#define TABLE_NAME "OS/2" namespace ots { bool ots_os2_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeOS2 *os2 = new OpenTypeOS2; file->os2 = os2; @@ -28,21 +30,21 @@ bool ots_os2_parse(OpenTypeFile *file, c !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(); + return OTS_FAILURE_MSG("Failed toi read basic os2 elements"); } if (os2->version > 4) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("os2 version too high %d", os2->version); } // Some linux fonts (e.g., Kedage-t.ttf and LucidaSansDemiOblique.ttf) have // weird weight/width classes. Overwrite them with FW_NORMAL/1/9. if (os2->weight_class < 100 || os2->weight_class > 900 || os2->weight_class % 100) { OTS_WARNING("bad weight: %u", os2->weight_class); @@ -89,45 +91,45 @@ bool ots_os2_parse(OpenTypeFile *file, c } if (os2->strikeout_size < 0) { OTS_WARNING("bad strikeout_size: %d", os2->strikeout_size); os2->strikeout_size = 0; } for (unsigned i = 0; i < 10; ++i) { if (!table.ReadU8(&os2->panose[i])) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read panose in os2 table"); } } 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(); + return OTS_FAILURE_MSG("Failed to read more basic os2 fields"); } // If bit 6 is set, then bits 0 and 5 must be clear. if (os2->selection & 0x40) { os2->selection &= 0xffdeu; } // the settings of bits 0 and 1 must be reflected in the macStyle bits // in the 'head' table. if (!file->head) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Head table missing from font as needed by os2 table"); } if ((os2->selection & 0x1) && !(file->head->mac_style & 0x2)) { OTS_WARNING("adjusting Mac style (italic)"); file->head->mac_style |= 0x2; } if ((os2->selection & 0x2) && !(file->head->mac_style & 0x4)) { @@ -141,24 +143,24 @@ bool ots_os2_parse(OpenTypeFile *file, c (file->head->mac_style & 0x3)) { OTS_WARNING("adjusting Mac style (regular)"); file->head->mac_style &= 0xfffcu; } if ((os2->version < 4) && (os2->selection & 0x300)) { // bit 8 and 9 must be unset in OS/2 table versions less than 4. - return OTS_FAILURE(); + return OTS_FAILURE_MSG("OS2 version %d incompatible with selection %d", os2->version, os2->selection); } // mask reserved bits. use only 0..9 bits. os2->selection &= 0x3ff; if (os2->first_char_index > os2->last_char_index) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("first char index %d > last char index %d in os2", os2->first_char_index, os2->last_char_index); } if (os2->typo_linegap < 0) { OTS_WARNING("bad linegap: %d", os2->typo_linegap); os2->typo_linegap = 0; } if (os2->version < 1) { // http://www.microsoft.com/typography/otspec/os2ver0.htm @@ -170,17 +172,17 @@ bool ots_os2_parse(OpenTypeFile *file, c // Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version // numbers. Fix them. os2->version = 0; return true; } if (!table.ReadU32(&os2->code_page_range_1) || !table.ReadU32(&os2->code_page_range_2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read codepage ranges"); } if (os2->version < 2) { // http://www.microsoft.com/typography/otspec/os2ver1.htm return true; } if (length < offsetof(OpenTypeOS2, max_context)) { @@ -191,17 +193,17 @@ bool ots_os2_parse(OpenTypeFile *file, c 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(); + return OTS_FAILURE_MSG("Failed to read os2 version 2 information"); } if (os2->x_height < 0) { OTS_WARNING("bad x_height: %d", os2->x_height); os2->x_height = 0; } if (os2->cap_height < 0) { OTS_WARNING("bad cap_height: %d", os2->cap_height); @@ -229,60 +231,60 @@ bool ots_os2_serialise(OTSStream *out, O !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(); + return OTS_FAILURE_MSG("Failed to write basic OS2 information"); } for (unsigned i = 0; i < 10; ++i) { if (!out->Write(&os2->panose[i], 1)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write os2 panose information"); } } 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(); + return OTS_FAILURE_MSG("Failed to write os2 version 1 information"); } if (os2->version < 1) { return true; } if (!out->WriteU32(os2->code_page_range_1) || !out->WriteU32(os2->code_page_range_2)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write codepage ranges"); } if (os2->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(); + return OTS_FAILURE_MSG("Failed to write os2 version 2 information"); } return true; } void ots_os2_free(OpenTypeFile *file) { delete file->os2; }
--- a/gfx/ots/src/ots.cc +++ b/gfx/ots/src/ots.cc @@ -9,54 +9,49 @@ #include <algorithm> #include <cstdlib> #include <cstring> #include <limits> #include <map> #include <vector> +#ifdef MOZ_OTS_WOFF2 +#include "woff2.h" +#endif + // The OpenType Font File // http://www.microsoft.com/typography/otspec/cmap.htm namespace { bool g_debug_output = true; +#ifdef MOZ_OTS_WOFF2 +bool g_enable_woff2 = false; +#endif -#ifdef MOZ_OTS_REPORT_ERRORS +ots::MessageFunc g_message_func = NULL; +void *g_message_user_data = NULL; + +ots::TableActionFunc g_table_action_func = NULL; +void *g_table_action_user_data = NULL; // 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_) -#else - -#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE() -#define OTS_FAILURE_MSG_HDR(msg_) OTS_FAILURE() - -#endif - struct OpenTypeTable { uint32_t tag; uint32_t chksum; uint32_t offset; uint32_t length; uint32_t uncompressed_length; }; -// Round a value up to the nearest multiple of 4. Don't round the value in the -// case that rounding up overflows. -template<typename T> T Round4(T value) { - if (std::numeric_limits<T>::max() - value < 3) { - return value; - } - return (value + 3) & ~3; -} - 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; } @@ -156,43 +151,22 @@ const struct { { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise, ots::ots_gpos_should_serialise, ots::ots_gpos_free, false }, { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise, ots::ots_gsub_should_serialise, ots::ots_gsub_free, false }, { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise, ots::ots_vhea_should_serialise, ots::ots_vhea_free, false }, { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise, ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false }, - // SILGraphite layout tables - not actually parsed, just copied - { "Silf", ots::ots_silf_parse, ots::ots_silf_serialise, - ots::ots_silf_should_serialise, ots::ots_silf_free, false }, - { "Sill", ots::ots_sill_parse, ots::ots_sill_serialise, - ots::ots_sill_should_serialise, ots::ots_sill_free, false }, - { "Gloc", ots::ots_gloc_parse, ots::ots_gloc_serialise, - ots::ots_gloc_should_serialise, ots::ots_gloc_free, false }, - { "Glat", ots::ots_glat_parse, ots::ots_glat_serialise, - ots::ots_glat_should_serialise, ots::ots_glat_free, false }, - { "Feat", ots::ots_feat_parse, ots::ots_feat_serialise, - ots::ots_feat_should_serialise, ots::ots_feat_free, false }, - // SVG glyph table - { "SVG ", ots::ots_svg_parse, ots::ots_svg_serialise, - ots::ots_svg_should_serialise, ots::ots_svg_free, false}, + { "MATH", ots::ots_math_parse, ots::ots_math_serialise, + ots::ots_math_should_serialise, ots::ots_math_free, false }, // TODO(bashi): Support mort, base, and jstf tables. { 0, NULL, NULL, NULL, NULL, false }, }; -bool IsValidVersionTag(uint32_t tag) { - return tag == Tag("\x00\x01\x00\x00") || - // OpenType fonts with CFF data have 'OTTO' tag. - tag == Tag("OTTO") || - // Older Mac fonts might have 'true' or 'typ1' tag. - tag == Tag("true") || - tag == Tag("typ1"); -} - bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature, ots::OTSStream *output, const uint8_t *data, size_t length, const std::vector<OpenTypeTable>& tables, ots::Buffer& file); bool ProcessTTF(ots::OpenTypeFile *header, @@ -202,17 +176,17 @@ bool ProcessTTF(ots::OpenTypeFile *heade // we disallow all files > 1GB in size for sanity. if (length > 1024 * 1024 * 1024) { return OTS_FAILURE_MSG_HDR("file exceeds 1GB"); } if (!file.ReadTag(&header->version)) { return OTS_FAILURE_MSG_HDR("error reading version tag"); } - if (!IsValidVersionTag(header->version)) { + if (!ots::IsValidVersionTag(header->version)) { return OTS_FAILURE_MSG_HDR("invalid version tag"); } if (!file.ReadU16(&header->num_tables) || !file.ReadU16(&header->search_range) || !file.ReadU16(&header->entry_selector) || !file.ReadU16(&header->range_shift)) { return OTS_FAILURE_MSG_HDR("error reading table directory search header"); @@ -288,17 +262,17 @@ bool ProcessWOFF(ots::OpenTypeFile *head if (woff_tag != Tag("wOFF")) { return OTS_FAILURE_MSG_HDR("invalid WOFF marker"); } if (!file.ReadTag(&header->version)) { return OTS_FAILURE_MSG_HDR("error reading version tag"); } - if (!IsValidVersionTag(header->version)) { + if (!ots::IsValidVersionTag(header->version)) { return OTS_FAILURE_MSG_HDR("invalid version tag"); } header->search_range = 0; header->entry_selector = 0; header->range_shift = 0; uint32_t reported_length; @@ -366,75 +340,144 @@ bool ProcessWOFF(ots::OpenTypeFile *head if (!file.ReadTag(&table.tag) || !file.ReadU32(&table.offset) || !file.ReadU32(&table.length) || !file.ReadU32(&table.uncompressed_length) || !file.ReadU32(&table.chksum)) { return OTS_FAILURE_MSG_HDR("error reading table directory"); } - total_sfnt_size += Round4(table.uncompressed_length); + total_sfnt_size += ots::Round4(table.uncompressed_length); if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) { return OTS_FAILURE_MSG_HDR("sfnt size overflow"); } tables.push_back(table); if (i == 0 || tables[first_index].offset > table.offset) first_index = i; if (i == 0 || tables[last_index].offset < table.offset) last_index = i; } if (reported_total_sfnt_size != total_sfnt_size) { return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch"); } // Table data must follow immediately after the header. - if (tables[first_index].offset != Round4(file.offset())) { + if (tables[first_index].offset != ots::Round4(file.offset())) { return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file"); } if (tables[last_index].offset >= length || length - tables[last_index].offset < tables[last_index].length) { return OTS_FAILURE_MSG_HDR("invalid table location/size"); } // Blocks must follow immediately after the previous block. // (Except for padding with a maximum of three null bytes) - uint64_t block_end = Round4( + uint64_t block_end = ots::Round4( static_cast<uint64_t>(tables[last_index].offset) + static_cast<uint64_t>(tables[last_index].length)); if (block_end > std::numeric_limits<uint32_t>::max()) { return OTS_FAILURE_MSG_HDR("invalid table location/size"); } if (meta_offset) { if (block_end != meta_offset) { return OTS_FAILURE_MSG_HDR("invalid metadata block location"); } - block_end = Round4(static_cast<uint64_t>(meta_offset) + - static_cast<uint64_t>(meta_length)); + block_end = ots::Round4(static_cast<uint64_t>(meta_offset) + + static_cast<uint64_t>(meta_length)); if (block_end > std::numeric_limits<uint32_t>::max()) { return OTS_FAILURE_MSG_HDR("invalid metadata block size"); } } if (priv_offset) { if (block_end != priv_offset) { return OTS_FAILURE_MSG_HDR("invalid private block location"); } - block_end = Round4(static_cast<uint64_t>(priv_offset) + - static_cast<uint64_t>(priv_length)); + block_end = ots::Round4(static_cast<uint64_t>(priv_offset) + + static_cast<uint64_t>(priv_length)); if (block_end > std::numeric_limits<uint32_t>::max()) { return OTS_FAILURE_MSG_HDR("invalid private block size"); } } - if (block_end != Round4(length)) { + if (block_end != ots::Round4(length)) { return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)"); } return ProcessGeneric(header, woff_tag, output, data, length, tables, file); } +#ifdef MOZ_OTS_WOFF2 +bool ProcessWOFF2(ots::OpenTypeFile *header, + ots::OTSStream *output, const uint8_t *data, size_t length) { + size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length); + if (decompressed_size == 0) { + return OTS_FAILURE(); + } + // decompressed font must be <= 30MB + if (decompressed_size > 30 * 1024 * 1024) { + return OTS_FAILURE(); + } + + std::vector<uint8_t> decompressed_buffer(decompressed_size); + if (!ots::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size, + data, length)) { + return OTS_FAILURE(); + } + return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size); +} +#endif + +ots::TableAction GetTableAction(uint32_t tag) { + ots::TableAction action = ots::TABLE_ACTION_DEFAULT; + + if (g_table_action_func != NULL) { + action = g_table_action_func(htonl(tag), g_table_action_user_data); + } + + if (action == ots::TABLE_ACTION_DEFAULT) { + action = ots::TABLE_ACTION_DROP; + + for (unsigned i = 0; ; ++i) { + if (table_parsers[i].parse == NULL) break; + + if (Tag(table_parsers[i].tag) == tag) { + action = ots::TABLE_ACTION_SANITIZE; + break; + } + } + } + + assert(action != ots::TABLE_ACTION_DEFAULT); // Should never return this. + return action; +} + +bool GetTableData(const uint8_t *data, + const OpenTypeTable table, + 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); + uLongf dest_len = *table_length; + int r = uncompress((Bytef*) *table_data, &dest_len, + data + table.offset, table.length); + if (r != Z_OK || dest_len != *table_length) { + return false; + } + } else { + // Uncompressed table. We can process directly from memory. + *table_data = data + table.offset; + *table_length = table.length; + } + + return true; +} + bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature, ots::OTSStream *output, const uint8_t *data, size_t length, const std::vector<OpenTypeTable>& tables, ots::Buffer& file) { const size_t data_offset = file.offset(); uint32_t uncompressed_sum = 0; @@ -490,17 +533,17 @@ bool ProcessGeneric(ots::OpenTypeFile *h uncompressed_sum += tables[i].uncompressed_length; } // since we required that the file be < 1GB in length, and that the table // length is < 1GB, the following addtion doesn't overflow uint32_t end_byte = tables[i].offset + tables[i].length; // Tables in the WOFF file must be aligned 4-byte boundary. if (signature == Tag("wOFF")) { - end_byte = Round4(end_byte); + end_byte = ots::Round4(end_byte); } if (!end_byte || end_byte > length) { return OTS_FAILURE_MSG_TAG("table overruns end of file", &tables[i].tag); } } // All decompressed tables uncompressed must be <= 30MB. if (uncompressed_sum > 30 * 1024 * 1024) { @@ -533,43 +576,33 @@ bool ProcessGeneric(ots::OpenTypeFile *h Arena arena; for (unsigned i = 0; ; ++i) { if (table_parsers[i].parse == NULL) break; const std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.find(Tag(table_parsers[i].tag)); + ots::TableAction action = GetTableAction(it->first); if (it == table_map.end()) { - if (table_parsers[i].required) { + if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) { return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag); } continue; } const uint8_t* table_data; size_t table_length; - if (it->second.uncompressed_length != it->second.length) { - // compressed table. Need to uncompress into memory first. - table_length = it->second.uncompressed_length; - table_data = arena.Allocate(table_length); - uLongf dest_len = table_length; - int r = uncompress((Bytef*) table_data, &dest_len, - data + it->second.offset, it->second.length); - if (r != Z_OK || dest_len != table_length) { - return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag); - } - } else { - // uncompressed table. We can process directly from memory. - table_data = data + it->second.offset; - table_length = it->second.length; + if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) { + return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag); } - if (!table_parsers[i].parse(header, table_data, table_length)) { + if (action == ots::TABLE_ACTION_SANITIZE && + !table_parsers[i].parse(header, table_data, table_length)) { // TODO: parsers should generate specific messages detailing the failure; // once those are all added, we won't need a generic failure message here return OTS_FAILURE_MSG_TAG("failed to parse table", table_parsers[i].tag); } } if (header->cff) { // font with PostScript glyph @@ -594,16 +627,24 @@ bool ProcessGeneric(ots::OpenTypeFile *h break; } if (table_parsers[i].should_serialise(header)) { num_output_tables++; } } + for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin(); + it != table_map.end(); ++it) { + ots::TableAction action = GetTableAction(it->first); + if (action == ots::TABLE_ACTION_PASSTHRU) { + num_output_tables++; + } + } + unsigned max_pow2 = 0; while (1u << (max_pow2 + 1) <= num_output_tables) { max_pow2++; } const uint16_t output_search_range = (1u << max_pow2) << 4; // most of the errors here are highly unlikely - they'd only occur if the // output stream returns a failure, e.g. lack of space to write @@ -658,16 +699,57 @@ bool ProcessGeneric(ots::OpenTypeFile *h // 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); } + for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin(); + it != table_map.end(); ++it) { + ots::TableAction action = GetTableAction(it->first); + if (action == ots::TABLE_ACTION_PASSTHRU) { + OutputTable out; + out.tag = it->second.tag; + out.offset = output->Tell(); + + output->ResetChecksum(); + if (it->second.tag == Tag("head")) { + head_table_offset = out.offset; + } + + 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"); + } + + 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); + } + } + const size_t end_of_file = output->Tell(); // Need to sort the output tables for inclusion in the file std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag); if (!output->Seek(table_record_offset)) { return OTS_FAILURE_MSG_HDR("error writing output"); } @@ -706,41 +788,64 @@ bool ProcessGeneric(ots::OpenTypeFile *h return true; } } // namespace namespace ots { +bool IsValidVersionTag(uint32_t tag) { + return tag == Tag("\x00\x01\x00\x00") || + // OpenType fonts with CFF data have 'OTTO' tag. + tag == Tag("OTTO") || + // Older Mac fonts might have 'true' or 'typ1' tag. + tag == Tag("true") || + tag == Tag("typ1"); +} + void DisableDebugOutput() { g_debug_output = false; } -bool OTS_API Process(OTSStream *output, const uint8_t *data, size_t length, -#ifdef MOZ_OTS_REPORT_ERRORS - MessageFunc message_func, void *user_data, +#ifdef MOZ_OTS_WOFF2 +void EnableWOFF2() { + g_enable_woff2 = true; +} #endif - bool preserveGraphite) { + +void SetMessageCallback(MessageFunc func, void *user_data) { + g_message_func = func; + g_message_user_data = user_data; +} + +void SetTableActionCallback(TableActionFunc func, void *user_data) { + g_table_action_func = func; + g_table_action_user_data = user_data; +} + +bool Process(OTSStream *output, const uint8_t *data, size_t length) { OpenTypeFile header; -#ifdef MOZ_OTS_REPORT_ERRORS - header.message_func = message_func; - header.user_data = user_data; -#endif + header.message_func = g_message_func; + header.user_data = g_message_user_data; if (length < 4) { return OTS_FAILURE_MSG_(&header, "file less than 4 bytes"); } - header.preserve_graphite = preserveGraphite; - bool result; if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') { result = ProcessWOFF(&header, output, data, length); +#ifdef MOZ_OTS_WOFF2 + } else if (g_enable_woff2 && + data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && + data[3] == '2') { + result = ProcessWOFF2(&header, output, data, length); +#endif } else { result = ProcessTTF(&header, output, data, length); } for (unsigned i = 0; ; ++i) { if (table_parsers[i].parse == NULL) break; table_parsers[i].free(&header); }
--- a/gfx/ots/src/ots.h +++ b/gfx/ots/src/ots.h @@ -6,19 +6,25 @@ #define OTS_H_ #include <stddef.h> #include <cstdarg> #include <cstddef> #include <cstdio> #include <cstdlib> #include <cstring> +#include <limits> #include "opentype-sanitiser.h" +// arraysize borrowed from base/basictypes.h +template <typename T, size_t N> +char (&ArraySizeHelper(T (&array)[N]))[N]; +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + namespace ots { #if defined(_MSC_VER) || !defined(OTS_DEBUG) #define OTS_FAILURE() false #else #define OTS_FAILURE() ots::Failure(__FILE__, __LINE__, __PRETTY_FUNCTION__) bool Failure(const char *f, int l, const char *fn); #endif @@ -33,49 +39,38 @@ bool Failure(const char *f, int l, const ots::Warning(__FILE__, __LINE__, format, ##args) void Warning(const char *f, int l, const char *format, ...) __attribute__((format(printf, 3, 4))); #else #define OTS_WARNING(format, args...) #endif #endif -#ifdef MOZ_OTS_REPORT_ERRORS - // All OTS_FAILURE_* macros ultimately evaluate to 'false', just like the original // message-less OTS_FAILURE(), so that the current parser will return 'false' as // its result (indicating a failure). -// If the message-callback feature is enabled, and a message_func pointer has been -// provided, this will be called before returning the 'false' status. +// If a message_func pointer has been provided, this will be called before returning +// the 'false' status. // Generate a simple message -#define OTS_FAILURE_MSG_(otf_,msg_) \ +#define OTS_FAILURE_MSG_(otf_,...) \ ((otf_)->message_func && \ - (*(otf_)->message_func)((otf_)->user_data, "%s", msg_) && \ + (*(otf_)->message_func)((otf_)->user_data, __VA_ARGS__) && \ false) // Generate a message with an associated table tag #define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \ ((otf_)->message_func && \ - (*(otf_)->message_func)((otf_)->user_data, "table '%4.4s': %s", tag_, msg_) && \ + (*(otf_)->message_func)((otf_)->user_data, "%4.4s: %s", tag_, msg_) && \ false) // Convenience macro 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. -#define OTS_FAILURE_MSG(msg_) OTS_FAILURE_MSG_TAG_(file, msg_, TABLE_NAME) - -#else - -// If the message-callback feature is not enabled, error messages are just dropped. -#define OTS_FAILURE_MSG_(otf_,msg_) OTS_FAILURE() -#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) OTS_FAILURE() -#define OTS_FAILURE_MSG(msg_) OTS_FAILURE() - -#endif +#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__) // Define OTS_NO_TRANSCODE_HINTS (i.e., g++ -DOTS_NO_TRANSCODE_HINTS) if you // want to omit TrueType hinting instructions and variables in glyf, fpgm, prep, // and cvt tables. #if defined(OTS_NO_TRANSCODE_HINTS) const bool g_transcode_hints = false; #else const bool g_transcode_hints = true; @@ -85,35 +80,35 @@ const bool g_transcode_hints = true; // Buffer helper class // // This class perform some trival buffer operations while checking for // out-of-bounds errors. As a family they return false if anything is amiss, // updating the current offset otherwise. // ----------------------------------------------------------------------------- class Buffer { public: - Buffer(const uint8_t *buffer, size_t len) - : buffer_(buffer), + Buffer(const uint8_t *buf, size_t len) + : buffer_(buf), length_(len), offset_(0) { } bool Skip(size_t n_bytes) { return Read(NULL, n_bytes); } - bool Read(uint8_t *buffer, size_t n_bytes) { + bool Read(uint8_t *buf, size_t n_bytes) { if (n_bytes > 1024 * 1024 * 1024) { return OTS_FAILURE(); } if ((offset_ + n_bytes > length_) || (offset_ > length_ - n_bytes)) { return OTS_FAILURE(); } - if (buffer) { - std::memcpy(buffer, buffer_ + offset_, n_bytes); + if (buf) { + std::memcpy(buf, buffer_ + offset_, n_bytes); } offset_ += n_bytes; return true; } inline bool ReadU8(uint8_t *value) { if (offset_ + 1 > length_) { return OTS_FAILURE(); @@ -187,16 +182,34 @@ class Buffer { void set_offset(size_t newoffset) { offset_ = newoffset; } private: const uint8_t * const buffer_; const size_t length_; size_t offset_; }; +// Round a value up to the nearest multiple of 4. Don't round the value in the +// case that rounding up overflows. +template<typename T> T Round4(T value) { + if (std::numeric_limits<T>::max() - value < 3) { + return value; + } + return (value + 3) & ~3; +} + +template<typename T> T Round2(T value) { + if (value == std::numeric_limits<T>::max()) { + return value; + } + return (value + 1) & ~1; +} + +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) \ @@ -204,31 +217,26 @@ class Buffer { 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) \ - F(silf, SILF) \ - F(sill, SILL) \ - F(glat, GLAT) \ - F(gloc, GLOC) \ - F(feat, FEAT) \ - F(svg, SVG) + F(vmtx, VMTX) #define F(name, capname) struct OpenType##capname; FOR_EACH_TABLE_TYPE #undef F struct OpenTypeFile { OpenTypeFile() { #define F(name, capname) name = NULL; @@ -237,24 +245,18 @@ struct OpenTypeFile { } uint32_t version; uint16_t num_tables; uint16_t search_range; uint16_t entry_selector; uint16_t range_shift; -#ifdef MOZ_OTS_REPORT_ERRORS MessageFunc message_func; void *user_data; -#endif - - // This is used to tell the relevant parsers whether to preserve the - // Graphite layout tables (currently _without_ any checking) - bool preserve_graphite; #define F(name, capname) OpenType##capname *name; FOR_EACH_TABLE_TYPE #undef F }; #define F(name, capname) \ bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
--- a/gfx/ots/src/post.cc +++ b/gfx/ots/src/post.cc @@ -2,177 +2,178 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "post.h" #include "maxp.h" // post - PostScript -// http://www.microsoft.com/opentype/otspec/post.htm +// http://www.microsoft.com/typography/otspec/post.htm + +#define TABLE_NAME "post" namespace ots { bool ots_post_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypePOST *post = new OpenTypePOST; file->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(); + return OTS_FAILURE_MSG("Failed to read post header"); } 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) { // 0x00025000 is deprecated. We don't accept it. - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad post version %x", post->version); } // 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(); + 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(); + return OTS_FAILURE_MSG("Failed to read number of glyphs"); } if (!file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("No maxp table required by post table"); } if (num_glyphs == 0) { if (file->maxp->num_glyphs > 258) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Can't have no glyphs in the post table if there are more than 256 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; } if (num_glyphs != file->maxp->num_glyphs) { // Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values. - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad number of glyphs in post table %d", num_glyphs); } post->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(); + return OTS_FAILURE_MSG("Failed to read post information for glyph %d", i); } - if (post->glyph_name_index[i] >= 32768) { - // Note: droid_arialuni.ttf fails this test. - return OTS_FAILURE(); // reserved area. - } + // Note: A strict interpretation of the specification requires name indexes + // are less than 32768. This, however, excludes fonts like unifont.ttf + // which cover all of unicode. } // Now we have an array of Pascal strings. We have to check that they are all // valid and read them in. const size_t strings_offset = table.offset(); const uint8_t *strings = data + strings_offset; const uint8_t *strings_end = data + length; for (;;) { if (strings == strings_end) break; const unsigned string_length = *strings; if (strings + 1 + string_length > strings_end) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad string length %d", string_length); } if (std::memchr(strings + 1, '\0', string_length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad string of length %d", string_length); } post->names.push_back( std::string(reinterpret_cast<const char*>(strings + 1), string_length)); strings += 1 + string_length; } const unsigned num_strings = post->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]; if (offset < 258) { continue; } offset -= 258; if (offset >= num_strings) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad string index %d", offset); } } return true; } bool ots_post_should_serialise(OpenTypeFile *file) { return file->post != NULL; } bool ots_post_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypePOST *post = file->post; // OpenType with CFF glyphs must have v3 post table. if (file->post && file->cff && file->post->version != 0x00030000) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad post version %x", post->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) || !out->WriteU32(0) || !out->WriteU32(0) || !out->WriteU32(0) || !out->WriteU32(0)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write post header"); } if (post->version != 0x00020000) { return true; // v1.0 and v3.0 does not have glyph names. } if (!out->WriteU16(post->glyph_name_index.size())) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write number of indices"); } for (unsigned i = 0; i < post->glyph_name_index.size(); ++i) { if (!out->WriteU16(post->glyph_name_index[i])) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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]; const uint8_t string_length = s.size(); if (!out->Write(&string_length, 1)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Failed to write string length for string %d", i); } } return true; } void ots_post_free(OpenTypeFile *file) { delete file->post;
--- a/gfx/ots/src/prep.cc +++ b/gfx/ots/src/prep.cc @@ -1,48 +1,50 @@ // 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. #include "prep.h" // prep - Control Value Program -// http://www.microsoft.com/opentype/otspec/prep.htm +// http://www.microsoft.com/typography/otspec/prep.htm + +#define TABLE_NAME "prep" namespace ots { bool ots_prep_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypePREP *prep = new OpenTypePREP; file->prep = prep; if (length >= 128 * 1024u) { - return OTS_FAILURE(); // almost all prep tables are less than 9k bytes. + return OTS_FAILURE_MSG("table length %ld > 120K", length); // almost all prep tables are less than 9k bytes. } if (!table.Skip(length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read table of length %ld", length); } prep->data = data; prep->length = length; return true; } bool ots_prep_should_serialise(OpenTypeFile *file) { if (!file->glyf) return false; // this table is not for CFF fonts. return g_transcode_hints && file->prep; } bool ots_prep_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypePREP *prep = file->prep; if (!out->Write(prep->data, prep->length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write table length"); } return true; } void ots_prep_free(OpenTypeFile *file) { delete file->prep; }
deleted file mode 100644 --- a/gfx/ots/src/svg.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2012 Mozilla Foundation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ots.h" - -#include "svg.h" - -#define NONFATAL_FAILURE(msg) \ - do { \ - OTS_WARNING(msg); \ - delete file->svg; file->svg = 0; \ - return true; \ - } while (0) - -namespace ots { - -bool ots_svg_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { - Buffer table(data, length); - - OpenTypeSVG *svg = new OpenTypeSVG; - file->svg = svg; - - uint16_t version; - if (!table.ReadU16(&version)) { - NONFATAL_FAILURE("Couldn't read SVG table header"); - } - if (version != 0) { - NONFATAL_FAILURE("Unknown SVG table version"); - } - - uint32_t doc_index_offset; - if (!table.ReadU32(&doc_index_offset)) { - NONFATAL_FAILURE("Couldn't read doc index offset from SVG table header"); - } - if (doc_index_offset == 0 || doc_index_offset >= length) { - NONFATAL_FAILURE("Invalid doc index offset"); - } - - uint32_t color_palettes_offset; - if (!table.ReadU32(&color_palettes_offset)) { - NONFATAL_FAILURE("Couldn't read color palettes offset from SVG table header"); - } - if (color_palettes_offset >= length) { - NONFATAL_FAILURE("Invalid doc index offset"); - } - - uint16_t start_glyph; - uint16_t end_glyph; - uint32_t doc_offset; - uint32_t doc_length; - uint16_t last_end_glyph = 0; - - table.set_offset(doc_index_offset); - uint16_t index_length; - if (!table.ReadU16(&index_length)) { - NONFATAL_FAILURE("Couldn't read SVG documents index"); - } - if (index_length == 0) { - NONFATAL_FAILURE("Zero-length documents index"); - } - - for (uint16_t i = 0; i < index_length; i++) { - if (!table.ReadU16(&start_glyph) || - !table.ReadU16(&end_glyph) || - !table.ReadU32(&doc_offset) || - !table.ReadU32(&doc_length)) { - NONFATAL_FAILURE("Couldn't read SVG table index"); - } - - if (end_glyph < start_glyph) { - NONFATAL_FAILURE("Bad SVG table index range"); - } - - if (last_end_glyph && start_glyph <= last_end_glyph) { - NONFATAL_FAILURE("SVG table index range overlapping or not sorted"); - } - - if (doc_offset > 1024 * 1024 * 1024 || - doc_length > 1024 * 1024 * 1024) { - NONFATAL_FAILURE("Bad SVG document length"); - } - - if (uint64_t(doc_index_offset) + doc_offset + doc_length > length) { - NONFATAL_FAILURE("SVG table document overflows table"); - } - - last_end_glyph = end_glyph; - } - - svg->data = data; - svg->length = length; - - return true; -} - -bool ots_svg_serialise(OTSStream *out, OpenTypeFile *file) { - OpenTypeSVG *svg = file->svg; - - if (!out->Write(svg->data, svg->length)) { - return OTS_FAILURE(); - } - - return true; -} - -bool ots_svg_should_serialise(OpenTypeFile *file) { - return file->svg; -} - -void ots_svg_free(OpenTypeFile *file) { - delete file->svg; -} - -}
deleted file mode 100644 --- a/gfx/ots/src/svg.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2012 Mozilla Foundation. 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_SVG_H -#define OTS_SVG_H - -#include <map> - -#include "ots.h" - -namespace ots { - -struct OpenTypeSVG { - const uint8_t *data; - size_t length; -}; - -} - -#endif
--- a/gfx/ots/src/vdmx.cc +++ b/gfx/ots/src/vdmx.cc @@ -1,48 +1,54 @@ // 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. #include "vdmx.h" // VDMX - Vertical Device Metrics -// http://www.microsoft.com/opentype/otspec/vdmx.htm +// http://www.microsoft.com/typography/otspec/vdmx.htm + +#define TABLE_NAME "VDMX" #define DROP_THIS_TABLE \ - do { delete file->vdmx; file->vdmx = 0; } while (0) + do { \ + delete file->vdmx; \ + file->vdmx = 0; \ + OTS_FAILURE_MSG("Table discarded"); \ + } while (0) namespace ots { bool ots_vdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); file->vdmx = new OpenTypeVDMX; OpenTypeVDMX * const vdmx = file->vdmx; if (!table.ReadU16(&vdmx->version) || !table.ReadU16(&vdmx->num_recs) || !table.ReadU16(&vdmx->num_ratios)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read table header"); } if (vdmx->version > 1) { OTS_WARNING("bad version: %u", vdmx->version); DROP_THIS_TABLE; return true; // continue transcoding } vdmx->rat_ranges.reserve(vdmx->num_ratios); for (unsigned i = 0; i < vdmx->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(); + return OTS_FAILURE_MSG("Failed to read ratio header %d", i); } if (rec.charset > 1) { OTS_WARNING("bad charset: %u", rec.charset); DROP_THIS_TABLE; return true; } @@ -68,40 +74,40 @@ bool ots_vdmx_parse(OpenTypeFile *file, } vdmx->offsets.reserve(vdmx->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) { uint16_t offset; if (!table.ReadU16(&offset)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read ratio offset %d", i); } if (current_offset + offset >= length) { // thus doesn't overflow. - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset, i); } vdmx->offsets.push_back(offset); } vdmx->groups.reserve(vdmx->num_recs); for (unsigned i = 0; i < vdmx->num_recs; ++i) { OpenTypeVDMXGroup group; if (!table.ReadU16(&group.recs) || !table.ReadU8(&group.startsz) || !table.ReadU8(&group.endsz)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read record header %d", i); } group.entries.reserve(group.recs); for (unsigned j = 0; j < group.recs; ++j) { OpenTypeVDMXVTable vt; if (!table.ReadU16(&vt.y_pel_height) || !table.ReadS16(&vt.y_max) || !table.ReadS16(&vt.y_min)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i, j); } if (vt.y_max < vt.y_min) { OTS_WARNING("bad y min/max"); DROP_THIS_TABLE; return true; } // This table must appear in sorted order (sorted by yPelHeight), @@ -126,48 +132,48 @@ bool ots_vdmx_should_serialise(OpenTypeF } bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) { OpenTypeVDMX * const vdmx = file->vdmx; if (!out->WriteU16(vdmx->version) || !out->WriteU16(vdmx->num_recs) || !out->WriteU16(vdmx->num_ratios)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write table header"); } for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) { const OpenTypeVDMXRatioRecord& rec = vdmx->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(); + return OTS_FAILURE_MSG("Failed to write ratio %d", i); } } for (unsigned i = 0; i < vdmx->offsets.size(); ++i) { if (!out->WriteU16(vdmx->offsets[i])) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write ratio offset %d", i); } } for (unsigned i = 0; i < vdmx->groups.size(); ++i) { const OpenTypeVDMXGroup& group = vdmx->groups[i]; if (!out->WriteU16(group.recs) || !out->Write(&group.startsz, 1) || !out->Write(&group.endsz, 1)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("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(); + return OTS_FAILURE_MSG("Failed to write group %d entry %d", i, j); } } } return true; } void ots_vdmx_free(OpenTypeFile *file) {
--- a/gfx/ots/src/vhea.cc +++ b/gfx/ots/src/vhea.cc @@ -4,51 +4,53 @@ #include "vhea.h" #include "gsub.h" #include "head.h" #include "maxp.h" // vhea - Vertical Header Table -// http://www.microsoft.com/opentype/otspec/vhea.htm +// http://www.microsoft.com/typography/otspec/vhea.htm + +#define TABLE_NAME "vhea" namespace ots { bool ots_vhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeVHEA *vhea = new OpenTypeVHEA; file->vhea = vhea; if (!table.ReadU32(&vhea->header.version)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read version"); } if (vhea->header.version != 0x00010000 && vhea->header.version != 0x00011000) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Bad vhea version %x", vhea->header.version); } if (!ParseMetricsHeader(file, &table, &vhea->header)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse metrics in vhea"); } return true; } bool ots_vhea_should_serialise(OpenTypeFile *file) { // vhea should'nt serialise when vmtx doesn't exist. // Firefox developer pointed out that vhea/vmtx should serialise iff GSUB is // preserved. See http://crbug.com/77386 return file->vhea != NULL && file->vmtx != NULL && ots_gsub_should_serialise(file); } bool ots_vhea_serialise(OTSStream *out, OpenTypeFile *file) { - if (!SerialiseMetricsHeader(out, &file->vhea->header)) { - return OTS_FAILURE(); + if (!SerialiseMetricsHeader(file, out, &file->vhea->header)) { + return OTS_FAILURE_MSG("Failed to write vhea metrics"); } return true; } void ots_vhea_free(OpenTypeFile *file) { delete file->vhea; }
--- a/gfx/ots/src/vmtx.cc +++ b/gfx/ots/src/vmtx.cc @@ -4,47 +4,49 @@ #include "vmtx.h" #include "gsub.h" #include "maxp.h" #include "vhea.h" // vmtx - Vertical Metrics Table -// http://www.microsoft.com/opentype/otspec/vmtx.htm +// http://www.microsoft.com/typography/otspec/vmtx.htm + +#define TABLE_NAME "vmtx" namespace ots { bool ots_vmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); OpenTypeVMTX *vmtx = new OpenTypeVMTX; file->vmtx = vmtx; if (!file->vhea || !file->maxp) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("vhea or maxp table missing as needed by vmtx"); } - if (!ParseMetricsTable(&table, file->maxp->num_glyphs, + if (!ParseMetricsTable(file, &table, file->maxp->num_glyphs, &file->vhea->header, &vmtx->metrics)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to parse vmtx metrics"); } return true; } bool ots_vmtx_should_serialise(OpenTypeFile *file) { // vmtx should serialise when vhea and GSUB are preserved. // See the comment in ots_vhea_should_serialise(). return file->vmtx != NULL && file->vhea != NULL && ots_gsub_should_serialise(file); } bool ots_vmtx_serialise(OTSStream *out, OpenTypeFile *file) { - if (!SerialiseMetricsTable(out, &file->vmtx->metrics)) { - return OTS_FAILURE(); + if (!SerialiseMetricsTable(file, out, &file->vmtx->metrics)) { + return OTS_FAILURE_MSG("Failed to write vmtx metrics"); } return true; } void ots_vmtx_free(OpenTypeFile *file) { delete file->vmtx; }
--- a/gfx/ots/src/vorg.cc +++ b/gfx/ots/src/vorg.cc @@ -2,34 +2,40 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "vorg.h" #include <vector> // VORG - Vertical Origin Table -// http://www.microsoft.com/opentype/otspec/vorg.htm +// http://www.microsoft.com/typography/otspec/vorg.htm + +#define TABLE_NAME "VORG" #define DROP_THIS_TABLE \ - do { delete file->vorg; file->vorg = 0; } while (0) + do { \ + delete file->vorg; \ + file->vorg = 0; \ + OTS_FAILURE_MSG("Table discarded"); \ + } while (0) namespace ots { bool ots_vorg_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { Buffer table(data, length); file->vorg = new OpenTypeVORG; OpenTypeVORG * const vorg = file->vorg; uint16_t num_recs; if (!table.ReadU16(&vorg->major_version) || !table.ReadU16(&vorg->minor_version) || !table.ReadS16(&vorg->default_vert_origin_y) || !table.ReadU16(&num_recs)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read header"); } if (vorg->major_version != 1) { OTS_WARNING("bad major version: %u", vorg->major_version); DROP_THIS_TABLE; return true; } if (vorg->minor_version != 0) { OTS_WARNING("bad minor version: %u", vorg->minor_version); @@ -44,17 +50,17 @@ bool ots_vorg_parse(OpenTypeFile *file, uint16_t last_glyph_index = 0; vorg->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(); + return OTS_FAILURE_MSG("Failed to read record %d", i); } if ((i != 0) && (rec.glyph_index <= last_glyph_index)) { OTS_WARNING("the table is not sorted"); DROP_THIS_TABLE; return true; } last_glyph_index = rec.glyph_index; @@ -71,24 +77,24 @@ bool ots_vorg_should_serialise(OpenTypeF bool ots_vorg_serialise(OTSStream *out, OpenTypeFile *file) { OpenTypeVORG * const vorg = file->vorg; if (!out->WriteU16(vorg->major_version) || !out->WriteU16(vorg->minor_version) || !out->WriteS16(vorg->default_vert_origin_y) || !out->WriteU16(vorg->metrics.size())) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write table header"); } for (unsigned i = 0; i < vorg->metrics.size(); ++i) { const OpenTypeVORGMetrics& rec = vorg->metrics[i]; if (!out->WriteU16(rec.glyph_index) || !out->WriteS16(rec.vert_origin_y)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to write record %d", i); } } return true; } void ots_vorg_free(OpenTypeFile *file) { delete file->vorg;
--- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -293,22 +293,26 @@ gfxPlatform::gfxPlatform() mFallbackUsesCmaps = UNINITIALIZED_VALUE; mWordCacheCharLimit = UNINITIALIZED_VALUE; mWordCacheMaxEntries = UNINITIALIZED_VALUE; mGraphiteShapingEnabled = UNINITIALIZED_VALUE; mOpenTypeSVGEnabled = UNINITIALIZED_VALUE; mBidiNumeralOption = UNINITIALIZED_VALUE; - mLayersPreferMemoryOverShmem = - XRE_GetProcessType() == GeckoProcessType_Default && - Preferences::GetBool("layers.prefer-memory-over-shmem", true); + mLayersPreferMemoryOverShmem = XRE_GetProcessType() == GeckoProcessType_Default; +#ifdef XP_WIN + // XXX - When 957560 is fixed, the pref can go away entirely mLayersUseDeprecated = - Preferences::GetBool("layers.use-deprecated-textures", true); + Preferences::GetBool("layers.use-deprecated-textures", true) + && !Preferences::GetBool("layers.prefer-opengl", false); +#else + mLayersUseDeprecated = false; +#endif Preferences::AddBoolVarCache(&mDrawLayerBorders, "layers.draw-borders", false); Preferences::AddBoolVarCache(&mDrawTileBorders, "layers.draw-tile-borders", false); Preferences::AddBoolVarCache(&mDrawBigImageBorders,
--- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -316,17 +316,31 @@ public: private: void* mPtr; size_t mLength; const size_t mLimit; off_t mOff; }; -#ifdef MOZ_OTS_REPORT_ERRORS +static ots::TableAction +OTSTableAction(uint32_t aTag, void *aUserData) +{ + // preserve Graphite and SVG tables + if (aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') || + aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') || + aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') || + aTag == TRUETYPE_TAG('G', 'l', 'a', 't') || + aTag == TRUETYPE_TAG('F', 'e', 'a', 't') || + aTag == TRUETYPE_TAG('S', 'V', 'G', ' ')) { + return ots::TABLE_ACTION_PASSTHRU; + } + return ots::TABLE_ACTION_DEFAULT; +} + struct OTSCallbackUserData { gfxUserFontSet *mFontSet; gfxMixedFontFamily *mFamily; gfxProxyFontEntry *mProxy; }; /* static */ bool gfxUserFontSet::OTSMessage(void *aUserData, const char *format, ...) @@ -341,43 +355,38 @@ gfxUserFontSet::OTSMessage(void *aUserDa va_end(va); OTSCallbackUserData *d = static_cast<OTSCallbackUserData*>(aUserData); d->mFontSet->LogMessage(d->mFamily, d->mProxy, buf); return false; } -#endif // Call the OTS library to sanitize an sfnt before attempting to use it. // Returns a newly-allocated block, or nullptr in case of fatal errors. const uint8_t* gfxUserFontSet::SanitizeOpenTypeData(gfxMixedFontFamily *aFamily, gfxProxyFontEntry *aProxy, const uint8_t* aData, uint32_t aLength, uint32_t& aSaneLength, bool aIsCompressed) { // limit output/expansion to 256MB ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength, 1024 * 1024 * 256); -#ifdef MOZ_OTS_REPORT_ERRORS OTSCallbackUserData userData; userData.mFontSet = this; userData.mFamily = aFamily; userData.mProxy = aProxy; -#define ERROR_REPORTING_ARGS &gfxUserFontSet::OTSMessage, &userData, -#else -#define ERROR_REPORTING_ARGS -#endif - if (ots::Process(&output, aData, aLength, - ERROR_REPORTING_ARGS - true)) { + ots::SetTableActionCallback(&OTSTableAction, nullptr); + ots::SetMessageCallback(&gfxUserFontSet::OTSMessage, &userData); + + if (ots::Process(&output, aData, aLength)) { aSaneLength = output.Tell(); return static_cast<uint8_t*>(output.forget()); } else { aSaneLength = 0; return nullptr; } }
--- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -409,19 +409,17 @@ protected: const uint8_t* SanitizeOpenTypeData(gfxMixedFontFamily *aFamily, gfxProxyFontEntry *aProxy, const uint8_t* aData, uint32_t aLength, uint32_t& aSaneLength, bool aIsCompressed); -#ifdef MOZ_OTS_REPORT_ERRORS static bool OTSMessage(void *aUserData, const char *format, ...); -#endif // font families defined by @font-face rules nsRefPtrHashtable<nsStringHashKey, gfxMixedFontFamily> mFontFamilies; uint64_t mGeneration; static PRLogModuleInfo* GetUserFontsLog();
--- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -298,13 +298,12 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind if CONFIG[var]: DEFINES[var] = True if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': # This is set for "normal Android", that is, when Gecko is running on # top of the android java runtime. DEFINES['MOZ_USING_ANDROID_JAVA_WIDGETS'] = True -DEFINES['MOZ_OTS_REPORT_ERRORS'] = True DEFINES['GRAPHITE2_STATIC'] = True if CONFIG['OS_TARGET'] == 'WINNT': DEFINES['OTS_DLL'] = True
--- a/js/src/jit-test/tests/auto-regress/bug746377.js +++ b/js/src/jit-test/tests/auto-regress/bug746377.js @@ -1,14 +1,10 @@ // |jit-test| error:InternalError -// This test is temporarily disabled in GGC builds (bug 950932). -if (getBuildConfiguration()['generational-gc']) - (function f() { f(); })(); - // Binary: cache/js-dbg-64-67bf9a4a1f77-linux // Flags: --ion-eager // var actual = ''; test(); function test() {
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug964229-2.js @@ -0,0 +1,58 @@ +function test1(re, test) { + return re.test(test); +} + +assertEq(true, test1(/undefined/, undefined)); +assertEq(true, test1(/undefined/, undefined)); + +function test2(re, test) { + return re.test(test); +} + +assertEq(true, test2(/null/, null)); +assertEq(true, test2(/null/, null)); + +function test3(re, test) { + return re.test(test); +} + +assertEq(true, test3(/0/, 0)); +assertEq(true, test3(/0/, 0)); + +function test4(re, test) { + return re.test(test); +} + +assertEq(true, test4(/12.12/, 12.12)); +assertEq(true, test4(/12.12/, 12.12)); + +function test5(re, test) { + return re.test(test); +} + +assertEq(true, test5(/true/, true)); +assertEq(true, test5(/false/, false)); +assertEq(true, test5(/true/, true)); +assertEq(true, test5(/false/, false)); + +function test6(re, test) { + return re.test(test); +} + +assertEq(true, test6(/object/, {})); +assertEq(true, test6(/object/, {})); + +assertEq(true, test1(/test/, "test")); +assertEq(true, test1(/test/, "test")); +assertEq(true, test1(/undefined/, undefined)); +assertEq(true, test1(/undefined/, undefined)); +assertEq(true, test1(/null/, null)); +assertEq(true, test1(/null/, null)); +assertEq(true, test1(/0.1/, 0.1)); +assertEq(true, test1(/0.1/, 0.1)); +assertEq(true, test1(/20000/, 20000)); +assertEq(true, test1(/20000/, 20000)); +assertEq(true, test1(/object/, {})); +assertEq(true, test1(/object/, {})); + +
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug964229.js @@ -0,0 +1,26 @@ +a = 'a'; +b = 0 +var i=0; +exhaustiveSliceTest("exhaustive slice test 1", a); +var i=1; +exhaustiveSliceTest("exhaustive slice test 2", b); +exhaustiveSliceTest("exhaustive slice test 3", 0); +var i=0; +var executed = false; +try { + exhaustiveSliceTest("exhaustive slice test 4", 0); +} catch(e) { + executed = true; +} +assertEq(executed, true); + +function exhaustiveSliceTest(testname, a) { + print(testname) + for (var y = 0; y < 2; y++) + { + print(a.length) + if (a.length == 2 || i == 1) + return 0; + var b = a.slice(0,0); + } +}
--- a/js/src/jit-test/tests/v8-v5/check-raytrace.js +++ b/js/src/jit-test/tests/v8-v5/check-raytrace.js @@ -1,13 +1,8 @@ - -// This test is temporarily disabled in GGC builds (bug 950931). -if (getBuildConfiguration()['generational-gc']) - quit(); - // The ray tracer code in this file is written by Adam Burmister. It // is available in its original form from: // // http://labs.flog.nz.co/raytracer/ // // It has been modified slightly by Google to work as a standalone // benchmark, but the all the computational code remains // untouched. This file also contains a copy of parts of the Prototype
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -692,16 +692,46 @@ CodeGenerator::visitTypeObjectDispatch(L } // Unknown function: jump to fallback block. LBlock *fallback = mir->getFallback()->lir(); masm.jump(fallback->label()); return true; } +bool +CodeGenerator::visitBooleanToString(LBooleanToString *lir) +{ + Register input = ToRegister(lir->input()); + Register output = ToRegister(lir->output()); + const JSAtomState &names = GetIonContext()->runtime->names(); + Label true_, done; + + masm.branchTest32(Assembler::NonZero, input, input, &true_); + masm.movePtr(ImmGCPtr(names.false_), output); + masm.jump(&done); + + masm.bind(&true_); + masm.movePtr(ImmGCPtr(names.true_), output); + + masm.bind(&done); + + return true; +} + +void +CodeGenerator::emitIntToString(Register input, Register output, Label *ool) +{ + masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), ool); + + // Fast path for small integers. + masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().intStaticTable), output); + masm.loadPtr(BaseIndex(output, input, ScalePointer), output); +} + typedef JSFlatString *(*IntToStringFn)(ThreadSafeContext *, int); typedef JSFlatString *(*IntToStringParFn)(ForkJoinSlice *, int); static const VMFunctionsModal IntToStringInfo = VMFunctionsModal( FunctionInfo<IntToStringFn>(Int32ToString<CanGC>), FunctionInfo<IntToStringParFn>(IntToStringPar)); bool CodeGenerator::visitIntToString(LIntToString *lir) @@ -709,21 +739,17 @@ CodeGenerator::visitIntToString(LIntToSt Register input = ToRegister(lir->input()); Register output = ToRegister(lir->output()); OutOfLineCode *ool = oolCallVM(IntToStringInfo, lir, (ArgList(), input), StoreRegisterTo(output)); if (!ool) return false; - masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), - ool->entry()); - - masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().intStaticTable), output); - masm.loadPtr(BaseIndex(output, input, ScalePointer), output); + emitIntToString(input, output, ool->entry()); masm.bind(ool->rejoin()); return true; } typedef JSString *(*DoubleToStringFn)(ThreadSafeContext *, double); typedef JSString *(*DoubleToStringParFn)(ForkJoinSlice *, double); static const VMFunctionsModal DoubleToStringInfo = VMFunctionsModal( @@ -737,23 +763,109 @@ CodeGenerator::visitDoubleToString(LDoub Register temp = ToRegister(lir->tempInt()); Register output = ToRegister(lir->output()); OutOfLineCode *ool = oolCallVM(DoubleToStringInfo, lir, (ArgList(), input), StoreRegisterTo(output)); if (!ool) return false; + // Try double to integer conversion and run integer to string code. masm.convertDoubleToInt32(input, temp, ool->entry(), true); - masm.branch32(Assembler::AboveOrEqual, temp, Imm32(StaticStrings::INT_STATIC_LIMIT), - ool->entry()); - - masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().intStaticTable), output); - masm.loadPtr(BaseIndex(output, temp, ScalePointer), output); - + emitIntToString(temp, output, ool->entry()); + + masm.bind(ool->rejoin()); + return true; +} + +typedef JSString *(*PrimitiveToStringFn)(JSContext *, HandleValue); +typedef JSString *(*PrimitiveToStringParFn)(ForkJoinSlice *, HandleValue); +static const VMFunctionsModal PrimitiveToStringInfo = VMFunctionsModal( + FunctionInfo<PrimitiveToStringFn>(ToStringSlow), + FunctionInfo<PrimitiveToStringParFn>(PrimitiveToStringPar)); + +bool +CodeGenerator::visitPrimitiveToString(LPrimitiveToString *lir) +{ + ValueOperand input = ToValue(lir, LPrimitiveToString::Input); + Register output = ToRegister(lir->output()); + + OutOfLineCode *ool = oolCallVM(PrimitiveToStringInfo, lir, (ArgList(), input), + StoreRegisterTo(output)); + if (!ool) + return false; + + Label done; + Register tag = masm.splitTagForTest(input); + const JSAtomState &names = GetIonContext()->runtime->names(); + + // String + if (lir->mir()->input()->mightBeType(MIRType_String)) { + Label notString; + masm.branchTestString(Assembler::NotEqual, tag, ¬String); + masm.unboxString(input, output); + masm.jump(&done); + masm.bind(¬String); + } + + // Integer + if (lir->mir()->input()->mightBeType(MIRType_Int32)) { + Label notInteger; + masm.branchTestInt32(Assembler::NotEqual, tag, ¬Integer); + Register unboxed = ToTempUnboxRegister(lir->tempToUnbox()); + unboxed = masm.extractInt32(input, unboxed); + emitIntToString(unboxed, output, ool->entry()); + masm.jump(&done); + masm.bind(¬Integer); + } + + // Double + if (lir->mir()->input()->mightBeType(MIRType_Double)) { + // Note: no fastpath. Need two extra registers and can only convert doubles + // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT. + masm.branchTestDouble(Assembler::Equal, tag, ool->entry()); + } + + // Undefined + if (lir->mir()->input()->mightBeType(MIRType_Undefined)) { + Label notUndefined; + masm.branchTestUndefined(Assembler::NotEqual, tag, ¬Undefined); + masm.movePtr(ImmGCPtr(names.undefined), output); + masm.jump(&done); + masm.bind(¬Undefined); + } + + // Null + if (lir->mir()->input()->mightBeType(MIRType_Null)) { + Label notNull; + masm.branchTestNull(Assembler::NotEqual, tag, ¬Null); + masm.movePtr(ImmGCPtr(names.null), output); + masm.jump(&done); + masm.bind(¬Null); + } + + // Boolean + if (lir->mir()->input()->mightBeType(MIRType_Boolean)) { + Label notBoolean, true_; + masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean); + masm.branchTestBooleanTruthy(true, input, &true_); + masm.movePtr(ImmGCPtr(names.false_), output); + masm.jump(&done); + masm.bind(&true_); + masm.movePtr(ImmGCPtr(names.true_), output); + masm.jump(&done); + masm.bind(¬Boolean); + } + +#ifdef DEBUG + // Objects are not supported or we see a type that wasn't accounted for. + masm.assumeUnreachable("Unexpected type for MPrimitiveToString."); +#endif + + masm.bind(&done); masm.bind(ool->rejoin()); return true; } typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *); static const VMFunction CloneRegExpObjectInfo = FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject);
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -82,18 +82,21 @@ class CodeGenerator : public CodeGenerat bool visitDoubleToFloat32(LDoubleToFloat32 *lir); bool visitInt32ToFloat32(LInt32ToFloat32 *lir); bool visitInt32ToDouble(LInt32ToDouble *lir); void emitOOLTestObject(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch); bool visitTestOAndBranch(LTestOAndBranch *lir); bool visitTestVAndBranch(LTestVAndBranch *lir); bool visitFunctionDispatch(LFunctionDispatch *lir); bool visitTypeObjectDispatch(LTypeObjectDispatch *lir); + bool visitBooleanToString(LBooleanToString *lir); + void emitIntToString(Register input, Register output, Label *ool); bool visitIntToString(LIntToString *lir); bool visitDoubleToString(LDoubleToString *lir); + bool visitPrimitiveToString(LPrimitiveToString *lir); bool visitInteger(LInteger *lir); bool visitRegExp(LRegExp *lir); bool visitRegExpExec(LRegExpExec *lir); bool visitRegExpTest(LRegExpTest *lir); bool visitRegExpReplace(LRegExpReplace *lir); bool visitStringReplace(LStringReplace *lir); bool visitLambda(LLambda *lir); bool visitLambdaForSingleton(LLambdaForSingleton *lir);
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -3092,16 +3092,31 @@ class LTruncateFToInt32 : public LInstru setTemp(0, temp); } const LDefinition *tempFloat() { return getTemp(0); } }; +// Convert a boolean value to a string. +class LBooleanToString : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(BooleanToString) + + LBooleanToString(const LAllocation &input) { + setOperand(0, input); + } + + const MToString *mir() { + return mir_->toToString(); + } +}; + // Convert an integer hosted on one definition to a string with a function call. class LIntToString : public LInstructionHelper<1, 1, 0> { public: LIR_HEADER(IntToString) LIntToString(const LAllocation &input) { setOperand(0, input); @@ -3126,16 +3141,38 @@ class LDoubleToString : public LInstruct const LDefinition *tempInt() { return getTemp(0); } const MToString *mir() { return mir_->toToString(); } }; +// Convert a primitive to a string with a function call. +class LPrimitiveToString : public LInstructionHelper<1, BOX_PIECES, 1> +{ + public: + LIR_HEADER(PrimitiveToString) + + LPrimitiveToString(const LDefinition &tempToUnbox) + { + setTemp(0, tempToUnbox); + } + + static const size_t Input = 0; + + const MToString *mir() { + return mir_->toToString(); + } + + const LDefinition *tempToUnbox() { + return getTemp(0); + } +}; + // No-op instruction that is used to hold the entry snapshot. This simplifies // register allocation as it doesn't need to sniff the snapshot out of the