@takumi-rs/wasm 2.0.0-rc.4 → 2.0.0-rc.6

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
@@ -3,6 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  let _takumi_rs_helpers = require("@takumi-rs/helpers");
6
+ let _takumi_rs_helpers_renderer = require("@takumi-rs/helpers/renderer");
6
7
  //#region pkg/takumi_wasm.js
7
8
  /**
8
9
  * The main renderer for Takumi image rendering engine.
@@ -517,142 +518,83 @@ async function __wbg_init(module_or_path) {
517
518
  }
518
519
  //#endregion
519
520
  //#region src/export.ts
520
- async function resolveImageLoaders(images) {
521
- const { sources = [], cache } = Array.isArray(images) ? { sources: images } : images;
522
- const bySrc = /* @__PURE__ */ new Map();
523
- for (const image of sources) bySrc.set(image.src, image);
524
- return Promise.all([...bySrc.values()].map(async ({ src, data, cache: own }) => ({
525
- src,
526
- data: typeof data === "function" ? await data() : data,
527
- cache: own ?? cache
528
- })));
529
- }
530
521
  var Renderer = class {
531
- fontsByKey = /* @__PURE__ */ new Map();
532
- fontsByData = /* @__PURE__ */ new WeakMap();
533
522
  inner = new Renderer$1();
534
- getFont(key) {
535
- return typeof key === "string" ? this.fontsByKey.get(key) : this.fontsByData.get(key);
536
- }
537
- setFont(key, family) {
538
- if (typeof key === "string") this.fontsByKey.set(key, family);
539
- else this.fontsByData.set(key, family);
540
- }
541
- deleteFont(key) {
542
- if (typeof key === "string") this.fontsByKey.delete(key);
543
- else this.fontsByData.delete(key);
544
- }
545
- async prepareFonts(fonts) {
546
- if (!fonts) return;
547
- const families = await Promise.all(fonts.map(this.registerFont.bind(this)));
548
- return [...new Set(families.flat().map((f) => f.name))];
549
- }
523
+ fonts = new _takumi_rs_helpers_renderer.FontRegistry((font) => this.inner.registerFont(font));
550
524
  async render(node, options) {
551
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
552
- const registeredFamilies = await this.prepareFonts(fonts && (0, _takumi_rs_helpers.subsetFonts)({
525
+ const { fonts, fontFamilies, signal, images, ...rest } = options ?? {};
526
+ signal?.throwIfAborted();
527
+ const resolved = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
553
528
  fonts,
554
529
  source: node
555
- }));
556
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
530
+ }), images, fontFamilies);
531
+ signal?.throwIfAborted();
557
532
  return this.inner.render(node, {
558
533
  ...rest,
559
- images: resolvedImages,
560
- fontFamilies: fontFamilies ?? registeredFamilies
534
+ ...resolved
561
535
  });
562
536
  }
563
537
  async renderAsDataUrl(node, options) {
564
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
565
- const registeredFamilies = await this.prepareFonts(fonts && (0, _takumi_rs_helpers.subsetFonts)({
538
+ const { fonts, fontFamilies, signal, images, ...rest } = options ?? {};
539
+ signal?.throwIfAborted();
540
+ const resolved = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
566
541
  fonts,
567
542
  source: node
568
- }));
569
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
543
+ }), images, fontFamilies);
544
+ signal?.throwIfAborted();
570
545
  return this.inner.renderAsDataUrl(node, {
571
546
  ...rest,
572
- images: resolvedImages,
573
- fontFamilies: fontFamilies ?? registeredFamilies
547
+ ...resolved
574
548
  });
575
549
  }
576
550
  async renderSvg(node, options) {
577
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
578
- const registeredFamilies = await this.prepareFonts(fonts && (0, _takumi_rs_helpers.subsetFonts)({
551
+ const { fonts, fontFamilies, signal, images, ...rest } = options ?? {};
552
+ signal?.throwIfAborted();
553
+ const resolved = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
579
554
  fonts,
580
555
  source: node
581
- }));
582
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
556
+ }), images, fontFamilies);
557
+ signal?.throwIfAborted();
583
558
  return this.inner.renderSvg(node, {
584
559
  ...rest,
585
- images: resolvedImages,
586
- fontFamilies: fontFamilies ?? registeredFamilies
560
+ ...resolved
587
561
  });
588
562
  }
589
563
  async measure(node, options) {
590
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
591
- const registeredFamilies = await this.prepareFonts(fonts && (0, _takumi_rs_helpers.subsetFonts)({
564
+ const { fonts, fontFamilies, signal, images, ...rest } = options ?? {};
565
+ signal?.throwIfAborted();
566
+ const resolved = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
592
567
  fonts,
593
568
  source: node
594
- }));
595
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
569
+ }), images, fontFamilies);
570
+ signal?.throwIfAborted();
596
571
  return this.inner.measure(node, {
597
572
  ...rest,
598
- images: resolvedImages,
599
- fontFamilies: fontFamilies ?? registeredFamilies
573
+ ...resolved
600
574
  });
601
575
  }
602
576
  async renderAnimation(options) {
603
- const { fonts, fontFamilies, images, ...rest } = options;
577
+ const { fonts, fontFamilies, signal, images, ...rest } = options;
578
+ signal?.throwIfAborted();
604
579
  const nodes = options.scenes.map((scene) => scene.node);
605
- const registeredFamilies = await this.prepareFonts(fonts && (0, _takumi_rs_helpers.subsetFonts)({
580
+ const resolved = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
606
581
  fonts,
607
582
  source: nodes
608
- }));
609
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
583
+ }), images, fontFamilies);
584
+ signal?.throwIfAborted();
610
585
  return this.inner.renderAnimation({
611
586
  ...rest,
612
- images: resolvedImages,
613
- fontFamilies: fontFamilies ?? registeredFamilies
587
+ ...resolved
614
588
  });
615
589
  }
590
+ registerFont(font) {
591
+ return this.fonts.register(font);
592
+ }
593
+ /** Releases the underlying wasm renderer's memory. */
616
594
  free() {
617
595
  this.inner.free();
618
596
  }
619
- async registerFont(font) {
620
- const loader = typeof font === "string" ? (0, _takumi_rs_helpers.fontFromUrl)(font) : font;
621
- const key = createFontKey(loader);
622
- const cached = this.getFont(key);
623
- if (cached) return cached;
624
- const extracted = extractFontBuffer(loader);
625
- const register = (data) => this.inner.registerFont(isBuffer(loader) ? data : {
626
- ...loader,
627
- data
628
- });
629
- if (isBuffer(extracted)) {
630
- const binded = register(extracted);
631
- this.setFont(key, Promise.resolve(binded));
632
- return binded;
633
- }
634
- const promise = extracted.then(register).catch((error) => {
635
- this.deleteFont(key);
636
- throw error;
637
- });
638
- this.setFont(key, promise);
639
- return promise;
640
- }
641
597
  };
642
- function extractFontBuffer(font) {
643
- if (isBuffer(font)) return font;
644
- if (typeof font.data !== "function") return font.data;
645
- return font.data();
646
- }
647
- function createFontKey(font) {
648
- if (isBuffer(font)) return font;
649
- if ("key" in font && font.key) return font.key;
650
- if (typeof font.data === "function") return `${font.name ?? ""}:${font.weight ?? ""}:${font.style ?? ""}`;
651
- return font.data;
652
- }
653
- function isBuffer(data) {
654
- return data instanceof Uint8Array || data instanceof ArrayBuffer || typeof Buffer !== "undefined" && Buffer.isBuffer(data);
655
- }
656
598
  //#endregion
657
599
  exports.Renderer = Renderer;
658
600
  exports.default = __wbg_init;
package/dist/export.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ContainerNode, ImageNode, Node, Node as Node$1, NodeMetadata, TextNode } from "@takumi-rs/helpers";
2
2
  import { Properties } from "csstype";
3
+ import { AnimationOutputFormatOptions, AnimationOutputFormatOptions as AnimationOutputFormatOptions$1, FontLoader, FontLoader as FontLoader$1, ImageLoader, ImagesInput, ImagesInput as ImagesInput$1, OutputFormatOptions, OutputFormatOptions as OutputFormatOptions$1 } from "@takumi-rs/helpers/renderer";
3
4
 
4
5
  //#region pkg/takumi_wasm.d.ts
5
6
  type ByteBuf = Uint8Array | ArrayBuffer | Buffer;
@@ -12,6 +13,14 @@ type KeyframesRuleList = {
12
13
  }[];
13
14
  }[];
14
15
  type Keyframes = KeyframesMap | KeyframesRuleList;
16
+ /** Output format for static images. */
17
+ type OutputFormat = "png" | "jpeg" | "webp" | "ico" | "raw";
18
+ /** Output format for animated images. */
19
+ type AnimationOutputFormat = "webp" | "apng" | "gif";
20
+ /** The output dithering algorithm. */
21
+ type DitheringAlgorithm = "none" | "ordered-bayer" | "floyd-steinberg";
22
+ /** Cache policy for a decoded image. Defaults to `"auto"`. */
23
+ type ImageCacheMode = "auto" | "none";
15
24
  type RenderOptions$1 = {
16
25
  /**
17
26
  * The width of the image. If not provided, the width will be automatically calculated based on the content.
@@ -25,11 +34,17 @@ type RenderOptions$1 = {
25
34
  * The format of the image.
26
35
  * @default "png"
27
36
  */
28
- format?: "png" | "jpeg" | "webp" | "ico" | "raw";
37
+ format?: OutputFormat;
29
38
  /**
30
- * The quality of JPEG format (0-100).
39
+ * The quality of lossy formats (0-100). For JPEG; on wasm, WebP is always
40
+ * lossless so this is ignored for WebP.
31
41
  */
32
42
  quality?: number;
43
+ /**
44
+ * Encode WebP losslessly. On wasm, WebP is always lossless, so this is
45
+ * accepted for parity with the native backend but has no effect.
46
+ */
47
+ lossless?: boolean;
33
48
  /**
34
49
  * Images keyed by `src`, each carrying raw bytes. Provided up front and used
35
50
  * in place of fetching external `src` URLs during rendering.
@@ -60,26 +75,33 @@ type RenderOptions$1 = {
60
75
  * The output dithering algorithm.
61
76
  * @default "none"
62
77
  */
63
- dithering?: "none" | "ordered-bayer" | "floyd-steinberg";
78
+ dithering?: DitheringAlgorithm;
64
79
  /**
65
80
  * Per-render font stack: ordered family names used as the fallback chain.
66
81
  * Defaults to all registered families in registration order.
67
82
  */
68
- fontFamilies?: string[];
83
+ fontFamilies?: string[]; /** Default BCP-47 language applied to the root, inherited by nodes without their own lang. */
84
+ lang?: string;
69
85
  };
70
86
  /**
71
87
  * SVG is a vector format, so the raster-only knobs do not apply.
72
88
  */
73
- type SvgRenderOptions$1 = Omit<RenderOptions$1, "format" | "quality" | "drawDebugBorder" | "devicePixelRatio" | "dithering">;
89
+ type SvgRenderOptions$1 = Omit<RenderOptions$1, "format" | "quality" | "lossless" | "drawDebugBorder" | "devicePixelRatio" | "dithering">;
74
90
  type RenderAnimationOptions$1 = {
75
91
  scenes: AnimationScene[];
76
92
  width: number;
77
93
  height: number;
78
- format?: "webp" | "apng" | "gif";
94
+ format?: AnimationOutputFormat;
79
95
  /**
80
- * The quality of WebP format (0-100). Ignored for APNG and GIF.
96
+ * The quality of lossy WebP (0-100). Ignored for APNG and GIF; on wasm, WebP
97
+ * is always lossless so this is ignored for WebP too.
81
98
  */
82
99
  quality?: number;
100
+ /**
101
+ * Encode WebP losslessly. On wasm, animated WebP is always lossless, so this
102
+ * is accepted for parity with the native backend but has no effect.
103
+ */
104
+ lossless?: boolean;
83
105
  /**
84
106
  * Images keyed by `src`, each carrying raw bytes. Provided up front and used
85
107
  * in place of fetching external `src` URLs during rendering.
@@ -103,7 +125,8 @@ type RenderAnimationOptions$1 = {
103
125
  * Per-render font stack: ordered family names used as the fallback chain.
104
126
  * Defaults to all registered families in registration order.
105
127
  */
106
- fontFamilies?: string[];
128
+ fontFamilies?: string[]; /** Default BCP-47 language applied to the root, inherited by nodes without their own lang. */
129
+ lang?: string;
107
130
  };
108
131
  type FontDetails = {
109
132
  name?: string;
@@ -120,7 +143,7 @@ type FontDetails = {
120
143
  type ImageSource = {
121
144
  src: string;
122
145
  data: ByteBuf; /** Cache policy for the decoded image. Defaults to `"auto"`. */
123
- cache?: "auto" | "none";
146
+ cache?: ImageCacheMode;
124
147
  };
125
148
  type KeyframeRule = {
126
149
  offsets: number[];
@@ -204,82 +227,32 @@ declare function __wbg_init(module_or_path?: {
204
227
  } | InitInput | Promise<InitInput>): Promise<InitOutput>;
205
228
  //#endregion
206
229
  //#region src/export.d.ts
207
- /**
208
- * A font to register. Either a URL string (fetched on demand, with name/weight/style read from the
209
- * file), raw bytes, or a descriptor with a lazy `data()` loader.
210
- */
211
- type FontLoader = string | Font | (Omit<FontDetails, "data"> & {
212
- data: () => Promise<FontDetails["data"]> | FontDetails["data"]; /** Inclusive codepoint ranges this face covers; lets `render` skip it when unused. */
213
- ranges?: [number, number][];
214
- } & ({
215
- key: string;
216
- } | {
217
- name: string;
218
- }));
219
- type ImageLoaderData = ImageSource["data"];
220
- type ImageLoader = Omit<ImageSource, "data"> & {
221
- data: ImageLoaderData | (() => ImageLoaderData | Promise<ImageLoaderData>);
222
- };
223
- /** Images for a render: pre-fetched entries, or a group with a decode-cache default. */
224
- type ImagesInput = ImageLoader[] | {
225
- /** Pre-fetched entries, same as the array form. */sources?: ImageLoader[]; /** Decode-cache default for every image this render; a source's own `cache` wins. */
226
- cache?: NonNullable<ImageLoader["cache"]>;
227
- };
228
- /**
229
- * Output format. Format-specific options live on the variant that supports them,
230
- * so `quality` cannot be paired with a lossless format. On wasm, WebP is always
231
- * lossless (lossy WebP is native-only).
232
- */
233
- type OutputFormatOptions = {
234
- format?: "png";
235
- } | {
236
- format: "jpeg";
237
- quality?: number;
238
- } | {
239
- format: "webp";
240
- } | {
241
- format: "ico";
242
- } | {
243
- format: "raw";
244
- };
245
- type RenderOptions = Omit<RenderOptions$1, "images" | "format" | "quality"> & OutputFormatOptions & {
246
- fonts?: FontLoader[];
247
- images?: ImagesInput;
248
- };
249
- /**
250
- * Animation output format. On wasm, WebP animation is always lossless (lossy
251
- * WebP is native-only).
252
- */
253
- type AnimationOutputFormatOptions = {
254
- format?: "webp";
255
- } | {
256
- format: "apng";
257
- } | {
258
- format: "gif";
230
+ type RenderOptions = Omit<RenderOptions$1, "images" | "format" | "quality" | "lossless"> & OutputFormatOptions$1 & {
231
+ fonts?: FontLoader$1[];
232
+ signal?: AbortSignal;
233
+ images?: ImagesInput$1;
259
234
  };
260
- type RenderAnimationOptions = Omit<RenderAnimationOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
261
- fonts?: FontLoader[];
262
- images?: ImagesInput;
235
+ type RenderAnimationOptions = Omit<RenderAnimationOptions$1, "images" | "format" | "quality" | "lossless"> & AnimationOutputFormatOptions$1 & {
236
+ fonts?: FontLoader$1[];
237
+ signal?: AbortSignal;
238
+ images?: ImagesInput$1;
263
239
  };
264
240
  type SvgRenderOptions = Omit<SvgRenderOptions$1, "images"> & {
265
- fonts?: FontLoader[];
266
- images?: ImagesInput;
241
+ fonts?: FontLoader$1[];
242
+ signal?: AbortSignal;
243
+ images?: ImagesInput$1;
267
244
  };
268
245
  declare class Renderer {
269
- private fontsByKey;
270
- private fontsByData;
271
246
  private inner;
272
- private getFont;
273
- private setFont;
274
- private deleteFont;
275
- private prepareFonts;
247
+ private fonts;
276
248
  render(node: Node, options?: RenderOptions): Promise<Uint8Array<ArrayBufferLike>>;
277
249
  renderAsDataUrl(node: Node, options?: RenderOptions): Promise<string>;
278
250
  renderSvg(node: Node, options?: SvgRenderOptions): Promise<string>;
279
251
  measure(node: Node, options?: RenderOptions): Promise<MeasuredNode>;
280
252
  renderAnimation(options: RenderAnimationOptions): Promise<Uint8Array<ArrayBufferLike>>;
253
+ registerFont(font: FontLoader$1): any;
254
+ /** Releases the underlying wasm renderer's memory. */
281
255
  free(): void;
282
- registerFont(font: FontLoader): Promise<RegisteredFamily[]>;
283
256
  }
284
257
  //#endregion
285
- export { AnimationOutputFormatOptions, AnimationScene, ByteBuf, ContainerNode, Font, FontDetails, FontLoader, ImageLoader, ImageNode, ImageSource, ImagesInput, 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, initSync };
258
+ export { AnimationOutputFormat, type AnimationOutputFormatOptions, AnimationScene, ByteBuf, ContainerNode, DitheringAlgorithm, Font, FontDetails, type FontLoader, ImageCacheMode, type ImageLoader, ImageNode, ImageSource, type ImagesInput, InitInput, InitOutput, KeyframeRule, Keyframes, KeyframesMap, KeyframesRule, KeyframesRuleList, MeasuredNode, MeasuredTextRun, type Node, NodeMetadata, OutputFormat, type OutputFormatOptions, RegisteredFace, RegisteredFamily, RenderAnimationOptions, RenderOptions, Renderer, SvgRenderOptions, SyncInitInput, TextNode, __wbg_init as default, initSync };
package/dist/export.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ContainerNode, ImageNode, Node, Node as Node$1, NodeMetadata, TextNode } from "@takumi-rs/helpers";
2
+ import { AnimationOutputFormatOptions, AnimationOutputFormatOptions as AnimationOutputFormatOptions$1, FontLoader, FontLoader as FontLoader$1, ImageLoader, ImagesInput, ImagesInput as ImagesInput$1, OutputFormatOptions, OutputFormatOptions as OutputFormatOptions$1 } from "@takumi-rs/helpers/renderer";
2
3
  import { Properties } from "csstype";
3
4
 
4
5
  //#region pkg/takumi_wasm.d.ts
@@ -12,6 +13,14 @@ type KeyframesRuleList = {
12
13
  }[];
13
14
  }[];
14
15
  type Keyframes = KeyframesMap | KeyframesRuleList;
16
+ /** Output format for static images. */
17
+ type OutputFormat = "png" | "jpeg" | "webp" | "ico" | "raw";
18
+ /** Output format for animated images. */
19
+ type AnimationOutputFormat = "webp" | "apng" | "gif";
20
+ /** The output dithering algorithm. */
21
+ type DitheringAlgorithm = "none" | "ordered-bayer" | "floyd-steinberg";
22
+ /** Cache policy for a decoded image. Defaults to `"auto"`. */
23
+ type ImageCacheMode = "auto" | "none";
15
24
  type RenderOptions$1 = {
16
25
  /**
17
26
  * The width of the image. If not provided, the width will be automatically calculated based on the content.
@@ -25,11 +34,17 @@ type RenderOptions$1 = {
25
34
  * The format of the image.
26
35
  * @default "png"
27
36
  */
28
- format?: "png" | "jpeg" | "webp" | "ico" | "raw";
37
+ format?: OutputFormat;
29
38
  /**
30
- * The quality of JPEG format (0-100).
39
+ * The quality of lossy formats (0-100). For JPEG; on wasm, WebP is always
40
+ * lossless so this is ignored for WebP.
31
41
  */
32
42
  quality?: number;
43
+ /**
44
+ * Encode WebP losslessly. On wasm, WebP is always lossless, so this is
45
+ * accepted for parity with the native backend but has no effect.
46
+ */
47
+ lossless?: boolean;
33
48
  /**
34
49
  * Images keyed by `src`, each carrying raw bytes. Provided up front and used
35
50
  * in place of fetching external `src` URLs during rendering.
@@ -60,26 +75,33 @@ type RenderOptions$1 = {
60
75
  * The output dithering algorithm.
61
76
  * @default "none"
62
77
  */
63
- dithering?: "none" | "ordered-bayer" | "floyd-steinberg";
78
+ dithering?: DitheringAlgorithm;
64
79
  /**
65
80
  * Per-render font stack: ordered family names used as the fallback chain.
66
81
  * Defaults to all registered families in registration order.
67
82
  */
68
- fontFamilies?: string[];
83
+ fontFamilies?: string[]; /** Default BCP-47 language applied to the root, inherited by nodes without their own lang. */
84
+ lang?: string;
69
85
  };
70
86
  /**
71
87
  * SVG is a vector format, so the raster-only knobs do not apply.
72
88
  */
73
- type SvgRenderOptions$1 = Omit<RenderOptions$1, "format" | "quality" | "drawDebugBorder" | "devicePixelRatio" | "dithering">;
89
+ type SvgRenderOptions$1 = Omit<RenderOptions$1, "format" | "quality" | "lossless" | "drawDebugBorder" | "devicePixelRatio" | "dithering">;
74
90
  type RenderAnimationOptions$1 = {
75
91
  scenes: AnimationScene[];
76
92
  width: number;
77
93
  height: number;
78
- format?: "webp" | "apng" | "gif";
94
+ format?: AnimationOutputFormat;
79
95
  /**
80
- * The quality of WebP format (0-100). Ignored for APNG and GIF.
96
+ * The quality of lossy WebP (0-100). Ignored for APNG and GIF; on wasm, WebP
97
+ * is always lossless so this is ignored for WebP too.
81
98
  */
82
99
  quality?: number;
100
+ /**
101
+ * Encode WebP losslessly. On wasm, animated WebP is always lossless, so this
102
+ * is accepted for parity with the native backend but has no effect.
103
+ */
104
+ lossless?: boolean;
83
105
  /**
84
106
  * Images keyed by `src`, each carrying raw bytes. Provided up front and used
85
107
  * in place of fetching external `src` URLs during rendering.
@@ -103,7 +125,8 @@ type RenderAnimationOptions$1 = {
103
125
  * Per-render font stack: ordered family names used as the fallback chain.
104
126
  * Defaults to all registered families in registration order.
105
127
  */
106
- fontFamilies?: string[];
128
+ fontFamilies?: string[]; /** Default BCP-47 language applied to the root, inherited by nodes without their own lang. */
129
+ lang?: string;
107
130
  };
108
131
  type FontDetails = {
109
132
  name?: string;
@@ -120,7 +143,7 @@ type FontDetails = {
120
143
  type ImageSource = {
121
144
  src: string;
122
145
  data: ByteBuf; /** Cache policy for the decoded image. Defaults to `"auto"`. */
123
- cache?: "auto" | "none";
146
+ cache?: ImageCacheMode;
124
147
  };
125
148
  type KeyframeRule = {
126
149
  offsets: number[];
@@ -204,82 +227,32 @@ declare function __wbg_init(module_or_path?: {
204
227
  } | InitInput | Promise<InitInput>): Promise<InitOutput>;
205
228
  //#endregion
206
229
  //#region src/export.d.ts
207
- /**
208
- * A font to register. Either a URL string (fetched on demand, with name/weight/style read from the
209
- * file), raw bytes, or a descriptor with a lazy `data()` loader.
210
- */
211
- type FontLoader = string | Font | (Omit<FontDetails, "data"> & {
212
- data: () => Promise<FontDetails["data"]> | FontDetails["data"]; /** Inclusive codepoint ranges this face covers; lets `render` skip it when unused. */
213
- ranges?: [number, number][];
214
- } & ({
215
- key: string;
216
- } | {
217
- name: string;
218
- }));
219
- type ImageLoaderData = ImageSource["data"];
220
- type ImageLoader = Omit<ImageSource, "data"> & {
221
- data: ImageLoaderData | (() => ImageLoaderData | Promise<ImageLoaderData>);
222
- };
223
- /** Images for a render: pre-fetched entries, or a group with a decode-cache default. */
224
- type ImagesInput = ImageLoader[] | {
225
- /** Pre-fetched entries, same as the array form. */sources?: ImageLoader[]; /** Decode-cache default for every image this render; a source's own `cache` wins. */
226
- cache?: NonNullable<ImageLoader["cache"]>;
227
- };
228
- /**
229
- * Output format. Format-specific options live on the variant that supports them,
230
- * so `quality` cannot be paired with a lossless format. On wasm, WebP is always
231
- * lossless (lossy WebP is native-only).
232
- */
233
- type OutputFormatOptions = {
234
- format?: "png";
235
- } | {
236
- format: "jpeg";
237
- quality?: number;
238
- } | {
239
- format: "webp";
240
- } | {
241
- format: "ico";
242
- } | {
243
- format: "raw";
244
- };
245
- type RenderOptions = Omit<RenderOptions$1, "images" | "format" | "quality"> & OutputFormatOptions & {
246
- fonts?: FontLoader[];
247
- images?: ImagesInput;
248
- };
249
- /**
250
- * Animation output format. On wasm, WebP animation is always lossless (lossy
251
- * WebP is native-only).
252
- */
253
- type AnimationOutputFormatOptions = {
254
- format?: "webp";
255
- } | {
256
- format: "apng";
257
- } | {
258
- format: "gif";
230
+ type RenderOptions = Omit<RenderOptions$1, "images" | "format" | "quality" | "lossless"> & OutputFormatOptions$1 & {
231
+ fonts?: FontLoader$1[];
232
+ signal?: AbortSignal;
233
+ images?: ImagesInput$1;
259
234
  };
260
- type RenderAnimationOptions = Omit<RenderAnimationOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
261
- fonts?: FontLoader[];
262
- images?: ImagesInput;
235
+ type RenderAnimationOptions = Omit<RenderAnimationOptions$1, "images" | "format" | "quality" | "lossless"> & AnimationOutputFormatOptions$1 & {
236
+ fonts?: FontLoader$1[];
237
+ signal?: AbortSignal;
238
+ images?: ImagesInput$1;
263
239
  };
264
240
  type SvgRenderOptions = Omit<SvgRenderOptions$1, "images"> & {
265
- fonts?: FontLoader[];
266
- images?: ImagesInput;
241
+ fonts?: FontLoader$1[];
242
+ signal?: AbortSignal;
243
+ images?: ImagesInput$1;
267
244
  };
268
245
  declare class Renderer {
269
- private fontsByKey;
270
- private fontsByData;
271
246
  private inner;
272
- private getFont;
273
- private setFont;
274
- private deleteFont;
275
- private prepareFonts;
247
+ private fonts;
276
248
  render(node: Node, options?: RenderOptions): Promise<Uint8Array<ArrayBufferLike>>;
277
249
  renderAsDataUrl(node: Node, options?: RenderOptions): Promise<string>;
278
250
  renderSvg(node: Node, options?: SvgRenderOptions): Promise<string>;
279
251
  measure(node: Node, options?: RenderOptions): Promise<MeasuredNode>;
280
252
  renderAnimation(options: RenderAnimationOptions): Promise<Uint8Array<ArrayBufferLike>>;
253
+ registerFont(font: FontLoader$1): any;
254
+ /** Releases the underlying wasm renderer's memory. */
281
255
  free(): void;
282
- registerFont(font: FontLoader): Promise<RegisteredFamily[]>;
283
256
  }
284
257
  //#endregion
285
- export { AnimationOutputFormatOptions, AnimationScene, ByteBuf, ContainerNode, Font, FontDetails, FontLoader, ImageLoader, ImageNode, ImageSource, ImagesInput, 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, initSync };
258
+ export { AnimationOutputFormat, type AnimationOutputFormatOptions, AnimationScene, ByteBuf, ContainerNode, DitheringAlgorithm, Font, FontDetails, type FontLoader, ImageCacheMode, type ImageLoader, ImageNode, ImageSource, type ImagesInput, InitInput, InitOutput, KeyframeRule, Keyframes, KeyframesMap, KeyframesRule, KeyframesRuleList, MeasuredNode, MeasuredTextRun, type Node, NodeMetadata, OutputFormat, type OutputFormatOptions, RegisteredFace, RegisteredFamily, RenderAnimationOptions, RenderOptions, Renderer, SvgRenderOptions, SyncInitInput, TextNode, __wbg_init as default, initSync };
package/dist/export.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { fontFromUrl, subsetFonts } from "@takumi-rs/helpers";
1
+ import { subsetFonts } from "@takumi-rs/helpers";
2
+ import { FontRegistry } from "@takumi-rs/helpers/renderer";
2
3
  //#region pkg/takumi_wasm.js
3
4
  /**
4
5
  * The main renderer for Takumi image rendering engine.
@@ -513,141 +514,82 @@ async function __wbg_init(module_or_path) {
513
514
  }
514
515
  //#endregion
515
516
  //#region src/export.ts
516
- async function resolveImageLoaders(images) {
517
- const { sources = [], cache } = Array.isArray(images) ? { sources: images } : images;
518
- const bySrc = /* @__PURE__ */ new Map();
519
- for (const image of sources) bySrc.set(image.src, image);
520
- return Promise.all([...bySrc.values()].map(async ({ src, data, cache: own }) => ({
521
- src,
522
- data: typeof data === "function" ? await data() : data,
523
- cache: own ?? cache
524
- })));
525
- }
526
517
  var Renderer = class {
527
- fontsByKey = /* @__PURE__ */ new Map();
528
- fontsByData = /* @__PURE__ */ new WeakMap();
529
518
  inner = new Renderer$1();
530
- getFont(key) {
531
- return typeof key === "string" ? this.fontsByKey.get(key) : this.fontsByData.get(key);
532
- }
533
- setFont(key, family) {
534
- if (typeof key === "string") this.fontsByKey.set(key, family);
535
- else this.fontsByData.set(key, family);
536
- }
537
- deleteFont(key) {
538
- if (typeof key === "string") this.fontsByKey.delete(key);
539
- else this.fontsByData.delete(key);
540
- }
541
- async prepareFonts(fonts) {
542
- if (!fonts) return;
543
- const families = await Promise.all(fonts.map(this.registerFont.bind(this)));
544
- return [...new Set(families.flat().map((f) => f.name))];
545
- }
519
+ fonts = new FontRegistry((font) => this.inner.registerFont(font));
546
520
  async render(node, options) {
547
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
548
- const registeredFamilies = await this.prepareFonts(fonts && subsetFonts({
521
+ const { fonts, fontFamilies, signal, images, ...rest } = options ?? {};
522
+ signal?.throwIfAborted();
523
+ const resolved = await this.fonts.resolveResources(fonts && subsetFonts({
549
524
  fonts,
550
525
  source: node
551
- }));
552
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
526
+ }), images, fontFamilies);
527
+ signal?.throwIfAborted();
553
528
  return this.inner.render(node, {
554
529
  ...rest,
555
- images: resolvedImages,
556
- fontFamilies: fontFamilies ?? registeredFamilies
530
+ ...resolved
557
531
  });
558
532
  }
559
533
  async renderAsDataUrl(node, options) {
560
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
561
- const registeredFamilies = await this.prepareFonts(fonts && subsetFonts({
534
+ const { fonts, fontFamilies, signal, images, ...rest } = options ?? {};
535
+ signal?.throwIfAborted();
536
+ const resolved = await this.fonts.resolveResources(fonts && subsetFonts({
562
537
  fonts,
563
538
  source: node
564
- }));
565
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
539
+ }), images, fontFamilies);
540
+ signal?.throwIfAborted();
566
541
  return this.inner.renderAsDataUrl(node, {
567
542
  ...rest,
568
- images: resolvedImages,
569
- fontFamilies: fontFamilies ?? registeredFamilies
543
+ ...resolved
570
544
  });
571
545
  }
572
546
  async renderSvg(node, options) {
573
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
574
- const registeredFamilies = await this.prepareFonts(fonts && subsetFonts({
547
+ const { fonts, fontFamilies, signal, images, ...rest } = options ?? {};
548
+ signal?.throwIfAborted();
549
+ const resolved = await this.fonts.resolveResources(fonts && subsetFonts({
575
550
  fonts,
576
551
  source: node
577
- }));
578
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
552
+ }), images, fontFamilies);
553
+ signal?.throwIfAborted();
579
554
  return this.inner.renderSvg(node, {
580
555
  ...rest,
581
- images: resolvedImages,
582
- fontFamilies: fontFamilies ?? registeredFamilies
556
+ ...resolved
583
557
  });
584
558
  }
585
559
  async measure(node, options) {
586
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
587
- const registeredFamilies = await this.prepareFonts(fonts && subsetFonts({
560
+ const { fonts, fontFamilies, signal, images, ...rest } = options ?? {};
561
+ signal?.throwIfAborted();
562
+ const resolved = await this.fonts.resolveResources(fonts && subsetFonts({
588
563
  fonts,
589
564
  source: node
590
- }));
591
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
565
+ }), images, fontFamilies);
566
+ signal?.throwIfAborted();
592
567
  return this.inner.measure(node, {
593
568
  ...rest,
594
- images: resolvedImages,
595
- fontFamilies: fontFamilies ?? registeredFamilies
569
+ ...resolved
596
570
  });
597
571
  }
598
572
  async renderAnimation(options) {
599
- const { fonts, fontFamilies, images, ...rest } = options;
573
+ const { fonts, fontFamilies, signal, images, ...rest } = options;
574
+ signal?.throwIfAborted();
600
575
  const nodes = options.scenes.map((scene) => scene.node);
601
- const registeredFamilies = await this.prepareFonts(fonts && subsetFonts({
576
+ const resolved = await this.fonts.resolveResources(fonts && subsetFonts({
602
577
  fonts,
603
578
  source: nodes
604
- }));
605
- const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
579
+ }), images, fontFamilies);
580
+ signal?.throwIfAborted();
606
581
  return this.inner.renderAnimation({
607
582
  ...rest,
608
- images: resolvedImages,
609
- fontFamilies: fontFamilies ?? registeredFamilies
583
+ ...resolved
610
584
  });
611
585
  }
586
+ registerFont(font) {
587
+ return this.fonts.register(font);
588
+ }
589
+ /** Releases the underlying wasm renderer's memory. */
612
590
  free() {
613
591
  this.inner.free();
614
592
  }
615
- async registerFont(font) {
616
- const loader = typeof font === "string" ? fontFromUrl(font) : font;
617
- const key = createFontKey(loader);
618
- const cached = this.getFont(key);
619
- if (cached) return cached;
620
- const extracted = extractFontBuffer(loader);
621
- const register = (data) => this.inner.registerFont(isBuffer(loader) ? data : {
622
- ...loader,
623
- data
624
- });
625
- if (isBuffer(extracted)) {
626
- const binded = register(extracted);
627
- this.setFont(key, Promise.resolve(binded));
628
- return binded;
629
- }
630
- const promise = extracted.then(register).catch((error) => {
631
- this.deleteFont(key);
632
- throw error;
633
- });
634
- this.setFont(key, promise);
635
- return promise;
636
- }
637
593
  };
638
- function extractFontBuffer(font) {
639
- if (isBuffer(font)) return font;
640
- if (typeof font.data !== "function") return font.data;
641
- return font.data();
642
- }
643
- function createFontKey(font) {
644
- if (isBuffer(font)) return font;
645
- if ("key" in font && font.key) return font.key;
646
- if (typeof font.data === "function") return `${font.name ?? ""}:${font.weight ?? ""}:${font.style ?? ""}`;
647
- return font.data;
648
- }
649
- function isBuffer(data) {
650
- return data instanceof Uint8Array || data instanceof ArrayBuffer || typeof Buffer !== "undefined" && Buffer.isBuffer(data);
651
- }
652
594
  //#endregion
653
595
  export { Renderer, __wbg_init as default, initSync };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takumi-rs/wasm",
3
- "version": "2.0.0-rc.4",
3
+ "version": "2.0.0-rc.6",
4
4
  "keywords": [
5
5
  "css",
6
6
  "image",
@@ -116,7 +116,7 @@
116
116
  "publish-lint": "attw --pack . && publint --strict ."
117
117
  },
118
118
  "dependencies": {
119
- "@takumi-rs/helpers": "2.0.0-rc.1",
119
+ "@takumi-rs/helpers": "2.0.0-rc.6",
120
120
  "csstype": "^3.2.3"
121
121
  },
122
122
  "devDependencies": {
Binary file