in src/core/type1_parser.js [553:715]
extractFontProgram(properties) {
const stream = this.stream;
const subrs = [],
charstrings = [];
const privateData = Object.create(null);
privateData.lenIV = 4;
const program = {
subrs: [],
charstrings: [],
properties: {
privateData,
},
};
let token, length, data, lenIV;
while ((token = this.getToken()) !== null) {
if (token !== "/") {
continue;
}
token = this.getToken();
switch (token) {
case "CharStrings":
// The number immediately following CharStrings must be greater or
// equal to the number of CharStrings.
this.getToken();
this.getToken(); // read in 'dict'
this.getToken(); // read in 'dup'
this.getToken(); // read in 'begin'
while (true) {
token = this.getToken();
if (token === null || token === "end") {
break;
}
if (token !== "/") {
continue;
}
const glyph = this.getToken();
length = this.readInt();
this.getToken(); // read in 'RD' or '-|'
data = length > 0 ? stream.getBytes(length) : new Uint8Array(0);
lenIV = program.properties.privateData.lenIV;
const encoded = this.readCharStrings(data, lenIV);
this.nextChar();
token = this.getToken(); // read in 'ND' or '|-'
if (token === "noaccess") {
this.getToken(); // read in 'def'
} else if (token === "/") {
// The expected 'ND' or '|-' token is missing, avoid swallowing
// the start of the next glyph (fixes issue14462_reduced.pdf).
this.prevChar();
}
charstrings.push({
glyph,
encoded,
});
}
break;
case "Subrs":
this.readInt(); // num
this.getToken(); // read in 'array'
while (this.getToken() === "dup") {
const index = this.readInt();
length = this.readInt();
this.getToken(); // read in 'RD' or '-|'
data = length > 0 ? stream.getBytes(length) : new Uint8Array(0);
lenIV = program.properties.privateData.lenIV;
const encoded = this.readCharStrings(data, lenIV);
this.nextChar();
token = this.getToken(); // read in 'NP' or '|'
if (token === "noaccess") {
this.getToken(); // read in 'put'
}
subrs[index] = encoded;
}
break;
case "BlueValues":
case "OtherBlues":
case "FamilyBlues":
case "FamilyOtherBlues":
const blueArray = this.readNumberArray();
// *Blue* values may contain invalid data: disables reading of
// those values when hinting is disabled.
if (
blueArray.length > 0 &&
blueArray.length % 2 === 0 &&
HINTING_ENABLED
) {
program.properties.privateData[token] = blueArray;
}
break;
case "StemSnapH":
case "StemSnapV":
program.properties.privateData[token] = this.readNumberArray();
break;
case "StdHW":
case "StdVW":
program.properties.privateData[token] = this.readNumberArray()[0];
break;
case "BlueShift":
case "lenIV":
case "BlueFuzz":
case "BlueScale":
case "LanguageGroup":
program.properties.privateData[token] = this.readNumber();
break;
case "ExpansionFactor":
// Firefox doesn't render correctly a font with a null factor on
// Windows (see issue 15289), hence we just reset it to its default
// value (0.06).
program.properties.privateData[token] = this.readNumber() || 0.06;
break;
case "ForceBold":
program.properties.privateData[token] = this.readBoolean();
break;
}
}
for (const { encoded, glyph } of charstrings) {
const charString = new Type1CharString();
const error = charString.convert(
encoded,
subrs,
this.seacAnalysisEnabled
);
let output = charString.output;
if (error) {
// It seems when FreeType encounters an error while evaluating a glyph
// that it completely ignores the glyph so we'll mimic that behaviour
// here and put an endchar to make the validator happy.
output = [14];
}
const charStringObject = {
glyphName: glyph,
charstring: output,
width: charString.width,
lsb: charString.lsb,
seac: charString.seac,
};
if (glyph === ".notdef") {
// Make sure .notdef is at index zero (issue #11477).
program.charstrings.unshift(charStringObject);
} else {
program.charstrings.push(charStringObject);
}
// Attempt to replace missing widths, from the font dictionary /Widths
// entry, with ones from the font data (fixes issue11150_reduced.pdf).
if (properties.builtInEncoding) {
const index = properties.builtInEncoding.indexOf(glyph);
if (
index > -1 &&
properties.widths[index] === undefined &&
index >= properties.firstChar &&
index <= properties.lastChar
) {
properties.widths[index] = charString.width;
}
}
}
return program;
}