Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/ots/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 29 kB image not shown  

Quelle  cff_charstring.cc   Sprache: C

 
// 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

Messung V0.5
C=88 H=94 G=90

¤ Dauer der Verarbeitung: 0.43 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.