Skip to content

Commit 9faaec6

Browse files
committed
Cache the "raw" standard font data in the worker-thread (PR 12726 follow-up)
*This implementation is basically a copy of the pre-existing `builtInCMapCache` implementation.* For some, badly generated, PDF documents it's possible that we'll end up having to fetch the *same* standard font data over and over (which is obviously inefficient). While not common, it's certainly possible that a PDF document uses *custom* font names where the actual font then references one of the standard fonts; see e.g. issue 11399 for one such example. Note that I did suggest adding worker-thread caching of standard font data in PR 12726, however it wasn't deemed necessary at the time. Now that we have a real-world example that benefit from caching, I think that we should simply implement this now.
1 parent ae531e5 commit 9faaec6

File tree

4 files changed

+46
-17
lines changed

4 files changed

+46
-17
lines changed

src/core/catalog.js

+2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class Catalog {
7171

7272
this.fontCache = new RefSetCache();
7373
this.builtInCMapCache = new Map();
74+
this.standardFontDataCache = new Map();
7475
this.globalImageCache = new GlobalImageCache();
7576
this.pageKidsCountCache = new RefSetCache();
7677
this.pageIndexCache = new RefSetCache();
@@ -1020,6 +1021,7 @@ class Catalog {
10201021
}
10211022
this.fontCache.clear();
10221023
this.builtInCMapCache.clear();
1024+
this.standardFontDataCache.clear();
10231025
});
10241026
}
10251027

src/core/document.js

+8
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class Page {
7979
globalIdFactory,
8080
fontCache,
8181
builtInCMapCache,
82+
standardFontDataCache,
8283
globalImageCache,
8384
nonBlendModesSet,
8485
xfaFactory,
@@ -90,6 +91,7 @@ class Page {
9091
this.ref = ref;
9192
this.fontCache = fontCache;
9293
this.builtInCMapCache = builtInCMapCache;
94+
this.standardFontDataCache = standardFontDataCache;
9395
this.globalImageCache = globalImageCache;
9496
this.nonBlendModesSet = nonBlendModesSet;
9597
this.evaluatorOptions = pdfManager.evaluatorOptions;
@@ -255,6 +257,7 @@ class Page {
255257
idFactory: this._localIdFactory,
256258
fontCache: this.fontCache,
257259
builtInCMapCache: this.builtInCMapCache,
260+
standardFontDataCache: this.standardFontDataCache,
258261
globalImageCache: this.globalImageCache,
259262
options: this.evaluatorOptions,
260263
});
@@ -321,6 +324,7 @@ class Page {
321324
idFactory: this._localIdFactory,
322325
fontCache: this.fontCache,
323326
builtInCMapCache: this.builtInCMapCache,
327+
standardFontDataCache: this.standardFontDataCache,
324328
globalImageCache: this.globalImageCache,
325329
options: this.evaluatorOptions,
326330
});
@@ -425,6 +429,7 @@ class Page {
425429
idFactory: this._localIdFactory,
426430
fontCache: this.fontCache,
427431
builtInCMapCache: this.builtInCMapCache,
432+
standardFontDataCache: this.standardFontDataCache,
428433
globalImageCache: this.globalImageCache,
429434
options: this.evaluatorOptions,
430435
});
@@ -883,6 +888,7 @@ class PDFDocument {
883888
idFactory: this._globalIdFactory,
884889
fontCache: this.catalog.fontCache,
885890
builtInCMapCache: this.catalog.builtInCMapCache,
891+
standardFontDataCache: this.catalog.standardFontDataCache,
886892
});
887893
const operatorList = new OperatorList();
888894
const initialState = {
@@ -1141,6 +1147,7 @@ class PDFDocument {
11411147
globalIdFactory: this._globalIdFactory,
11421148
fontCache: catalog.fontCache,
11431149
builtInCMapCache: catalog.builtInCMapCache,
1150+
standardFontDataCache: catalog.standardFontDataCache,
11441151
globalImageCache: catalog.globalImageCache,
11451152
nonBlendModesSet: catalog.nonBlendModesSet,
11461153
xfaFactory: this.xfaFactory,
@@ -1163,6 +1170,7 @@ class PDFDocument {
11631170
globalIdFactory: this._globalIdFactory,
11641171
fontCache: catalog.fontCache,
11651172
builtInCMapCache: catalog.builtInCMapCache,
1173+
standardFontDataCache: catalog.standardFontDataCache,
11661174
globalImageCache: catalog.globalImageCache,
11671175
nonBlendModesSet: catalog.nonBlendModesSet,
11681176
xfaFactory: null,

src/core/evaluator.js

+35-17
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ class PartialEvaluator {
206206
idFactory,
207207
fontCache,
208208
builtInCMapCache,
209+
standardFontDataCache,
209210
globalImageCache,
210211
options = null,
211212
}) {
@@ -215,6 +216,7 @@ class PartialEvaluator {
215216
this.idFactory = idFactory;
216217
this.fontCache = fontCache;
217218
this.builtInCMapCache = builtInCMapCache;
219+
this.standardFontDataCache = standardFontDataCache;
218220
this.globalImageCache = globalImageCache;
219221
this.options = options || DefaultPartialEvaluatorOptions;
220222
this.parsingType3Font = false;
@@ -389,40 +391,56 @@ class PartialEvaluator {
389391
}
390392

391393
async fetchStandardFontData(name) {
394+
const cachedData = this.standardFontDataCache.get(name);
395+
if (cachedData) {
396+
return new Stream(cachedData);
397+
}
398+
392399
// The symbol fonts are not consistent across platforms, always load the
393-
// font data for them.
400+
// standard font data for them.
394401
if (
395402
this.options.useSystemFonts &&
396403
name !== "Symbol" &&
397404
name !== "ZapfDingbats"
398405
) {
399406
return null;
400407
}
401-
const standardFontNameToFileName = getStdFontNameToFileMap();
402-
const filename = standardFontNameToFileName[name];
408+
409+
const standardFontNameToFileName = getStdFontNameToFileMap(),
410+
filename = standardFontNameToFileName[name];
411+
let data;
412+
403413
if (this.options.standardFontDataUrl !== null) {
404414
const url = `${this.options.standardFontDataUrl}${filename}.pfb`;
405415
const response = await fetch(url);
406416
if (!response.ok) {
407417
warn(
408-
`fetchStandardFontData failed to fetch file "${url}" with "${response.statusText}".`
418+
`fetchStandardFontData: failed to fetch file "${url}" with "${response.statusText}".`
419+
);
420+
} else {
421+
data = await response.arrayBuffer();
422+
}
423+
} else {
424+
// Get the data on the main-thread instead.
425+
try {
426+
data = await this.handler.sendWithPromise("FetchStandardFontData", {
427+
filename,
428+
});
429+
} catch (e) {
430+
warn(
431+
`fetchStandardFontData: failed to fetch file "${filename}" with "${e}".`
409432
);
410-
return null;
411433
}
412-
return new Stream(await response.arrayBuffer());
413434
}
414-
// Get the data on the main thread instead.
415-
try {
416-
const data = await this.handler.sendWithPromise("FetchStandardFontData", {
417-
filename,
418-
});
419-
return new Stream(data);
420-
} catch (e) {
421-
warn(
422-
`fetchStandardFontData failed to fetch file "${filename}" with "${e}".`
423-
);
435+
436+
if (!data) {
437+
return null;
424438
}
425-
return null;
439+
// Cache the "raw" standard font data, to avoid fetching it repeateadly
440+
// (see e.g. issue 11399).
441+
this.standardFontDataCache.set(name, data);
442+
443+
return new Stream(data);
426444
}
427445

428446
async buildFormXObject(

test/unit/annotation_spec.js

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ describe("annotation", function () {
125125
idFactory: createIdFactory(/* pageIndex = */ 0),
126126
fontCache: new RefSetCache(),
127127
builtInCMapCache,
128+
standardFontDataCache: new Map(),
128129
});
129130
});
130131

0 commit comments

Comments
 (0)