: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
glyphs: cff.charStrings.objects,
subrs: cff.topDict.privateDict?.subrsIndex?.objects,
gsubrs: cff.globalSubrIndex?.objects,
isCFFCIDFont: cff.isCIDFont,
function parseGlyfTable(glyf, loca, isGlyphLocationsLong) {
let itemSize, itemDecode;
if (isGlyphLocationsLong) {
itemDecode = (data, offset) => 2 * getUint16(data, offset);
let startOffset = itemDecode(loca, 0);
for (let j = itemSize; j < loca.length; j += itemSize) {
const endOffset = itemDecode(loca, j);
glyphs.push(glyf.subarray(startOffset, endOffset));
function lookupCmap(ranges, unicode) {
const code = unicode.codePointAt(0);
const c = l + r + 1 >> 1;
if (code < ranges[c].start) {
if (ranges[l].start <= code && code <= ranges[l].end) {
gid = ranges[l].idDelta + (ranges[l].ids ? ranges[l].ids[code - ranges[l].start] : code) & 0xffff;
function compileGlyf(code, cmds, font) {
cmds.add(FontRenderOps.MOVE_TO, [x, y]);
cmds.add(FontRenderOps.LINE_TO, [x, y]);
function quadraticCurveTo(xa, ya, x, y) {
cmds.add(FontRenderOps.QUADRATIC_CURVE_TO, [xa, ya, x, y]);
const numberOfContours = getInt16(code, i);
if (numberOfContours < 0) {
flags = getUint16(code, i);
const glyphIndex = getUint16(code, i + 2);
arg1 = getInt16(code, i);
arg2 = getInt16(code, i + 2);
arg1 = getUint16(code, i);
arg2 = getUint16(code, i + 2);
} else if (flags & 0x02) {
arg1 = getInt8(code, i++);
arg2 = getInt8(code, i++);
scaleX = scaleY = getFloat214(code, i);
} else if (flags & 0x40) {
scaleX = getFloat214(code, i);
scaleY = getFloat214(code, i + 2);
} else if (flags & 0x80) {
scaleX = getFloat214(code, i);
scale01 = getFloat214(code, i + 2);
scale10 = getFloat214(code, i + 4);
scaleY = getFloat214(code, i + 6);
const subglyph = font.glyphs[glyphIndex];
cmds.add(FontRenderOps.SAVE);
cmds.add(FontRenderOps.TRANSFORM, [scaleX, scale01, scale10, scaleY, x, y]);
compileGlyf(subglyph, cmds, font);
cmds.add(FontRenderOps.RESTORE);
const endPtsOfContours = [];
for (j = 0; j < numberOfContours; j++) {
endPtsOfContours.push(getUint16(code, i));
const instructionLength = getUint16(code, i);
i += 2 + instructionLength;
const numberOfPoints = endPtsOfContours.at(-1) + 1;
while (points.length < numberOfPoints) {
for (j = 0; j < numberOfPoints; j++) {
switch (points[j].flags & 0x12) {
for (j = 0; j < numberOfPoints; j++) {
switch (points[j].flags & 0x24) {
for (i = 0; i < numberOfContours; i++) {
const endPoint = endPtsOfContours[i];
const contour = points.slice(startPoint, endPoint + 1);
if (contour[0].flags & 1) {
contour.push(contour[0]);
} else if (contour.at(-1).flags & 1) {
contour.unshift(contour.at(-1));
x: (contour[0].x + contour.at(-1).x) / 2,
y: (contour[0].y + contour.at(-1).y) / 2
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);
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;
function compileCharString(charStringCode, cmds, font, glyphId) {
cmds.add(FontRenderOps.MOVE_TO, [x, y]);
cmds.add(FontRenderOps.LINE_TO, [x, y]);
function bezierCurveTo(x1, y1, x2, y2, x, y) {
cmds.add(FontRenderOps.BEZIER_CURVE_TO, [x1, y1, x2, y2, x, y]);
while (i < code.length) {
let xa, xb, ya, yb, y1, y2, y3, n, subrCode;
stems += stack.length >> 1;
stems += stack.length >> 1;
while (stack.length > 0) {
while (stack.length > 0) {
if (stack.length === 0) {
while (stack.length > 0) {
if (stack.length === 0) {
while (stack.length > 0) {
bezierCurveTo(xa, ya, xb, yb, x, y);
const fdIndex = font.fdSelect.getFDIndex(glyphId);
if (fdIndex >= 0 && fdIndex < font.fdArray.length) {
const fontDict = font.fdArray[fdIndex];
if (fontDict.privateDict?.subrsIndex) {
subrs = fontDict.privateDict.subrsIndex.objects;
n += getSubroutineBias(subrs);
warn("Invalid fd index for glyph index.");
subrCode = font.subrs[n + font.subrsBias];
bezierCurveTo(xa, y, xb, y1, x, y1);
bezierCurveTo(xa, y1, xb, y, x, y);
bezierCurveTo(xa, ya, xb, yb, x, y);
bezierCurveTo(xa, ya, xb, yb, x, y);
bezierCurveTo(xa, y1, xb, y2, x, y2);
bezierCurveTo(xa, y2, xb, y3, x, y);
bezierCurveTo(xa, ya, xb, yb, x, y);
if (Math.abs(x - x0) > Math.abs(y - y0)) {
bezierCurveTo(xa, ya, xb, yb, x, y);
throw new FormatError(`unknown operator: 12 ${v}`);
const achar = stack.pop();
const bchar = stack.pop();
cmds.add(FontRenderOps.SAVE);
cmds.add(FontRenderOps.TRANSLATE, [x, y]);
let cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[achar]]));
compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);
cmds.add(FontRenderOps.RESTORE);
cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[bchar]]));
compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);
stems += stack.length >> 1;
stems += stack.length >> 1;
stems += stack.length >> 1;
stems += stack.length >> 1;
while (stack.length > 2) {
bezierCurveTo(xa, ya, xb, yb, x, y);
while (stack.length > 6) {
bezierCurveTo(xa, ya, xb, yb, x, y);
while (stack.length > 0) {
bezierCurveTo(xa, ya, xb, yb, x, y);
while (stack.length > 0) {
bezierCurveTo(xa, ya, xb, yb, x, y);
stack.push((code[i] << 24 | code[i + 1] << 16) >> 16);
n = stack.pop() + font.gsubrsBias;
subrCode = font.gsubrs[n];
while (stack.length > 0) {
y = yb + (stack.length === 1 ? stack.shift() : 0);
bezierCurveTo(xa, ya, xb, yb, x, y);
if (stack.length === 0) {