Skip to content

Commit dfd66ee

Browse files
ryzokukenAditi-1400
authored andcommitted
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 1c12b07 commit dfd66ee

10 files changed

Lines changed: 614 additions & 65 deletions

File tree

src/core/document.js

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

131-
#createPartialEvaluator(handler) {
131+
#createPartialEvaluator(handler, rendererHandler) {
132132
return new PartialEvaluator({
133133
xref: this.xref,
134134
handler,
135+
rendererHandler,
135136
pageIndex: this.pageIndex,
136137
idFactory: this._localIdFactory,
137138
fontCache: this.fontCache,
@@ -459,6 +460,7 @@ class Page {
459460

460461
async getOperatorList({
461462
handler,
463+
rendererHandler,
462464
sink,
463465
task,
464466
intent,
@@ -471,7 +473,10 @@ class Page {
471473
const contentStreamPromise = this.getContentStream();
472474
const resourcesPromise = this.loadResources(RESOURCES_KEYS_OPERATOR_LIST);
473475

474-
const partialEvaluator = this.#createPartialEvaluator(handler);
476+
const partialEvaluator = this.#createPartialEvaluator(
477+
handler,
478+
rendererHandler
479+
);
475480

476481
const newAnnotsByPage = !this.xfaFactory
477482
? getNewAnnotationsMap(annotationStorage)
@@ -1310,7 +1315,7 @@ class PDFDocument {
13101315
this.xfaFactory.setImages(xfaImages);
13111316
}
13121317

1313-
async #loadXfaFonts(handler, task) {
1318+
async #loadXfaFonts(handler, task, rendererHandler) {
13141319
const acroForm = await this.pdfManager.ensureCatalog("acroForm");
13151320
if (!acroForm) {
13161321
return;
@@ -1336,6 +1341,7 @@ class PDFDocument {
13361341
const partialEvaluator = new PartialEvaluator({
13371342
xref: this.xref,
13381343
handler,
1344+
rendererHandler,
13391345
pageIndex: -1,
13401346
idFactory: this._globalIdFactory,
13411347
fontCache,
@@ -1448,9 +1454,9 @@ class PDFDocument {
14481454
this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts);
14491455
}
14501456

1451-
loadXfaResources(handler, task) {
1457+
loadXfaResources(handler, task, rendererHandler) {
14521458
return Promise.all([
1453-
this.#loadXfaFonts(handler, task).catch(() => {
1459+
this.#loadXfaFonts(handler, task, rendererHandler).catch(() => {
14541460
// Ignore errors, to allow the document to load.
14551461
}),
14561462
this.#loadXfaImages(),

src/core/evaluator.js

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ class PartialEvaluator {
226226
constructor({
227227
xref,
228228
handler,
229+
rendererHandler,
229230
pageIndex,
230231
idFactory,
231232
fontCache,
@@ -238,6 +239,7 @@ class PartialEvaluator {
238239
}) {
239240
this.xref = xref;
240241
this.handler = handler;
242+
this.rendererHandler = rendererHandler;
241243
this.pageIndex = pageIndex;
242244
this.idFactory = idFactory;
243245
this.fontCache = fontCache;
@@ -557,13 +559,19 @@ class PartialEvaluator {
557559
const transfers = imgData ? [imgData.bitmap || imgData.data.buffer] : null;
558560

559561
if (this.parsingType3Font || cacheGlobally) {
560-
return this.handler.send(
562+
this.handler.send("commonobj", [objId, "Image", imgData], transfers);
563+
return this.rendererHandler.send(
561564
"commonobj",
562565
[objId, "Image", imgData],
563566
transfers
564567
);
565568
}
566-
return this.handler.send(
569+
this.handler.send(
570+
"obj",
571+
[objId, this.pageIndex, "Image", imgData],
572+
transfers
573+
);
574+
return this.rendererHandler.send(
567575
"obj",
568576
[objId, this.pageIndex, "Image", imgData],
569577
transfers
@@ -791,11 +799,10 @@ class PartialEvaluator {
791799
// globally, check if the image is still cached locally on the main-thread
792800
// to avoid having to re-parse the image (since that can be slow).
793801
if (w * h > 250000 || hasMask) {
794-
const localLength = await this.handler.sendWithPromise("commonobj", [
795-
objId,
796-
"CopyLocalImage",
797-
{ imageRef },
798-
]);
802+
const localLength = await this.rendererHandler.sendWithPromise(
803+
"commonobj",
804+
[objId, "CopyLocalImage", { imageRef }]
805+
);
799806

800807
if (localLength) {
801808
this.globalImageCache.setData(imageRef, globalCacheData);
@@ -1025,6 +1032,7 @@ class PartialEvaluator {
10251032

10261033
state.font = translated.font;
10271034
translated.send(this.handler);
1035+
translated.send(this.rendererHandler);
10281036
return translated.loadedName;
10291037
}
10301038

@@ -1045,7 +1053,7 @@ class PartialEvaluator {
10451053
PartialEvaluator.buildFontPaths(
10461054
font,
10471055
glyphs,
1048-
this.handler,
1056+
this.rendererHandler,
10491057
this.options
10501058
);
10511059
}
@@ -1526,8 +1534,19 @@ class PartialEvaluator {
15261534
const patternBuffer = PatternInfo.write(patternIR);
15271535
transfers.push(patternBuffer);
15281536
this.handler.send("commonobj", [id, "Pattern", patternBuffer], transfers);
1537+
this.rendererHandler.send(
1538+
"commonobj",
1539+
[id, "Pattern", patternBuffer],
1540+
transfers
1541+
);
15291542
} else {
15301543
this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]);
1544+
this.rendererHandler.send("obj", [
1545+
id,
1546+
this.pageIndex,
1547+
"Pattern",
1548+
patternIR,
1549+
]);
15311550
}
15321551
return id;
15331552
}

src/core/worker.js

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

8484
static setup(handler, port) {
8585
let testMessageProcessed = false;
86+
let rendererHandler = null;
87+
8688
handler.on("test", data => {
8789
if (testMessageProcessed) {
8890
return; // we already processed 'test' message once
@@ -95,12 +97,19 @@ class WorkerMessageHandler {
9597

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

100-
handler.on("GetDocRequest", data => this.createDocumentHandler(data, port));
107+
handler.on("GetDocRequest", data =>
108+
this.createDocumentHandler(data, port, rendererHandler)
109+
);
101110
}
102111

103-
static createDocumentHandler(docParams, port) {
112+
static createDocumentHandler(docParams, port, rendererHandler) {
104113
// This context is actually holds references on pdfManager and handler,
105114
// until the latter is destroyed.
106115
let pdfManager;
@@ -174,7 +183,11 @@ class WorkerMessageHandler {
174183
const task = new WorkerTask("loadXfaResources");
175184
startWorkerTask(task);
176185

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

@@ -865,6 +878,7 @@ class WorkerMessageHandler {
865878
page
866879
.getOperatorList({
867880
handler,
881+
rendererHandler,
868882
sink,
869883
task,
870884
intent: data.intent,
@@ -950,8 +964,8 @@ class WorkerMessageHandler {
950964
.then(page => pdfManager.ensure(page, "getStructTree"));
951965
});
952966

953-
handler.on("FontFallback", function (data) {
954-
return pdfManager.fontFallback(data.id, handler);
967+
rendererHandler.on("FontFallback", function (data) {
968+
return pdfManager.fontFallback(data.id, rendererHandler);
955969
});
956970

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

0 commit comments

Comments
 (0)