@quillmark/wasm 0.129.0 → 0.131.0

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.
package/render/wasm.js CHANGED
@@ -441,8 +441,11 @@ export class Document {
441
441
  return Document.__wrap(ret);
442
442
  }
443
443
  /**
444
- * Storage version this build writes via [`toStored`](Document::to_stored). The
445
- * tag advances only when the wire format changes, not on every release.
444
+ * The newest storage version this build writes via
445
+ * [`toStored`](Document::to_stored): a document spelling a font mark, a
446
+ * `table` element, a merged table cell or a table head 0.130 cannot draw
447
+ * carries it, every other the tag before it, which a 0.130 build reads. The tag advances only when the wire format changes, not on
448
+ * every release.
446
449
  * @returns {string}
447
450
  */
448
451
  static currentStorageVersion() {
@@ -978,9 +981,9 @@ export class Document {
978
981
  }
979
982
  /**
980
983
  * Read the storage version tag from a raw storage DTO string without a full
981
- * parse, or `undefined`. Unknown future versions come back as-is, which
982
- * distinguishes "build too old" from "payload corrupt" when `fromStored`
983
- * throws. This is the storage version, not a field schema, though the JSON
984
+ * parse, or `undefined`. A tag this build does not read, newer or retired,
985
+ * comes back as-is, which tells a version mismatch from a corrupt payload
986
+ * when `fromStored` throws. This is the storage version, not a field schema, though the JSON
984
987
  * key is spelled `"schema"`: that is the DTO's serde tag.
985
988
  * @param {string} json
986
989
  * @returns {string | undefined}
@@ -1203,11 +1206,51 @@ export class Engine {
1203
1206
  const ptr = this.__destroy_into_raw();
1204
1207
  wasm.__wbg_engine_free(ptr, 0);
1205
1208
  }
1206
- constructor() {
1207
- const ret = wasm.engine_new();
1208
- this.__wbg_ptr = ret;
1209
- EngineFinalization.register(this, this.__wbg_ptr, this);
1210
- return this;
1209
+ /**
1210
+ * The text faces a `font_family` mark may set in a session of `quill`:
1211
+ * the host library's, in library order.
1212
+ * @param {Quill} quill
1213
+ * @returns {FontFace[]}
1214
+ */
1215
+ fontFaces(quill) {
1216
+ try {
1217
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
1218
+ _assertClass(quill, Quill);
1219
+ wasm.engine_fontFaces(retptr, this.__wbg_ptr, quill.__wbg_ptr);
1220
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
1221
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
1222
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
1223
+ if (r2) {
1224
+ throw takeObject(r1);
1225
+ }
1226
+ return takeObject(r0);
1227
+ } finally {
1228
+ wasm.__wbindgen_add_to_stack_pointer(16);
1229
+ }
1230
+ }
1231
+ /**
1232
+ * An engine whose sessions also set text in `fonts`, the host's font
1233
+ * library: each a `.ttf`, `.otf`, `.ttc` or `.otc` file, parsed once
1234
+ * here. A file in another format throws `engine::font_unsupported`, and
1235
+ * one holding no readable face `engine::font_unreadable`.
1236
+ * @param {Uint8Array[] | undefined} fonts
1237
+ */
1238
+ constructor(fonts) {
1239
+ try {
1240
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
1241
+ wasm.engine_new(retptr, addHeapObject(fonts));
1242
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
1243
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
1244
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
1245
+ if (r2) {
1246
+ throw takeObject(r1);
1247
+ }
1248
+ this.__wbg_ptr = r0;
1249
+ EngineFinalization.register(this, this.__wbg_ptr, this);
1250
+ return this;
1251
+ } finally {
1252
+ wasm.__wbindgen_add_to_stack_pointer(16);
1253
+ }
1211
1254
  }
1212
1255
  /**
1213
1256
  * Open a live render session for `doc` against `quill`'s backend, on the
@@ -2452,6 +2495,9 @@ function __wbg_get_imports() {
2452
2495
  const ret = Reflect.getPrototypeOf(getObject(arg0));
2453
2496
  return addHeapObject(ret);
2454
2497
  }, arguments); },
2498
+ __wbg_getRandomValues_436a51d0629d84e1: function() { return handleError(function (arg0, arg1) {
2499
+ globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
2500
+ }, arguments); },
2455
2501
  __wbg_get_658f6698067d9515: function() { return handleError(function (arg0, arg1) {
2456
2502
  const ret = Reflect.get(getObject(arg0), getObject(arg1));
2457
2503
  return addHeapObject(ret);
@@ -2603,6 +2649,10 @@ function __wbg_get_imports() {
2603
2649
  __wbg_prototypesetcall_bc27214492979395: function(arg0, arg1, arg2) {
2604
2650
  Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), getObject(arg2));
2605
2651
  },
2652
+ __wbg_push_2baf45db356cf468: function(arg0, arg1) {
2653
+ const ret = getObject(arg0).push(getObject(arg1));
2654
+ return ret;
2655
+ },
2606
2656
  __wbg_putImageData_11570c06086c9e05: function() { return handleError(function (arg0, arg1, arg2, arg3) {
2607
2657
  getObject(arg0).putImageData(getObject(arg1), arg2, arg3);
2608
2658
  }, arguments); },
Binary file
@@ -52,7 +52,8 @@ export const document_toAnnotatedMarkdown: (a: number, b: number) => void;
52
52
  export const document_toMarkdown: (a: number, b: number) => void;
53
53
  export const document_toStored: (a: number, b: number) => void;
54
54
  export const document_warnings: (a: number, b: number) => void;
55
- export const engine_new: () => number;
55
+ export const engine_fontFaces: (a: number, b: number, c: number) => void;
56
+ export const engine_new: (a: number, b: number) => void;
56
57
  export const engine_open: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
57
58
  export const engine_render: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
58
59
  export const engine_supportedFormats: (a: number, b: number, c: number) => void;
@@ -415,6 +415,31 @@ export interface EngineOptions {
415
415
  * `"typst"` and `"acroform"` to the bundled render build.
416
416
  */
417
417
  backends?: Record<string, BackendDescriptor>;
418
+
419
+ /**
420
+ * The host's font library: each a `.ttf`, `.otf`, `.ttc` or `.otc` file,
421
+ * whose faces set only text a `font_family` mark sets. The bytes are
422
+ * copied at construction, cross into the render build once, when it loads,
423
+ * and are parsed there once. A file it refuses rejects every verb that
424
+ * needs that build, the first and each later one, with the same error:
425
+ * `engine::font_unsupported` for another format, `engine::font_unreadable`
426
+ * for a file holding no readable face. A value that is not an array of
427
+ * `Uint8Array` throws at construction.
428
+ */
429
+ fonts?: Uint8Array[];
430
+ }
431
+
432
+ /**
433
+ * One face a session sets text in, with what an editor needs for an
434
+ * `@font-face` rule: the family, the CSS weight and style, and the font file.
435
+ * `index` is the face's place in `data` where the file is a collection.
436
+ */
437
+ export interface FontFace {
438
+ family: string;
439
+ weight: number;
440
+ style: 'normal' | 'italic' | 'oblique';
441
+ index: number;
442
+ data: Uint8Array;
418
443
  }
419
444
 
420
445
  /**
@@ -485,6 +510,15 @@ export declare class Engine {
485
510
  * backend binary or cloning the quill. Async for API stability.
486
511
  */
487
512
  supportedFormats(quill: Quill): Promise<OutputFormat[]>;
513
+
514
+ /**
515
+ * The text faces a `font_family` mark may set in a session of `quill`: the
516
+ * host library's, in the order of `fonts`. A quill's own fonts and the
517
+ * embedded fallback set its plate, never a mark. Loads the backend and
518
+ * clones the quill as `open` does; a backend that reads no fonts answers
519
+ * `[]`.
520
+ */
521
+ fontFaces(quill: Quill): Promise<FontFace[]>;
488
522
  }
489
523
 
490
524
  /**
@@ -470,10 +470,22 @@ export class Engine {
470
470
  */
471
471
  /** load → Promise<build module>, memoized so each build loads once. */
472
472
  #modules = new Map();
473
- /** load → that build's engine instance (the WASM backend registry). */
473
+ /**
474
+ * load → `{ engine }`, that build's engine instance (the WASM backend
475
+ * registry), or `{ refused }`, the error its construction threw. A refusal
476
+ * is the font library's, which every retry would parse and refuse again.
477
+ */
474
478
  #engines = new Map();
475
479
  /** backendId → descriptor `{ load, formats }`. */
476
480
  #loaders;
481
+ /**
482
+ * The host's font library, copied at construction and handed to each
483
+ * build's engine as it loads; `null` once every build has had it.
484
+ * @type {Uint8Array[] | null}
485
+ */
486
+ #fonts;
487
+ /** How many distinct builds the loaders name. */
488
+ #builds;
477
489
  /**
478
490
  * load → WeakMap<canonical Quill, build-memory clone>, caching the
479
491
  * expensive materialization. WeakMap so dropping the canonical quill makes
@@ -486,10 +498,15 @@ export class Engine {
486
498
  * `load` must resolve to a READY module: a registrant shipping its own
487
499
  * `--target web` build instantiates inside the thunk, and more than one
488
500
  * `Engine` may call it, so memoize (the built-ins do, at module scope).
489
- * @param {{ backends?: Record<string, { load: () => Promise<unknown>, formats: string[] }> }} [options]
501
+ * @param {{ backends?: Record<string, { load: () => Promise<unknown>, formats: string[] }>, fonts?: Uint8Array[] }} [options]
490
502
  */
491
503
  constructor(options) {
492
- rejectUnknownKeys(options, ['backends'], 'Engine options');
504
+ rejectUnknownKeys(options, ['backends', 'fonts'], 'Engine options');
505
+ const fonts = options?.fonts ?? [];
506
+ if (!Array.isArray(fonts) || !fonts.every((font) => font instanceof Uint8Array)) {
507
+ throw new Error('Engine: `fonts` must be an array of Uint8Array.');
508
+ }
509
+ this.#fonts = fonts.map((font) => font.slice());
493
510
  const merged = { ...DEFAULT_BACKENDS, ...(options?.backends ?? {}) };
494
511
  /** @type {Record<string, { load: () => Promise<unknown>, formats: string[] }>} */
495
512
  const loaders = {};
@@ -497,6 +514,7 @@ export class Engine {
497
514
  loaders[id] = validateBackend(id, entry);
498
515
  }
499
516
  this.#loaders = loaders;
517
+ this.#builds = new Set(Object.values(loaders).map((descriptor) => descriptor.load)).size;
500
518
  }
501
519
 
502
520
  /**
@@ -554,12 +572,18 @@ export class Engine {
554
572
  }
555
573
  const mod = await modPromise;
556
574
 
557
- let engine = this.#engines.get(load);
558
- if (!engine) {
559
- engine = new mod.Engine();
560
- this.#engines.set(load, engine);
575
+ let built = this.#engines.get(load);
576
+ if (!built) {
577
+ try {
578
+ built = { engine: new mod.Engine(this.#fonts) };
579
+ } catch (refused) {
580
+ built = { refused };
581
+ }
582
+ this.#engines.set(load, built);
583
+ if (this.#engines.size === this.#builds) this.#fonts = null;
561
584
  }
562
- return { mod, engine };
585
+ if ('refused' in built) throw built.refused;
586
+ return { mod, engine: built.engine };
563
587
  }
564
588
 
565
589
  /**
@@ -658,6 +682,36 @@ export class Engine {
658
682
  );
659
683
  }
660
684
 
685
+ /**
686
+ * Run `fn` against `quill`'s build-memory clone, `quill` snapshotted before
687
+ * the first await as `#withClones` snapshots it.
688
+ * @param {string} method the caller's name, for the rejection message
689
+ * @param {Quill} quill
690
+ * @param {(ctx: { engine: any, quill: any }) => any} fn
691
+ */
692
+ async #withQuill(method, quill, fn) {
693
+ const backendId = this.#backendOf(quill, method);
694
+ const quillTree = this.#quillClones.get(this.#descriptorFor(backendId).load)?.has(quill)
695
+ ? null
696
+ : quill.toTree();
697
+ const { mod, engine } = await this.#resolveBackend(backendId);
698
+ try {
699
+ return fn({ engine, quill: this.#cachedQuillClone(mod, backendId, quill, quillTree) });
700
+ } catch (err) {
701
+ throw backendTrap(err);
702
+ }
703
+ }
704
+
705
+ /**
706
+ * @param {Quill} quill
707
+ * @returns {Promise<import('./runtime.d.ts').FontFace[]>}
708
+ */
709
+ async fontFaces(quill) {
710
+ return this.#withQuill('engine.fontFaces(quill)', quill, ({ engine, quill: q }) =>
711
+ typeof engine.fontFaces === 'function' ? engine.fontFaces(q) : []
712
+ );
713
+ }
714
+
661
715
  /**
662
716
  * @param {Quill} quill
663
717
  * @returns {Promise<void>}