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;
}
}
}