: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
applyStandardFontGlyphMap(map, getSupplementalGlyphMapForCalibri());
for (const charCode in map) {
const cid = map[charCode];
if (cidToGidMap[cid] !== undefined) {
map[+charCode] = cidToGidMap[cid];
if (cidToGidMap.length !== this.toUnicode.length && properties.hasIncludedToUnicodeMap && this.toUnicode instanceof IdentityToUnicodeMap) {
this.toUnicode.forEach(function (charCode, unicodeCharCode) {
const cid = map[charCode];
if (cidToGidMap[cid] === undefined) {
map[+charCode] = unicodeCharCode;
if (!(this.toUnicode instanceof IdentityToUnicodeMap)) {
this.toUnicode.forEach(function (charCode, unicodeCharCode) {
map[+charCode] = unicodeCharCode;
this.toUnicode = new ToUnicodeMap(map);
} else if (/Symbol/i.test(fontName)) {
this.toFontChar = buildToFontChar(SymbolSetEncoding, getGlyphsUnicode(), this.differences);
} else if (/Dingbats/i.test(fontName)) {
this.toFontChar = buildToFontChar(ZapfDingbatsEncoding, getDingbatsGlyphsUnicode(), this.differences);
} else if (isStandardFont) {
const map = buildToFontChar(this.defaultEncoding, getGlyphsUnicode(), this.differences);
if (type === "CIDFontType2" && !this.cidEncoding.startsWith("Identity-") && !(this.toUnicode instanceof IdentityToUnicodeMap)) {
this.toUnicode.forEach(function (charCode, unicodeCharCode) {
map[+charCode] = unicodeCharCode;
const glyphsUnicodeMap = getGlyphsUnicode();
this.toUnicode.forEach((charCode, unicodeCharCode) => {
const glyphName = this.differences[charCode] || this.defaultEncoding[charCode];
const unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);
unicodeCharCode = unicode;
map[+charCode] = unicodeCharCode;
if (this.composite && this.toUnicode instanceof IdentityToUnicodeMap) {
if (/Tahoma|Verdana/i.test(name)) {
applyStandardFontGlyphMap(map, getGlyphMapForStandardFonts());
amendFallbackToUnicode(properties);
this.loadedName = fontName.split("-", 1)[0];
checkAndRepair(name, font, properties) {
const VALID_TABLES = ["OS/2", "cmap", "head", "hhea", "hmtx", "maxp", "name", "post", "loca", "glyf", "fpgm", "prep", "cvt ", "CFF "];
function readTables(file, numTables) {
const tables = Object.create(null);
for (let i = 0; i < numTables; i++) {
const table = readTableEntry(file);
if (!VALID_TABLES.includes(table.tag)) {
if (table.length === 0) {
tables[table.tag] = table;
function readTableEntry(file) {
const tag = file.getString(4);
const checksum = file.getInt32() >>> 0;
const offset = file.getInt32() >>> 0;
const length = file.getInt32() >>> 0;
const previousPosition = file.pos;
file.pos = file.start || 0;
const data = file.getBytes(length);
file.pos = previousPosition;
data[8] = data[9] = data[10] = data[11] = 0;
function readOpenTypeHeader(ttf) {
version: ttf.getString(4),
numTables: ttf.getUint16(),
searchRange: ttf.getUint16(),
entrySelector: ttf.getUint16(),
rangeShift: ttf.getUint16()
function readTrueTypeCollectionHeader(ttc) {
const ttcTag = ttc.getString(4);
assert(ttcTag === "ttcf", "Must be a TrueType Collection font.");
const majorVersion = ttc.getUint16();
const minorVersion = ttc.getUint16();
const numFonts = ttc.getInt32() >>> 0;
for (let i = 0; i < numFonts; i++) {
offsetTable.push(ttc.getInt32() >>> 0);
header.dsigTag = ttc.getInt32() >>> 0;
header.dsigLength = ttc.getInt32() >>> 0;
header.dsigOffset = ttc.getInt32() >>> 0;
throw new FormatError(`Invalid TrueType Collection majorVersion: ${majorVersion}.`);
function readTrueTypeCollectionData(ttc, fontName) {
} = readTrueTypeCollectionHeader(ttc);
const fontNameParts = fontName.split("+");
for (let i = 0; i < numFonts; i++) {
ttc.pos = (ttc.start || 0) + offsetTable[i];
const potentialHeader = readOpenTypeHeader(ttc);
const potentialTables = readTables(ttc, potentialHeader.numTables);
if (!potentialTables.name) {
throw new FormatError('TrueType Collection font must contain a "name" table.');
const [nameTable] = readNameTable(potentialTables.name);
for (let j = 0, jj = nameTable.length; j < jj; j++) {
for (let k = 0, kk = nameTable[j].length; k < kk; k++) {
const nameEntry = nameTable[j][k]?.replaceAll(/\s/g, "");
if (nameEntry === fontName) {
if (fontNameParts.length < 2) {
for (const part of fontNameParts) {
if (nameEntry === part) {
warn(`TrueType Collection does not contain "${fontName}" font, ` + `falling back to "${fallbackData.name}" font instead.`);
header: fallbackData.header,
tables: fallbackData.tables
throw new FormatError(`TrueType Collection does not contain "${fontName}" font.`);
function readCmapTable(cmap, file, isSymbolicFont, hasEncoding) {
warn("No cmap table available.");
let start = (file.start || 0) + cmap.offset;
const numTables = file.getUint16();
for (let i = 0; i < numTables; i++) {
const platformId = file.getUint16();
const encodingId = file.getUint16();
const offset = file.getInt32() >>> 0;
if (potentialTable?.platformId === platformId && potentialTable?.encodingId === encodingId) {
if (platformId === 0 && (encodingId === 0 || encodingId === 1 || encodingId === 3)) {
} else if (platformId === 1 && encodingId === 0) {
} else if (platformId === 3 && encodingId === 1 && (hasEncoding || !potentialTable)) {
} else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
let correctlySorted = true;
const nextBytes = file.peekBytes(2),
nextPlatformId = int16(nextBytes[0], nextBytes[1]);
if (nextPlatformId < platformId) {
file.pos = start + potentialTable.offset;
if (!potentialTable || file.peekByte() === -1) {
warn("Could not find a preferred cmap table.");
const format = file.getUint16();
let hasShortCmap = false;
for (j = 0; j < 256; j++) {
const index = file.getByte();
} else if (format === 2) {
const subHeaderKeys = [];
for (let i = 0; i < 256; i++) {
const subHeaderKey = file.getUint16() >> 3;
subHeaderKeys.push(subHeaderKey);
maxSubHeaderKey = Math.max(subHeaderKey, maxSubHeaderKey);
for (let i = 0; i <= maxSubHeaderKey; i++) {
firstCode: file.getUint16(),
entryCount: file.getUint16(),
idDelta: signedInt16(file.getByte(), file.getByte()),
idRangePos: file.pos + file.getUint16()
for (let i = 0; i < 256; i++) {
if (subHeaderKeys[i] === 0) {
file.pos = subHeaders[0].idRangePos + 2 * i;
glyphId = file.getUint16();
const s = subHeaders[subHeaderKeys[i]];
for (j = 0; j < s.entryCount; j++) {
const charCode = (i << 8) + j + s.firstCode;
file.pos = s.idRangePos + 2 * j;
glyphId = file.getUint16();
glyphId = (glyphId + s.idDelta) % 65536;
} else if (format === 4) {
const segCount = file.getUint16() >> 1;
for (segIndex = 0; segIndex < segCount; segIndex++) {
for (segIndex = 0; segIndex < segCount; segIndex++) {
segments[segIndex].start = file.getUint16();
for (segIndex = 0; segIndex < segCount; segIndex++) {
segments[segIndex].delta = file.getUint16();
for (segIndex = 0; segIndex < segCount; segIndex++) {
segment = segments[segIndex];
const rangeOffset = file.getUint16();
segment.offsetIndex = -1;
offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);
segment.offsetIndex = offsetIndex;
offsetsCount = Math.max(offsetsCount, offsetIndex + segment.end - segment.start + 1);
for (j = 0; j < offsetsCount; j++) {
offsets.push(file.getUint16());
for (segIndex = 0; segIndex < segCount; segIndex++) {
segment = segments[segIndex];
const delta = segment.delta;
offsetIndex = segment.offsetIndex;
for (j = start; j <= end; j++) {
glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];
glyphId = glyphId + delta & 0xffff;
} else if (format === 6) {
const firstCode = file.getUint16();
const entryCount = file.getUint16();
for (j = 0; j < entryCount; j++) {
glyphId = file.getUint16();
const charCode = firstCode + j;
} else if (format === 12) {
const nGroups = file.getInt32() >>> 0;
for (j = 0; j < nGroups; j++) {
const startCharCode = file.getInt32() >>> 0;
const endCharCode = file.getInt32() >>> 0;
let glyphCode = file.getInt32() >>> 0;
for (let charCode = startCharCode; charCode <= endCharCode; charCode++) {
warn("cmap table has unsupported format: " + format);
mappings.sort(function (a, b) {
return a.charCode - b.charCode;
for (let i = 1; i < mappings.length; i++) {
if (mappings[i - 1].charCode === mappings[i].charCode) {
platformId: potentialTable.platformId,
encodingId: potentialTable.encodingId,
function sanitizeMetrics(file, header, metrics, headTable, numGlyphs, dupFirstEntry) {
file.pos = (file.start || 0) + header.offset;
const caretOffset = file.getUint16();
let numOfMetrics = file.getUint16();
const macStyle = int16(headTable.data[44], headTable.data[45]);
if (numOfMetrics > numGlyphs) {
info(`The numOfMetrics (${numOfMetrics}) should not be ` + `greater than the numGlyphs (${numGlyphs}).`);
numOfMetrics = numGlyphs;
header.data[34] = (numOfMetrics & 0xff00) >> 8;
header.data[35] = numOfMetrics & 0x00ff;
const numOfSidebearings = numGlyphs - numOfMetrics;
const numMissing = numOfSidebearings - (metrics.length - numOfMetrics * 4 >> 1);
const entries = new Uint8Array(metrics.length + numMissing * 2);
entries.set(metrics.data);
entries[metrics.length] = metrics.data[2];
entries[metrics.length + 1] = metrics.data[3];
function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart, hintsValid) {
if (sourceStart < 0 || sourceStart >= source.length || sourceEnd > source.length || sourceEnd - sourceStart <= 12) {
const glyf = source.subarray(sourceStart, sourceEnd);
const xMin = signedInt16(glyf[2], glyf[3]);
const yMin = signedInt16(glyf[4], glyf[5]);
const xMax = signedInt16(glyf[6], glyf[7]);
const yMax = signedInt16(glyf[8], glyf[9]);
writeSignedInt16(glyf, 2, xMax);
writeSignedInt16(glyf, 6, xMin);
writeSignedInt16(glyf, 4, yMax);
writeSignedInt16(glyf, 8, yMin);
const contoursCount = signedInt16(glyf[0], glyf[1]);
if (contoursCount < -1) {