@takumi-rs/wasm 1.8.7 → 2.0.0-beta.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/dist/export.cjs CHANGED
@@ -23,13 +23,6 @@ var Renderer$1 = class {
23
23
  wasm.__wbg_renderer_free(ptr, 0);
24
24
  }
25
25
  /**
26
- * Clears the renderer's internal image store.
27
- */
28
- clearImageStore() {
29
- const ret = wasm.renderer_clearImageStore(this.__wbg_ptr);
30
- if (ret[1]) throw takeFromExternrefTable0(ret[0]);
31
- }
32
- /**
33
26
  * Encodes a precomputed frame sequence into an animated image buffer.
34
27
  * @param {AnimationFrameSource[]} frames
35
28
  * @param {EncodeFramesOptions} options
@@ -45,14 +38,6 @@ var Renderer$1 = class {
45
38
  return v2;
46
39
  }
47
40
  /**
48
- * Loads a font into the renderer.
49
- * @param {Font} font
50
- */
51
- loadFont(font) {
52
- const ret = wasm.renderer_loadFont(this.__wbg_ptr, font);
53
- if (ret[1]) throw takeFromExternrefTable0(ret[0]);
54
- }
55
- /**
56
41
  * Measures a node tree and returns layout information.
57
42
  * @param {Node} node
58
43
  * @param {RenderOptions | null} [options]
@@ -65,22 +50,23 @@ var Renderer$1 = class {
65
50
  }
66
51
  /**
67
52
  * Creates a new Renderer instance.
68
- * @param {ConstructRendererOptions | null} [options]
69
53
  */
70
- constructor(options) {
71
- const ret = wasm.renderer_new(isLikeNone(options) ? 0 : addToExternrefTable0(options));
54
+ constructor() {
55
+ const ret = wasm.renderer_new();
72
56
  if (ret[2]) throw takeFromExternrefTable0(ret[1]);
73
57
  this.__wbg_ptr = ret[0];
74
58
  RendererFinalization.register(this, this.__wbg_ptr, this);
75
59
  return this;
76
60
  }
77
61
  /**
78
- * Puts a persistent image into the renderer's internal store.
79
- * @param {ImageSource} data
62
+ * Registers fonts into the renderer, returning the families each font produced.
63
+ * @param {Font} font
64
+ * @returns {RegisteredFamily[]}
80
65
  */
81
- putPersistentImage(data) {
82
- const ret = wasm.renderer_putPersistentImage(this.__wbg_ptr, data);
83
- if (ret[1]) throw takeFromExternrefTable0(ret[0]);
66
+ registerFont(font) {
67
+ const ret = wasm.renderer_registerFont(this.__wbg_ptr, font);
68
+ if (ret[2]) throw takeFromExternrefTable0(ret[1]);
69
+ return takeFromExternrefTable0(ret[0]);
84
70
  }
85
71
  /**
86
72
  * Renders a node tree into an image buffer.
@@ -134,6 +120,31 @@ var Renderer$1 = class {
134
120
  wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
135
121
  }
136
122
  }
123
+ /**
124
+ * Renders a node tree into an SVG document string.
125
+ * @param {Node} node
126
+ * @param {SvgRenderOptions | null} [options]
127
+ * @returns {string}
128
+ */
129
+ renderSvg(node, options) {
130
+ let deferred2_0;
131
+ let deferred2_1;
132
+ try {
133
+ const ret = wasm.renderer_renderSvg(this.__wbg_ptr, node, isLikeNone(options) ? 0 : addToExternrefTable0(options));
134
+ var ptr1 = ret[0];
135
+ var len1 = ret[1];
136
+ if (ret[3]) {
137
+ ptr1 = 0;
138
+ len1 = 0;
139
+ throw takeFromExternrefTable0(ret[2]);
140
+ }
141
+ deferred2_0 = ptr1;
142
+ deferred2_1 = len1;
143
+ return getStringFromWasm0(ptr1, len1);
144
+ } finally {
145
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
146
+ }
147
+ }
137
148
  };
138
149
  if (Symbol.dispose) Renderer$1.prototype[Symbol.dispose] = Renderer$1.prototype.free;
139
150
  function __wbg_get_imports() {
@@ -543,124 +554,114 @@ async function __wbg_init(module_or_path) {
543
554
  }
544
555
  //#endregion
545
556
  //#region src/export.ts
546
- var Renderer = class extends Renderer$1 {
547
- fontsMark = /* @__PURE__ */ new Set();
548
- fontBuffersMark = /* @__PURE__ */ new WeakSet();
549
- persistentImageSrcMark = /* @__PURE__ */ new Set();
550
- pendingPersistentImages = /* @__PURE__ */ new Map();
551
- putPersistentImage(data, signal) {
552
- if (signal?.aborted) return;
553
- if (!this.isNewPersistentImage(data.src)) return this.pendingPersistentImages.get(data.src);
554
- const resolved = resolveImageLoader(data);
555
- if (isPromise(resolved)) {
556
- const pending = resolved.then((value) => {
557
- if (signal?.aborted) return;
558
- super.putPersistentImage(value);
559
- this.persistentImageSrcMark.add(data.src);
560
- }).finally(() => {
561
- this.pendingPersistentImages.delete(data.src);
562
- });
563
- this.pendingPersistentImages.set(data.src, pending);
564
- return pending;
565
- }
566
- if (signal?.aborted) return;
567
- super.putPersistentImage(resolved);
568
- this.persistentImageSrcMark.add(data.src);
569
- }
570
- clearImageStore() {
571
- super.clearImageStore();
572
- this.persistentImageSrcMark.clear();
573
- this.pendingPersistentImages.clear();
574
- }
575
- async loadFonts(fonts, signal) {
576
- let loaded = 0;
577
- for (const font of fonts) if (await this.loadFontInternal(font, signal)) loaded += 1;
578
- return loaded;
579
- }
580
- loadFont(data, signal) {
581
- const loaded = this.loadFontInternal(data, signal);
582
- if (isPromise(loaded)) return loaded.then(() => void 0);
583
- }
584
- loadFontInternal(font, signal) {
585
- if (signal?.aborted) return false;
586
- const resolved = resolveFontLoader(font);
587
- if (isPromise(resolved)) return resolved.then((value) => {
588
- if (signal?.aborted || !this.checkAndMarkFont(value)) return false;
589
- try {
590
- super.loadFont(value);
591
- return true;
592
- } catch (error) {
593
- this.unmarkFont(value);
594
- throw error;
595
- }
557
+ async function resolveImageLoaders(images) {
558
+ const bySrc = /* @__PURE__ */ new Map();
559
+ for (const image of images) bySrc.set(image.src, image);
560
+ return Promise.all([...bySrc.values()].map(async ({ src, data, cache }) => ({
561
+ src,
562
+ data: typeof data === "function" ? await data() : data,
563
+ cache
564
+ })));
565
+ }
566
+ var Renderer = class {
567
+ fontMapping = /* @__PURE__ */ new Map();
568
+ inner = new Renderer$1();
569
+ async prepareFonts(fonts) {
570
+ if (!fonts) return;
571
+ const families = await Promise.all(fonts.map(this.registerFont.bind(this)));
572
+ return [...new Set(families.flat().map((f) => f.name))];
573
+ }
574
+ async render(node, options) {
575
+ const { fonts, fontFamilies, images, ...rest } = options ?? {};
576
+ const registeredFamilies = await this.prepareFonts(fonts);
577
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
578
+ return this.inner.render(node, {
579
+ ...rest,
580
+ images: resolvedImages,
581
+ fontFamilies: fontFamilies ?? registeredFamilies
596
582
  });
597
- if (!this.checkAndMarkFont(resolved)) return false;
598
- try {
599
- super.loadFont(resolved);
600
- return true;
601
- } catch (error) {
602
- this.unmarkFont(resolved);
603
- throw error;
604
- }
605
583
  }
606
- checkAndMarkFont(font) {
607
- const key = createFontKey(font);
608
- if (isBuffer(key)) {
609
- const isNew = !this.fontBuffersMark.has(key);
610
- this.fontBuffersMark.add(key);
611
- return isNew;
612
- }
613
- const isNew = !this.fontsMark.has(key);
614
- this.fontsMark.add(key);
615
- return isNew;
584
+ async renderAsDataUrl(node, options) {
585
+ const { fonts, fontFamilies, images, ...rest } = options ?? {};
586
+ const registeredFamilies = await this.prepareFonts(fonts);
587
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
588
+ return this.inner.renderAsDataUrl(node, {
589
+ ...rest,
590
+ images: resolvedImages,
591
+ fontFamilies: fontFamilies ?? registeredFamilies
592
+ });
593
+ }
594
+ async renderSvg(node, options) {
595
+ const { fonts, fontFamilies, images, ...rest } = options ?? {};
596
+ const registeredFamilies = await this.prepareFonts(fonts);
597
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
598
+ return this.inner.renderSvg(node, {
599
+ ...rest,
600
+ images: resolvedImages,
601
+ fontFamilies: fontFamilies ?? registeredFamilies
602
+ });
603
+ }
604
+ async measure(node, options) {
605
+ const { fonts, fontFamilies, images, ...rest } = options ?? {};
606
+ const registeredFamilies = await this.prepareFonts(fonts);
607
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
608
+ return this.inner.measure(node, {
609
+ ...rest,
610
+ images: resolvedImages,
611
+ fontFamilies: fontFamilies ?? registeredFamilies
612
+ });
613
+ }
614
+ async renderAnimation(options) {
615
+ const { fonts, fontFamilies, images, ...rest } = options;
616
+ const registeredFamilies = await this.prepareFonts(fonts);
617
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
618
+ return this.inner.renderAnimation({
619
+ ...rest,
620
+ images: resolvedImages,
621
+ fontFamilies: fontFamilies ?? registeredFamilies
622
+ });
623
+ }
624
+ async encodeFrames(frames, options) {
625
+ const { fonts, fontFamilies, images, ...rest } = options;
626
+ const registeredFamilies = await this.prepareFonts(fonts);
627
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
628
+ return this.inner.encodeFrames(frames, {
629
+ ...rest,
630
+ images: resolvedImages,
631
+ fontFamilies: fontFamilies ?? registeredFamilies
632
+ });
616
633
  }
617
- unmarkFont(font) {
634
+ free() {
635
+ this.inner.free();
636
+ }
637
+ async registerFont(font) {
618
638
  const key = createFontKey(font);
619
- if (isBuffer(key)) {
620
- this.fontBuffersMark.delete(key);
621
- return;
639
+ const cached = this.fontMapping.get(key);
640
+ if (cached) return cached;
641
+ const extracted = extractFontBuffer(font);
642
+ if (isBuffer(extracted)) {
643
+ const binded = this.inner.registerFont(extracted);
644
+ this.fontMapping.set(key, Promise.resolve(binded));
645
+ return binded;
622
646
  }
623
- this.fontsMark.delete(key);
624
- }
625
- isNewPersistentImage(src) {
626
- return !this.persistentImageSrcMark.has(src) && !this.pendingPersistentImages.has(src);
647
+ const promise = extracted.then(this.inner.registerFont.bind(this.inner)).catch((error) => {
648
+ this.fontMapping.delete(key);
649
+ throw error;
650
+ });
651
+ this.fontMapping.set(key, promise);
652
+ return promise;
627
653
  }
628
654
  };
629
- function createFontKey(font) {
630
- if ("key" in font && font.key) return font.key;
655
+ function extractFontBuffer(font) {
631
656
  if (isBuffer(font)) return font;
632
- return `${font.name ?? ""}-${font.style ?? ""}-${font.weight ?? ""}`;
633
- }
634
- function resolveFontLoader(font) {
635
- if ("data" in font && typeof font.data === "function") {
636
- const resolved = font.data();
637
- if (isPromise(resolved)) return resolved.then((data) => ({
638
- ...font,
639
- data
640
- }));
641
- return {
642
- ...font,
643
- data: resolved
644
- };
645
- }
646
- return font;
647
- }
648
- function resolveImageLoader(source) {
649
- if (typeof source.data === "function") {
650
- const resolved = source.data();
651
- if (isPromise(resolved)) return resolved.then((data) => ({
652
- ...source,
653
- data
654
- }));
655
- return {
656
- ...source,
657
- data: resolved
658
- };
659
- }
660
- return source;
657
+ if (typeof font.data !== "function") return font.data;
658
+ return font.data();
661
659
  }
662
- function isPromise(value) {
663
- return typeof value === "object" && value !== null && "then" in value;
660
+ function createFontKey(font) {
661
+ if (isBuffer(font)) return font;
662
+ if ("key" in font && font.key) return font.key;
663
+ if (typeof font.data === "function") return `${font.name ?? ""}:${font.weight ?? ""}:${font.style ?? ""}`;
664
+ return font.data;
664
665
  }
665
666
  function isBuffer(data) {
666
667
  return data instanceof Uint8Array || data instanceof ArrayBuffer || typeof Buffer !== "undefined" && Buffer.isBuffer(data);
package/dist/export.d.cts CHANGED
@@ -12,7 +12,7 @@ type KeyframesRuleList = {
12
12
  }[];
13
13
  }[];
14
14
  type Keyframes = KeyframesMap | KeyframesRuleList;
15
- type RenderOptions = {
15
+ type RenderOptions$1 = {
16
16
  /**
17
17
  * The width of the image. If not provided, the width will be automatically calculated based on the content.
18
18
  */
@@ -31,9 +31,10 @@ type RenderOptions = {
31
31
  */
32
32
  quality?: number;
33
33
  /**
34
- * The resources fetched externally. You should collect the fetch tasks first using `extractResourceUrls` and then pass the resources here.
34
+ * Images keyed by `src`, each carrying raw bytes. Provided up front and used
35
+ * in place of fetching external `src` URLs during rendering.
35
36
  */
36
- fetchedResources?: ImageSource[];
37
+ images?: ImageSource[];
37
38
  /**
38
39
  * CSS stylesheets to apply before rendering.
39
40
  */
@@ -60,8 +61,17 @@ type RenderOptions = {
60
61
  * @default "none"
61
62
  */
62
63
  dithering?: "none" | "ordered-bayer" | "floyd-steinberg";
64
+ /**
65
+ * Per-render font stack: ordered family names used as the fallback chain.
66
+ * Defaults to all registered families in registration order.
67
+ */
68
+ fontFamilies?: string[];
63
69
  };
64
- type RenderAnimationOptions = {
70
+ /**
71
+ * SVG is a vector format, so the raster-only knobs do not apply.
72
+ */
73
+ type SvgRenderOptions$1 = Omit<RenderOptions$1, "format" | "quality" | "drawDebugBorder" | "devicePixelRatio" | "dithering">;
74
+ type RenderAnimationOptions$1 = {
65
75
  scenes: AnimationSceneSource[];
66
76
  width: number;
67
77
  height: number;
@@ -71,9 +81,10 @@ type RenderAnimationOptions = {
71
81
  */
72
82
  quality?: number;
73
83
  /**
74
- * The resources fetched externally. You should collect the fetch tasks first using `extractResourceUrls` and then pass the resources here.
84
+ * Images keyed by `src`, each carrying raw bytes. Provided up front and used
85
+ * in place of fetching external `src` URLs during rendering.
75
86
  */
76
- fetchedResources?: ImageSource[];
87
+ images?: ImageSource[];
77
88
  drawDebugBorder?: boolean;
78
89
  /**
79
90
  * CSS stylesheets to apply before rendering.
@@ -88,8 +99,13 @@ type RenderAnimationOptions = {
88
99
  * Frames per second for timeline sampling.
89
100
  */
90
101
  fps: number;
102
+ /**
103
+ * Per-render font stack: ordered family names used as the fallback chain.
104
+ * Defaults to all registered families in registration order.
105
+ */
106
+ fontFamilies?: string[];
91
107
  };
92
- type EncodeFramesOptions = {
108
+ type EncodeFramesOptions$1 = {
93
109
  width: number;
94
110
  height: number;
95
111
  format?: "webp" | "apng" | "gif";
@@ -98,9 +114,10 @@ type EncodeFramesOptions = {
98
114
  */
99
115
  quality?: number;
100
116
  /**
101
- * The resources fetched externally. You should collect the fetch tasks first using `extractResourceUrls` and then pass the resources here.
117
+ * Images keyed by `src`, each carrying raw bytes. Provided up front and used
118
+ * in place of fetching external `src` URLs during rendering.
102
119
  */
103
- fetchedResources?: ImageSource[];
120
+ images?: ImageSource[];
104
121
  drawDebugBorder?: boolean;
105
122
  /**
106
123
  * CSS stylesheets to apply before rendering.
@@ -111,6 +128,11 @@ type EncodeFramesOptions = {
111
128
  * @default 1.0
112
129
  */
113
130
  devicePixelRatio?: number;
131
+ /**
132
+ * Per-render font stack: ordered family names used as the fallback chain.
133
+ * Defaults to all registered families in registration order.
134
+ */
135
+ fontFamilies?: string[];
114
136
  };
115
137
  type FontDetails = {
116
138
  name?: string;
@@ -120,7 +142,8 @@ type FontDetails = {
120
142
  };
121
143
  type ImageSource = {
122
144
  src: string;
123
- data: ByteBuf;
145
+ data: ByteBuf; /** Cache policy for the decoded image. Defaults to `"auto"`. */
146
+ cache?: "auto" | "none";
124
147
  };
125
148
  type KeyframeRule = {
126
149
  offsets: number[];
@@ -131,20 +154,15 @@ type KeyframesRule = {
131
154
  keyframes: KeyframeRule[];
132
155
  };
133
156
  type Font = FontDetails | ByteBuf;
134
- type ConstructRendererOptions = {
135
- /**
136
- * The images that needs to be preloaded into the renderer.
137
- */
138
- persistentImages?: ImageSource[];
139
- /**
140
- * The fonts being used.
141
- */
142
- fonts?: Font[];
143
- /**
144
- * Whether to load the default fonts.
145
- * If `fonts` are provided, this will be `false` by default.
146
- */
147
- loadDefaultFonts?: boolean;
157
+ type RegisteredFace = {
158
+ weight: number;
159
+ style: string;
160
+ width: number;
161
+ index: number;
162
+ };
163
+ type RegisteredFamily = {
164
+ name: string;
165
+ faces: RegisteredFace[];
148
166
  };
149
167
  type MeasuredTextRun = {
150
168
  text: string;
@@ -168,68 +186,18 @@ type AnimationSceneSource = {
168
186
  node: Node$1;
169
187
  durationMs: number;
170
188
  };
171
- /**
172
- * The main renderer for Takumi image rendering engine.
173
- *
174
- * State lives behind a lock and every method takes `&self`, mirroring the
175
- * napi bindings: a panic mid-call can't leave the wasm-bindgen borrow flag
176
- * permanently set, which would otherwise fail all subsequent calls.
177
- */
178
- declare class Renderer$1 {
179
- free(): void;
180
- [Symbol.dispose](): void;
181
- /**
182
- * Clears the renderer's internal image store.
183
- */
184
- clearImageStore(): void;
185
- /**
186
- * Encodes a precomputed frame sequence into an animated image buffer.
187
- */
188
- encodeFrames(frames: AnimationFrameSource[], options: EncodeFramesOptions): Uint8Array;
189
- /**
190
- * Loads a font into the renderer.
191
- */
192
- loadFont(font: Font): void;
193
- /**
194
- * Measures a node tree and returns layout information.
195
- */
196
- measure(node: Node$1, options?: RenderOptions | null): MeasuredNode;
197
- /**
198
- * Creates a new Renderer instance.
199
- */
200
- constructor(options?: ConstructRendererOptions | null);
201
- /**
202
- * Puts a persistent image into the renderer's internal store.
203
- */
204
- putPersistentImage(data: ImageSource): void;
205
- /**
206
- * Renders a node tree into an image buffer.
207
- */
208
- render(node: Node$1, options?: RenderOptions | null): Uint8Array;
209
- /**
210
- * Renders a sequential animation timeline into a buffer.
211
- */
212
- renderAnimation(options: RenderAnimationOptions): Uint8Array;
213
- /**
214
- * Renders a node tree into a data URL.
215
- *
216
- * `raw` format is not supported for data URL.
217
- */
218
- renderAsDataUrl(node: Node$1, options: RenderOptions): string;
219
- }
220
189
  type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
221
190
  interface InitOutput {
222
191
  readonly memory: WebAssembly.Memory;
223
192
  readonly __wbg_renderer_free: (a: number, b: number) => void;
224
- readonly renderer_clearImageStore: (a: number) => [number, number];
225
193
  readonly renderer_encodeFrames: (a: number, b: number, c: number, d: any) => [number, number, number, number];
226
- readonly renderer_loadFont: (a: number, b: any) => [number, number];
227
194
  readonly renderer_measure: (a: number, b: any, c: number) => [number, number, number];
228
- readonly renderer_new: (a: number) => [number, number, number];
229
- readonly renderer_putPersistentImage: (a: number, b: any) => [number, number];
195
+ readonly renderer_new: () => [number, number, number];
196
+ readonly renderer_registerFont: (a: number, b: any) => [number, number, number];
230
197
  readonly renderer_render: (a: number, b: any, c: number) => [number, number, number, number];
231
198
  readonly renderer_renderAnimation: (a: number, b: any) => [number, number, number, number];
232
199
  readonly renderer_renderAsDataUrl: (a: number, b: any, c: any) => [number, number, number, number];
200
+ readonly renderer_renderSvg: (a: number, b: any, c: number) => [number, number, number, number];
233
201
  readonly __wbindgen_malloc: (a: number, b: number) => number;
234
202
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
235
203
  readonly __wbindgen_exn_store: (a: number) => void;
@@ -265,35 +233,73 @@ declare function __wbg_init(module_or_path?: {
265
233
  } | InitInput | Promise<InitInput>): Promise<InitOutput>;
266
234
  //#endregion
267
235
  //#region src/export.d.ts
268
- type ImageSourceLoader = Omit<ImageSource, "data"> & {
269
- data: ImageSource["data"] | (() => Promise<ImageSource["data"]> | ImageSource["data"]);
270
- };
271
236
  type FontLoader = Font | (Omit<FontDetails, "data"> & {
272
- key?: string;
273
- data: FontDetails["data"] | (() => Promise<FontDetails["data"]> | FontDetails["data"]);
274
- });
275
- type ImageSourceLoaderSync = Omit<ImageSource, "data"> & {
276
- data: ImageSource["data"] | (() => ImageSource["data"]);
237
+ data: () => Promise<FontDetails["data"]> | FontDetails["data"];
238
+ } & ({
239
+ key: string;
240
+ } | {
241
+ name: string;
242
+ }));
243
+ type ImageLoaderData = ImageSource["data"];
244
+ type ImageLoader = Omit<ImageSource, "data"> & {
245
+ data: ImageLoaderData | (() => ImageLoaderData | Promise<ImageLoaderData>);
277
246
  };
278
- type FontLoaderSync = Font | (Omit<FontDetails, "data"> & {
279
- key?: string;
280
- data: FontDetails["data"] | (() => FontDetails["data"]);
281
- });
282
- declare class Renderer extends Renderer$1 {
283
- private fontsMark;
284
- private fontBuffersMark;
285
- private persistentImageSrcMark;
286
- private pendingPersistentImages;
287
- putPersistentImage(data: ImageSourceLoaderSync, signal?: AbortSignal): void;
288
- putPersistentImage(data: ImageSourceLoader, signal?: AbortSignal): Promise<void>;
289
- clearImageStore(): void;
290
- loadFonts(fonts: FontLoader[], signal?: AbortSignal): Promise<number>;
291
- loadFont(data: FontLoaderSync, signal?: AbortSignal): void;
292
- loadFont(data: FontLoader, signal?: AbortSignal): Promise<void>;
293
- private loadFontInternal;
294
- private checkAndMarkFont;
295
- private unmarkFont;
296
- private isNewPersistentImage;
247
+ /**
248
+ * Output format. Format-specific options live on the variant that supports them,
249
+ * so `quality` cannot be paired with a lossless format. On wasm, WebP is always
250
+ * lossless (lossy WebP is native-only).
251
+ */
252
+ type OutputFormatOptions = {
253
+ format?: "png";
254
+ } | {
255
+ format: "jpeg";
256
+ quality?: number;
257
+ } | {
258
+ format: "webp";
259
+ } | {
260
+ format: "ico";
261
+ } | {
262
+ format: "raw";
263
+ };
264
+ type RenderOptions = Omit<RenderOptions$1, "images" | "format" | "quality"> & OutputFormatOptions & {
265
+ fonts?: FontLoader[];
266
+ images?: ImageLoader[];
267
+ };
268
+ /**
269
+ * Animation output format. On wasm, WebP animation is always lossless (lossy
270
+ * WebP is native-only).
271
+ */
272
+ type AnimationOutputFormatOptions = {
273
+ format?: "webp";
274
+ } | {
275
+ format: "apng";
276
+ } | {
277
+ format: "gif";
278
+ };
279
+ type RenderAnimationOptions = Omit<RenderAnimationOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
280
+ fonts?: FontLoader[];
281
+ images?: ImageLoader[];
282
+ };
283
+ type EncodeFramesOptions = Omit<EncodeFramesOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
284
+ fonts?: FontLoader[];
285
+ images?: ImageLoader[];
286
+ };
287
+ type SvgRenderOptions = Omit<SvgRenderOptions$1, "images"> & {
288
+ fonts?: FontLoader[];
289
+ images?: ImageLoader[];
290
+ };
291
+ declare class Renderer {
292
+ private fontMapping;
293
+ private inner;
294
+ private prepareFonts;
295
+ render(node: Node, options?: RenderOptions): Promise<Uint8Array<ArrayBufferLike>>;
296
+ renderAsDataUrl(node: Node, options?: RenderOptions): Promise<string>;
297
+ renderSvg(node: Node, options?: SvgRenderOptions): Promise<string>;
298
+ measure(node: Node, options?: RenderOptions): Promise<MeasuredNode>;
299
+ renderAnimation(options: RenderAnimationOptions): Promise<Uint8Array<ArrayBufferLike>>;
300
+ encodeFrames(frames: AnimationFrameSource[], options: EncodeFramesOptions): Promise<Uint8Array<ArrayBufferLike>>;
301
+ free(): void;
302
+ registerFont(font: FontLoader): Promise<any>;
297
303
  }
298
304
  //#endregion
299
- export { AnimationFrameSource, AnimationSceneSource, ByteBuf, ConstructRendererOptions, ContainerNode, EncodeFramesOptions, Font, FontDetails, FontLoader, FontLoaderSync, ImageNode, ImageSource, ImageSourceLoader, ImageSourceLoaderSync, InitInput, InitOutput, KeyframeRule, Keyframes, KeyframesMap, KeyframesRule, KeyframesRuleList, MeasuredNode, MeasuredTextRun, type Node, NodeMetadata, RenderAnimationOptions, RenderOptions, Renderer, SyncInitInput, TextNode, __wbg_init as default, extractResourceUrls, initSync };
305
+ export { AnimationFrameSource, AnimationOutputFormatOptions, AnimationSceneSource, ByteBuf, ContainerNode, EncodeFramesOptions, Font, FontDetails, FontLoader, ImageLoader, ImageNode, ImageSource, InitInput, InitOutput, KeyframeRule, Keyframes, KeyframesMap, KeyframesRule, KeyframesRuleList, MeasuredNode, MeasuredTextRun, type Node, NodeMetadata, OutputFormatOptions, RegisteredFace, RegisteredFamily, RenderAnimationOptions, RenderOptions, Renderer, SvgRenderOptions, SyncInitInput, TextNode, __wbg_init as default, extractResourceUrls, initSync };
package/dist/export.d.mts CHANGED
@@ -12,7 +12,7 @@ type KeyframesRuleList = {
12
12
  }[];
13
13
  }[];
14
14
  type Keyframes = KeyframesMap | KeyframesRuleList;
15
- type RenderOptions = {
15
+ type RenderOptions$1 = {
16
16
  /**
17
17
  * The width of the image. If not provided, the width will be automatically calculated based on the content.
18
18
  */
@@ -31,9 +31,10 @@ type RenderOptions = {
31
31
  */
32
32
  quality?: number;
33
33
  /**
34
- * The resources fetched externally. You should collect the fetch tasks first using `extractResourceUrls` and then pass the resources here.
34
+ * Images keyed by `src`, each carrying raw bytes. Provided up front and used
35
+ * in place of fetching external `src` URLs during rendering.
35
36
  */
36
- fetchedResources?: ImageSource[];
37
+ images?: ImageSource[];
37
38
  /**
38
39
  * CSS stylesheets to apply before rendering.
39
40
  */
@@ -60,8 +61,17 @@ type RenderOptions = {
60
61
  * @default "none"
61
62
  */
62
63
  dithering?: "none" | "ordered-bayer" | "floyd-steinberg";
64
+ /**
65
+ * Per-render font stack: ordered family names used as the fallback chain.
66
+ * Defaults to all registered families in registration order.
67
+ */
68
+ fontFamilies?: string[];
63
69
  };
64
- type RenderAnimationOptions = {
70
+ /**
71
+ * SVG is a vector format, so the raster-only knobs do not apply.
72
+ */
73
+ type SvgRenderOptions$1 = Omit<RenderOptions$1, "format" | "quality" | "drawDebugBorder" | "devicePixelRatio" | "dithering">;
74
+ type RenderAnimationOptions$1 = {
65
75
  scenes: AnimationSceneSource[];
66
76
  width: number;
67
77
  height: number;
@@ -71,9 +81,10 @@ type RenderAnimationOptions = {
71
81
  */
72
82
  quality?: number;
73
83
  /**
74
- * The resources fetched externally. You should collect the fetch tasks first using `extractResourceUrls` and then pass the resources here.
84
+ * Images keyed by `src`, each carrying raw bytes. Provided up front and used
85
+ * in place of fetching external `src` URLs during rendering.
75
86
  */
76
- fetchedResources?: ImageSource[];
87
+ images?: ImageSource[];
77
88
  drawDebugBorder?: boolean;
78
89
  /**
79
90
  * CSS stylesheets to apply before rendering.
@@ -88,8 +99,13 @@ type RenderAnimationOptions = {
88
99
  * Frames per second for timeline sampling.
89
100
  */
90
101
  fps: number;
102
+ /**
103
+ * Per-render font stack: ordered family names used as the fallback chain.
104
+ * Defaults to all registered families in registration order.
105
+ */
106
+ fontFamilies?: string[];
91
107
  };
92
- type EncodeFramesOptions = {
108
+ type EncodeFramesOptions$1 = {
93
109
  width: number;
94
110
  height: number;
95
111
  format?: "webp" | "apng" | "gif";
@@ -98,9 +114,10 @@ type EncodeFramesOptions = {
98
114
  */
99
115
  quality?: number;
100
116
  /**
101
- * The resources fetched externally. You should collect the fetch tasks first using `extractResourceUrls` and then pass the resources here.
117
+ * Images keyed by `src`, each carrying raw bytes. Provided up front and used
118
+ * in place of fetching external `src` URLs during rendering.
102
119
  */
103
- fetchedResources?: ImageSource[];
120
+ images?: ImageSource[];
104
121
  drawDebugBorder?: boolean;
105
122
  /**
106
123
  * CSS stylesheets to apply before rendering.
@@ -111,6 +128,11 @@ type EncodeFramesOptions = {
111
128
  * @default 1.0
112
129
  */
113
130
  devicePixelRatio?: number;
131
+ /**
132
+ * Per-render font stack: ordered family names used as the fallback chain.
133
+ * Defaults to all registered families in registration order.
134
+ */
135
+ fontFamilies?: string[];
114
136
  };
115
137
  type FontDetails = {
116
138
  name?: string;
@@ -120,7 +142,8 @@ type FontDetails = {
120
142
  };
121
143
  type ImageSource = {
122
144
  src: string;
123
- data: ByteBuf;
145
+ data: ByteBuf; /** Cache policy for the decoded image. Defaults to `"auto"`. */
146
+ cache?: "auto" | "none";
124
147
  };
125
148
  type KeyframeRule = {
126
149
  offsets: number[];
@@ -131,20 +154,15 @@ type KeyframesRule = {
131
154
  keyframes: KeyframeRule[];
132
155
  };
133
156
  type Font = FontDetails | ByteBuf;
134
- type ConstructRendererOptions = {
135
- /**
136
- * The images that needs to be preloaded into the renderer.
137
- */
138
- persistentImages?: ImageSource[];
139
- /**
140
- * The fonts being used.
141
- */
142
- fonts?: Font[];
143
- /**
144
- * Whether to load the default fonts.
145
- * If `fonts` are provided, this will be `false` by default.
146
- */
147
- loadDefaultFonts?: boolean;
157
+ type RegisteredFace = {
158
+ weight: number;
159
+ style: string;
160
+ width: number;
161
+ index: number;
162
+ };
163
+ type RegisteredFamily = {
164
+ name: string;
165
+ faces: RegisteredFace[];
148
166
  };
149
167
  type MeasuredTextRun = {
150
168
  text: string;
@@ -168,68 +186,18 @@ type AnimationSceneSource = {
168
186
  node: Node$1;
169
187
  durationMs: number;
170
188
  };
171
- /**
172
- * The main renderer for Takumi image rendering engine.
173
- *
174
- * State lives behind a lock and every method takes `&self`, mirroring the
175
- * napi bindings: a panic mid-call can't leave the wasm-bindgen borrow flag
176
- * permanently set, which would otherwise fail all subsequent calls.
177
- */
178
- declare class Renderer$1 {
179
- free(): void;
180
- [Symbol.dispose](): void;
181
- /**
182
- * Clears the renderer's internal image store.
183
- */
184
- clearImageStore(): void;
185
- /**
186
- * Encodes a precomputed frame sequence into an animated image buffer.
187
- */
188
- encodeFrames(frames: AnimationFrameSource[], options: EncodeFramesOptions): Uint8Array;
189
- /**
190
- * Loads a font into the renderer.
191
- */
192
- loadFont(font: Font): void;
193
- /**
194
- * Measures a node tree and returns layout information.
195
- */
196
- measure(node: Node$1, options?: RenderOptions | null): MeasuredNode;
197
- /**
198
- * Creates a new Renderer instance.
199
- */
200
- constructor(options?: ConstructRendererOptions | null);
201
- /**
202
- * Puts a persistent image into the renderer's internal store.
203
- */
204
- putPersistentImage(data: ImageSource): void;
205
- /**
206
- * Renders a node tree into an image buffer.
207
- */
208
- render(node: Node$1, options?: RenderOptions | null): Uint8Array;
209
- /**
210
- * Renders a sequential animation timeline into a buffer.
211
- */
212
- renderAnimation(options: RenderAnimationOptions): Uint8Array;
213
- /**
214
- * Renders a node tree into a data URL.
215
- *
216
- * `raw` format is not supported for data URL.
217
- */
218
- renderAsDataUrl(node: Node$1, options: RenderOptions): string;
219
- }
220
189
  type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
221
190
  interface InitOutput {
222
191
  readonly memory: WebAssembly.Memory;
223
192
  readonly __wbg_renderer_free: (a: number, b: number) => void;
224
- readonly renderer_clearImageStore: (a: number) => [number, number];
225
193
  readonly renderer_encodeFrames: (a: number, b: number, c: number, d: any) => [number, number, number, number];
226
- readonly renderer_loadFont: (a: number, b: any) => [number, number];
227
194
  readonly renderer_measure: (a: number, b: any, c: number) => [number, number, number];
228
- readonly renderer_new: (a: number) => [number, number, number];
229
- readonly renderer_putPersistentImage: (a: number, b: any) => [number, number];
195
+ readonly renderer_new: () => [number, number, number];
196
+ readonly renderer_registerFont: (a: number, b: any) => [number, number, number];
230
197
  readonly renderer_render: (a: number, b: any, c: number) => [number, number, number, number];
231
198
  readonly renderer_renderAnimation: (a: number, b: any) => [number, number, number, number];
232
199
  readonly renderer_renderAsDataUrl: (a: number, b: any, c: any) => [number, number, number, number];
200
+ readonly renderer_renderSvg: (a: number, b: any, c: number) => [number, number, number, number];
233
201
  readonly __wbindgen_malloc: (a: number, b: number) => number;
234
202
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
235
203
  readonly __wbindgen_exn_store: (a: number) => void;
@@ -265,35 +233,73 @@ declare function __wbg_init(module_or_path?: {
265
233
  } | InitInput | Promise<InitInput>): Promise<InitOutput>;
266
234
  //#endregion
267
235
  //#region src/export.d.ts
268
- type ImageSourceLoader = Omit<ImageSource, "data"> & {
269
- data: ImageSource["data"] | (() => Promise<ImageSource["data"]> | ImageSource["data"]);
270
- };
271
236
  type FontLoader = Font | (Omit<FontDetails, "data"> & {
272
- key?: string;
273
- data: FontDetails["data"] | (() => Promise<FontDetails["data"]> | FontDetails["data"]);
274
- });
275
- type ImageSourceLoaderSync = Omit<ImageSource, "data"> & {
276
- data: ImageSource["data"] | (() => ImageSource["data"]);
237
+ data: () => Promise<FontDetails["data"]> | FontDetails["data"];
238
+ } & ({
239
+ key: string;
240
+ } | {
241
+ name: string;
242
+ }));
243
+ type ImageLoaderData = ImageSource["data"];
244
+ type ImageLoader = Omit<ImageSource, "data"> & {
245
+ data: ImageLoaderData | (() => ImageLoaderData | Promise<ImageLoaderData>);
277
246
  };
278
- type FontLoaderSync = Font | (Omit<FontDetails, "data"> & {
279
- key?: string;
280
- data: FontDetails["data"] | (() => FontDetails["data"]);
281
- });
282
- declare class Renderer extends Renderer$1 {
283
- private fontsMark;
284
- private fontBuffersMark;
285
- private persistentImageSrcMark;
286
- private pendingPersistentImages;
287
- putPersistentImage(data: ImageSourceLoaderSync, signal?: AbortSignal): void;
288
- putPersistentImage(data: ImageSourceLoader, signal?: AbortSignal): Promise<void>;
289
- clearImageStore(): void;
290
- loadFonts(fonts: FontLoader[], signal?: AbortSignal): Promise<number>;
291
- loadFont(data: FontLoaderSync, signal?: AbortSignal): void;
292
- loadFont(data: FontLoader, signal?: AbortSignal): Promise<void>;
293
- private loadFontInternal;
294
- private checkAndMarkFont;
295
- private unmarkFont;
296
- private isNewPersistentImage;
247
+ /**
248
+ * Output format. Format-specific options live on the variant that supports them,
249
+ * so `quality` cannot be paired with a lossless format. On wasm, WebP is always
250
+ * lossless (lossy WebP is native-only).
251
+ */
252
+ type OutputFormatOptions = {
253
+ format?: "png";
254
+ } | {
255
+ format: "jpeg";
256
+ quality?: number;
257
+ } | {
258
+ format: "webp";
259
+ } | {
260
+ format: "ico";
261
+ } | {
262
+ format: "raw";
263
+ };
264
+ type RenderOptions = Omit<RenderOptions$1, "images" | "format" | "quality"> & OutputFormatOptions & {
265
+ fonts?: FontLoader[];
266
+ images?: ImageLoader[];
267
+ };
268
+ /**
269
+ * Animation output format. On wasm, WebP animation is always lossless (lossy
270
+ * WebP is native-only).
271
+ */
272
+ type AnimationOutputFormatOptions = {
273
+ format?: "webp";
274
+ } | {
275
+ format: "apng";
276
+ } | {
277
+ format: "gif";
278
+ };
279
+ type RenderAnimationOptions = Omit<RenderAnimationOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
280
+ fonts?: FontLoader[];
281
+ images?: ImageLoader[];
282
+ };
283
+ type EncodeFramesOptions = Omit<EncodeFramesOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
284
+ fonts?: FontLoader[];
285
+ images?: ImageLoader[];
286
+ };
287
+ type SvgRenderOptions = Omit<SvgRenderOptions$1, "images"> & {
288
+ fonts?: FontLoader[];
289
+ images?: ImageLoader[];
290
+ };
291
+ declare class Renderer {
292
+ private fontMapping;
293
+ private inner;
294
+ private prepareFonts;
295
+ render(node: Node, options?: RenderOptions): Promise<Uint8Array<ArrayBufferLike>>;
296
+ renderAsDataUrl(node: Node, options?: RenderOptions): Promise<string>;
297
+ renderSvg(node: Node, options?: SvgRenderOptions): Promise<string>;
298
+ measure(node: Node, options?: RenderOptions): Promise<MeasuredNode>;
299
+ renderAnimation(options: RenderAnimationOptions): Promise<Uint8Array<ArrayBufferLike>>;
300
+ encodeFrames(frames: AnimationFrameSource[], options: EncodeFramesOptions): Promise<Uint8Array<ArrayBufferLike>>;
301
+ free(): void;
302
+ registerFont(font: FontLoader): Promise<any>;
297
303
  }
298
304
  //#endregion
299
- export { AnimationFrameSource, AnimationSceneSource, ByteBuf, ConstructRendererOptions, ContainerNode, EncodeFramesOptions, Font, FontDetails, FontLoader, FontLoaderSync, ImageNode, ImageSource, ImageSourceLoader, ImageSourceLoaderSync, InitInput, InitOutput, KeyframeRule, Keyframes, KeyframesMap, KeyframesRule, KeyframesRuleList, MeasuredNode, MeasuredTextRun, type Node, NodeMetadata, RenderAnimationOptions, RenderOptions, Renderer, SyncInitInput, TextNode, __wbg_init as default, extractResourceUrls, initSync };
305
+ export { AnimationFrameSource, AnimationOutputFormatOptions, AnimationSceneSource, ByteBuf, ContainerNode, EncodeFramesOptions, Font, FontDetails, FontLoader, ImageLoader, ImageNode, ImageSource, InitInput, InitOutput, KeyframeRule, Keyframes, KeyframesMap, KeyframesRule, KeyframesRuleList, MeasuredNode, MeasuredTextRun, type Node, NodeMetadata, OutputFormatOptions, RegisteredFace, RegisteredFamily, RenderAnimationOptions, RenderOptions, Renderer, SvgRenderOptions, SyncInitInput, TextNode, __wbg_init as default, extractResourceUrls, initSync };
package/dist/export.mjs CHANGED
@@ -19,13 +19,6 @@ var Renderer$1 = class {
19
19
  wasm.__wbg_renderer_free(ptr, 0);
20
20
  }
21
21
  /**
22
- * Clears the renderer's internal image store.
23
- */
24
- clearImageStore() {
25
- const ret = wasm.renderer_clearImageStore(this.__wbg_ptr);
26
- if (ret[1]) throw takeFromExternrefTable0(ret[0]);
27
- }
28
- /**
29
22
  * Encodes a precomputed frame sequence into an animated image buffer.
30
23
  * @param {AnimationFrameSource[]} frames
31
24
  * @param {EncodeFramesOptions} options
@@ -41,14 +34,6 @@ var Renderer$1 = class {
41
34
  return v2;
42
35
  }
43
36
  /**
44
- * Loads a font into the renderer.
45
- * @param {Font} font
46
- */
47
- loadFont(font) {
48
- const ret = wasm.renderer_loadFont(this.__wbg_ptr, font);
49
- if (ret[1]) throw takeFromExternrefTable0(ret[0]);
50
- }
51
- /**
52
37
  * Measures a node tree and returns layout information.
53
38
  * @param {Node} node
54
39
  * @param {RenderOptions | null} [options]
@@ -61,22 +46,23 @@ var Renderer$1 = class {
61
46
  }
62
47
  /**
63
48
  * Creates a new Renderer instance.
64
- * @param {ConstructRendererOptions | null} [options]
65
49
  */
66
- constructor(options) {
67
- const ret = wasm.renderer_new(isLikeNone(options) ? 0 : addToExternrefTable0(options));
50
+ constructor() {
51
+ const ret = wasm.renderer_new();
68
52
  if (ret[2]) throw takeFromExternrefTable0(ret[1]);
69
53
  this.__wbg_ptr = ret[0];
70
54
  RendererFinalization.register(this, this.__wbg_ptr, this);
71
55
  return this;
72
56
  }
73
57
  /**
74
- * Puts a persistent image into the renderer's internal store.
75
- * @param {ImageSource} data
58
+ * Registers fonts into the renderer, returning the families each font produced.
59
+ * @param {Font} font
60
+ * @returns {RegisteredFamily[]}
76
61
  */
77
- putPersistentImage(data) {
78
- const ret = wasm.renderer_putPersistentImage(this.__wbg_ptr, data);
79
- if (ret[1]) throw takeFromExternrefTable0(ret[0]);
62
+ registerFont(font) {
63
+ const ret = wasm.renderer_registerFont(this.__wbg_ptr, font);
64
+ if (ret[2]) throw takeFromExternrefTable0(ret[1]);
65
+ return takeFromExternrefTable0(ret[0]);
80
66
  }
81
67
  /**
82
68
  * Renders a node tree into an image buffer.
@@ -130,6 +116,31 @@ var Renderer$1 = class {
130
116
  wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
131
117
  }
132
118
  }
119
+ /**
120
+ * Renders a node tree into an SVG document string.
121
+ * @param {Node} node
122
+ * @param {SvgRenderOptions | null} [options]
123
+ * @returns {string}
124
+ */
125
+ renderSvg(node, options) {
126
+ let deferred2_0;
127
+ let deferred2_1;
128
+ try {
129
+ const ret = wasm.renderer_renderSvg(this.__wbg_ptr, node, isLikeNone(options) ? 0 : addToExternrefTable0(options));
130
+ var ptr1 = ret[0];
131
+ var len1 = ret[1];
132
+ if (ret[3]) {
133
+ ptr1 = 0;
134
+ len1 = 0;
135
+ throw takeFromExternrefTable0(ret[2]);
136
+ }
137
+ deferred2_0 = ptr1;
138
+ deferred2_1 = len1;
139
+ return getStringFromWasm0(ptr1, len1);
140
+ } finally {
141
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
142
+ }
143
+ }
133
144
  };
134
145
  if (Symbol.dispose) Renderer$1.prototype[Symbol.dispose] = Renderer$1.prototype.free;
135
146
  function __wbg_get_imports() {
@@ -539,124 +550,114 @@ async function __wbg_init(module_or_path) {
539
550
  }
540
551
  //#endregion
541
552
  //#region src/export.ts
542
- var Renderer = class extends Renderer$1 {
543
- fontsMark = /* @__PURE__ */ new Set();
544
- fontBuffersMark = /* @__PURE__ */ new WeakSet();
545
- persistentImageSrcMark = /* @__PURE__ */ new Set();
546
- pendingPersistentImages = /* @__PURE__ */ new Map();
547
- putPersistentImage(data, signal) {
548
- if (signal?.aborted) return;
549
- if (!this.isNewPersistentImage(data.src)) return this.pendingPersistentImages.get(data.src);
550
- const resolved = resolveImageLoader(data);
551
- if (isPromise(resolved)) {
552
- const pending = resolved.then((value) => {
553
- if (signal?.aborted) return;
554
- super.putPersistentImage(value);
555
- this.persistentImageSrcMark.add(data.src);
556
- }).finally(() => {
557
- this.pendingPersistentImages.delete(data.src);
558
- });
559
- this.pendingPersistentImages.set(data.src, pending);
560
- return pending;
561
- }
562
- if (signal?.aborted) return;
563
- super.putPersistentImage(resolved);
564
- this.persistentImageSrcMark.add(data.src);
553
+ async function resolveImageLoaders(images) {
554
+ const bySrc = /* @__PURE__ */ new Map();
555
+ for (const image of images) bySrc.set(image.src, image);
556
+ return Promise.all([...bySrc.values()].map(async ({ src, data, cache }) => ({
557
+ src,
558
+ data: typeof data === "function" ? await data() : data,
559
+ cache
560
+ })));
561
+ }
562
+ var Renderer = class {
563
+ fontMapping = /* @__PURE__ */ new Map();
564
+ inner = new Renderer$1();
565
+ async prepareFonts(fonts) {
566
+ if (!fonts) return;
567
+ const families = await Promise.all(fonts.map(this.registerFont.bind(this)));
568
+ return [...new Set(families.flat().map((f) => f.name))];
569
+ }
570
+ async render(node, options) {
571
+ const { fonts, fontFamilies, images, ...rest } = options ?? {};
572
+ const registeredFamilies = await this.prepareFonts(fonts);
573
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
574
+ return this.inner.render(node, {
575
+ ...rest,
576
+ images: resolvedImages,
577
+ fontFamilies: fontFamilies ?? registeredFamilies
578
+ });
565
579
  }
566
- clearImageStore() {
567
- super.clearImageStore();
568
- this.persistentImageSrcMark.clear();
569
- this.pendingPersistentImages.clear();
580
+ async renderAsDataUrl(node, options) {
581
+ const { fonts, fontFamilies, images, ...rest } = options ?? {};
582
+ const registeredFamilies = await this.prepareFonts(fonts);
583
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
584
+ return this.inner.renderAsDataUrl(node, {
585
+ ...rest,
586
+ images: resolvedImages,
587
+ fontFamilies: fontFamilies ?? registeredFamilies
588
+ });
570
589
  }
571
- async loadFonts(fonts, signal) {
572
- let loaded = 0;
573
- for (const font of fonts) if (await this.loadFontInternal(font, signal)) loaded += 1;
574
- return loaded;
590
+ async renderSvg(node, options) {
591
+ const { fonts, fontFamilies, images, ...rest } = options ?? {};
592
+ const registeredFamilies = await this.prepareFonts(fonts);
593
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
594
+ return this.inner.renderSvg(node, {
595
+ ...rest,
596
+ images: resolvedImages,
597
+ fontFamilies: fontFamilies ?? registeredFamilies
598
+ });
575
599
  }
576
- loadFont(data, signal) {
577
- const loaded = this.loadFontInternal(data, signal);
578
- if (isPromise(loaded)) return loaded.then(() => void 0);
600
+ async measure(node, options) {
601
+ const { fonts, fontFamilies, images, ...rest } = options ?? {};
602
+ const registeredFamilies = await this.prepareFonts(fonts);
603
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
604
+ return this.inner.measure(node, {
605
+ ...rest,
606
+ images: resolvedImages,
607
+ fontFamilies: fontFamilies ?? registeredFamilies
608
+ });
579
609
  }
580
- loadFontInternal(font, signal) {
581
- if (signal?.aborted) return false;
582
- const resolved = resolveFontLoader(font);
583
- if (isPromise(resolved)) return resolved.then((value) => {
584
- if (signal?.aborted || !this.checkAndMarkFont(value)) return false;
585
- try {
586
- super.loadFont(value);
587
- return true;
588
- } catch (error) {
589
- this.unmarkFont(value);
590
- throw error;
591
- }
610
+ async renderAnimation(options) {
611
+ const { fonts, fontFamilies, images, ...rest } = options;
612
+ const registeredFamilies = await this.prepareFonts(fonts);
613
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
614
+ return this.inner.renderAnimation({
615
+ ...rest,
616
+ images: resolvedImages,
617
+ fontFamilies: fontFamilies ?? registeredFamilies
592
618
  });
593
- if (!this.checkAndMarkFont(resolved)) return false;
594
- try {
595
- super.loadFont(resolved);
596
- return true;
597
- } catch (error) {
598
- this.unmarkFont(resolved);
599
- throw error;
600
- }
601
619
  }
602
- checkAndMarkFont(font) {
603
- const key = createFontKey(font);
604
- if (isBuffer(key)) {
605
- const isNew = !this.fontBuffersMark.has(key);
606
- this.fontBuffersMark.add(key);
607
- return isNew;
608
- }
609
- const isNew = !this.fontsMark.has(key);
610
- this.fontsMark.add(key);
611
- return isNew;
620
+ async encodeFrames(frames, options) {
621
+ const { fonts, fontFamilies, images, ...rest } = options;
622
+ const registeredFamilies = await this.prepareFonts(fonts);
623
+ const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
624
+ return this.inner.encodeFrames(frames, {
625
+ ...rest,
626
+ images: resolvedImages,
627
+ fontFamilies: fontFamilies ?? registeredFamilies
628
+ });
629
+ }
630
+ free() {
631
+ this.inner.free();
612
632
  }
613
- unmarkFont(font) {
633
+ async registerFont(font) {
614
634
  const key = createFontKey(font);
615
- if (isBuffer(key)) {
616
- this.fontBuffersMark.delete(key);
617
- return;
635
+ const cached = this.fontMapping.get(key);
636
+ if (cached) return cached;
637
+ const extracted = extractFontBuffer(font);
638
+ if (isBuffer(extracted)) {
639
+ const binded = this.inner.registerFont(extracted);
640
+ this.fontMapping.set(key, Promise.resolve(binded));
641
+ return binded;
618
642
  }
619
- this.fontsMark.delete(key);
620
- }
621
- isNewPersistentImage(src) {
622
- return !this.persistentImageSrcMark.has(src) && !this.pendingPersistentImages.has(src);
643
+ const promise = extracted.then(this.inner.registerFont.bind(this.inner)).catch((error) => {
644
+ this.fontMapping.delete(key);
645
+ throw error;
646
+ });
647
+ this.fontMapping.set(key, promise);
648
+ return promise;
623
649
  }
624
650
  };
625
- function createFontKey(font) {
626
- if ("key" in font && font.key) return font.key;
651
+ function extractFontBuffer(font) {
627
652
  if (isBuffer(font)) return font;
628
- return `${font.name ?? ""}-${font.style ?? ""}-${font.weight ?? ""}`;
653
+ if (typeof font.data !== "function") return font.data;
654
+ return font.data();
629
655
  }
630
- function resolveFontLoader(font) {
631
- if ("data" in font && typeof font.data === "function") {
632
- const resolved = font.data();
633
- if (isPromise(resolved)) return resolved.then((data) => ({
634
- ...font,
635
- data
636
- }));
637
- return {
638
- ...font,
639
- data: resolved
640
- };
641
- }
642
- return font;
643
- }
644
- function resolveImageLoader(source) {
645
- if (typeof source.data === "function") {
646
- const resolved = source.data();
647
- if (isPromise(resolved)) return resolved.then((data) => ({
648
- ...source,
649
- data
650
- }));
651
- return {
652
- ...source,
653
- data: resolved
654
- };
655
- }
656
- return source;
657
- }
658
- function isPromise(value) {
659
- return typeof value === "object" && value !== null && "then" in value;
656
+ function createFontKey(font) {
657
+ if (isBuffer(font)) return font;
658
+ if ("key" in font && font.key) return font.key;
659
+ if (typeof font.data === "function") return `${font.name ?? ""}:${font.weight ?? ""}:${font.style ?? ""}`;
660
+ return font.data;
660
661
  }
661
662
  function isBuffer(data) {
662
663
  return data instanceof Uint8Array || data instanceof ArrayBuffer || typeof Buffer !== "undefined" && Buffer.isBuffer(data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takumi-rs/wasm",
3
- "version": "1.8.7",
3
+ "version": "2.0.0-beta.0",
4
4
  "keywords": [
5
5
  "css",
6
6
  "image",
@@ -90,7 +90,7 @@
90
90
  "publish-lint": "attw --pack . && publint --strict ."
91
91
  },
92
92
  "dependencies": {
93
- "@takumi-rs/helpers": "1.8.7"
93
+ "@takumi-rs/helpers": "2.0.0-beta.0"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@types/bun": "catalog:",
Binary file
@@ -2,15 +2,14 @@
2
2
  /* eslint-disable */
3
3
  export const memory: WebAssembly.Memory;
4
4
  export const __wbg_renderer_free: (a: number, b: number) => void;
5
- export const renderer_clearImageStore: (a: number) => [number, number];
6
5
  export const renderer_encodeFrames: (a: number, b: number, c: number, d: any) => [number, number, number, number];
7
- export const renderer_loadFont: (a: number, b: any) => [number, number];
8
6
  export const renderer_measure: (a: number, b: any, c: number) => [number, number, number];
9
- export const renderer_new: (a: number) => [number, number, number];
10
- export const renderer_putPersistentImage: (a: number, b: any) => [number, number];
7
+ export const renderer_new: () => [number, number, number];
8
+ export const renderer_registerFont: (a: number, b: any) => [number, number, number];
11
9
  export const renderer_render: (a: number, b: any, c: number) => [number, number, number, number];
12
10
  export const renderer_renderAnimation: (a: number, b: any) => [number, number, number, number];
13
11
  export const renderer_renderAsDataUrl: (a: number, b: any, c: any) => [number, number, number, number];
12
+ export const renderer_renderSvg: (a: number, b: any, c: number) => [number, number, number, number];
14
13
  export const __wbindgen_malloc: (a: number, b: number) => number;
15
14
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
16
15
  export const __wbindgen_exn_store: (a: number) => void;