convert()

in src/core/type1_parser.js [90:324]


  convert(encoded, subrs, seacAnalysisEnabled) {
    const count = encoded.length;
    let error = false;
    let wx, sbx, subrNumber;
    for (let i = 0; i < count; i++) {
      let value = encoded[i];
      if (value < 32) {
        if (value === 12) {
          value = (value << 8) + encoded[++i];
        }
        switch (value) {
          case 1: // hstem
            if (!HINTING_ENABLED) {
              this.stack = [];
              break;
            }
            error = this.executeCommand(2, COMMAND_MAP.hstem);
            break;
          case 3: // vstem
            if (!HINTING_ENABLED) {
              this.stack = [];
              break;
            }
            error = this.executeCommand(2, COMMAND_MAP.vstem);
            break;
          case 4: // vmoveto
            if (this.flexing) {
              if (this.stack.length < 1) {
                error = true;
                break;
              }
              // Add the dx for flex and but also swap the values so they are
              // the right order.
              const dy = this.stack.pop();
              this.stack.push(0, dy);
              break;
            }
            error = this.executeCommand(1, COMMAND_MAP.vmoveto);
            break;
          case 5: // rlineto
            error = this.executeCommand(2, COMMAND_MAP.rlineto);
            break;
          case 6: // hlineto
            error = this.executeCommand(1, COMMAND_MAP.hlineto);
            break;
          case 7: // vlineto
            error = this.executeCommand(1, COMMAND_MAP.vlineto);
            break;
          case 8: // rrcurveto
            error = this.executeCommand(6, COMMAND_MAP.rrcurveto);
            break;
          case 9: // closepath
            // closepath is a Type1 command that does not take argument and is
            // useless in Type2 and it can simply be ignored.
            this.stack = [];
            break;
          case 10: // callsubr
            if (this.stack.length < 1) {
              error = true;
              break;
            }
            subrNumber = this.stack.pop();
            if (!subrs[subrNumber]) {
              error = true;
              break;
            }
            error = this.convert(subrs[subrNumber], subrs, seacAnalysisEnabled);
            break;
          case 11: // return
            return error;
          case 13: // hsbw
            if (this.stack.length < 2) {
              error = true;
              break;
            }
            // To convert to type2 we have to move the width value to the
            // first part of the charstring and then use hmoveto with lsb.
            wx = this.stack.pop();
            sbx = this.stack.pop();
            this.lsb = sbx;
            this.width = wx;
            this.stack.push(wx, sbx);
            error = this.executeCommand(2, COMMAND_MAP.hmoveto);
            break;
          case 14: // endchar
            this.output.push(COMMAND_MAP.endchar[0]);
            break;
          case 21: // rmoveto
            if (this.flexing) {
              break;
            }
            error = this.executeCommand(2, COMMAND_MAP.rmoveto);
            break;
          case 22: // hmoveto
            if (this.flexing) {
              // Add the dy for flex.
              this.stack.push(0);
              break;
            }
            error = this.executeCommand(1, COMMAND_MAP.hmoveto);
            break;
          case 30: // vhcurveto
            error = this.executeCommand(4, COMMAND_MAP.vhcurveto);
            break;
          case 31: // hvcurveto
            error = this.executeCommand(4, COMMAND_MAP.hvcurveto);
            break;
          case (12 << 8) + 0: // dotsection
            // dotsection is a Type1 command to specify some hinting feature
            // for dots that do not take a parameter and it can safely be
            // ignored for Type2.
            this.stack = [];
            break;
          case (12 << 8) + 1: // vstem3
            if (!HINTING_ENABLED) {
              this.stack = [];
              break;
            }
            // [vh]stem3 are Type1 only and Type2 supports [vh]stem with
            // multiple parameters, so instead of returning [vh]stem3 take a
            // shortcut and return [vhstem] instead.
            error = this.executeCommand(2, COMMAND_MAP.vstem);
            break;
          case (12 << 8) + 2: // hstem3
            if (!HINTING_ENABLED) {
              this.stack = [];
              break;
            }
            // See vstem3.
            error = this.executeCommand(2, COMMAND_MAP.hstem);
            break;
          case (12 << 8) + 6: // seac
            // seac is like type 2's special endchar but it doesn't use the
            // first argument asb, so remove it.
            if (seacAnalysisEnabled) {
              const asb = this.stack.at(-5);
              this.seac = this.stack.splice(-4, 4);
              this.seac[0] += this.lsb - asb;
              error = this.executeCommand(0, COMMAND_MAP.endchar);
            } else {
              error = this.executeCommand(4, COMMAND_MAP.endchar);
            }
            break;
          case (12 << 8) + 7: // sbw
            if (this.stack.length < 4) {
              error = true;
              break;
            }
            // To convert to type2 we have to move the width value to the
            // first part of the charstring and then use rmoveto with
            // (dx, dy). The height argument will not be used for vmtx and
            // vhea tables reconstruction -- ignoring it.
            this.stack.pop(); // wy
            wx = this.stack.pop();
            const sby = this.stack.pop();
            sbx = this.stack.pop();
            this.lsb = sbx;
            this.width = wx;
            this.stack.push(wx, sbx, sby);
            error = this.executeCommand(3, COMMAND_MAP.rmoveto);
            break;
          case (12 << 8) + 12: // div
            if (this.stack.length < 2) {
              error = true;
              break;
            }
            const num2 = this.stack.pop();
            const num1 = this.stack.pop();
            this.stack.push(num1 / num2);
            break;
          case (12 << 8) + 16: // callothersubr
            if (this.stack.length < 2) {
              error = true;
              break;
            }
            subrNumber = this.stack.pop();
            const numArgs = this.stack.pop();
            if (subrNumber === 0 && numArgs === 3) {
              const flexArgs = this.stack.splice(-17, 17);
              this.stack.push(
                flexArgs[2] + flexArgs[0], // bcp1x + rpx
                flexArgs[3] + flexArgs[1], // bcp1y + rpy
                flexArgs[4], // bcp2x
                flexArgs[5], // bcp2y
                flexArgs[6], // p2x
                flexArgs[7], // p2y
                flexArgs[8], // bcp3x
                flexArgs[9], // bcp3y
                flexArgs[10], // bcp4x
                flexArgs[11], // bcp4y
                flexArgs[12], // p3x
                flexArgs[13], // p3y
                flexArgs[14] // flexDepth
                // 15 = finalx unused by flex
                // 16 = finaly unused by flex
              );
              error = this.executeCommand(13, COMMAND_MAP.flex, true);
              this.flexing = false;
              this.stack.push(flexArgs[15], flexArgs[16]);
            } else if (subrNumber === 1 && numArgs === 0) {
              this.flexing = true;
            }
            break;
          case (12 << 8) + 17: // pop
            // Ignore this since it is only used with othersubr.
            break;
          case (12 << 8) + 33: // setcurrentpoint
            // Ignore for now.
            this.stack = [];
            break;
          default:
            warn('Unknown type 1 charstring command of "' + value + '"');
            break;
        }
        if (error) {
          break;
        }
        continue;
      } else if (value <= 246) {
        value -= 139;
      } else if (value <= 250) {
        value = (value - 247) * 256 + encoded[++i] + 108;
      } else if (value <= 254) {
        value = -((value - 251) * 256) - encoded[++i] - 108;
      } else {
        value =
          ((encoded[++i] & 0xff) << 24) |
          ((encoded[++i] & 0xff) << 16) |
          ((encoded[++i] & 0xff) << 8) |
          ((encoded[++i] & 0xff) << 0);
      }
      this.stack.push(value);
    }
    return error;
  }