| Index: third_party/ots/src/cff_type2_charstring.cc
 | 
| diff --git a/third_party/ots/src/cff_type2_charstring.cc b/third_party/ots/src/cff_type2_charstring.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..6dd47664d09128a1dfcbe7a92921e37e97d1cac6
 | 
| --- /dev/null
 | 
| +++ b/third_party/ots/src/cff_type2_charstring.cc
 | 
| @@ -0,0 +1,914 @@
 | 
| +// Copyright (c) 2010 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.
 | 
| +
 | 
| +// A parser for the Type 2 Charstring Format.
 | 
| +// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
 | 
| +
 | 
| +#include "cff_type2_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 kMaxArgumentStack = 48;
 | 
| +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 ExecuteType2CharString(ots::OpenTypeFile *file,
 | 
| +                            size_t call_depth,
 | 
| +                            const ots::CFFIndex& global_subrs_index,
 | 
| +                            const ots::CFFIndex& local_subrs_index,
 | 
| +                            ots::Buffer *cff_table,
 | 
| +                            ots::Buffer *char_string,
 | 
| +                            std::stack<int32_t> *argument_stack,
 | 
| +                            bool *out_found_endchar,
 | 
| +                            bool *out_found_width,
 | 
| +                            size_t *in_out_num_stems);
 | 
| +
 | 
| +#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:
 | 
| +    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::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 ReadNextNumberFromType2CharString(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
 | 
| +    // ExecuteType2CharStringOperator().
 | 
| +    *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;
 | 
| +}
 | 
| +
 | 
| +// Executes |op| and updates |argument_stack|. Returns true if the execution
 | 
| +// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
 | 
| +// calls ExecuteType2CharString() function. The arguments other than |op| and
 | 
| +// |argument_stack| are passed for that reason.
 | 
| +bool ExecuteType2CharStringOperator(ots::OpenTypeFile *file,
 | 
| +                                    int32_t op,
 | 
| +                                    size_t call_depth,
 | 
| +                                    const ots::CFFIndex& global_subrs_index,
 | 
| +                                    const ots::CFFIndex& local_subrs_index,
 | 
| +                                    ots::Buffer *cff_table,
 | 
| +                                    ots::Buffer *char_string,
 | 
| +                                    std::stack<int32_t> *argument_stack,
 | 
| +                                    bool *out_found_endchar,
 | 
| +                                    bool *in_out_found_width,
 | 
| +                                    size_t *in_out_num_stems) {
 | 
| +  const size_t stack_size = argument_stack->size();
 | 
| +
 | 
| +  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 ExecuteType2CharString(file,
 | 
| +                                  call_depth + 1,
 | 
| +                                  global_subrs_index,
 | 
| +                                  local_subrs_index,
 | 
| +                                  cff_table,
 | 
| +                                  &char_string_to_jump,
 | 
| +                                  argument_stack,
 | 
| +                                  out_found_endchar,
 | 
| +                                  in_out_found_width,
 | 
| +                                  in_out_num_stems);
 | 
| +  }
 | 
| +
 | 
| +  case ots::kReturn:
 | 
| +    return true;
 | 
| +
 | 
| +  case ots::kEndChar:
 | 
| +    *out_found_endchar = true;
 | 
| +    *in_out_found_width = true;  // just in case.
 | 
| +    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 ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) {
 | 
| +      // The -1 is for "width" argument. For details, see Adobe Technical Note
 | 
| +      // #5177, page 16, note 4.
 | 
| +      successful = true;
 | 
| +    }
 | 
| +    (*in_out_num_stems) += (stack_size / 2);
 | 
| +    if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    while (!argument_stack->empty())
 | 
| +      argument_stack->pop();
 | 
| +    *in_out_found_width = 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 ((!(*in_out_found_width)) && (stack_size - 1 == 2)) {
 | 
| +      successful = true;
 | 
| +    }
 | 
| +    while (!argument_stack->empty())
 | 
| +      argument_stack->pop();
 | 
| +    *in_out_found_width = true;
 | 
| +    return successful ? true : OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  case ots::kVMoveTo:
 | 
| +  case ots::kHMoveTo: {
 | 
| +    bool successful = false;
 | 
| +    if (stack_size == 1) {
 | 
| +      successful = true;
 | 
| +    } else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) {
 | 
| +      successful = true;
 | 
| +    }
 | 
| +    while (!argument_stack->empty())
 | 
| +      argument_stack->pop();
 | 
| +    *in_out_found_width = true;
 | 
| +    return successful ? true : OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  case ots::kHintMask:
 | 
| +  case ots::kCntrMask: {
 | 
| +    bool successful = false;
 | 
| +    if (stack_size == 0) {
 | 
| +      successful = true;
 | 
| +    } else if ((!(*in_out_found_width)) && (stack_size == 1)) {
 | 
| +      // A number for "width" is found.
 | 
| +      successful = true;
 | 
| +    } else if ((!(*in_out_found_width)) ||  // in this case, any sizes are ok.
 | 
| +               ((stack_size % 2) == 0)) {
 | 
| +      // The numbers are vstem definition.
 | 
| +      // See Adobe Technical Note #5177, page 24, hintmask.
 | 
| +      (*in_out_num_stems) += (stack_size / 2);
 | 
| +      if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
 | 
| +        return OTS_FAILURE();
 | 
| +      }
 | 
| +      successful = true;
 | 
| +    }
 | 
| +    if (!successful) {
 | 
| +       return OTS_FAILURE();
 | 
| +    }
 | 
| +
 | 
| +    if ((*in_out_num_stems) == 0) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    const size_t mask_bytes = (*in_out_num_stems + 7) / 8;
 | 
| +    if (!char_string->Skip(mask_bytes)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    while (!argument_stack->empty())
 | 
| +      argument_stack->pop();
 | 
| +    *in_out_found_width = true;
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  case ots::kRLineTo:
 | 
| +    if (!(*in_out_found_width)) {
 | 
| +      // 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 (!(*in_out_found_width)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (stack_size < 1) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    while (!argument_stack->empty())
 | 
| +      argument_stack->pop();
 | 
| +    return true;
 | 
| +
 | 
| +  case ots::kRRCurveTo:
 | 
| +    if (!(*in_out_found_width)) {
 | 
| +      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 (!(*in_out_found_width)) {
 | 
| +      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 (!(*in_out_found_width)) {
 | 
| +      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 (!(*in_out_found_width)) {
 | 
| +      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 (!(*in_out_found_width)) {
 | 
| +      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 (!(*in_out_found_width)) {
 | 
| +      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 (argument_stack->size() > kMaxArgumentStack) {
 | 
| +      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 (!(*in_out_found_width)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (stack_size != 7) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    while (!argument_stack->empty())
 | 
| +      argument_stack->pop();
 | 
| +    return true;
 | 
| +
 | 
| +  case ots::kFlex:
 | 
| +    if (!(*in_out_found_width)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (stack_size != 13) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    while (!argument_stack->empty())
 | 
| +      argument_stack->pop();
 | 
| +    return true;
 | 
| +
 | 
| +  case ots::kHFlex1:
 | 
| +    if (!(*in_out_found_width)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (stack_size != 9) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    while (!argument_stack->empty())
 | 
| +      argument_stack->pop();
 | 
| +    return true;
 | 
| +
 | 
| +  case ots::kFlex1:
 | 
| +    if (!(*in_out_found_width)) {
 | 
| +      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|.
 | 
| +//
 | 
| +// 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.
 | 
| +// out_found_endchar: true is set if |char_string| contains 'endchar'.
 | 
| +// in_out_found_width: true is set if |char_string| contains 'width' byte (which
 | 
| +//                     is 0 or 1 byte.)
 | 
| +// in_out_num_stems: total number of hstems and vstems processed so far.
 | 
| +bool ExecuteType2CharString(ots::OpenTypeFile *file,
 | 
| +                            size_t call_depth,
 | 
| +                            const ots::CFFIndex& global_subrs_index,
 | 
| +                            const ots::CFFIndex& local_subrs_index,
 | 
| +                            ots::Buffer *cff_table,
 | 
| +                            ots::Buffer *char_string,
 | 
| +                            std::stack<int32_t> *argument_stack,
 | 
| +                            bool *out_found_endchar,
 | 
| +                            bool *in_out_found_width,
 | 
| +                            size_t *in_out_num_stems) {
 | 
| +  if (call_depth > kMaxSubrNesting) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +  *out_found_endchar = false;
 | 
| +
 | 
| +  const size_t length = char_string->length();
 | 
| +  while (char_string->offset() < length) {
 | 
| +    int32_t operator_or_operand = 0;
 | 
| +    bool is_operator = false;
 | 
| +    if (!ReadNextNumberFromType2CharString(char_string,
 | 
| +                                           &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(
 | 
| +               ots::Type2CharStringOperator(operator_or_operand))
 | 
| +           );
 | 
| +      }
 | 
| +#endif
 | 
| +
 | 
| +    if (!is_operator) {
 | 
| +      argument_stack->push(operator_or_operand);
 | 
| +      if (argument_stack->size() > kMaxArgumentStack) {
 | 
| +        return OTS_FAILURE();
 | 
| +      }
 | 
| +      continue;
 | 
| +    }
 | 
| +
 | 
| +    // An operator is found. Execute it.
 | 
| +    if (!ExecuteType2CharStringOperator(file,
 | 
| +                                        operator_or_operand,
 | 
| +                                        call_depth,
 | 
| +                                        global_subrs_index,
 | 
| +                                        local_subrs_index,
 | 
| +                                        cff_table,
 | 
| +                                        char_string,
 | 
| +                                        argument_stack,
 | 
| +                                        out_found_endchar,
 | 
| +                                        in_out_found_width,
 | 
| +                                        in_out_num_stems)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (*out_found_endchar) {
 | 
| +      return true;
 | 
| +    }
 | 
| +    if (operator_or_operand == ots::kReturn) {
 | 
| +      return true;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // No endchar operator is found.
 | 
| +  return OTS_FAILURE();
 | 
| +}
 | 
| +
 | 
| +// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
 | 
| +// |out_local_subrs_to_use|. Returns true on success.
 | 
| +bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
 | 
| +                     const std::vector<ots::CFFIndex *> &local_subrs_per_font,
 | 
| +                     const ots::CFFIndex *local_subrs,
 | 
| +                     uint16_t glyph_index,  // 0-origin
 | 
| +                     const ots::CFFIndex **out_local_subrs_to_use) {
 | 
| +  *out_local_subrs_to_use = NULL;
 | 
| +
 | 
| +  // First, find local subrs from |local_subrs_per_font|.
 | 
| +  if ((fd_select.size() > 0) &&
 | 
| +      (!local_subrs_per_font.empty())) {
 | 
| +    // Look up FDArray index for the glyph.
 | 
| +    std::map<uint16_t, uint8_t>::const_iterator iter =
 | 
| +        fd_select.find(glyph_index);
 | 
| +    if (iter == fd_select.end()) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    const uint8_t fd_index = iter->second;
 | 
| +    if (fd_index >= local_subrs_per_font.size()) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    *out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
 | 
| +  } else if (local_subrs) {
 | 
| +    // Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
 | 
| +    // entries. If The font has a local subrs index associated with the Top
 | 
| +    // DICT (not FDArrays), use it.
 | 
| +    *out_local_subrs_to_use = local_subrs;
 | 
| +  } else {
 | 
| +    // Just return NULL.
 | 
| +    *out_local_subrs_to_use = NULL;
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +namespace ots {
 | 
| +
 | 
| +bool ValidateType2CharStringIndex(
 | 
| +    ots::OpenTypeFile *file,
 | 
| +    const CFFIndex& char_strings_index,
 | 
| +    const CFFIndex& global_subrs_index,
 | 
| +    const std::map<uint16_t, uint8_t> &fd_select,
 | 
| +    const std::vector<CFFIndex *> &local_subrs_per_font,
 | 
| +    const CFFIndex *local_subrs,
 | 
| +    Buffer* cff_table) {
 | 
| +  const uint16_t num_offsets =
 | 
| +      static_cast<uint16_t>(char_strings_index.offsets.size());
 | 
| +  if (num_offsets != char_strings_index.offsets.size() || num_offsets == 0) {
 | 
| +    return OTS_FAILURE();  // no charstring.
 | 
| +  }
 | 
| +
 | 
| +  // For each glyph, validate the corresponding charstring.
 | 
| +  for (uint16_t i = 1; i < num_offsets; ++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 uint16_t glyph_index = i - 1;  // index in the map is 0-origin.
 | 
| +    const CFFIndex *local_subrs_to_use = NULL;
 | 
| +    if (!SelectLocalSubr(fd_select,
 | 
| +                         local_subrs_per_font,
 | 
| +                         local_subrs,
 | 
| +                         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;
 | 
| +    bool found_endchar = false;
 | 
| +    bool found_width = false;
 | 
| +    size_t num_stems = 0;
 | 
| +    if (!ExecuteType2CharString(file,
 | 
| +                                0 /* initial call_depth is zero */,
 | 
| +                                global_subrs_index, *local_subrs_to_use,
 | 
| +                                cff_table, &char_string, &argument_stack,
 | 
| +                                &found_endchar, &found_width, &num_stems)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (!found_endchar) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +}  // namespace ots
 | 
| +
 | 
| +#undef TABLE_NAME
 | 
| 
 |