Skip to content

Commit

Permalink
Merge pull request #19563 from Snuffleupagus/loadType3Data-once
Browse files Browse the repository at this point in the history
Invoke `TranslatedFont.prototype.loadType3Data` only *once* per font
  • Loading branch information
Snuffleupagus authored Feb 27, 2025
2 parents 56a683b + bdfa968 commit 50c573d
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 59 deletions.
4 changes: 1 addition & 3 deletions src/core/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -1177,9 +1177,7 @@ class Catalog {
this.pageDictCache.clear();
this.nonBlendModesSet.clear();

const translatedFonts = await Promise.all(this.fontCache);

for (const { dict } of translatedFonts) {
for (const { dict } of await Promise.all(this.fontCache)) {
delete dict.cacheKey;
}
this.fontCache.clear();
Expand Down
103 changes: 47 additions & 56 deletions src/core/evaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -1046,27 +1046,19 @@ class PartialEvaluator {
) {
const fontName = fontArgs?.[0] instanceof Name ? fontArgs[0].name : null;

let translated = await this.loadFont(
const translated = await this.loadFont(
fontName,
fontRef,
resources,
task,
fallbackFontDict,
cssFontInfo
);

if (translated.font.isType3Font) {
try {
await translated.loadType3Data(this, resources, task);
// Add the dependencies to the parent operatorList so they are
// 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,
});
}
// Add the dependencies to the parent operatorList so they are
// resolved before Type3 operatorLists are executed synchronously.
operatorList.addDependencies(translated.type3Dependencies);
}

state.font = translated.font;
Expand Down Expand Up @@ -1228,6 +1220,7 @@ class PartialEvaluator {
fontName,
font,
resources,
task,
fallbackFontDict = null,
cssFontInfo = null
) {
Expand Down Expand Up @@ -1356,14 +1349,21 @@ class PartialEvaluator {
font.loadedName = `${this.idFactory.getDocId()}_${fontID}`;

this.translateFont(preEvaluatedFont)
.then(translatedFont => {
resolve(
new TranslatedFont({
loadedName: font.loadedName,
font: translatedFont,
dict: font,
})
);
.then(async translatedFont => {
const translated = new TranslatedFont({
loadedName: font.loadedName,
font: translatedFont,
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 => {
// TODO reject?
Expand All @@ -1372,9 +1372,7 @@ class PartialEvaluator {
resolve(
new TranslatedFont({
loadedName: font.loadedName,
font: new ErrorFont(
reason instanceof Error ? reason.message : reason
),
font: new ErrorFont(reason?.message),
dict: font,
})
);
Expand Down Expand Up @@ -2616,16 +2614,12 @@ class PartialEvaluator {
}

async function handleSetFont(fontName, fontRef) {
const translated = await self.loadFont(fontName, fontRef, resources);

if (translated.font.isType3Font) {
try {
await translated.loadType3Data(self, resources, task);
} catch {
// Ignore Type3-parsing errors, since we only use `loadType3Data`
// here to ensure that we'll always obtain a useful /FontBBox.
}
}
const translated = await self.loadFont(
fontName,
fontRef,
resources,
task
);

textState.loadedName = translated.loadedName;
textState.font = translated.font;
Expand Down Expand Up @@ -4602,20 +4596,22 @@ class PartialEvaluator {
}

class TranslatedFont {
#sent = false;

#type3Loaded = null;

constructor({ loadedName, font, dict }) {
this.loadedName = loadedName;
this.font = font;
this.dict = dict;
this.type3Loaded = null;
this.type3Dependencies = font.isType3Font ? new Set() : null;
this.sent = false;
}

send(handler) {
if (this.sent) {
if (this.#sent) {
return;
}
this.sent = true;
this.#sent = true;

handler.send("commonobj", [
this.loadedName,
Expand Down Expand Up @@ -4645,12 +4641,12 @@ class TranslatedFont {
}

loadType3Data(evaluator, resources, task) {
if (this.type3Loaded) {
return this.type3Loaded;
}
if (!this.font.isType3Font) {
throw new Error("Must be a Type3 font.");
if (this.#type3Loaded) {
return this.#type3Loaded;
}
const { font, type3Dependencies } = this;
assert(font.isType3Font, "Must be a Type3 font.");

// 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
// make sense to only be able to render a Type3 glyph partially.
Expand All @@ -4662,14 +4658,12 @@ class TranslatedFont {
}
type3Evaluator.type3FontRefs = type3FontRefs;

const translatedFont = this.font,
type3Dependencies = this.type3Dependencies;
let loadCharProcsPromise = Promise.resolve();
const charProcs = this.dict.get("CharProcs");
const fontResources = this.dict.get("Resources") || resources;
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],
height = fontBBox[3] - fontBBox[1];
const fontBBoxSize = Math.hypot(width, height);
Expand All @@ -4693,7 +4687,7 @@ class TranslatedFont {
// colour-related parameters) in the graphics state;
// any use of such operators shall be ignored."
if (operatorList.fnArray[0] === OPS.setCharWidthAndBounds) {
this._removeType3ColorOperators(operatorList, fontBBoxSize);
this.#removeType3ColorOperators(operatorList, fontBBoxSize);
}
charProcOperatorList[key] = operatorList.getIR();

Expand All @@ -4708,20 +4702,17 @@ class TranslatedFont {
});
});
}
this.type3Loaded = loadCharProcsPromise.then(() => {
translatedFont.charProcOperatorList = charProcOperatorList;
this.#type3Loaded = loadCharProcsPromise.then(() => {
font.charProcOperatorList = charProcOperatorList;
if (this._bbox) {
translatedFont.isCharBBox = true;
translatedFont.bbox = this._bbox;
font.isCharBBox = true;
font.bbox = this._bbox;
}
});
return this.type3Loaded;
return this.#type3Loaded;
}

/**
* @private
*/
_removeType3ColorOperators(operatorList, fontBBoxSize = NaN) {
#removeType3ColorOperators(operatorList, fontBBoxSize = NaN) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
operatorList.fnArray[0] === OPS.setCharWidthAndBounds,
Expand Down

0 comments on commit 50c573d

Please sign in to comment.