@docmentis/udoc-wasm 0.0.0-stage → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +434 -0
  2. package/README.md +53 -2
  3. package/dist/error.d.ts +16 -0
  4. package/dist/error.js +36 -0
  5. package/dist/font-fulfill.d.ts +92 -0
  6. package/dist/font-fulfill.js +203 -0
  7. package/dist/fonts/Caladea-Bold.ttf +0 -0
  8. package/dist/fonts/Caladea-BoldItalic.ttf +0 -0
  9. package/dist/fonts/Caladea-Italic.ttf +0 -0
  10. package/dist/fonts/Caladea-Regular.ttf +0 -0
  11. package/dist/fonts/Carlito-Bold.ttf +0 -0
  12. package/dist/fonts/Carlito-BoldItalic.ttf +0 -0
  13. package/dist/fonts/Carlito-Italic.ttf +0 -0
  14. package/dist/fonts/Carlito-Regular.ttf +0 -0
  15. package/dist/fonts/LICENSE-Liberation.txt +102 -0
  16. package/dist/fonts/LiberationMono-Bold.ttf +0 -0
  17. package/dist/fonts/LiberationMono-BoldItalic.ttf +0 -0
  18. package/dist/fonts/LiberationMono-Italic.ttf +0 -0
  19. package/dist/fonts/LiberationMono-Regular.ttf +0 -0
  20. package/dist/fonts/LiberationSans-Bold.ttf +0 -0
  21. package/dist/fonts/LiberationSans-BoldItalic.ttf +0 -0
  22. package/dist/fonts/LiberationSans-Italic.ttf +0 -0
  23. package/dist/fonts/LiberationSans-Regular.ttf +0 -0
  24. package/dist/fonts/LiberationSerif-Bold.ttf +0 -0
  25. package/dist/fonts/LiberationSerif-BoldItalic.ttf +0 -0
  26. package/dist/fonts/LiberationSerif-Italic.ttf +0 -0
  27. package/dist/fonts/LiberationSerif-Regular.ttf +0 -0
  28. package/dist/fonts/OFL-Caladea.txt +93 -0
  29. package/dist/fonts/OFL-Carlito.txt +94 -0
  30. package/dist/google-font-catalog.d.ts +21 -0
  31. package/dist/google-font-catalog.js +1860 -0
  32. package/dist/index.d.ts +58 -0
  33. package/dist/index.js +150 -0
  34. package/dist/meta-url.d.ts +3 -0
  35. package/dist/meta-url.js +46 -0
  36. package/dist/office-compatible-fonts.d.ts +3 -0
  37. package/dist/office-compatible-fonts.js +33 -0
  38. package/dist/wasm/THIRD-PARTY-NOTICES.txt +9395 -0
  39. package/dist/wasm/udoc.d.ts +20260 -0
  40. package/dist/wasm/udoc.js +4600 -0
  41. package/dist/wasm/udoc_bg.wasm +0 -0
  42. package/package.json +41 -5
@@ -0,0 +1,58 @@
1
+ import { WasmSession } from "./wasm/udoc.js";
2
+ import type { GoogleFontFamily, SessionConfig, UrlFont } from "./wasm/udoc.js";
3
+ export * from "./wasm/udoc.js";
4
+ export { default } from "./wasm/udoc.js";
5
+ export { GOOGLE_FONT_CATALOG } from "./google-font-catalog.js";
6
+ export { OFFICE_COMPATIBLE_FONTS } from "./office-compatible-fonts.js";
7
+ export { CSS2_ENDPOINT, createFontFetchMemory, fetchFontBinaries } from "./font-fulfill.js";
8
+ export type { Fetcher, FontFetchMemory } from "./font-fulfill.js";
9
+ export { UDocError } from "./error.js";
10
+ export type { UDocErrorCode, UDocErrorDetail } from "./error.js";
11
+ /**
12
+ * Where this package's office-compatible fonts are served: the directory its
13
+ * `dist/` is served as, which each font's path resolves against (a host's own
14
+ * copy, a CDN), or each path's own URL (`meta-url`'s `bundledFontUrls`, which
15
+ * a bundler emits file by file).
16
+ */
17
+ export type FontLocation = string | Readonly<Record<string, string>>;
18
+ /** The {@link OFFICE_COMPATIBLE_FONTS}, each at its URL in `fonts`. */
19
+ export declare function officeCompatibleFontsAt(fonts: FontLocation): UrlFont[];
20
+ /**
21
+ * Start a session as the glue's `startSession` does, offering it this
22
+ * package's fonts: the {@link OFFICE_COMPATIBLE_FONTS} by URL, from `fonts`,
23
+ * and `googleFontFamilies` — the {@link GOOGLE_FONT_CATALOG} unless given, `[]`
24
+ * for no Google Fonts at all. Rejects, with no session left behind, when the
25
+ * license refuses or the list isn't a list of `GoogleFontFamily`.
26
+ *
27
+ * It shadows the glue's export of the same name, so every host that starts a
28
+ * session through this module offers both unless it says otherwise.
29
+ */
30
+ export declare function startSession(config: SessionConfig, fonts: FontLocation, googleFontFamilies?: readonly GoogleFontFamily[]): Promise<WasmSession>;
31
+ declare module "./wasm/udoc.js" {
32
+ interface WasmSession {
33
+ readonly portable: DocNamespace<"portable">;
34
+ readonly presentation: DocNamespace<"presentation">;
35
+ readonly spreadsheet: DocNamespace<"spreadsheet">;
36
+ readonly wordprocessing: DocNamespace<"wordprocessing">;
37
+ }
38
+ }
39
+ /** `session.<Doc>`: its methods' names parsed into levels. */
40
+ type DocNamespace<Doc extends string> = Level<Extract<keyof WasmSession, `__${Doc}_${string}`>, `__${Doc}`, []>;
41
+ /** The level at the name prefix `At`, among the methods `K`; `Taken` holds the
42
+ * arguments the calls on the way took. */
43
+ type Level<K extends keyof WasmSession & string, At extends string, Taken extends unknown[]> = {
44
+ readonly [S in PropertyStep<K, At> | CallStep<K, At> | LastStep<K, At>]: S extends LastStep<K, At> ? Last<WasmSession[`${At}_${S}` & keyof WasmSession], Taken> : S extends CallStep<K, At> ? (arg: Argument<WasmSession[Extract<K, `${At}__${S}_${string}`>], Taken>) => Level<K, `${At}__${S}`, [...Taken, unknown]> : Level<K, `${At}_${S}`, Taken>;
45
+ };
46
+ /** The property steps at `At`: `_step` followed by more. */
47
+ type PropertyStep<K extends string, At extends string> = K extends `${At}_${infer S}_${string}` ? S extends "" ? never : S : never;
48
+ /** The call steps at `At`: `__step` followed by more. */
49
+ type CallStep<K extends string, At extends string> = K extends `${At}__${infer S}_${string}` ? S extends "" ? never : S : never;
50
+ /** The last steps at `At`: `_step` ending the name. */
51
+ type LastStep<K extends string, At extends string> = K extends `${At}_${infer S}` ? S extends "" | `${string}_${string}` ? never : S : never;
52
+ /** The argument a call step takes: the parameter of each method under it after
53
+ * the ones `Taken` already covers. */
54
+ type Argument<M, Taken extends unknown[]> = M extends (...args: infer P) => unknown ? P[Taken["length"]] : never;
55
+ /** A last step: its method, less the parameters the calls on the way took. */
56
+ type Last<M, Taken extends unknown[]> = Taken extends [] ? M : M extends (...args: infer P) => infer R ? (...args: Drop<P, Taken>) => R : never;
57
+ /** `P` without as many leading elements as `Taken` has. */
58
+ type Drop<P extends unknown[], Taken extends unknown[]> = Taken extends [unknown, ...infer T] ? P extends [unknown, ...infer R] ? Drop<R, T> : [] : P;
package/dist/index.js ADDED
@@ -0,0 +1,150 @@
1
+ // The package root: the wasm-bindgen glue (`wasm/udoc.js`), re-exported whole,
2
+ // with each doc type's reads grouped under `session.<doc>` on its `WasmSession`.
3
+ //
4
+ // The glue declares every read flat, as a private `__<doc>…` method. This module
5
+ // groups them by name onto the class's prototype, so every session
6
+ // `startSession` returns carries them, however a host loaded this module —
7
+ // nothing wraps the session. A read's name is its path:
8
+ //
9
+ // __portable_model_root_pageIds() session.portable.model.root.pageIds()
10
+ // __portable_model__page_shapeInfo(p, s) session.portable.model.page(p).shapeInfo(s)
11
+ //
12
+ // After `__<doc>`, `_step` is a property and `__step` a call taking one
13
+ // argument. The last step is always a call, written `_step`, taking every
14
+ // argument left. A step is a middle step or a last one, never both. The runtime
15
+ // below parses the names off the prototype and the types parse them out of
16
+ // `udoc.d.ts`, so a new read in Rust needs no line here.
17
+ //
18
+ // The glue is imported by relative path, not by package name, so this module
19
+ // still resolves it when served unbundled from a CDN or a host's own directory.
20
+ //
21
+ // Beside the glue it exports the two font offers the package ships — the
22
+ // office-compatible fonts and the Google font catalog, which its own
23
+ // `startSession` offers every session it starts — what fills a session's font
24
+ // orders, and `UDocError`, the error every failure is reported as.
25
+ import { WasmSession, startSession as startGlueSession } from "./wasm/udoc.js";
26
+ import { UDocError } from "./error.js";
27
+ import { GOOGLE_FONT_CATALOG } from "./google-font-catalog.js";
28
+ import { OFFICE_COMPATIBLE_FONTS } from "./office-compatible-fonts.js";
29
+ export * from "./wasm/udoc.js";
30
+ export { default } from "./wasm/udoc.js";
31
+ export { GOOGLE_FONT_CATALOG } from "./google-font-catalog.js";
32
+ export { OFFICE_COMPATIBLE_FONTS } from "./office-compatible-fonts.js";
33
+ export { CSS2_ENDPOINT, createFontFetchMemory, fetchFontBinaries } from "./font-fulfill.js";
34
+ export { UDocError } from "./error.js";
35
+ /** The {@link OFFICE_COMPATIBLE_FONTS}, each at its URL in `fonts`. */
36
+ export function officeCompatibleFontsAt(fonts) {
37
+ return OFFICE_COMPATIBLE_FONTS.map((font) => {
38
+ const url = typeof fonts === "string" ? new URL(font.url, fonts).href : fonts[font.url];
39
+ if (url === undefined)
40
+ throw new UDocError("invalidRequest", `no URL is given for the font ${font.url}`);
41
+ return { ...font, url };
42
+ });
43
+ }
44
+ /**
45
+ * Start a session as the glue's `startSession` does, offering it this
46
+ * package's fonts: the {@link OFFICE_COMPATIBLE_FONTS} by URL, from `fonts`,
47
+ * and `googleFontFamilies` — the {@link GOOGLE_FONT_CATALOG} unless given, `[]`
48
+ * for no Google Fonts at all. Rejects, with no session left behind, when the
49
+ * license refuses or the list isn't a list of `GoogleFontFamily`.
50
+ *
51
+ * It shadows the glue's export of the same name, so every host that starts a
52
+ * session through this module offers both unless it says otherwise.
53
+ */
54
+ export async function startSession(config, fonts, googleFontFamilies = GOOGLE_FONT_CATALOG) {
55
+ const officeCompatibleFonts = officeCompatibleFontsAt(fonts);
56
+ const session = await startGlueSession(config);
57
+ try {
58
+ session.setGoogleFontFamilies(googleFontFamilies);
59
+ session.registerUrlFonts(officeCompatibleFonts);
60
+ }
61
+ catch (error) {
62
+ session.free();
63
+ throw error;
64
+ }
65
+ return session;
66
+ }
67
+ /** The doc types grouped under `session.<doc>` — named, not found, because
68
+ * wasm-bindgen's own `__destroy_into_raw` sits on the same prototype. */
69
+ const DOCS = [
70
+ "portable",
71
+ "presentation",
72
+ "spreadsheet",
73
+ "wordprocessing",
74
+ ];
75
+ /** One separator and the step it introduces: `_step` or `__step`. */
76
+ const SEGMENT = /(__?)([^_]+)/y;
77
+ /** The steps under `session.<doc>`, parsed from the names of its methods. */
78
+ function parse(doc) {
79
+ const root = new Map();
80
+ const prefix = `__${doc}`;
81
+ for (const name of Object.getOwnPropertyNames(WasmSession.prototype)) {
82
+ if (!name.startsWith(`${prefix}_`))
83
+ continue;
84
+ const method = Object.getOwnPropertyDescriptor(WasmSession.prototype, name).value;
85
+ const path = segments(name, prefix.length);
86
+ let steps = root;
87
+ path.forEach(([separator, step], i) => {
88
+ const existing = steps.get(step);
89
+ if (i === path.length - 1) {
90
+ if (separator !== "_")
91
+ throw new Error(`WasmSession.${name}: the last step is written \`_${step}\``);
92
+ if (existing)
93
+ throw new Error(`WasmSession.${name}: \`${step}\` is already a step`);
94
+ steps.set(step, { kind: "last", method });
95
+ return;
96
+ }
97
+ const kind = separator === "_" ? "property" : "call";
98
+ if (!existing) {
99
+ const next = new Map();
100
+ steps.set(step, { kind, next });
101
+ steps = next;
102
+ return;
103
+ }
104
+ if (existing.kind === "last" || existing.kind !== kind) {
105
+ throw new Error(`WasmSession.${name}: \`${step}\` is already a different step`);
106
+ }
107
+ steps = existing.next;
108
+ });
109
+ }
110
+ return root;
111
+ }
112
+ /** `name` from `from` on, as `[separator, step]` pairs. */
113
+ function segments(name, from) {
114
+ const found = [];
115
+ SEGMENT.lastIndex = from;
116
+ let at = from;
117
+ for (let match = SEGMENT.exec(name); match; match = SEGMENT.exec(name)) {
118
+ found.push([match[1], match[2]]);
119
+ at = SEGMENT.lastIndex;
120
+ }
121
+ if (found.length === 0 || at !== name.length)
122
+ throw new Error(`WasmSession.${name}: not a namespace path`);
123
+ return found;
124
+ }
125
+ /** `steps` bound to `session`, `args` holding what the calls on the way took. */
126
+ function bind(steps, session, args) {
127
+ const level = {};
128
+ for (const [name, step] of steps) {
129
+ switch (step.kind) {
130
+ case "property":
131
+ level[name] = bind(step.next, session, args);
132
+ break;
133
+ case "call":
134
+ level[name] = (arg) => bind(step.next, session, [...args, arg]);
135
+ break;
136
+ case "last":
137
+ level[name] = (...rest) => step.method.apply(session, [...args, ...rest]);
138
+ break;
139
+ }
140
+ }
141
+ return level;
142
+ }
143
+ for (const doc of DOCS) {
144
+ const steps = parse(doc);
145
+ Object.defineProperty(WasmSession.prototype, doc, {
146
+ get() {
147
+ return bind(steps, this, []);
148
+ },
149
+ });
150
+ }
@@ -0,0 +1,3 @@
1
+ export declare const bundledWasmUrl: string;
2
+ /** Each office-compatible font's URL, by its path in `OFFICE_COMPATIBLE_FONTS`. */
3
+ export declare const bundledFontUrls: Readonly<Record<string, string>>;
@@ -0,0 +1,46 @@
1
+ // `import.meta.url`-based resolution of the bundled WASM artifacts.
2
+ //
3
+ // `scripts/copy-wasm.mjs` copies the wasm-bindgen output into `dist/wasm/`, and
4
+ // `scripts/copy-fonts.mjs` the office-compatible fonts into `dist/fonts/`, so
5
+ // they sit next to the compiled JS. We resolve each against this module's URL,
6
+ // which keeps the package self-contained: the browser at runtime, or Node,
7
+ // finds them beside this module, and a consumer's bundler emits each one as an
8
+ // asset, since every file is named by its own literal `new URL(…,
9
+ // import.meta.url)` — a pattern Vite and webpack follow, where a computed path
10
+ // would not be.
11
+ //
12
+ // Isolated in its own file and loaded **only via dynamic `import()`** (from
13
+ // udoc-ui's `worker/wasm-loader.ts`) so environments that don't support
14
+ // `import.meta` at parse time (e.g. StackBlitz's turbo_modules) don't choke.
15
+ function abs(u) {
16
+ // Some bundlers emit a root-relative path; make it absolute against the
17
+ // current origin so a worker (different base URL) resolves it the same way.
18
+ if (u.startsWith("/") && typeof globalThis.location !== "undefined") {
19
+ return globalThis.location.origin + u;
20
+ }
21
+ return u;
22
+ }
23
+ export const bundledWasmUrl = abs(new URL("./wasm/udoc_bg.wasm", import.meta.url).href);
24
+ /** Each office-compatible font's URL, by its path in `OFFICE_COMPATIBLE_FONTS`. */
25
+ export const bundledFontUrls = {
26
+ "fonts/LiberationSans-Regular.ttf": abs(new URL("./fonts/LiberationSans-Regular.ttf", import.meta.url).href),
27
+ "fonts/LiberationSans-Bold.ttf": abs(new URL("./fonts/LiberationSans-Bold.ttf", import.meta.url).href),
28
+ "fonts/LiberationSans-Italic.ttf": abs(new URL("./fonts/LiberationSans-Italic.ttf", import.meta.url).href),
29
+ "fonts/LiberationSans-BoldItalic.ttf": abs(new URL("./fonts/LiberationSans-BoldItalic.ttf", import.meta.url).href),
30
+ "fonts/LiberationSerif-Regular.ttf": abs(new URL("./fonts/LiberationSerif-Regular.ttf", import.meta.url).href),
31
+ "fonts/LiberationSerif-Bold.ttf": abs(new URL("./fonts/LiberationSerif-Bold.ttf", import.meta.url).href),
32
+ "fonts/LiberationSerif-Italic.ttf": abs(new URL("./fonts/LiberationSerif-Italic.ttf", import.meta.url).href),
33
+ "fonts/LiberationSerif-BoldItalic.ttf": abs(new URL("./fonts/LiberationSerif-BoldItalic.ttf", import.meta.url).href),
34
+ "fonts/LiberationMono-Regular.ttf": abs(new URL("./fonts/LiberationMono-Regular.ttf", import.meta.url).href),
35
+ "fonts/LiberationMono-Bold.ttf": abs(new URL("./fonts/LiberationMono-Bold.ttf", import.meta.url).href),
36
+ "fonts/LiberationMono-Italic.ttf": abs(new URL("./fonts/LiberationMono-Italic.ttf", import.meta.url).href),
37
+ "fonts/LiberationMono-BoldItalic.ttf": abs(new URL("./fonts/LiberationMono-BoldItalic.ttf", import.meta.url).href),
38
+ "fonts/Carlito-Regular.ttf": abs(new URL("./fonts/Carlito-Regular.ttf", import.meta.url).href),
39
+ "fonts/Carlito-Bold.ttf": abs(new URL("./fonts/Carlito-Bold.ttf", import.meta.url).href),
40
+ "fonts/Carlito-Italic.ttf": abs(new URL("./fonts/Carlito-Italic.ttf", import.meta.url).href),
41
+ "fonts/Carlito-BoldItalic.ttf": abs(new URL("./fonts/Carlito-BoldItalic.ttf", import.meta.url).href),
42
+ "fonts/Caladea-Regular.ttf": abs(new URL("./fonts/Caladea-Regular.ttf", import.meta.url).href),
43
+ "fonts/Caladea-Bold.ttf": abs(new URL("./fonts/Caladea-Bold.ttf", import.meta.url).href),
44
+ "fonts/Caladea-Italic.ttf": abs(new URL("./fonts/Caladea-Italic.ttf", import.meta.url).href),
45
+ "fonts/Caladea-BoldItalic.ttf": abs(new URL("./fonts/Caladea-BoldItalic.ttf", import.meta.url).href),
46
+ };
@@ -0,0 +1,3 @@
1
+ import type { UrlFont } from "./wasm/udoc.js";
2
+ /** Every face this package ships, each `url` its path in `dist/`. */
3
+ export declare const OFFICE_COMPATIBLE_FONTS: readonly UrlFont[];
@@ -0,0 +1,33 @@
1
+ // The metric-compatible office fonts — Liberation, Carlito, Caladea — that a
2
+ // native build of the engine bundles and this wasm build leaves to its host.
3
+ //
4
+ // `scripts/copy-fonts.mjs` copies them from the drawing crate into `dist/fonts/`
5
+ // at build time. Each `url` here is the file's path in `dist/`: `startSession`
6
+ // takes where `dist/` is served, or each file's own URL (`meta-url`'s
7
+ // `bundledFontUrls`), and offers the fonts by URL to every session it starts,
8
+ // so a document fetches only the faces its layout asks for. A host with no
9
+ // fetching offers the same files as bytes as well.
10
+ //
11
+ // Each family is registered under its own true name, never under the Microsoft
12
+ // font it stands in for: mapping Arial → Liberation Sans is the engine's
13
+ // substitute table's job.
14
+ /** The bold / italic variants of a family, and the suffix of each one's file. */
15
+ const STYLES = [
16
+ ["Regular", false, false],
17
+ ["Bold", true, false],
18
+ ["Italic", false, true],
19
+ ["BoldItalic", true, true],
20
+ ];
21
+ /** Each family's true name, and the prefix of its files. */
22
+ const FAMILIES = [
23
+ ["Liberation Sans", "LiberationSans"],
24
+ ["Liberation Serif", "LiberationSerif"],
25
+ ["Liberation Mono", "LiberationMono"],
26
+ ["Carlito", "Carlito"],
27
+ ["Caladea", "Caladea"],
28
+ ];
29
+ /** Every face this package ships, each `url` its path in `dist/`. */
30
+ export const OFFICE_COMPATIBLE_FONTS = FAMILIES.flatMap(([family, file]) => STYLES.map(([style, bold, italic]) => ({
31
+ descriptor: { family, bold, italic },
32
+ url: `fonts/${file}-${style}.ttf`,
33
+ })));