Skip to content

Commit 950d293

Browse files
committed
Implement offscreen rendering with workers
Move the work of drawing the PDF onto the cavas to a worker thread using OffscreenCanvas. This should free up the main thread a bit by moving all of the CanvasGraphics operations to this "renderer" worker.
1 parent 43fefff commit 950d293

File tree

11 files changed

+544
-176
lines changed

11 files changed

+544
-176
lines changed

src/core/document.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,11 @@ class Page {
127127
};
128128
}
129129

130-
#createPartialEvaluator(handler) {
130+
#createPartialEvaluator(handler, rendererHandler) {
131131
return new PartialEvaluator({
132132
xref: this.xref,
133133
handler,
134+
rendererHandler,
134135
pageIndex: this.pageIndex,
135136
idFactory: this._localIdFactory,
136137
fontCache: this.fontCache,
@@ -430,6 +431,7 @@ class Page {
430431

431432
async getOperatorList({
432433
handler,
434+
rendererHandler,
433435
sink,
434436
task,
435437
intent,
@@ -440,7 +442,10 @@ class Page {
440442
const contentStreamPromise = this.getContentStream();
441443
const resourcesPromise = this.loadResources(RESOURCES_KEYS_OPERATOR_LIST);
442444

443-
const partialEvaluator = this.#createPartialEvaluator(handler);
445+
const partialEvaluator = this.#createPartialEvaluator(
446+
handler,
447+
rendererHandler
448+
);
444449

445450
const newAnnotsByPage = !this.xfaFactory
446451
? getNewAnnotationsMap(annotationStorage)
@@ -1263,7 +1268,7 @@ class PDFDocument {
12631268
this.xfaFactory.setImages(xfaImages);
12641269
}
12651270

1266-
async #loadXfaFonts(handler, task) {
1271+
async #loadXfaFonts(handler, task, rendererHandler) {
12671272
const acroForm = await this.pdfManager.ensureCatalog("acroForm");
12681273
if (!acroForm) {
12691274
return;
@@ -1289,6 +1294,7 @@ class PDFDocument {
12891294
const partialEvaluator = new PartialEvaluator({
12901295
xref: this.xref,
12911296
handler,
1297+
rendererHandler,
12921298
pageIndex: -1,
12931299
idFactory: this._globalIdFactory,
12941300
fontCache,
@@ -1401,9 +1407,9 @@ class PDFDocument {
14011407
this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts);
14021408
}
14031409

1404-
loadXfaResources(handler, task) {
1410+
loadXfaResources(handler, task, rendererHandler) {
14051411
return Promise.all([
1406-
this.#loadXfaFonts(handler, task).catch(() => {
1412+
this.#loadXfaFonts(handler, task, rendererHandler).catch(() => {
14071413
// Ignore errors, to allow the document to load.
14081414
}),
14091415
this.#loadXfaImages(),

src/core/evaluator.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ class PartialEvaluator {
221221
constructor({
222222
xref,
223223
handler,
224+
rendererHandler,
224225
pageIndex,
225226
idFactory,
226227
fontCache,
@@ -233,6 +234,7 @@ class PartialEvaluator {
233234
}) {
234235
this.xref = xref;
235236
this.handler = handler;
237+
this.rendererHandler = rendererHandler;
236238
this.pageIndex = pageIndex;
237239
this.idFactory = idFactory;
238240
this.fontCache = fontCache;
@@ -552,13 +554,13 @@ class PartialEvaluator {
552554
const transfers = imgData ? [imgData.bitmap || imgData.data.buffer] : null;
553555

554556
if (this.parsingType3Font || cacheGlobally) {
555-
return this.handler.send(
557+
return this.rendererHandler.send(
556558
"commonobj",
557559
[objId, "Image", imgData],
558560
transfers
559561
);
560562
}
561-
return this.handler.send(
563+
return this.rendererHandler.send(
562564
"obj",
563565
[objId, this.pageIndex, "Image", imgData],
564566
transfers
@@ -786,11 +788,10 @@ class PartialEvaluator {
786788
// globally, check if the image is still cached locally on the main-thread
787789
// to avoid having to re-parse the image (since that can be slow).
788790
if (w * h > 250000 || hasMask) {
789-
const localLength = await this.handler.sendWithPromise("commonobj", [
790-
objId,
791-
"CopyLocalImage",
792-
{ imageRef },
793-
]);
791+
const localLength = await this.rendererHandler.sendWithPromise(
792+
"commonobj",
793+
[objId, "CopyLocalImage", { imageRef }]
794+
);
794795

795796
if (localLength) {
796797
this.globalImageCache.setData(imageRef, globalCacheData);
@@ -1019,7 +1020,7 @@ class PartialEvaluator {
10191020
}
10201021

10211022
state.font = translated.font;
1022-
translated.send(this.handler);
1023+
translated.send(this.rendererHandler);
10231024
return translated.loadedName;
10241025
}
10251026

@@ -1039,7 +1040,7 @@ class PartialEvaluator {
10391040
PartialEvaluator.buildFontPaths(
10401041
font,
10411042
glyphs,
1042-
this.handler,
1043+
this.rendererHandler,
10431044
this.options
10441045
);
10451046
}
@@ -1516,9 +1517,14 @@ class PartialEvaluator {
15161517
localShadingPatternCache.set(shading, id);
15171518

15181519
if (this.parsingType3Font) {
1519-
this.handler.send("commonobj", [id, "Pattern", patternIR]);
1520+
this.rendererHandler.send("commonobj", [id, "Pattern", patternIR]);
15201521
} else {
1521-
this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]);
1522+
this.rendererHandler.send("obj", [
1523+
id,
1524+
this.pageIndex,
1525+
"Pattern",
1526+
patternIR,
1527+
]);
15221528
}
15231529
return id;
15241530
}

src/core/worker.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ class WorkerMessageHandler {
8282

8383
static setup(handler, port) {
8484
let testMessageProcessed = false;
85+
let rendererHandler = null;
86+
8587
handler.on("test", data => {
8688
if (testMessageProcessed) {
8789
return; // we already processed 'test' message once
@@ -94,12 +96,19 @@ class WorkerMessageHandler {
9496

9597
handler.on("configure", data => {
9698
setVerbosityLevel(data.verbosity);
99+
rendererHandler = new MessageHandler(
100+
"worker-channel",
101+
"renderer-channel",
102+
data.channelPort
103+
);
97104
});
98105

99-
handler.on("GetDocRequest", data => this.createDocumentHandler(data, port));
106+
handler.on("GetDocRequest", data =>
107+
this.createDocumentHandler(data, port, rendererHandler)
108+
);
100109
}
101110

102-
static createDocumentHandler(docParams, port) {
111+
static createDocumentHandler(docParams, port, rendererHandler) {
103112
// This context is actually holds references on pdfManager and handler,
104113
// until the latter is destroyed.
105114
let pdfManager;
@@ -173,7 +182,11 @@ class WorkerMessageHandler {
173182
const task = new WorkerTask("loadXfaResources");
174183
startWorkerTask(task);
175184

176-
await pdfManager.ensureDoc("loadXfaResources", [handler, task]);
185+
await pdfManager.ensureDoc("loadXfaResources", [
186+
handler,
187+
task,
188+
rendererHandler,
189+
]);
177190
finishWorkerTask(task);
178191
}
179192

@@ -725,6 +738,7 @@ class WorkerMessageHandler {
725738
page
726739
.getOperatorList({
727740
handler,
741+
rendererHandler,
728742
sink,
729743
task,
730744
intent: data.intent,
@@ -808,8 +822,8 @@ class WorkerMessageHandler {
808822
.then(page => pdfManager.ensure(page, "getStructTree"));
809823
});
810824

811-
handler.on("FontFallback", function (data) {
812-
return pdfManager.fontFallback(data.id, handler);
825+
rendererHandler.on("FontFallback", function (data) {
826+
return pdfManager.fontFallback(data.id, rendererHandler);
813827
});
814828

815829
handler.on("Cleanup", function (data) {

0 commit comments

Comments
 (0)