Skip to content

Commit eace3c2

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 e0783cd commit eace3c2

File tree

10 files changed

+594
-65
lines changed

10 files changed

+594
-65
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,
@@ -436,6 +437,7 @@ class Page {
436437

437438
async getOperatorList({
438439
handler,
440+
rendererHandler,
439441
sink,
440442
task,
441443
intent,
@@ -446,7 +448,10 @@ class Page {
446448
const contentStreamPromise = this.getContentStream();
447449
const resourcesPromise = this.loadResources(RESOURCES_KEYS_OPERATOR_LIST);
448450

449-
const partialEvaluator = this.#createPartialEvaluator(handler);
451+
const partialEvaluator = this.#createPartialEvaluator(
452+
handler,
453+
rendererHandler
454+
);
450455

451456
const newAnnotsByPage = !this.xfaFactory
452457
? getNewAnnotationsMap(annotationStorage)
@@ -1269,7 +1274,7 @@ class PDFDocument {
12691274
this.xfaFactory.setImages(xfaImages);
12701275
}
12711276

1272-
async #loadXfaFonts(handler, task) {
1277+
async #loadXfaFonts(handler, task, rendererHandler) {
12731278
const acroForm = await this.pdfManager.ensureCatalog("acroForm");
12741279
if (!acroForm) {
12751280
return;
@@ -1295,6 +1300,7 @@ class PDFDocument {
12951300
const partialEvaluator = new PartialEvaluator({
12961301
xref: this.xref,
12971302
handler,
1303+
rendererHandler,
12981304
pageIndex: -1,
12991305
idFactory: this._globalIdFactory,
13001306
fontCache,
@@ -1407,9 +1413,9 @@ class PDFDocument {
14071413
this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts);
14081414
}
14091415

1410-
loadXfaResources(handler, task) {
1416+
loadXfaResources(handler, task, rendererHandler) {
14111417
return Promise.all([
1412-
this.#loadXfaFonts(handler, task).catch(() => {
1418+
this.#loadXfaFonts(handler, task, rendererHandler).catch(() => {
14131419
// Ignore errors, to allow the document to load.
14141420
}),
14151421
this.#loadXfaImages(),

src/core/evaluator.js

Lines changed: 23 additions & 8 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,19 @@ class PartialEvaluator {
552554
const transfers = imgData ? [imgData.bitmap || imgData.data.buffer] : null;
553555

554556
if (this.parsingType3Font || cacheGlobally) {
555-
return this.handler.send(
557+
this.handler.send("commonobj", [objId, "Image", imgData], transfers);
558+
return this.rendererHandler.send(
556559
"commonobj",
557560
[objId, "Image", imgData],
558561
transfers
559562
);
560563
}
561-
return this.handler.send(
564+
this.handler.send(
565+
"obj",
566+
[objId, this.pageIndex, "Image", imgData],
567+
transfers
568+
);
569+
return this.rendererHandler.send(
562570
"obj",
563571
[objId, this.pageIndex, "Image", imgData],
564572
transfers
@@ -786,11 +794,10 @@ class PartialEvaluator {
786794
// globally, check if the image is still cached locally on the main-thread
787795
// to avoid having to re-parse the image (since that can be slow).
788796
if (w * h > 250000 || hasMask) {
789-
const localLength = await this.handler.sendWithPromise("commonobj", [
790-
objId,
791-
"CopyLocalImage",
792-
{ imageRef },
793-
]);
797+
const localLength = await this.rendererHandler.sendWithPromise(
798+
"commonobj",
799+
[objId, "CopyLocalImage", { imageRef }]
800+
);
794801

795802
if (localLength) {
796803
this.globalImageCache.setData(imageRef, globalCacheData);
@@ -1020,6 +1027,7 @@ class PartialEvaluator {
10201027

10211028
state.font = translated.font;
10221029
translated.send(this.handler);
1030+
translated.send(this.rendererHandler);
10231031
return translated.loadedName;
10241032
}
10251033

@@ -1039,7 +1047,7 @@ class PartialEvaluator {
10391047
PartialEvaluator.buildFontPaths(
10401048
font,
10411049
glyphs,
1042-
this.handler,
1050+
this.rendererHandler,
10431051
this.options
10441052
);
10451053
}
@@ -1517,8 +1525,15 @@ class PartialEvaluator {
15171525

15181526
if (this.parsingType3Font) {
15191527
this.handler.send("commonobj", [id, "Pattern", patternIR]);
1528+
this.rendererHandler.send("commonobj", [id, "Pattern", patternIR]);
15201529
} else {
15211530
this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]);
1531+
this.rendererHandler.send("obj", [
1532+
id,
1533+
this.pageIndex,
1534+
"Pattern",
1535+
patternIR,
1536+
]);
15221537
}
15231538
return id;
15241539
}

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)