Skip to content
Source
Sign inSign up
Open History to see the last commit

word-protocol.ts

93 lines4.1 KB

1// Adapted from opencode (MIT). Copyright (c) 2025 opencode. See LICENSE.opencode.
2import type { VectorBitmap, VectorRequest } from "./vector-pictures";
3import type { Method } from "./worker-rpc";
4​
5// What the Word preview's window and its document worker say to each other. The window keeps only the page column, the
6// text layer's DOM and the painted bitmaps; the engine, its fonts, the layout and the painting live in the worker.
7​
8/** Where a link in the document leads. */
9export type WordLink =
10 /** A web or mail address the system browser opens. */
11 | { readonly kind: "external"; readonly url: string }
12 /** A bookmark in the same document, such as a table of contents entry's heading. */
13 | { readonly kind: "internal"; readonly bookmark: string };
14​
15/**
16 * One transparent piece of the text layer, in units that scale with the page: positions are fractions of the page and
17 * lengths are ems of `size`, so the layer follows any page width without being rebuilt.
18 */
19export type WordSpan = {
20 readonly text: string;
21 /** Left and top edges, as fractions of the page's width and height. */
22 readonly left: number;
23 readonly top: number;
24 /** The font size, as a fraction of the page's width. */
25 readonly size: number;
26 /** Stretches the layer font's natural width to the painted glyphs' width, about the left edge. */
27 readonly transform: string;
28 readonly dir: "ltr" | "rtl";
29 readonly link: WordLink | undefined;
30 readonly title: string | undefined;
31 /**
32 * What separates it from the next span in copied text: a line break between paragraphs, table cells and regions, a
33 * tab across a gap with no painted space, such as a tab stop or after a list number.
34 */
35 readonly after: "br" | "tab" | undefined;
36};
37​
38/** What the page column needs to lay a page out before its content is built. */
39export type WordPageSize = {
40 readonly width: number;
41 readonly height: number;
42 readonly background: string | undefined;
43};
44​
45/** The layer's line height, in ems: the height a painted line's glyph box takes, descenders included. */
46export const lineHeight = 1.2;
47​
48/** The family the text layer measures and sets its text in; it stays transparent, so only its widths matter. */
49export const layerFont = "sans-serif";
50​
51/** One step of the rest of the layout. */
52export type WordLayoutStep =
53 /** More of the document is left to lay out. */
54 | { readonly done: false }
55 /**
56 * The whole document is laid out. `kept` tells, per page, whether the page is the one shown so far, so its row and
57 * bitmap can stay; a page the full layout changed paints again.
58 */
59 | {
60 readonly done: true;
61 readonly pages: readonly WordPageSize[];
62 readonly kept: readonly boolean[];
63 };
64​
65/** The document worker's methods. Every method but `open` needs a document `open` opened. */
66export type WordMethods = {
67 /** Opens the file's bytes and lays out its first pages; the reply lists them and whether more follow. */
68 readonly open: Method<
69 { readonly bytes: ArrayBuffer },
70 { readonly pages: readonly WordPageSize[]; readonly loading: boolean }
71 >;
72 /** Lays out a short step more of the document. */
73 readonly layout: Method<undefined, WordLayoutStep>;
74 /**
75 * Paints a page at `scale` page pixels per CSS pixel times `ratio` device pixels per CSS pixel. `vectors` lists the
76 * SVG pictures it drew without, or too coarse: paint again once the worker has them.
77 */
78 readonly paint: Method<
79 { readonly index: number; readonly scale: number; readonly ratio: number },
80 { readonly bitmap: ImageBitmap; readonly vectors: readonly VectorRequest[] } | undefined
81 >;
82 /** Hands over SVG pictures rasterized for `VectorRequest`s. */
83 readonly vectors: Method<readonly VectorBitmap[], undefined>;
84 /** Builds a page's content ahead of its paint. */
85 readonly build: Method<{ readonly index: number }, undefined>;
86 /** A page's text layer. */
87 readonly text: Method<{ readonly index: number }, readonly WordSpan[]>;
88 /** Where a bookmark sits in the pages laid out so far: its page and the height on it, in page pixels. */
89 readonly bookmark: Method<
90 { readonly name: string },
91 { readonly index: number; readonly y: number } | undefined
92 >;
93};