: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
return parseFloatOperand();
} else if (value === 28) {
value = (value << 24 | dict[pos++] << 16) >> 16;
} else if (value === 29) {
value = value << 8 | dict[pos++];
value = value << 8 | dict[pos++];
value = value << 8 | dict[pos++];
} else if (value >= 32 && value <= 246) {
} else if (value >= 247 && value <= 250) {
return (value - 247) * 256 + dict[pos++] + 108;
} else if (value >= 251 && value <= 254) {
return -((value - 251) * 256) - dict[pos++] - 108;
warn('CFFParser_parseDict: "' + value + '" is a reserved command.');
function parseFloatOperand() {
const lookup = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "E", "E-", null, "-"];
const length = dict.length;
b = b << 8 | dict[++pos];
entries.push([b, operands]);
operands.push(parseOperand());
const cffIndex = new CFFIndex();
const bytes = this.bytes;
const count = bytes[pos++] << 8 | bytes[pos++];
const offsetSize = bytes[pos++];
const startPos = pos + (count + 1) * offsetSize - 1;
for (i = 0, ii = count + 1; i < ii; ++i) {
for (let j = 0; j < offsetSize; ++j) {
offsets.push(startPos + offset);
for (i = 0, ii = offsets.length - 1; i < ii; ++i) {
const offsetStart = offsets[i];
const offsetEnd = offsets[i + 1];
cffIndex.add(bytes.subarray(offsetStart, offsetEnd));
for (let i = 0, ii = index.count; i < ii; ++i) {
const name = index.get(i);
names.push(bytesToString(name));
parseStringIndex(index) {
const strings = new CFFStrings();
for (let i = 0, ii = index.count; i < ii; ++i) {
const data = index.get(i);
strings.add(bytesToString(data));
createDict(Type, dict, strings) {
const cffDict = new Type(strings);
for (const [key, value] of dict) {
cffDict.setByKey(key, value);
parseCharString(state, data, localSubrIndex, globalSubrIndex) {
if (!data || state.callDepth > MAX_SUBR_NESTING) {
let stackSize = state.stackSize;
const stack = state.stack;
let length = data.length;
for (let j = 0; j < length;) {
let validationCommand = null;
validationCommand = CharstringValidationData12[q];
} else if (value === 28) {
stack[stackSize] = (data[j] << 24 | data[j + 1] << 16) >> 16;
} else if (value === 14) {
if (this.seacAnalysisEnabled) {
state.seac = stack.slice(stackSize, stackSize + 4);
validationCommand = CharstringValidationData[value];
} else if (value >= 32 && value <= 246) {
stack[stackSize] = value - 139;
} else if (value >= 247 && value <= 254) {
stack[stackSize] = value < 251 ? (value - 247 << 8) + data[j] + 108 : -(value - 251 << 8) - data[j] - 108;
} else if (value === 255) {
stack[stackSize] = (data[j] << 24 | data[j + 1] << 16 | data[j + 2] << 8 | data[j + 3]) / 65536;
} else if (value === 19 || value === 20) {
state.hints += stackSize >> 1;
data.copyWithin(j - 1, j, -1);
j += state.hints + 7 >> 3;
validationCommand = CharstringValidationData[value];
} else if (value === 10 || value === 29) {
const subrsIndex = value === 10 ? localSubrIndex : globalSubrIndex;
validationCommand = CharstringValidationData[value];
warn("Missing subrsIndex for " + validationCommand.id);
if (subrsIndex.count < 1240) {
} else if (subrsIndex.count < 33900) {
const subrNumber = stack[--stackSize] + bias;
if (subrNumber < 0 || subrNumber >= subrsIndex.count || isNaN(subrNumber)) {
validationCommand = CharstringValidationData[value];
warn("Out of bounds subrIndex for " + validationCommand.id);
state.stackSize = stackSize;
const valid = this.parseCharString(state, subrsIndex.get(subrNumber), localSubrIndex, globalSubrIndex);
stackSize = state.stackSize;
} else if (value === 11) {
state.stackSize = stackSize;
} else if (value === 0 && j === data.length) {
validationCommand = CharstringValidationData[14];
} else if (value === 9) {
data.copyWithin(j - 1, j, -1);
validationCommand = CharstringValidationData[value];
if (validationCommand.stem) {
state.hints += stackSize >> 1;
if (value === 3 || value === 23) {
} else if (state.hasVStems && (value === 1 || value === 18)) {
warn("CFF stem hints are in wrong order");
data[j - 1] = value === 1 ? 3 : 23;
if ("min" in validationCommand) {
if (!state.undefStack && stackSize < validationCommand.min) {
warn("Not enough parameters for " + validationCommand.id + "; actual: " + stackSize + ", expected: " + validationCommand.min);
if (state.firstStackClearing && validationCommand.stackClearing) {
state.firstStackClearing = false;
stackSize -= validationCommand.min;
if (stackSize >= 2 && validationCommand.stem) {
} else if (stackSize > 1) {
warn("Found too many parameters for stack-clearing command");
state.width = stack[stackSize - 1];
if ("stackDelta" in validationCommand) {
if ("stackFn" in validationCommand) {
validationCommand.stackFn(stack, stackSize);
stackSize += validationCommand.stackDelta;
} else if (validationCommand.stackClearing) {
} else if (validationCommand.resetStack) {
state.undefStack = false;
} else if (validationCommand.undefStack) {
state.firstStackClearing = false;
if (length < data.length) {
state.stackSize = stackSize;
const count = charStrings.count;
for (let i = 0; i < count; i++) {
const charstring = charStrings.get(i);
firstStackClearing: true,
let localSubrToUse = null;
let privateDictToUse = privateDict;
if (fdSelect && fdArray.length) {
const fdIndex = fdSelect.getFDIndex(i);
warn("Glyph index is not in fd select.");
if (fdIndex >= fdArray.length) {
warn("Invalid fd index for glyph index.");
privateDictToUse = fdArray[fdIndex].privateDict;
localSubrToUse = privateDictToUse.subrsIndex;
} else if (localSubrIndex) {
localSubrToUse = localSubrIndex;
valid = this.parseCharString(state, charstring, localSubrToUse, globalSubrIndex);
if (state.width !== null) {
const nominalWidth = privateDictToUse.getByName("nominalWidthX");
widths[i] = nominalWidth + state.width;
const defaultWidth = privateDictToUse.getByName("defaultWidthX");
widths[i] = defaultWidth;
if (state.seac !== null) {
charStrings.set(i, new Uint8Array([14]));
emptyPrivateDictionary(parentDict) {
const privateDict = this.createDict(CFFPrivateDict, [], parentDict.strings);
parentDict.setByKey(18, [0, 0]);
parentDict.privateDict = privateDict;
parsePrivateDict(parentDict) {
if (!parentDict.hasName("Private")) {
this.emptyPrivateDictionary(parentDict);
const privateOffset = parentDict.getByName("Private");
if (!Array.isArray(privateOffset) || privateOffset.length !== 2) {
parentDict.removeByName("Private");
const size = privateOffset[0];
const offset = privateOffset[1];
if (size === 0 || offset >= this.bytes.length) {
this.emptyPrivateDictionary(parentDict);
const privateDictEnd = offset + size;
const dictData = this.bytes.subarray(offset, privateDictEnd);
const dict = this.parseDict(dictData);
const privateDict = this.createDict(CFFPrivateDict, dict, parentDict.strings);
parentDict.privateDict = privateDict;
if (privateDict.getByName("ExpansionFactor") === 0) {
privateDict.setByName("ExpansionFactor", 0.06);
if (!privateDict.getByName("Subrs")) {
const subrsOffset = privateDict.getByName("Subrs");
const relativeOffset = offset + subrsOffset;
if (subrsOffset === 0 || relativeOffset >= this.bytes.length) {
this.emptyPrivateDictionary(parentDict);
const subrsIndex = this.parseIndex(relativeOffset);
privateDict.subrsIndex = subrsIndex.obj;
parseCharsets(pos, length, strings, cid) {
return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE, ISOAdobeCharset);
return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT, ExpertCharset);
return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET, ExpertSubsetCharset);
const bytes = this.bytes;
const format = bytes[pos++];
const charset = [cid ? 0 : ".notdef"];
for (i = 0; i < length; i++) {
id = bytes[pos++] << 8 | bytes[pos++];
charset.push(cid ? id : strings.get(id));
while (charset.length <= length) {
id = bytes[pos++] << 8 | bytes[pos++];
for (i = 0; i <= count; i++) {
charset.push(cid ? id++ : strings.get(id++));
while (charset.length <= length) {
id = bytes[pos++] << 8 | bytes[pos++];
count = bytes[pos++] << 8 | bytes[pos++];
for (i = 0; i <= count; i++) {
charset.push(cid ? id++ : strings.get(id++));
throw new FormatError("Unknown charset format");
const raw = bytes.subarray(start, end);
return new CFFCharset(false, format, charset, raw);
parseEncoding(pos, properties, strings, charset) {
const encoding = Object.create(null);
const bytes = this.bytes;
function readSupplement() {
const supplementsCount = bytes[pos++];
for (i = 0; i < supplementsCount; i++) {
const code = bytes[pos++];
const sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);
encoding[code] = charset.indexOf(strings.get(sid));
if (pos === 0 || pos === 1) {
const baseEncoding = pos ? ExpertEncoding : StandardEncoding;
for (i = 0, ii = charset.length; i < ii; i++) {
const index = baseEncoding.indexOf(charset[i]);
const glyphsCount = bytes[pos++];
for (i = 1; i <= glyphsCount; i++) {
encoding[bytes[pos++]] = i;
const rangesCount = bytes[pos++];
for (i = 0; i < rangesCount; i++) {
const start = bytes[pos++];
const left = bytes[pos++];
for (let j = start; j <= start + left; j++) {
throw new FormatError(`Unknown encoding format: ${format} in CFF`);
bytes[dataStart] &= 0x7f;
raw = bytes.subarray(dataStart, dataEnd);
return new CFFEncoding(predefined, format, encoding, raw);
parseFDSelect(pos, length) {
const bytes = this.bytes;
const format = bytes[pos++];
for (i = 0; i < length; ++i) {
const rangesCount = bytes[pos++] << 8 | bytes[pos++];
for (i = 0; i < rangesCount; ++i) {
let first = bytes[pos++] << 8 | bytes[pos++];
if (i === 0 && first !== 0) {
warn("parseFDSelect: The first range must have a first GID of 0" + " -- trying to recover.");
const fdIndex = bytes[pos++];
const next = bytes[pos] << 8 | bytes[pos + 1];
for (let j = first; j < next; ++j) {