function compileGlyf()

in src/core/font_renderer.js [168:352]


function compileGlyf(code, cmds, font) {
  function moveTo(x, y) {
    if (firstPoint) {
      // Close the current subpath in adding a straight line to the first point.
      cmds.add("L", firstPoint);
    }
    firstPoint = [x, y];
    cmds.add("M", [x, y]);
  }
  function lineTo(x, y) {
    cmds.add("L", [x, y]);
  }
  function quadraticCurveTo(xa, ya, x, y) {
    cmds.add("Q", [xa, ya, x, y]);
  }

  let i = 0;
  const numberOfContours = readInt16(code, i);
  let flags;
  let firstPoint = null;
  let x = 0,
    y = 0;
  i += 10;
  if (numberOfContours < 0) {
    // composite glyph
    do {
      flags = readUint16(code, i);
      const glyphIndex = readUint16(code, i + 2);
      i += 4;
      let arg1, arg2;
      if (flags & 0x01) {
        if (flags & 0x02) {
          arg1 = readInt16(code, i);
          arg2 = readInt16(code, i + 2);
        } else {
          arg1 = readUint16(code, i);
          arg2 = readUint16(code, i + 2);
        }
        i += 4;
      } else if (flags & 0x02) {
        arg1 = readInt8(code, i++);
        arg2 = readInt8(code, i++);
      } else {
        arg1 = code[i++];
        arg2 = code[i++];
      }
      if (flags & 0x02) {
        x = arg1;
        y = arg2;
      } else {
        x = 0;
        y = 0;
      }
      let scaleX = 1,
        scaleY = 1,
        scale01 = 0,
        scale10 = 0;
      if (flags & 0x08) {
        scaleX = scaleY = getFloat214(code, i);
        i += 2;
      } else if (flags & 0x40) {
        scaleX = getFloat214(code, i);
        scaleY = getFloat214(code, i + 2);
        i += 4;
      } else if (flags & 0x80) {
        scaleX = getFloat214(code, i);
        scale01 = getFloat214(code, i + 2);
        scale10 = getFloat214(code, i + 4);
        scaleY = getFloat214(code, i + 6);
        i += 8;
      }
      const subglyph = font.glyphs[glyphIndex];
      if (subglyph) {
        // TODO: the transform should be applied only if there is a scale:
        // https://github.com/freetype/freetype/blob/edd4fedc5427cf1cf1f4b045e53ff91eb282e9d4/src/truetype/ttgload.c#L1205
        cmds.save();
        cmds.transform([scaleX, scale01, scale10, scaleY, x, y]);

        if (!(flags & 0x02)) {
          // TODO: we must use arg1 and arg2 to make something similar to:
          // https://github.com/freetype/freetype/blob/edd4fedc5427cf1cf1f4b045e53ff91eb282e9d4/src/truetype/ttgload.c#L1209
        }
        compileGlyf(subglyph, cmds, font);
        cmds.restore();
      }
    } while (flags & 0x20);
  } else {
    // simple glyph
    const endPtsOfContours = [];
    let j, jj;
    for (j = 0; j < numberOfContours; j++) {
      endPtsOfContours.push(readUint16(code, i));
      i += 2;
    }
    const instructionLength = readUint16(code, i);
    i += 2 + instructionLength; // skipping the instructions
    const numberOfPoints = endPtsOfContours.at(-1) + 1;
    const points = [];
    while (points.length < numberOfPoints) {
      flags = code[i++];
      let repeat = 1;
      if (flags & 0x08) {
        repeat += code[i++];
      }
      while (repeat-- > 0) {
        points.push({ flags });
      }
    }
    for (j = 0; j < numberOfPoints; j++) {
      switch (points[j].flags & 0x12) {
        case 0x00:
          x += readInt16(code, i);
          i += 2;
          break;
        case 0x02:
          x -= code[i++];
          break;
        case 0x12:
          x += code[i++];
          break;
      }
      points[j].x = x;
    }
    for (j = 0; j < numberOfPoints; j++) {
      switch (points[j].flags & 0x24) {
        case 0x00:
          y += readInt16(code, i);
          i += 2;
          break;
        case 0x04:
          y -= code[i++];
          break;
        case 0x24:
          y += code[i++];
          break;
      }
      points[j].y = y;
    }

    let startPoint = 0;
    for (i = 0; i < numberOfContours; i++) {
      const endPoint = endPtsOfContours[i];
      // contours might have implicit points, which is located in the middle
      // between two neighboring off-curve points
      const contour = points.slice(startPoint, endPoint + 1);
      if (contour[0].flags & 1) {
        contour.push(contour[0]); // using start point at the contour end
      } else if (contour.at(-1).flags & 1) {
        // first is off-curve point, trying to use one from the end
        contour.unshift(contour.at(-1));
      } else {
        // start and end are off-curve points, creating implicit one
        const p = {
          flags: 1,
          x: (contour[0].x + contour.at(-1).x) / 2,
          y: (contour[0].y + contour.at(-1).y) / 2,
        };
        contour.unshift(p);
        contour.push(p);
      }
      moveTo(contour[0].x, contour[0].y);
      for (j = 1, jj = contour.length; j < jj; j++) {
        if (contour[j].flags & 1) {
          lineTo(contour[j].x, contour[j].y);
        } else if (contour[j + 1].flags & 1) {
          quadraticCurveTo(
            contour[j].x,
            contour[j].y,
            contour[j + 1].x,
            contour[j + 1].y
          );
          j++;
        } else {
          quadraticCurveTo(
            contour[j].x,
            contour[j].y,
            (contour[j].x + contour[j + 1].x) / 2,
            (contour[j].y + contour[j + 1].y) / 2
          );
        }
      }
      startPoint = endPoint + 1;
    }
  }
}