summaryrefslogtreecommitdiffstats
path: root/gfx/ots/src/cff_charstring.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/ots/src/cff_charstring.cc
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/ots/src/cff_charstring.cc')
-rw-r--r--gfx/ots/src/cff_charstring.cc1020
1 files changed, 1020 insertions, 0 deletions
diff --git a/gfx/ots/src/cff_charstring.cc b/gfx/ots/src/cff_charstring.cc
new file mode 100644
index 0000000000..9ea5d407e2
--- /dev/null
+++ b/gfx/ots/src/cff_charstring.cc
@@ -0,0 +1,1020 @@
+// Copyright (c) 2010-2017 The OTS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A parser for the Type 2 Charstring Format.
+// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
+
+#include "cff_charstring.h"
+
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <stack>
+#include <string>
+#include <utility>
+
+#define TABLE_NAME "CFF"
+
+namespace {
+
+// Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical
+// Note #5177.
+const int32_t kMaxSubrsCount = 65536;
+const size_t kMaxCharStringLength = 65535;
+const size_t kMaxNumberOfStemHints = 96;
+const size_t kMaxSubrNesting = 10;
+
+// |dummy_result| should be a huge positive integer so callsubr and callgsubr
+// will fail with the dummy value.
+const int32_t dummy_result = INT_MAX;
+
+bool ExecuteCharString(ots::OpenTypeCFF& cff,
+ size_t call_depth,
+ const ots::CFFIndex& global_subrs_index,
+ const ots::CFFIndex& local_subrs_index,
+ ots::Buffer *cff_table,
+ ots::Buffer *char_string,
+ std::stack<int32_t> *argument_stack,
+ ots::CharStringContext& cs_ctx);
+
+bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) {
+ if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) ||
+ (!cff2 && argument_stack->size() > ots::kMaxCFF1ArgumentStack)) {
+ return true;
+ }
+ return false;
+}
+
+#ifdef DUMP_T2CHARSTRING
+// Converts |op| to a string and returns it.
+const char *CharStringOperatorToString(ots::CharStringOperator op) {
+ switch (op) {
+ case ots::kHStem:
+ return "hstem";
+ case ots::kVStem:
+ return "vstem";
+ case ots::kVMoveTo:
+ return "vmoveto";
+ case ots::kRLineTo:
+ return "rlineto";
+ case ots::kHLineTo:
+ return "hlineto";
+ case ots::kVLineTo:
+ return "vlineto";
+ case ots::kRRCurveTo:
+ return "rrcurveto";
+ case ots::kCallSubr:
+ return "callsubr";
+ case ots::kReturn:
+ return "return";
+ case ots::kEndChar:
+ return "endchar";
+ case ots::kVSIndex:
+ return "vsindex";
+ case ots::kBlend:
+ return "blend";
+ case ots::kHStemHm:
+ return "hstemhm";
+ case ots::kHintMask:
+ return "hintmask";
+ case ots::kCntrMask:
+ return "cntrmask";
+ case ots::kRMoveTo:
+ return "rmoveto";
+ case ots::kHMoveTo:
+ return "hmoveto";
+ case ots::kVStemHm:
+ return "vstemhm";
+ case ots::kRCurveLine:
+ return "rcurveline";
+ case ots::kRLineCurve:
+ return "rlinecurve";
+ case ots::kVVCurveTo:
+ return "VVCurveTo";
+ 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";
+ case ots::kAdd:
+ return "add";
+ case ots::kSub:
+ return "sub";
+ case ots::kDiv:
+ return "div";
+ case ots::kNeg:
+ return "neg";
+ case ots::kEq:
+ return "eq";
+ case ots::kDrop:
+ return "drop";
+ case ots::kPut:
+ return "put";
+ case ots::kGet:
+ return "get";
+ case ots::kIfElse:
+ return "ifelse";
+ case ots::kRandom:
+ return "random";
+ case ots::kMul:
+ return "mul";
+ case ots::kSqrt:
+ return "sqrt";
+ case ots::kDup:
+ return "dup";
+ case ots::kExch:
+ return "exch";
+ case ots::kIndex:
+ return "index";
+ case ots::kRoll:
+ return "roll";
+ case ots::kHFlex:
+ return "hflex";
+ case ots::kFlex:
+ return "flex";
+ 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 ReadNextNumberFromCharString(ots::Buffer *char_string,
+ int32_t *out_number,
+ bool *out_is_operator) {
+ uint8_t v = 0;
+ if (!char_string->ReadU8(&v)) {
+ return OTS_FAILURE();
+ }
+ *out_is_operator = false;
+
+ // The conversion algorithm is described in Adobe Technical Note #5177, page
+ // 13, Table 1.
+ if (v <= 11) {
+ *out_number = v;
+ *out_is_operator = true;
+ } else if (v == 12) {
+ uint16_t result = (v << 8);
+ if (!char_string->ReadU8(&v)) {
+ return OTS_FAILURE();
+ }
+ result += v;
+ *out_number = result;
+ *out_is_operator = true;
+ } else if (v <= 27) {
+ // Special handling for v==19 and v==20 are implemented in
+ // ExecuteCharStringOperator().
+ *out_number = v;
+ *out_is_operator = true;
+ } else if (v == 28) {
+ if (!char_string->ReadU8(&v)) {
+ return OTS_FAILURE();
+ }
+ uint16_t result = (v << 8);
+ if (!char_string->ReadU8(&v)) {
+ return OTS_FAILURE();
+ }
+ result += v;
+ *out_number = result;
+ } else if (v <= 31) {
+ *out_number = v;
+ *out_is_operator = true;
+ } else if (v <= 246) {
+ *out_number = static_cast<int32_t>(v) - 139;
+ } else if (v <= 250) {
+ uint8_t w = 0;
+ if (!char_string->ReadU8(&w)) {
+ return OTS_FAILURE();
+ }
+ *out_number = ((static_cast<int32_t>(v) - 247) * 256) +
+ static_cast<int32_t>(w) + 108;
+ } else if (v <= 254) {
+ uint8_t w = 0;
+ if (!char_string->ReadU8(&w)) {
+ return OTS_FAILURE();
+ }
+ *out_number = -((static_cast<int32_t>(v) - 251) * 256) -
+ static_cast<int32_t>(w) - 108;
+ } else if (v == 255) {
+ // TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255,
+ // we should treat the following 4-bytes as a 16.16 fixed-point number
+ // rather than 32bit signed int.
+ if (!char_string->Skip(4)) {
+ return OTS_FAILURE();
+ }
+ *out_number = dummy_result;
+ } else {
+ return OTS_FAILURE();
+ }
+
+ return true;
+}
+
+bool ValidCFF2Operator(int32_t op) {
+ switch (op) {
+ case ots::kReturn:
+ case ots::kEndChar:
+ case ots::kAbs:
+ case ots::kAdd:
+ case ots::kSub:
+ case ots::kDiv:
+ case ots::kNeg:
+ case ots::kRandom:
+ case ots::kMul:
+ case ots::kSqrt:
+ case ots::kDrop:
+ case ots::kExch:
+ case ots::kIndex:
+ case ots::kRoll:
+ case ots::kDup:
+ case ots::kPut:
+ case ots::kGet:
+ case ots::kDotSection:
+ case ots::kAnd:
+ case ots::kOr:
+ case ots::kNot:
+ case ots::kEq:
+ case ots::kIfElse:
+ return false;
+ }
+
+ return true;
+}
+
+// Executes |op| and updates |argument_stack|. Returns true if the execution
+// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
+// calls ExecuteCharString() function. The |cs_ctx| argument holds values that
+// need to persist through these calls (see CharStringContext for details)
+bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff,
+ int32_t op,
+ size_t call_depth,
+ const ots::CFFIndex& global_subrs_index,
+ const ots::CFFIndex& local_subrs_index,
+ ots::Buffer *cff_table,
+ ots::Buffer *char_string,
+ std::stack<int32_t> *argument_stack,
+ ots::CharStringContext& cs_ctx) {
+ ots::Font* font = cff.GetFont();
+ const size_t stack_size = argument_stack->size();
+
+ if (cs_ctx.cff2 && !ValidCFF2Operator(op)) {
+ return OTS_FAILURE();
+ }
+
+ switch (op) {
+ case ots::kCallSubr:
+ case ots::kCallGSubr: {
+ const ots::CFFIndex& subrs_index =
+ (op == ots::kCallSubr ? local_subrs_index : global_subrs_index);
+
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ int32_t subr_number = argument_stack->top();
+ argument_stack->pop();
+ if (subr_number == dummy_result) {
+ // For safety, we allow subr calls only with immediate subr numbers for
+ // now. For example, we allow "123 callgsubr", but does not allow "100 12
+ // add callgsubr". Please note that arithmetic and conditional operators
+ // always push the |dummy_result| in this implementation.
+ return OTS_FAILURE();
+ }
+
+ // See Adobe Technical Note #5176 (CFF), "16. Local/GlobalSubrs INDEXes."
+ int32_t bias = 32768;
+ if (subrs_index.count < 1240) {
+ bias = 107;
+ } else if (subrs_index.count < 33900) {
+ bias = 1131;
+ }
+ subr_number += bias;
+
+ // Sanity checks of |subr_number|.
+ if (subr_number < 0) {
+ return OTS_FAILURE();
+ }
+ if (subr_number >= kMaxSubrsCount) {
+ return OTS_FAILURE();
+ }
+ if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) {
+ return OTS_FAILURE(); // The number is out-of-bounds.
+ }
+
+ // Prepare ots::Buffer where we're going to jump.
+ const size_t length =
+ subrs_index.offsets[subr_number + 1] - subrs_index.offsets[subr_number];
+ if (length > kMaxCharStringLength) {
+ return OTS_FAILURE();
+ }
+ const size_t offset = subrs_index.offsets[subr_number];
+ cff_table->set_offset(offset);
+ if (!cff_table->Skip(length)) {
+ return OTS_FAILURE();
+ }
+ ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
+
+ return ExecuteCharString(cff,
+ call_depth + 1,
+ global_subrs_index,
+ local_subrs_index,
+ cff_table,
+ &char_string_to_jump,
+ argument_stack,
+ cs_ctx);
+ }
+
+ case ots::kReturn:
+ return true;
+
+ case ots::kEndChar:
+ cs_ctx.endchar_seen = true;
+ cs_ctx.width_seen = true; // just in case.
+ return true;
+
+ case ots::kVSIndex: {
+ if (!cs_ctx.cff2) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 1) {
+ return OTS_FAILURE();
+ }
+ if (cs_ctx.blend_seen || cs_ctx.vsindex_seen) {
+ return OTS_FAILURE();
+ }
+ if (argument_stack->top() < 0 ||
+ argument_stack->top() >= (int32_t)cff.region_index_count.size()) {
+ return OTS_FAILURE();
+ }
+ cs_ctx.vsindex_seen = true;
+ cs_ctx.vsindex = argument_stack->top();
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+ }
+
+ case ots::kBlend: {
+ if (!cs_ctx.cff2) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ if (cs_ctx.vsindex >= (int32_t)cff.region_index_count.size()) {
+ return OTS_FAILURE();
+ }
+ uint16_t k = cff.region_index_count.at(cs_ctx.vsindex);
+ uint16_t n = argument_stack->top();
+ if (stack_size < n * (k + 1u) + 1u) {
+ return OTS_FAILURE();
+ }
+
+ // Keep the 1st n operands on the stack for the next operator to use and
+ // pop the rest. There can be multiple consecutive blend operators, so this
+ // makes sure the operands of all of them are kept on the stack.
+ while (argument_stack->size() > stack_size - ((n * k) + 1))
+ argument_stack->pop();
+ cs_ctx.blend_seen = true;
+ return true;
+ }
+
+ case ots::kHStem:
+ case ots::kVStem:
+ case ots::kHStemHm:
+ case ots::kVStemHm: {
+ bool successful = false;
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ if ((stack_size % 2) == 0) {
+ successful = true;
+ } else if ((!(cs_ctx.width_seen)) && (((stack_size - 1) % 2) == 0)) {
+ // The -1 is for "width" argument. For details, see Adobe Technical Note
+ // #5177, page 16, note 4.
+ successful = true;
+ }
+ cs_ctx.num_stems += (stack_size / 2);
+ if ((cs_ctx.num_stems) > kMaxNumberOfStemHints) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ cs_ctx.width_seen = true; // always set true since "w" might be 0 byte.
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kRMoveTo: {
+ bool successful = false;
+ if (stack_size == 2) {
+ successful = true;
+ } else if ((!(cs_ctx.width_seen)) && (stack_size - 1 == 2)) {
+ successful = true;
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ cs_ctx.width_seen = true;
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kVMoveTo:
+ case ots::kHMoveTo: {
+ bool successful = false;
+ if (stack_size == 1) {
+ successful = true;
+ } else if ((!(cs_ctx.width_seen)) && (stack_size - 1 == 1)) {
+ successful = true;
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ cs_ctx.width_seen = true;
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kHintMask:
+ case ots::kCntrMask: {
+ bool successful = false;
+ if (stack_size == 0) {
+ successful = true;
+ } else if ((!(cs_ctx.width_seen)) && (stack_size == 1)) {
+ // A number for "width" is found.
+ successful = true;
+ } else if ((!(cs_ctx.width_seen)) || // in this case, any sizes are ok.
+ ((stack_size % 2) == 0)) {
+ // The numbers are vstem definition.
+ // See Adobe Technical Note #5177, page 24, hintmask.
+ cs_ctx.num_stems += (stack_size / 2);
+ if ((cs_ctx.num_stems) > kMaxNumberOfStemHints) {
+ return OTS_FAILURE();
+ }
+ successful = true;
+ }
+ if (!successful) {
+ return OTS_FAILURE();
+ }
+
+ if ((cs_ctx.num_stems) == 0) {
+ return OTS_FAILURE();
+ }
+ const size_t mask_bytes = (cs_ctx.num_stems + 7) / 8;
+ if (!char_string->Skip(mask_bytes)) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ cs_ctx.width_seen = true;
+ return true;
+ }
+
+ case ots::kRLineTo:
+ if (!(cs_ctx.width_seen)) {
+ // The first stack-clearing operator should be one of hstem, hstemhm,
+ // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or
+ // endchar. For details, see Adobe Technical Note #5177, page 16, note 4.
+ return OTS_FAILURE();
+ }
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ if ((stack_size % 2) != 0) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kHLineTo:
+ case ots::kVLineTo:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kRRCurveTo:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 6) {
+ return OTS_FAILURE();
+ }
+ if ((stack_size % 6) != 0) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kRCurveLine:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 8) {
+ return OTS_FAILURE();
+ }
+ if (((stack_size - 2) % 6) != 0) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kRLineCurve:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 8) {
+ return OTS_FAILURE();
+ }
+ if (((stack_size - 6) % 2) != 0) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kVVCurveTo:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 4) {
+ return OTS_FAILURE();
+ }
+ if (((stack_size % 4) != 0) &&
+ (((stack_size - 1) % 4) != 0)) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kHHCurveTo: {
+ bool successful = false;
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 4) {
+ return OTS_FAILURE();
+ }
+ if ((stack_size % 4) == 0) {
+ // {dxa dxb dyb dxc}+
+ successful = true;
+ } else if (((stack_size - 1) % 4) == 0) {
+ // dy1? {dxa dxb dyb dxc}+
+ successful = true;
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return successful ? true : OTS_FAILURE();
+ }
+
+ case ots::kVHCurveTo:
+ case ots::kHVCurveTo: {
+ bool successful = false;
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size < 4) {
+ return OTS_FAILURE();
+ }
+ if (((stack_size - 4) % 8) == 0) {
+ // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}*
+ successful = true;
+ } else if ((stack_size >= 5) &&
+ ((stack_size - 5) % 8) == 0) {
+ // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf
+ successful = true;
+ } else if ((stack_size >= 8) &&
+ ((stack_size - 8) % 8) == 0) {
+ // {dxa dxb dyb dyc dyd dxe dye dxf}+
+ successful = true;
+ } else if ((stack_size >= 9) &&
+ ((stack_size - 9) % 8) == 0) {
+ // {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();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kNot:
+ case ots::kAbs:
+ case ots::kNeg:
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kDiv:
+ // TODO(yusukes): Should detect div-by-zero errors.
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kDrop:
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ return true;
+
+ case ots::kPut:
+ case ots::kGet:
+ case ots::kIndex:
+ // For now, just call OTS_FAILURE since there is no way to check whether the
+ // index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType
+ // fonts I have (except malicious ones!) use the operators.
+ // TODO(yusukes): Implement them in a secure way.
+ return OTS_FAILURE();
+
+ case ots::kRoll:
+ // Likewise, just call OTS_FAILURE for kRoll since there is no way to check
+ // whether |N| is smaller than the current stack depth or not.
+ // TODO(yusukes): Implement them in a secure way.
+ return OTS_FAILURE();
+
+ case ots::kRandom:
+ // For now, we don't handle the 'random' operator since the operator makes
+ // it hard to analyze hinting code statically.
+ return OTS_FAILURE();
+
+ case ots::kIfElse:
+ if (stack_size < 4) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kMul:
+ // TODO(yusukes): Should detect overflows.
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kSqrt:
+ // TODO(yusukes): Should check if the argument is negative.
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kDup:
+ if (stack_size < 1) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ argument_stack->push(dummy_result);
+ if (ArgumentStackOverflows(argument_stack, cs_ctx.cff2)) {
+ return OTS_FAILURE();
+ }
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kExch:
+ if (stack_size < 2) {
+ return OTS_FAILURE();
+ }
+ argument_stack->pop();
+ argument_stack->pop();
+ argument_stack->push(dummy_result);
+ argument_stack->push(dummy_result);
+ // TODO(yusukes): Implement this. We should push a real value for all
+ // arithmetic and conditional operations.
+ return true;
+
+ case ots::kHFlex:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 7) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kFlex:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 13) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kHFlex1:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 9) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+
+ case ots::kFlex1:
+ if (!(cs_ctx.width_seen)) {
+ return OTS_FAILURE();
+ }
+ if (stack_size != 11) {
+ return OTS_FAILURE();
+ }
+ while (!argument_stack->empty())
+ argument_stack->pop();
+ return true;
+ }
+
+ return OTS_FAILURE_MSG("Undefined operator: %d (0x%x)", op, op);
+}
+
+// Executes |char_string| and updates |argument_stack|.
+//
+// cff: parent OpenTypeCFF reference
+// call_depth: The current call depth. Initial value is zero.
+// global_subrs_index: Global subroutines.
+// local_subrs_index: Local subroutines for the current glyph.
+// cff_table: A whole CFF table which contains all global and local subroutines.
+// char_string: A charstring we'll execute. |char_string| can be a main routine
+// in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr.
+// argument_stack: The stack which an operator in |char_string| operates.
+// cs_ctx: a CharStringContext holding values to persist across subrs, etc.
+// endchar_seen: true is set if |char_string| contains 'endchar'.
+// width_seen: true is set if |char_string| contains 'width' byte (which
+// is 0 or 1 byte) or if cff2
+// num_stems: total number of hstems and vstems processed so far.
+// cff2: true if this is a CFF2 table
+// blend_seen: initially false; set to true if 'blend' operator encountered.
+// vsindex_seen: initially false; set to true if 'vsindex' encountered.
+// vsindex: initially = PrivateDICT's vsindex; may be changed by 'vsindex'
+// operator in CharString
+bool ExecuteCharString(ots::OpenTypeCFF& cff,
+ size_t call_depth,
+ const ots::CFFIndex& global_subrs_index,
+ const ots::CFFIndex& local_subrs_index,
+ ots::Buffer *cff_table,
+ ots::Buffer *char_string,
+ std::stack<int32_t> *argument_stack,
+ ots::CharStringContext& cs_ctx) {
+ if (call_depth > kMaxSubrNesting) {
+ return OTS_FAILURE();
+ }
+ cs_ctx.endchar_seen = false;
+
+ const size_t length = char_string->length();
+ while (char_string->offset() < length) {
+ int32_t operator_or_operand = 0;
+ bool is_operator = false;
+ if (!ReadNextNumberFromCharString(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",
+ CharStringOperatorToString(
+ ots::CharStringOperator(operator_or_operand))
+ );
+ }
+#endif
+
+ if (!is_operator) {
+ argument_stack->push(operator_or_operand);
+ if (ArgumentStackOverflows(argument_stack, cs_ctx.cff2)) {
+ return OTS_FAILURE();
+ }
+ continue;
+ }
+
+ // An operator is found. Execute it.
+ if (!ExecuteCharStringOperator(cff,
+ operator_or_operand,
+ call_depth,
+ global_subrs_index,
+ local_subrs_index,
+ cff_table,
+ char_string,
+ argument_stack,
+ cs_ctx)) {
+ return OTS_FAILURE();
+ }
+ if (cs_ctx.endchar_seen) {
+ return true;
+ }
+ if (operator_or_operand == ots::kReturn) {
+ return true;
+ }
+ }
+
+ // No endchar operator is found (CFF1 only)
+ if (cs_ctx.cff2)
+ return true;
+ return OTS_FAILURE();
+}
+
+// Selects a set of subroutines for |glyph_index| from |cff| and sets it on
+// |out_local_subrs_to_use|. Returns true on success.
+bool SelectLocalSubr(const ots::OpenTypeCFF& cff,
+ uint16_t glyph_index, // 0-origin
+ const ots::CFFIndex **out_local_subrs_to_use) {
+ bool cff2 = (cff.major == 2);
+ *out_local_subrs_to_use = NULL;
+
+ // First, find local subrs from |local_subrs_per_font|.
+ if ((cff.fd_select.size() > 0) &&
+ (!cff.local_subrs_per_font.empty())) {
+ // Look up FDArray index for the glyph.
+ const auto& iter = cff.fd_select.find(glyph_index);
+ if (iter == cff.fd_select.end()) {
+ return OTS_FAILURE();
+ }
+ const auto fd_index = iter->second;
+ if (fd_index >= cff.local_subrs_per_font.size()) {
+ return OTS_FAILURE();
+ }
+ *out_local_subrs_to_use = cff.local_subrs_per_font.at(fd_index);
+ } else if (cff.local_subrs) {
+ // Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
+ // entries. If The font has a local subrs index associated with the Top
+ // DICT (not FDArrays), use it.
+ *out_local_subrs_to_use = cff.local_subrs;
+ } else if (cff2 && cff.local_subrs_per_font.size() == 1) {
+ *out_local_subrs_to_use = cff.local_subrs_per_font.at(0);
+ } else {
+ // Just return NULL.
+ *out_local_subrs_to_use = NULL;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace ots {
+
+bool ValidateCFFCharStrings(
+ ots::OpenTypeCFF& cff,
+ const CFFIndex& global_subrs_index,
+ Buffer* cff_table) {
+ const CFFIndex& char_strings_index = *(cff.charstrings_index);
+ if (char_strings_index.offsets.size() == 0) {
+ return OTS_FAILURE(); // no charstring.
+ }
+
+ // For each glyph, validate the corresponding charstring.
+ for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) {
+ // Prepare a Buffer object, |char_string|, which contains the charstring
+ // for the |i|-th glyph.
+ const size_t length =
+ char_strings_index.offsets[i] - char_strings_index.offsets[i - 1];
+ if (length > kMaxCharStringLength) {
+ return OTS_FAILURE();
+ }
+ const size_t offset = char_strings_index.offsets[i - 1];
+ cff_table->set_offset(offset);
+ if (!cff_table->Skip(length)) {
+ return OTS_FAILURE();
+ }
+ Buffer char_string(cff_table->buffer() + offset, length);
+
+ // Get a local subrs for the glyph.
+ const unsigned glyph_index = i - 1; // index in the map is 0-origin.
+ const CFFIndex *local_subrs_to_use = NULL;
+ if (!SelectLocalSubr(cff,
+ glyph_index,
+ &local_subrs_to_use)) {
+ return OTS_FAILURE();
+ }
+ // If |local_subrs_to_use| is still NULL, use an empty one.
+ CFFIndex default_empty_subrs;
+ if (!local_subrs_to_use){
+ local_subrs_to_use = &default_empty_subrs;
+ }
+
+ // Check a charstring for the |i|-th glyph.
+ std::stack<int32_t> argument_stack;
+ // Context to store values that must persist across subrs, etc.
+ CharStringContext cs_ctx;
+ cs_ctx.cff2 = (cff.major == 2);
+ // CFF2 CharString has no value for width, so we start with true here to
+ // error out if width is found.
+ cs_ctx.width_seen = cs_ctx.cff2;
+ // CFF2 CharStrings' default vsindex comes from the associated PrivateDICT
+ if (cs_ctx.cff2) {
+ const auto& iter = cff.fd_select.find(glyph_index);
+ auto fd_index = 0;
+ if (iter != cff.fd_select.end()) {
+ fd_index = iter->second;
+ }
+ if (fd_index >= (int32_t)cff.vsindex_per_font.size()) {
+ // shouldn't get this far with a font in this condition, but just in case
+ return OTS_FAILURE(); // fd_index out-of-range
+ }
+ cs_ctx.vsindex = cff.vsindex_per_font.at(fd_index);
+ }
+
+#ifdef DUMP_T2CHARSTRING
+ fprintf(stderr, "\n---- CharString %*d ----\n", 5, glyph_index);
+#endif
+
+ if (!ExecuteCharString(cff,
+ 0 /* initial call_depth is zero */,
+ global_subrs_index, *local_subrs_to_use,
+ cff_table, &char_string, &argument_stack,
+ cs_ctx)) {
+ return OTS_FAILURE();
+ }
+ if (!cs_ctx.cff2 && !cs_ctx.endchar_seen) {
+ return OTS_FAILURE();
+ }
+ }
+ return true;
+}
+
+} // namespace ots
+
+#undef TABLE_NAME