Merge pull request #19563 from Snuffleupagus/loadType3Data-once

Invoke `TranslatedFont.prototype.loadType3Data` only *once* per font
This commit is contained in:
Jonas Jenwald 2025-02-27 11:20:26 +01:00 committed by GitHub
commit 50c573d16d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 59 deletions

View File

@ -1177,9 +1177,7 @@ class Catalog {
this.pageDictCache.clear(); this.pageDictCache.clear();
this.nonBlendModesSet.clear(); this.nonBlendModesSet.clear();
const translatedFonts = await Promise.all(this.fontCache); for (const { dict } of await Promise.all(this.fontCache)) {
for (const { dict } of translatedFonts) {
delete dict.cacheKey; delete dict.cacheKey;
} }
this.fontCache.clear(); this.fontCache.clear();

View File

@ -1046,27 +1046,19 @@ class PartialEvaluator {
) { ) {
const fontName = fontArgs?.[0] instanceof Name ? fontArgs[0].name : null; const fontName = fontArgs?.[0] instanceof Name ? fontArgs[0].name : null;
let translated = await this.loadFont( const translated = await this.loadFont(
fontName, fontName,
fontRef, fontRef,
resources, resources,
task,
fallbackFontDict, fallbackFontDict,
cssFontInfo cssFontInfo
); );
if (translated.font.isType3Font) { if (translated.font.isType3Font) {
try { // Add the dependencies to the parent operatorList so they are
await translated.loadType3Data(this, resources, task); // resolved before Type3 operatorLists are executed synchronously.
// Add the dependencies to the parent operatorList so they are operatorList.addDependencies(translated.type3Dependencies);
// resolved before Type3 operatorLists are executed synchronously.
operatorList.addDependencies(translated.type3Dependencies);
} catch (reason) {
translated = new TranslatedFont({
loadedName: "g_font_error",
font: new ErrorFont(`Type3 font load error: ${reason}`),
dict: translated.font,
});
}
} }
state.font = translated.font; state.font = translated.font;
@ -1228,6 +1220,7 @@ class PartialEvaluator {
fontName, fontName,
font, font,
resources, resources,
task,
fallbackFontDict = null, fallbackFontDict = null,
cssFontInfo = null cssFontInfo = null
) { ) {
@ -1356,14 +1349,21 @@ class PartialEvaluator {
font.loadedName = `${this.idFactory.getDocId()}_${fontID}`; font.loadedName = `${this.idFactory.getDocId()}_${fontID}`;
this.translateFont(preEvaluatedFont) this.translateFont(preEvaluatedFont)
.then(translatedFont => { .then(async translatedFont => {
resolve( const translated = new TranslatedFont({
new TranslatedFont({ loadedName: font.loadedName,
loadedName: font.loadedName, font: translatedFont,
font: translatedFont, dict: font,
dict: font, });
})
); if (translatedFont.isType3Font) {
try {
await translated.loadType3Data(this, resources, task);
} catch (reason) {
throw new Error(`Type3 font load error: ${reason}`);
}
}
resolve(translated);
}) })
.catch(reason => { .catch(reason => {
// TODO reject? // TODO reject?
@ -1372,9 +1372,7 @@ class PartialEvaluator {
resolve( resolve(
new TranslatedFont({ new TranslatedFont({
loadedName: font.loadedName, loadedName: font.loadedName,
font: new ErrorFont( font: new ErrorFont(reason?.message),
reason instanceof Error ? reason.message : reason
),
dict: font, dict: font,
}) })
); );
@ -2616,16 +2614,12 @@ class PartialEvaluator {
} }
async function handleSetFont(fontName, fontRef) { async function handleSetFont(fontName, fontRef) {
const translated = await self.loadFont(fontName, fontRef, resources); const translated = await self.loadFont(
fontName,
if (translated.font.isType3Font) { fontRef,
try { resources,
await translated.loadType3Data(self, resources, task); task
} catch { );
// Ignore Type3-parsing errors, since we only use `loadType3Data`
// here to ensure that we'll always obtain a useful /FontBBox.
}
}
textState.loadedName = translated.loadedName; textState.loadedName = translated.loadedName;
textState.font = translated.font; textState.font = translated.font;
@ -4602,20 +4596,22 @@ class PartialEvaluator {
} }
class TranslatedFont { class TranslatedFont {
#sent = false;
#type3Loaded = null;
constructor({ loadedName, font, dict }) { constructor({ loadedName, font, dict }) {
this.loadedName = loadedName; this.loadedName = loadedName;
this.font = font; this.font = font;
this.dict = dict; this.dict = dict;
this.type3Loaded = null;
this.type3Dependencies = font.isType3Font ? new Set() : null; this.type3Dependencies = font.isType3Font ? new Set() : null;
this.sent = false;
} }
send(handler) { send(handler) {
if (this.sent) { if (this.#sent) {
return; return;
} }
this.sent = true; this.#sent = true;
handler.send("commonobj", [ handler.send("commonobj", [
this.loadedName, this.loadedName,
@ -4645,12 +4641,12 @@ class TranslatedFont {
} }
loadType3Data(evaluator, resources, task) { loadType3Data(evaluator, resources, task) {
if (this.type3Loaded) { if (this.#type3Loaded) {
return this.type3Loaded; return this.#type3Loaded;
}
if (!this.font.isType3Font) {
throw new Error("Must be a Type3 font.");
} }
const { font, type3Dependencies } = this;
assert(font.isType3Font, "Must be a Type3 font.");
// 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.
@ -4662,14 +4658,12 @@ class TranslatedFont {
} }
type3Evaluator.type3FontRefs = type3FontRefs; type3Evaluator.type3FontRefs = type3FontRefs;
const translatedFont = this.font,
type3Dependencies = this.type3Dependencies;
let loadCharProcsPromise = Promise.resolve(); let loadCharProcsPromise = Promise.resolve();
const charProcs = this.dict.get("CharProcs"); const charProcs = this.dict.get("CharProcs");
const fontResources = this.dict.get("Resources") || resources; const fontResources = this.dict.get("Resources") || resources;
const charProcOperatorList = Object.create(null); const charProcOperatorList = Object.create(null);
const fontBBox = Util.normalizeRect(translatedFont.bbox || [0, 0, 0, 0]), const fontBBox = Util.normalizeRect(font.bbox || [0, 0, 0, 0]),
width = fontBBox[2] - fontBBox[0], width = fontBBox[2] - fontBBox[0],
height = fontBBox[3] - fontBBox[1]; height = fontBBox[3] - fontBBox[1];
const fontBBoxSize = Math.hypot(width, height); const fontBBoxSize = Math.hypot(width, height);
@ -4693,7 +4687,7 @@ class TranslatedFont {
// colour-related parameters) in the graphics state; // colour-related parameters) in the graphics state;
// any use of such operators shall be ignored." // any use of such operators shall be ignored."
if (operatorList.fnArray[0] === OPS.setCharWidthAndBounds) { if (operatorList.fnArray[0] === OPS.setCharWidthAndBounds) {
this._removeType3ColorOperators(operatorList, fontBBoxSize); this.#removeType3ColorOperators(operatorList, fontBBoxSize);
} }
charProcOperatorList[key] = operatorList.getIR(); charProcOperatorList[key] = operatorList.getIR();
@ -4708,20 +4702,17 @@ class TranslatedFont {
}); });
}); });
} }
this.type3Loaded = loadCharProcsPromise.then(() => { this.#type3Loaded = loadCharProcsPromise.then(() => {
translatedFont.charProcOperatorList = charProcOperatorList; font.charProcOperatorList = charProcOperatorList;
if (this._bbox) { if (this._bbox) {
translatedFont.isCharBBox = true; font.isCharBBox = true;
translatedFont.bbox = this._bbox; font.bbox = this._bbox;
} }
}); });
return this.type3Loaded; return this.#type3Loaded;
} }
/** #removeType3ColorOperators(operatorList, fontBBoxSize = NaN) {
* @private
*/
_removeType3ColorOperators(operatorList, fontBBoxSize = NaN) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert( assert(
operatorList.fnArray[0] === OPS.setCharWidthAndBounds, operatorList.fnArray[0] === OPS.setCharWidthAndBounds,