Skip to content
Source
Sign inSign up

document.worker.ts

51 lines1.8 KB

1// Adapted from opencode (MIT). Copyright (c) 2025 opencode. See LICENSE.opencode.
2import { openWordDocument, pageSize } from "./word-document";
3import type { WordMethods } from "./word-protocol";
4import { serveWorker, Transfer } from "./worker-rpc";
5​
6// One worker holds one open Word document. The window terminates the worker when the document closes, which is the only
7// way the engine's memory is given back.
8​
9/** The document this worker opened, once it has. */
10const opened: { value: Awaited<ReturnType<typeof openWordDocument>> | undefined } = {
11 value: undefined,
12};
13​
14serveWorker<WordMethods>({
15 open: async (input) => {
16 if (opened.value) throw new Error("The worker already holds a document");
17​
18 // SAFETY: a worker's global scope is a FontFaceSource, whose `fonts` is a FontFaceSet (CSS Font Loading, exposed to
19 // workers). The window's types declare it on documents only. Without one, the canvas keeps the system's fonts.
20 const fonts = "fonts" in self ? (self.fonts as FontFaceSet) : undefined;
21 const loaded = await openWordDocument(new Uint8Array(input.bytes), fonts);
22​
23 opened.value = loaded;
24​
25 return { pages: loaded.pages().map(pageSize), loading: loaded.loading() };
26 },
27 layout: () => required().step(),
28 paint: async (input, signal) => {
29 const painted = await required().paint(input.index, input.scale, input.ratio, signal);
30​
31 return painted ? new Transfer(painted, [painted.bitmap]) : undefined;
32 },
33 vectors: (input) => {
34 required().vectors(input);
35​
36 return undefined;
37 },
38 build: (input) => {
39 required().built(input.index);
40​
41 return undefined;
42 },
43 text: (input) => required().text(input.index),
44 bookmark: (input) => required().target(input.name),
45});
46​
47function required() {
48 if (!opened.value) throw new Error("The worker holds no document");
49​
50 return opened.value;