Merge pull request #21719 from Snuffleupagus/loadType3Data-async

Convert `TranslatedFont.prototype.loadType3Data` to an asynchronous method
This commit is contained in:
Tim van der Meij 2026-08-06 21:52:45 +02:00 committed by GitHub
commit f43ce86470
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -4885,83 +4885,79 @@ class TranslatedFont {
); );
} }
loadType3Data(evaluator, resources, task, seenRefs = null) { async loadType3Data(evaluator, resources, task, seenRefs = null) {
if (this.#type3Loaded) { if (this.#type3Loaded) {
return this.#type3Loaded; return this.#type3Loaded;
} }
const { font, type3Dependencies } = this; const { dict, font, type3Dependencies } = this;
assert(font.isType3Font, "Must be a Type3 font."); assert(font.isType3Font, "Must be a Type3 font.");
const { promise, resolve } = Promise.withResolvers();
this.#type3Loaded = promise;
// When parsing Type3 glyphs, always ignore them if there are errors. // When parsing Type3 glyphs, always ignore them if there are errors.
// Compared to the parsing of e.g. an entire page, it doesn't really // Compared to the parsing of e.g. an entire page, it doesn't really
// make sense to only be able to render a Type3 glyph partially. // make sense to only be able to render a Type3 glyph partially.
const type3Evaluator = evaluator.clone({ ignoreErrors: false }); const type3Evaluator = evaluator.clone({ ignoreErrors: false });
// Prevent circular references in Type3 fonts. // Prevent circular references in Type3 fonts.
const type3FontRefs = new RefSet(evaluator.type3FontRefs); const type3FontRefs = new RefSet(evaluator.type3FontRefs);
if (this.dict.objId && !type3FontRefs.has(this.dict.objId)) { if (dict.objId && !type3FontRefs.has(dict.objId)) {
type3FontRefs.put(this.dict.objId); type3FontRefs.put(dict.objId);
} }
type3Evaluator.type3FontRefs = type3FontRefs; type3Evaluator.type3FontRefs = type3FontRefs;
let loadCharProcsPromise = Promise.resolve(); const charProcs = dict.get("CharProcs");
const charProcs = this.dict.get("CharProcs"); const fontResources = dict.get("Resources") || resources;
const fontResources = this.dict.get("Resources") || resources;
const charProcOperatorList = Object.create(null); const charProcOperatorList = Object.create(null);
const [x0, y0, x1, y1] = font.bbox, const [x0, y0, x1, y1] = font.bbox;
width = x1 - x0, const fontBBoxSize = Math.hypot(x1 - x0, y1 - y0);
height = y1 - y0;
const fontBBoxSize = Math.hypot(width, height);
for (const key of charProcs.getKeys()) { for (const key of charProcs.getKeys()) {
loadCharProcsPromise = loadCharProcsPromise.then(() => { try {
const glyphStream = charProcs.get(key);
const operatorList = new OperatorList(); const operatorList = new OperatorList();
return type3Evaluator await type3Evaluator.getOperatorList({
.getOperatorList({ stream: charProcs.get(key),
stream: glyphStream, task,
task, resources: fontResources,
resources: fontResources, operatorList,
operatorList, prevRefs: seenRefs,
prevRefs: seenRefs, });
})
.then(() => {
// According to the PDF specification, section "9.6.5 Type 3 Fonts"
// and "Table 113":
// "A glyph description that begins with the d1 operator should
// not execute any operators that set the colour (or other
// colour-related parameters) in the graphics state;
// any use of such operators shall be ignored."
switch (operatorList.fnArray[0]) {
case OPS.setCharWidthAndBounds:
this.#removeType3ColorOperators(operatorList, fontBBoxSize);
break;
case OPS.setCharWidth:
if (!fontBBoxSize) {
this.#guessType3FontBBox(operatorList);
}
break;
}
charProcOperatorList[key] = operatorList.getIR();
for (const dependency of operatorList.dependencies) { // According to the PDF specification, section "9.6.5 Type 3 Fonts"
type3Dependencies.add(dependency); // and "Table 113":
// "A glyph description that begins with the d1 operator should
// not execute any operators that set the colour (or other
// colour-related parameters) in the graphics state;
// any use of such operators shall be ignored."
switch (operatorList.fnArray[0]) {
case OPS.setCharWidthAndBounds:
this.#removeType3ColorOperators(operatorList, fontBBoxSize);
break;
case OPS.setCharWidth:
if (!fontBBoxSize) {
this.#guessType3FontBBox(operatorList);
} }
}) break;
.catch(function (reason) { }
warn(`Type3 font resource "${key}" is not available.`); charProcOperatorList[key] = operatorList.getIR();
const dummyOperatorList = new OperatorList();
charProcOperatorList[key] = dummyOperatorList.getIR(); for (const dependency of operatorList.dependencies) {
}); type3Dependencies.add(dependency);
}); }
} } catch {
this.#type3Loaded = loadCharProcsPromise.then(() => { warn(`Type3 font resource "${key}" is not available.`);
font.charProcOperatorList = charProcOperatorList; charProcOperatorList[key] = new OperatorList().getIR();
if (this._bbox) {
font.isCharBBox = true;
font.bbox = this._bbox;
} }
}); }
font.charProcOperatorList = charProcOperatorList;
if (this._bbox) {
font.isCharBBox = true;
font.bbox = this._bbox;
}
resolve();
return this.#type3Loaded; return this.#type3Loaded;
} }