parseCFFCharstring function

dynamic parseCFFCharstring(
  1. Font font,
  2. dynamic glyph,
  3. dynamic code
)

Implementation

parseCFFCharstring(Font font, glyph, code) {
  var c1x;
  var c1y;
  var c2x;
  var c2y;
  var p = new Path();
  List<num> stack = [];
  var nStems = 0;
  var haveWidth = false;
  var open = false;
  num x = 0;
  num y = 0;
  var subrs;
  var subrsBias;
  var defaultWidthX;
  var nominalWidthX;
  if (font.isCIDFont) {
    var fdIndex = font.tables["cff"]["topDict"]["_fdSelect"][glyph.index];
    var fdDict = font.tables["cff"]["topDict"]["_fdArray"][fdIndex];
    subrs = fdDict["_subrs"];
    subrsBias = fdDict["_subrsBias"];
    defaultWidthX = fdDict["_defaultWidthX"];
    nominalWidthX = fdDict["_nominalWidthX"];
  } else {
    subrs = font.tables["cff"]["topDict"]["_subrs"];
    subrsBias = font.tables["cff"]["topDict"]["_subrsBias"];
    defaultWidthX = font.tables["cff"]["topDict"]["_defaultWidthX"];
    nominalWidthX = font.tables["cff"]["topDict"]["_nominalWidthX"];
  }
  var width = defaultWidthX;

  Function newContour = (x, y) {
    if (open) {
      p.closePath();
    }

    p.moveTo(x, y);
    open = true;
  };

  Function parseStems = () {
    var hasWidthArg;

    // The number of stem operators on the stack is always even.
    // If the value is uneven, that means a width is specified.
    hasWidthArg = stack.length % 2 != 0;
    if (hasWidthArg && !haveWidth) {
      width = stack.removeAt(0) + nominalWidthX;
    }

    nStems += stack.length >> 1;
    stack.length = 0;
    haveWidth = true;
  };

  __parse(code) {
    var b1;
    var b2;
    var b3;
    var b4;
    var codeIndex;
    var subrCode;
    var jpx;
    var jpy;
    var c3x;
    var c3y;
    var c4x;
    var c4y;

    var i = 0;
    while (i < code.length) {
      var v = code[i];
      i += 1;
      switch (v) {
        case 1: // hstem
          parseStems();
          break;
        case 3: // vstem
          parseStems();
          break;
        case 4: // vmoveto
          if (stack.length > 1 && !haveWidth) {
            width = stack.removeAt(0) + nominalWidthX;
            haveWidth = true;
          }

          y += stack.removeLast();
          newContour(x, y);
          break;
        case 5: // rlineto
          while (stack.length > 0) {
            x += stack.removeAt(0);
            y += stack.removeAt(0);
            p.lineTo(x, y);
          }

          break;
        case 6: // hlineto
          while (stack.length > 0) {
            x += stack.removeAt(0);
            p.lineTo(x, y);
            if (stack.length == 0) {
              break;
            }

            y += stack.removeAt(0);
            p.lineTo(x, y);
          }

          break;
        case 7: // vlineto
          while (stack.length > 0) {
            y += stack.removeAt(0);
            p.lineTo(x, y);
            if (stack.length == 0) {
              break;
            }

            x += stack.removeAt(0);
            p.lineTo(x, y);
          }

          break;
        case 8: // rrcurveto
          while (stack.length > 0) {
            c1x = x + stack.removeAt(0);
            c1y = y + stack.removeAt(0);
            c2x = c1x + stack.removeAt(0);
            c2y = c1y + stack.removeAt(0);
            x = c2x + stack.removeAt(0);
            y = c2y + stack.removeAt(0);
            p.curveTo(c1x, c1y, c2x, c2y, x, y);
          }

          break;
        case 10: // callsubr
          codeIndex = stack.removeLast() + subrsBias;
          subrCode = subrs[codeIndex];
          if (subrCode) {
            __parse(subrCode);
          }

          break;
        case 11: // return
          return;
        case 12: // flex operators
          v = code[i];
          i += 1;
          switch (v) {
            case 35: // flex
              // |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 dx6 dy6 fd flex (12 35) |-
              c1x = x + stack.removeAt(0); // dx1
              c1y = y + stack.removeAt(0); // dy1
              c2x = c1x + stack.removeAt(0); // dx2
              c2y = c1y + stack.removeAt(0); // dy2
              jpx = c2x + stack.removeAt(0); // dx3
              jpy = c2y + stack.removeAt(0); // dy3
              c3x = jpx + stack.removeAt(0); // dx4
              c3y = jpy + stack.removeAt(0); // dy4
              c4x = c3x + stack.removeAt(0); // dx5
              c4y = c3y + stack.removeAt(0); // dy5
              x = c4x + stack.removeAt(0); // dx6
              y = c4y + stack.removeAt(0); // dy6
              stack.removeAt(0); // flex depth
              p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
              p.curveTo(c3x, c3y, c4x, c4y, x, y);
              break;
            case 34: // hflex
              // |- dx1 dx2 dy2 dx3 dx4 dx5 dx6 hflex (12 34) |-
              c1x = x + stack.removeAt(0); // dx1
              c1y = y; // dy1
              c2x = c1x + stack.removeAt(0); // dx2
              c2y = c1y + stack.removeAt(0); // dy2
              jpx = c2x + stack.removeAt(0); // dx3
              jpy = c2y; // dy3
              c3x = jpx + stack.removeAt(0); // dx4
              c3y = c2y; // dy4
              c4x = c3x + stack.removeAt(0); // dx5
              c4y = y; // dy5
              x = c4x + stack.removeAt(0); // dx6
              p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
              p.curveTo(c3x, c3y, c4x, c4y, x, y);
              break;
            case 36: // hflex1
              // |- dx1 dy1 dx2 dy2 dx3 dx4 dx5 dy5 dx6 hflex1 (12 36) |-
              c1x = x + stack.removeAt(0); // dx1
              c1y = y + stack.removeAt(0); // dy1
              c2x = c1x + stack.removeAt(0); // dx2
              c2y = c1y + stack.removeAt(0); // dy2
              jpx = c2x + stack.removeAt(0); // dx3
              jpy = c2y; // dy3
              c3x = jpx + stack.removeAt(0); // dx4
              c3y = c2y; // dy4
              c4x = c3x + stack.removeAt(0); // dx5
              c4y = c3y + stack.removeAt(0); // dy5
              x = c4x + stack.removeAt(0); // dx6
              p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
              p.curveTo(c3x, c3y, c4x, c4y, x, y);
              break;
            case 37: // flex1
              // |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 d6 flex1 (12 37) |-
              c1x = x + stack.removeAt(0); // dx1
              c1y = y + stack.removeAt(0); // dy1
              c2x = c1x + stack.removeAt(0); // dx2
              c2y = c1y + stack.removeAt(0); // dy2
              jpx = c2x + stack.removeAt(0); // dx3
              jpy = c2y + stack.removeAt(0); // dy3
              c3x = jpx + stack.removeAt(0); // dx4
              c3y = jpy + stack.removeAt(0); // dy4
              c4x = c3x + stack.removeAt(0); // dx5
              c4y = c3y + stack.removeAt(0); // dy5
              if (Math.abs(c4x - x) > Math.abs(c4y - y)) {
                x = c4x + stack.removeAt(0);
              } else {
                y = c4y + stack.removeAt(0);
              }

              p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
              p.curveTo(c3x, c3y, c4x, c4y, x, y);
              break;
            default:
              print('Glyph ' + glyph.index + ': unknown operator ${1200}' + v);
              stack.length = 0;
          }
          break;
        case 14: // endchar
          if (stack.length > 0 && !haveWidth) {
            width = stack.removeAt(0) + nominalWidthX;
            haveWidth = true;
          }

          if (open) {
            p.closePath();
            open = false;
          }

          break;
        case 18: // hstemhm
          parseStems();
          break;
        case 19: // hintmask
        case 20: // cntrmask
          parseStems();
          i += (nStems + 7) >> 3;
          break;
        case 21: // rmoveto
          if (stack.length > 2 && !haveWidth) {
            width = stack.removeAt(0) + nominalWidthX;
            haveWidth = true;
          }

          y += stack.removeLast();
          x += stack.removeLast();
          newContour(x, y);
          break;
        case 22: // hmoveto
          if (stack.length > 1 && !haveWidth) {
            width = stack.removeAt(0) + nominalWidthX;
            haveWidth = true;
          }

          x += stack.removeLast();
          newContour(x, y);
          break;
        case 23: // vstemhm
          parseStems();
          break;
        case 24: // rcurveline
          while (stack.length > 2) {
            c1x = x + stack.removeAt(0);
            c1y = y + stack.removeAt(0);
            c2x = c1x + stack.removeAt(0);
            c2y = c1y + stack.removeAt(0);
            x = c2x + stack.removeAt(0);
            y = c2y + stack.removeAt(0);
            p.curveTo(c1x, c1y, c2x, c2y, x, y);
          }

          x += stack.removeAt(0);
          y += stack.removeAt(0);
          p.lineTo(x, y);
          break;
        case 25: // rlinecurve
          while (stack.length > 6) {
            x += stack.removeAt(0);
            y += stack.removeAt(0);
            p.lineTo(x, y);
          }

          c1x = x + stack.removeAt(0);
          c1y = y + stack.removeAt(0);
          c2x = c1x + stack.removeAt(0);
          c2y = c1y + stack.removeAt(0);
          x = c2x + stack.removeAt(0);
          y = c2y + stack.removeAt(0);
          p.curveTo(c1x, c1y, c2x, c2y, x, y);
          break;
        case 26: // vvcurveto
          if (stack.length % 2 != 0) {
            x += stack.removeAt(0);
          }

          while (stack.length > 0) {
            c1x = x;
            c1y = y + stack.removeAt(0);
            c2x = c1x + stack.removeAt(0);
            c2y = c1y + stack.removeAt(0);
            x = c2x;
            y = c2y + stack.removeAt(0);
            p.curveTo(c1x, c1y, c2x, c2y, x, y);
          }

          break;
        case 27: // hhcurveto
          if (stack.length % 2 != 0) {
            y += stack.removeAt(0);
          }

          while (stack.length > 0) {
            c1x = x + stack.removeAt(0);
            c1y = y;
            c2x = c1x + stack.removeAt(0);
            c2y = c1y + stack.removeAt(0);
            x = c2x + stack.removeAt(0);
            y = c2y;
            p.curveTo(c1x, c1y, c2x, c2y, x, y);
          }

          break;
        case 28: // shortint
          b1 = code[i];
          b2 = code[i + 1];
          stack.add(((b1 << 24) | (b2 << 16)) >> 16);
          i += 2;
          break;
        case 29: // callgsubr
          codeIndex = stack.removeLast() + font.gsubrsBias;
          subrCode = font.gsubrs[codeIndex];
          if (subrCode) {
            __parse(subrCode);
          }

          break;
        case 30: // vhcurveto
          while (stack.length > 0) {
            c1x = x;
            c1y = y + stack.removeAt(0);
            c2x = c1x + stack.removeAt(0);
            c2y = c1y + stack.removeAt(0);
            x = c2x + stack.removeAt(0);
            y = c2y + (stack.length == 1 ? stack.removeAt(0) : 0);
            p.curveTo(c1x, c1y, c2x, c2y, x, y);
            if (stack.length == 0) {
              break;
            }

            c1x = x + stack.removeAt(0);
            c1y = y;
            c2x = c1x + stack.removeAt(0);
            c2y = c1y + stack.removeAt(0);
            y = c2y + stack.removeAt(0);
            x = c2x + (stack.length == 1 ? stack.removeAt(0) : 0);
            p.curveTo(c1x, c1y, c2x, c2y, x, y);
          }

          break;
        case 31: // hvcurveto
          while (stack.length > 0) {
            c1x = x + stack.removeAt(0);
            c1y = y;
            c2x = c1x + stack.removeAt(0);
            c2y = c1y + stack.removeAt(0);
            y = c2y + stack.removeAt(0);
            x = c2x + (stack.length == 1 ? stack.removeAt(0) : 0);
            p.curveTo(c1x, c1y, c2x, c2y, x, y);
            if (stack.length == 0) {
              break;
            }

            c1x = x;
            c1y = y + stack.removeAt(0);
            c2x = c1x + stack.removeAt(0);
            c2y = c1y + stack.removeAt(0);
            x = c2x + stack.removeAt(0);
            y = c2y + (stack.length == 1 ? stack.removeAt(0) : 0);
            p.curveTo(c1x, c1y, c2x, c2y, x, y);
          }

          break;
        default:
          if (v < 32) {
            print('Glyph ' + glyph.index + ': unknown operator ' + v);
          } else if (v < 247) {
            stack.add(v - 139);
          } else if (v < 251) {
            b1 = code[i];
            i += 1;
            stack.add((v - 247) * 256 + b1 + 108);
          } else if (v < 255) {
            b1 = code[i];
            i += 1;
            stack.add(-(v - 251) * 256 - b1 - 108);
          } else {
            b1 = code[i];
            b2 = code[i + 1];
            b3 = code[i + 2];
            b4 = code[i + 3];
            i += 4;
            stack.add(((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) / 65536);
          }
      }
    }
  }

  __parse(code);

  glyph.advanceWidth = width;
  return p;
}