@arraypress/covers 1.0.0 → 1.1.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.
Files changed (213) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +296 -11
  3. package/package.json +33 -5
  4. package/recipes/brand.json +1143 -0
  5. package/schema/recipe.schema.json +7300 -770
  6. package/src/animate/index.d.ts +19 -0
  7. package/src/animate/index.js +116 -0
  8. package/src/builtins.js +4 -0
  9. package/src/effects/ascii.d.ts +7 -0
  10. package/src/effects/ascii.js +93 -0
  11. package/src/effects/buffer.d.ts +60 -0
  12. package/src/effects/buffer.js +472 -0
  13. package/src/effects/detail.d.ts +12 -0
  14. package/src/effects/detail.js +124 -0
  15. package/src/effects/digital.d.ts +14 -0
  16. package/src/effects/digital.js +216 -0
  17. package/src/effects/distort.d.ts +20 -0
  18. package/src/effects/distort.js +314 -0
  19. package/src/effects/dither.d.ts +8 -0
  20. package/src/effects/dither.js +147 -0
  21. package/src/effects/drop-shadow.d.ts +4 -0
  22. package/src/effects/drop-shadow.js +45 -0
  23. package/src/effects/extrude.d.ts +5 -0
  24. package/src/effects/extrude.js +55 -0
  25. package/src/effects/film.d.ts +11 -0
  26. package/src/effects/film.js +204 -0
  27. package/src/effects/glow.d.ts +8 -0
  28. package/src/effects/glow.js +56 -0
  29. package/src/effects/grade.d.ts +30 -0
  30. package/src/effects/grade.js +274 -0
  31. package/src/effects/halftone.d.ts +8 -0
  32. package/src/effects/halftone.js +95 -0
  33. package/src/effects/index.d.ts +125 -0
  34. package/src/effects/index.js +277 -0
  35. package/src/effects/ink.d.ts +12 -0
  36. package/src/effects/ink.js +70 -0
  37. package/src/effects/lens.d.ts +14 -0
  38. package/src/effects/lens.js +277 -0
  39. package/src/effects/light.d.ts +10 -0
  40. package/src/effects/light.js +59 -0
  41. package/src/effects/looks.d.ts +9 -0
  42. package/src/effects/looks.js +74 -0
  43. package/src/effects/mask.d.ts +52 -0
  44. package/src/effects/mask.js +314 -0
  45. package/src/effects/motion.d.ts +6 -0
  46. package/src/effects/motion.js +60 -0
  47. package/src/effects/newsprint.d.ts +6 -0
  48. package/src/effects/newsprint.js +89 -0
  49. package/src/effects/optics.d.ts +10 -0
  50. package/src/effects/optics.js +213 -0
  51. package/src/effects/options.d.ts +6 -0
  52. package/src/effects/options.js +106 -0
  53. package/src/effects/paint-presets.d.ts +10 -0
  54. package/src/effects/paint-presets.js +47 -0
  55. package/src/effects/paint.d.ts +49 -0
  56. package/src/effects/paint.js +66 -0
  57. package/src/effects/pixels.d.ts +24 -0
  58. package/src/effects/pixels.js +138 -0
  59. package/src/effects/polar.d.ts +10 -0
  60. package/src/effects/polar.js +265 -0
  61. package/src/effects/region.d.ts +8 -0
  62. package/src/effects/region.js +136 -0
  63. package/src/effects/round-border.d.ts +5 -0
  64. package/src/effects/round-border.js +62 -0
  65. package/src/effects/screen.d.ts +14 -0
  66. package/src/effects/screen.js +212 -0
  67. package/src/effects/sort.d.ts +8 -0
  68. package/src/effects/sort.js +125 -0
  69. package/src/effects/symmetry.d.ts +6 -0
  70. package/src/effects/symmetry.js +74 -0
  71. package/src/effects/tile.d.ts +7 -0
  72. package/src/effects/tile.js +55 -0
  73. package/src/effects/tone.d.ts +16 -0
  74. package/src/effects/tone.js +117 -0
  75. package/src/effects/transform.d.ts +35 -0
  76. package/src/effects/transform.js +127 -0
  77. package/src/effects/util.d.ts +16 -0
  78. package/src/effects/util.js +85 -0
  79. package/src/extract.js +4 -3
  80. package/src/fonts.d.ts +55 -0
  81. package/src/fonts.js +246 -53
  82. package/src/formats/cut.d.ts +10 -0
  83. package/src/formats/cut.js +77 -0
  84. package/src/formats/index.d.ts +6 -0
  85. package/src/formats/index.js +9 -0
  86. package/src/formats/presets.d.ts +41 -0
  87. package/src/formats/presets.js +89 -0
  88. package/src/formats/print.d.ts +25 -0
  89. package/src/formats/print.js +117 -0
  90. package/src/formats/zones.d.ts +10 -0
  91. package/src/formats/zones.js +64 -0
  92. package/src/geometry.js +1 -1
  93. package/src/icons.js +43 -9
  94. package/src/index.d.ts +205 -19
  95. package/src/index.js +12 -4
  96. package/src/inter-data.js +27 -0
  97. package/src/json-recipe.js +181 -23
  98. package/src/kit/index.d.ts +20 -0
  99. package/src/kit/index.js +58 -0
  100. package/src/layer-options.js +23 -4
  101. package/src/layers/barcode.d.ts +19 -0
  102. package/src/layers/barcode.js +63 -0
  103. package/src/layers/code128.d.ts +4 -0
  104. package/src/layers/code128.js +28 -0
  105. package/src/layers/expr.d.ts +6 -0
  106. package/src/layers/expr.js +121 -0
  107. package/src/layers/geometry.d.ts +6 -0
  108. package/src/layers/geometry.js +238 -0
  109. package/src/layers/index.d.ts +18 -0
  110. package/src/layers/index.js +15 -0
  111. package/src/layers/perforation.d.ts +14 -0
  112. package/src/layers/perforation.js +47 -0
  113. package/src/layers/repeat.d.ts +29 -0
  114. package/src/layers/repeat.js +90 -0
  115. package/src/layers/shape.d.ts +46 -0
  116. package/src/layers/shape.js +115 -0
  117. package/src/layers/soft.d.ts +5 -0
  118. package/src/layers/soft.js +43 -0
  119. package/src/layers/svg.d.ts +23 -0
  120. package/src/layers/svg.js +109 -0
  121. package/src/layers/table.d.ts +25 -0
  122. package/src/layers/table.js +80 -0
  123. package/src/layers/tear.d.ts +21 -0
  124. package/src/layers/tear.js +74 -0
  125. package/src/layers/texture.d.ts +17 -0
  126. package/src/layers/texture.js +106 -0
  127. package/src/layers/util.d.ts +8 -0
  128. package/src/layers/util.js +48 -0
  129. package/src/layers.js +125 -41
  130. package/src/lint/image-size.d.ts +4 -0
  131. package/src/lint/image-size.js +34 -0
  132. package/src/lint/index.d.ts +16 -0
  133. package/src/lint/index.js +127 -0
  134. package/src/lint/rules.d.ts +5 -0
  135. package/src/lint/rules.js +19 -0
  136. package/src/node.d.ts +2 -0
  137. package/src/node.js +20 -0
  138. package/src/platform/bytes.d.ts +22 -0
  139. package/src/platform/bytes.js +103 -0
  140. package/src/platform/compress.d.ts +12 -0
  141. package/src/platform/compress.js +81 -0
  142. package/src/platform/harfbuzz.d.ts +9 -0
  143. package/src/platform/harfbuzz.js +80 -0
  144. package/src/platform/index.d.ts +8 -0
  145. package/src/platform/index.js +13 -0
  146. package/src/platform/load.d.ts +6 -0
  147. package/src/platform/load.js +72 -0
  148. package/src/platform/no-harfbuzz.d.ts +2 -0
  149. package/src/platform/no-harfbuzz.js +11 -0
  150. package/src/platform/node.d.ts +26 -0
  151. package/src/platform/node.js +86 -0
  152. package/src/platform/settings.d.ts +28 -0
  153. package/src/platform/settings.js +51 -0
  154. package/src/png.js +77 -28
  155. package/src/print.js +8 -7
  156. package/src/raster/index.d.ts +24 -0
  157. package/src/raster/index.js +171 -0
  158. package/src/recipes.js +4 -3
  159. package/src/render/backdrop.d.ts +6 -0
  160. package/src/render/backdrop.js +55 -0
  161. package/src/render/content.d.ts +5 -0
  162. package/src/render/content.js +58 -0
  163. package/src/render/context.d.ts +82 -0
  164. package/src/render/grain.d.ts +7 -0
  165. package/src/render/grain.js +113 -0
  166. package/src/render/group.d.ts +23 -0
  167. package/src/render/group.js +67 -0
  168. package/src/render/key.d.ts +5 -0
  169. package/src/render/key.js +91 -0
  170. package/src/render/report.d.ts +5 -0
  171. package/src/render/report.js +19 -0
  172. package/src/render/tiles.d.ts +6 -0
  173. package/src/render/tiles.js +77 -0
  174. package/src/render/use.d.ts +27 -0
  175. package/src/render/use.js +73 -0
  176. package/src/render/wrap.d.ts +14 -0
  177. package/src/render/wrap.js +199 -0
  178. package/src/render.js +303 -212
  179. package/src/text.d.ts +29 -0
  180. package/src/text.js +22 -15
  181. package/src/type/auto-color.d.ts +9 -0
  182. package/src/type/auto-color.js +44 -0
  183. package/src/type/fallback.d.ts +11 -0
  184. package/src/type/fallback.js +92 -0
  185. package/src/type/glyphs.d.ts +24 -0
  186. package/src/type/glyphs.js +87 -0
  187. package/src/type/index.d.ts +5 -0
  188. package/src/type/index.js +10 -0
  189. package/src/type/kern.d.ts +4 -0
  190. package/src/type/kern.js +144 -0
  191. package/src/type/layout-ref.d.ts +10 -0
  192. package/src/type/layout-ref.js +74 -0
  193. package/src/type/line.d.ts +11 -0
  194. package/src/type/line.js +105 -0
  195. package/src/type/live.d.ts +6 -0
  196. package/src/type/live.js +39 -0
  197. package/src/type/name.d.ts +4 -0
  198. package/src/type/name.js +32 -0
  199. package/src/type/path.d.ts +18 -0
  200. package/src/type/path.js +89 -0
  201. package/src/type/pixel.d.ts +9 -0
  202. package/src/type/pixel.js +115 -0
  203. package/src/type/sfnt.d.ts +20 -0
  204. package/src/type/sfnt.js +258 -0
  205. package/src/type/spans.d.ts +33 -0
  206. package/src/type/spans.js +138 -0
  207. package/src/type/symbols.d.ts +7 -0
  208. package/src/type/symbols.js +89 -0
  209. package/src/type/typeset.d.ts +10 -0
  210. package/src/type/typeset.js +352 -0
  211. package/src/type/woff2.d.ts +6 -0
  212. package/src/type/woff2.js +301 -0
  213. package/src/version.js +4 -0
@@ -0,0 +1,25 @@
1
+ // Print set-up: sizes at a dpi, bleed, safe margin, crop marks, die lines.
2
+ import type { Zones, FormatName, Format } from './presets.js';
3
+
4
+ /** Print set-up, px. */
5
+ export interface PrintInfo {
6
+ dpi: number;
7
+ unit: 'mm' | 'in';
8
+ trimWidth: number;
9
+ trimHeight: number;
10
+ bleed: number;
11
+ safe: number;
12
+ slug: number;
13
+ cropMarks: boolean;
14
+ cut?: import('./cut.js').Cut;
15
+ cutLine: boolean;
16
+ mm: number;
17
+ }
18
+
19
+
20
+ /** A length in a unit as px at a dpi. */
21
+ export function toPx(v: number, unit: string, dpi: number): number;
22
+ /** A cover's `format` as its canvas, print set-up and zones (undefined without one). */
23
+ export function resolveFormat(o: { format?: FormatName | (Partial<Format> & { width: number; height: number }); width?: number; height?: number; dpi?: number; bleed?: number | false; safe?: number; cropMarks?: boolean; cutLine?: boolean; cut?: import('./cut.js').Cut }): { width: number; height: number; format?: string; zones?: Zones; print?: PrintInfo } | undefined;
24
+ /** Crop marks (slug coordinates) and the die line (bleed-box coordinates). */
25
+ export function printMarks(p: PrintInfo, w: number, h: number): { marks: string; cut: string };
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Named formats: release covers, social posts, flyers and print stationery,
3
+ * alongside `aspect`.
4
+ *
5
+ * Screen formats are pixel sizes. Print formats are a trim size in
6
+ * millimetres or inches, rendered at `dpi` (300 by default) with a bleed
7
+ * (3 mm by default) and a safe area inside the trim; `cropMarks: true`
8
+ * adds a slug with crop marks round the bleed. The layers are drawn on the
9
+ * bleed box (trim plus bleed on every side), so backgrounds run off the
10
+ * edge, and text and icons keep their default margins inside the safe area.
11
+ *
12
+ * @module formats/print
13
+ */
14
+
15
+ import { FORMATS } from './presets.js';
16
+ import { scaleZones } from './zones.js';
17
+ import { cutPath } from './cut.js';
18
+
19
+ /** @param {number} v @param {string} unit @param {number} dpi @returns {number} px */
20
+ export function toPx(v, unit, dpi) {
21
+ return unit === 'mm' ? (v / 25.4) * dpi : unit === 'in' ? v * dpi : v;
22
+ }
23
+
24
+ /**
25
+ * The print set-up of a cover, in px.
26
+ *
27
+ * @typedef {object} PrintInfo
28
+ * @property {number} dpi
29
+ * @property {'mm' | 'in'} unit - The format's unit.
30
+ * @property {number} trimWidth - px.
31
+ * @property {number} trimHeight - px.
32
+ * @property {number} bleed - px on each side.
33
+ * @property {number} safe - px inside the trim on each side.
34
+ * @property {number} slug - px of slug outside the bleed (crop marks), 0 without.
35
+ * @property {boolean} cropMarks
36
+ * @property {import('./cut.js').Cut} [cut] - Die-cut shape.
37
+ * @property {boolean} cutLine - Draw the die line.
38
+ * @property {number} mm - px per millimetre.
39
+ */
40
+
41
+ /**
42
+ * Resolve a cover's `format` (a name or a custom `{ width, height, unit }`)
43
+ * to its canvas and print set-up.
44
+ *
45
+ * @param {any} o - Cover options: `format`, `dpi`, `bleed` (mm, or `false`), `safe` (mm), `cropMarks`, `cutLine`.
46
+ * @returns {{ width: number, height: number, print?: PrintInfo, format?: string, zones?: import('./index.js').Zones } | undefined}
47
+ */
48
+ export function resolveFormat(o) {
49
+ if (o.format == null) return undefined;
50
+ const f = typeof o.format === 'string' ? FORMATS[o.format] : { unit: 'px', ...o.format };
51
+ if (!f) throw new RangeError(`Unknown format "${o.format}". Formats: ${Object.keys(FORMATS).join(', ')}`);
52
+ const name = typeof o.format === 'string' ? o.format : undefined;
53
+ if (f.unit === 'px') {
54
+ // A screen format: one side given scales the zones and the other side.
55
+ const k = o.width && !o.height ? o.width / f.width : 1;
56
+ const zones = f.zones ? scaleZones(f.zones, k) : undefined;
57
+ return { width: Math.round(f.width * k), height: Math.round(f.height * k), format: name, zones };
58
+ }
59
+ const dpi = o.dpi ?? 300;
60
+ const mm = dpi / 25.4;
61
+ const trimWidth = Math.round(toPx(f.width, f.unit, dpi));
62
+ const trimHeight = Math.round(toPx(f.height, f.unit, dpi));
63
+ const bleed = o.bleed === false ? 0 : Math.round((o.bleed ?? f.bleed ?? 3) * mm);
64
+ const safe = Math.round((o.safe ?? f.safe ?? 3) * mm);
65
+ const cropMarks = Boolean(o.cropMarks);
66
+ const slug = cropMarks ? Math.round(Math.max(6, (o.bleed === false ? 0 : o.bleed ?? f.bleed ?? 3) + 3) * mm) : 0;
67
+ return {
68
+ width: trimWidth + 2 * bleed,
69
+ height: trimHeight + 2 * bleed,
70
+ format: name,
71
+ print: { dpi, unit: /** @type {'mm' | 'in'} */ (f.unit), trimWidth, trimHeight, bleed, safe, slug, cropMarks, cut: o.cut ?? f.cut, cutLine: Boolean(o.cutLine), mm },
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Crop marks in the slug, a die line, and the document's outer size.
77
+ *
78
+ * @param {PrintInfo} p
79
+ * @param {number} w - Bleed-box width, px.
80
+ * @param {number} h - Bleed-box height, px.
81
+ * @returns {{ marks: string, cut: string }} Markup: marks in slug coordinates (the bleed box starts at `slug`), and the die line in bleed-box coordinates.
82
+ */
83
+ export function printMarks(p, w, h) {
84
+ const n = (/** @type {number} */ v) => Math.round(v * 100) / 100;
85
+ let marks = '';
86
+ if (p.cropMarks) {
87
+ const s = p.slug;
88
+ const b = p.bleed;
89
+ const len = s - b - p.mm;
90
+ const gap = b + p.mm;
91
+ const stroke = Math.max(0.5, 0.25 * p.mm);
92
+ const xs = [s + b, s + w - b];
93
+ const ys = [s + b, s + h - b];
94
+ let d = '';
95
+ for (const x of xs) for (const y of ys) {
96
+ const dx = x < s + w / 2 ? -1 : 1;
97
+ const dy = y < s + h / 2 ? -1 : 1;
98
+ d += `M${n(x + dx * gap)} ${n(y)}h${n(dx * len)}M${n(x)} ${n(y + dy * gap)}v${n(dy * len)}`;
99
+ }
100
+ marks = `<path d="${d}" fill="none" stroke="#000" stroke-width="${n(stroke)}"/>`;
101
+ }
102
+ let cut = '';
103
+ if (p.cutLine) {
104
+ const x = p.bleed;
105
+ const y = p.bleed;
106
+ const cw = p.trimWidth;
107
+ const ch = p.trimHeight;
108
+ const custom = p.cut && typeof p.cut === 'object';
109
+ const shape = custom
110
+ ? `<path d="${cutPath(p.cut, { x, y, width: cw, height: ch }, p.mm).map((g) => g.cmd + g.pts.map(n).join(' ')).join('')}"`
111
+ : p.cut === 'circle'
112
+ ? `<ellipse cx="${n(x + cw / 2)}" cy="${n(y + ch / 2)}" rx="${n(cw / 2)}" ry="${n(ch / 2)}"`
113
+ : `<rect x="${n(x)}" y="${n(y)}" width="${n(cw)}" height="${n(ch)}"${p.cut === 'rounded' ? ` rx="${n(3 * p.mm)}"` : ''}`;
114
+ cut = `${shape} fill="none" stroke="#ec008c" stroke-width="${n(Math.max(0.5, 0.25 * p.mm))}" id="CutContour"/>`;
115
+ }
116
+ return { marks, cut };
117
+ }
@@ -0,0 +1,10 @@
1
+ // Safe zones.
2
+ import type { Zones } from './presets.js';
3
+ import type { PrintInfo } from './print.js';
4
+
5
+ /** A preview overlay of the zones: safe area dashed green, covered areas red, crops blue. */
6
+ export function zonesOverlay(z: Zones, w: number, h: number): string;
7
+ /** Zones scaled by `k`. */
8
+ export function scaleZones(z: Zones, k: number): Zones;
9
+ /** A cover's safe box, px. */
10
+ export function safeBox(c: { width: number; height: number; print?: PrintInfo; zones?: Zones }): { x: number; y: number; width: number; height: number } | undefined;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Safe zones: the box layouts fit into, scaling, and a preview overlay.
3
+ *
4
+ * @module formats/zones
5
+ */
6
+
7
+ /**
8
+ * A preview overlay of a format's zones: the safe area dashed, covered
9
+ * areas tinted red, device crops outlined.
10
+ *
11
+ * @param {import('./index.js').Zones} z
12
+ * @param {number} w @param {number} h
13
+ * @returns {string} Markup.
14
+ */
15
+ export function zonesOverlay(z, w, h) {
16
+ const n = (/** @type {number} */ v) => Math.round(v * 100) / 100;
17
+ const sw = n(Math.max(1, Math.min(w, h) / 360));
18
+ let out = '';
19
+ for (const c of z.crops ?? []) {
20
+ const [x, y, cw, ch] = c.rect;
21
+ if (c.name === 'circle') {
22
+ out += `<path d="M0 0H${n(w)}V${n(h)}H0ZM${n(x)} ${n(y + ch / 2)}a${n(cw / 2)} ${n(ch / 2)} 0 1 0 ${n(cw)} 0a${n(cw / 2)} ${n(ch / 2)} 0 1 0 ${n(-cw)} 0Z" fill="#000" fill-opacity="0.45" fill-rule="evenodd"/>`;
23
+ } else {
24
+ out += `<rect x="${n(x)}" y="${n(y)}" width="${n(cw)}" height="${n(ch)}" fill="none" stroke="#00b3ff" stroke-width="${sw}" stroke-dasharray="${n(sw * 2)} ${n(sw * 3)}"/>`;
25
+ }
26
+ }
27
+ for (const a of z.avoid ?? []) {
28
+ if (a.circle) out += `<circle cx="${n(a.circle[0])}" cy="${n(a.circle[1])}" r="${n(a.circle[2])}" fill="#ff2d55" fill-opacity="0.35" stroke="#ff2d55" stroke-width="${sw}"/>`;
29
+ if (a.rect) out += `<rect x="${n(a.rect[0])}" y="${n(a.rect[1])}" width="${n(a.rect[2])}" height="${n(a.rect[3])}" fill="#ff2d55" fill-opacity="0.3"/>`;
30
+ }
31
+ const [x, y, sw2, sh] = z.safe;
32
+ out += `<rect x="${n(x)}" y="${n(y)}" width="${n(sw2)}" height="${n(sh)}" fill="none" stroke="#18e05a" stroke-width="${n(sw * 2)}" stroke-dasharray="${n(sw * 8)} ${n(sw * 5)}"/>`;
33
+ return `<g pointer-events="none">${out}</g>`;
34
+ }
35
+
36
+ /**
37
+ * @param {import('./index.js').Zones} z @param {number} k
38
+ * @returns {import('./index.js').Zones}
39
+ */
40
+ export function scaleZones(z, k) {
41
+ if (k === 1) return z;
42
+ const r = (/** @type {number[]} */ a) => /** @type {any} */ (a.map((v) => Math.round(v * k)));
43
+ return { ...z, safe: r(z.safe), avoid: z.avoid?.map((a) => ({ ...a, rect: a.rect && r(a.rect), circle: a.circle && r(a.circle) })), crops: z.crops?.map((c) => ({ ...c, rect: r(c.rect) })) };
44
+ }
45
+
46
+
47
+ /**
48
+ * The safe box of a cover, px: a format's safe zone, or a print piece's
49
+ * trim less its safe margin. Undefined for a plain canvas.
50
+ *
51
+ * @param {{ width: number, height: number, print?: import('./index.js').PrintInfo, zones?: import('./index.js').Zones }} c
52
+ * @returns {{ x: number, y: number, width: number, height: number } | undefined}
53
+ */
54
+ export function safeBox(c) {
55
+ if (c.zones) {
56
+ const [x, y, width, height] = c.zones.safe;
57
+ return { x, y, width, height };
58
+ }
59
+ if (c.print) {
60
+ const i = c.print.bleed + c.print.safe;
61
+ return { x: i, y: i, width: c.width - 2 * i, height: c.height - 2 * i };
62
+ }
63
+ return undefined;
64
+ }
package/src/geometry.js CHANGED
@@ -106,7 +106,7 @@ export function len(value, canvas, fallback = 0) {
106
106
  }
107
107
 
108
108
  /** The nine anchors, as fractions of the free space along x and y. */
109
- const ANCHORS = Object.freeze({
109
+ export const ANCHORS = Object.freeze({
110
110
  'top-left': [0, 0], top: [0.5, 0], 'top-right': [1, 0],
111
111
  left: [0, 0.5], center: [0.5, 0.5], right: [1, 0.5],
112
112
  'bottom-left': [0, 1], bottom: [0.5, 1], 'bottom-right': [1, 1],
package/src/icons.js CHANGED
@@ -14,12 +14,8 @@
14
14
  * @module icons
15
15
  */
16
16
 
17
- import { readFileSync } from 'node:fs';
18
- import { createRequire } from 'node:module';
19
- import { join } from 'node:path';
20
- import { fileURLToPath } from 'node:url';
21
-
22
- const require = createRequire(import.meta.url);
17
+ import { node, settings, loadBytes } from './platform/index.js';
18
+ import { utf8 } from './platform/bytes.js';
23
19
 
24
20
  /** @type {Map<string, IconSet>} */
25
21
  const sets = new Map();
@@ -72,17 +68,53 @@ function loadSet(prefix) {
72
68
  let set = sets.get(prefix);
73
69
  if (set) return set;
74
70
  const pkg = `@iconify-json/${prefix}`;
71
+ const mod = node('module');
72
+ const fs = node('fs');
73
+ const path = node('path');
74
+ const url = node('url');
75
75
  let file;
76
76
  try {
77
- file = join(require.resolve(`${pkg}/package.json`, { paths: [process.cwd(), fileURLToPath(new URL('.', import.meta.url))] }), '..', 'icons.json');
77
+ if (!mod || !fs) throw new Error('no node');
78
+ const require = mod.createRequire(import.meta.url);
79
+ const here = String(import.meta.url).startsWith('file:') ? [url.fileURLToPath(new URL('.', import.meta.url))] : [];
80
+ file = path.join(require.resolve(`${pkg}/package.json`, { paths: [/** @type {any} */ (globalThis).process.cwd(), ...here] }), '..', 'icons.json');
78
81
  } catch {
79
- throw new Error(`Icon set "${prefix}" is not available. Install ${pkg}, or pass the set to registerIcons().`);
82
+ throw new Error(`Icon set "${prefix}" is not available. Install ${pkg}, or pass the set to registerIcons()${mod ? '' : ' (or configure({ icons }) with a URL to load sets from)'}.`);
80
83
  }
81
- set = /** @type {IconSet} */ (JSON.parse(readFileSync(file, 'utf8')));
84
+ set = /** @type {IconSet} */ (JSON.parse(fs.readFileSync(file, 'utf8')));
82
85
  sets.set(prefix, set);
83
86
  return set;
84
87
  }
85
88
 
89
+ /**
90
+ * Make sure the icon sets these names need are loaded: from Node's
91
+ * `node_modules`, or (in a browser or Worker) from `configure({ icons })`,
92
+ * a URL template like `'https://cdn.jsdelivr.net/npm/@iconify-json/{prefix}/icons.json'`
93
+ * or a function of the prefix. Names whose set can't be found are left for
94
+ * {@link resolveIcon} to report.
95
+ *
96
+ * @param {Iterable<string>} names - Candidate icon names (`prefix:name`).
97
+ * @returns {Promise<void>}
98
+ */
99
+ export async function preloadIcons(names) {
100
+ const prefixes = new Set();
101
+ for (const n of names) {
102
+ const m = /^([a-z0-9-]+):([a-z0-9-]+)$/i.exec(n);
103
+ if (m && !sets.has(m[1])) prefixes.add(m[1]);
104
+ }
105
+ for (const prefix of prefixes) {
106
+ if (node('module')) {
107
+ try { loadSet(prefix); } catch { /* reported when drawn */ }
108
+ continue;
109
+ }
110
+ const where = typeof settings.icons === 'function' ? settings.icons(prefix) : settings.icons?.replace('{prefix}', prefix);
111
+ if (!where) continue;
112
+ try {
113
+ registerIcons(JSON.parse(utf8(await loadBytes(where, 'icons'))));
114
+ } catch { /* reported when drawn */ }
115
+ }
116
+ }
117
+
86
118
  /**
87
119
  * Resolve an Iconify name (`'prefix:name'`) to its markup and viewBox.
88
120
  *
@@ -128,6 +160,8 @@ export function resolveIcon(name) {
128
160
  */
129
161
  export function parseSvgIcon(svg) {
130
162
  const open = svg.match(/<svg\b[^>]*>/i);
163
+ // Bare markup (a mark's paths on a 512 grid): wrap it.
164
+ if (!open && /^\s*<(path|g|circle|rect|polygon|ellipse|line|polyline)\b/i.test(svg)) return parseSvgIcon(`<svg viewBox="0 0 512 512">${svg}</svg>`);
131
165
  if (!open) throw new Error('Icon SVG must contain an <svg> element');
132
166
  const tag = open[0];
133
167
  const vb = tag.match(/viewBox\s*=\s*["']([^"']+)["']/i);
package/src/index.d.ts CHANGED
@@ -5,6 +5,36 @@
5
5
  */
6
6
 
7
7
  /** A CSS colour: hex (3/4/6/8 digits), `rgb()`/`rgba()` or `hsl()`/`hsla()`. Alpha is ignored. */
8
+ import type { LayerTransform, Effect, EffectKind, EffectMask } from './effects/index.js';
9
+ import type { Paint } from './effects/paint.js';
10
+ import type { TextPath } from './type/path.js';
11
+ import type { GroupLayer } from './render/group.js';
12
+ import type { DefsLayer, UseLayer } from './render/use.js';
13
+ import type { RepeatLayer } from './layers/repeat.js';
14
+ import type { TextSpan } from './type/spans.js';
15
+ import type { GlyphControl } from './type/glyphs.js';
16
+ import type { Cut } from './formats/cut.js';
17
+ import type { ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer } from './layers/index.js';
18
+ import type { Zones, PrintInfo, FormatName } from './formats/index.js';
19
+
20
+ export type { RepeatLayer, DefsLayer, UseLayer, TextSpan, GlyphControl, Cut };
21
+ export type { LayerTransform, Effect, EffectKind, EffectMask, Paint, TextPath, GroupLayer, ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer, Zones, PrintInfo, FormatName };
22
+ export type { ShapeSpec, TableCell } from './layers/index.js';
23
+ export type { Format } from './formats/index.js';
24
+ export type { LintIssue } from './lint/index.js';
25
+ export type { Settings } from './platform/index.js';
26
+
27
+ export { safeBox } from './formats/index.js';
28
+ export { rasterize } from './raster/index.js';
29
+ import type { RasterizeOptions } from './raster/index.js';
30
+ export type { RasterizeOptions };
31
+ export { lint } from './lint/index.js';
32
+ export { kit, KIT_FORMATS } from './kit/index.js';
33
+ export { FORMATS } from './formats/index.js';
34
+ export { configure } from './platform/index.js';
35
+ export { EFFECTS, EFFECT_OPTIONS } from './effects/index.js';
36
+ export { valueAt } from './animate/index.js';
37
+
8
38
  export type Color = string;
9
39
 
10
40
  /** Pixels, or a viewport unit of the canvas: `'6vmin'`, `'4vw'`, `'4vh'`, `'5vmax'`. */
@@ -67,15 +97,22 @@ export type PaletteInput =
67
97
  onAccent?: Color;
68
98
  };
69
99
 
70
- /** A font the text layers can use. TrueType, OpenType or WOFF (not WOFF2). */
100
+ /** A font the text layers can use. TrueType, OpenType or WOFF (WOFF2 only for live text). */
71
101
  export interface FontSource {
72
102
  /** Family name, as text layers ask for it (`font: 'Inter'`). */
73
103
  name: string;
74
- data: Uint8Array | ArrayBuffer;
75
- /** Default 400. */
76
- weight?: number;
104
+ /** The font's bytes; or give `src`. */
105
+ data?: Uint8Array | ArrayBuffer;
106
+ /** Where to load it from: a path (Node), a URL, or anything a `configure({ load })` hook reads. */
107
+ src?: string | URL;
108
+ /** Default 400. A variable font's range (`[100, 900]`) matches any weight in it and sets `wght`. */
109
+ weight?: number | [number, number];
77
110
  /** Default `'normal'`. */
78
111
  style?: 'normal' | 'italic';
112
+ /** Variable-font axes applied whenever this face is used. */
113
+ axes?: Record<string, number>;
114
+ /** Use this face for characters other fonts lack (glyph fallback). */
115
+ fallback?: boolean;
79
116
  }
80
117
 
81
118
  /** Options every layer takes. */
@@ -87,6 +124,18 @@ export interface BaseLayer {
87
124
  /** 0–1. */
88
125
  opacity?: number;
89
126
  blend?: BlendMode;
127
+ /** Move, turn, scale, skew or mirror the layer. (On a text layer, a case keyword is still its case.) */
128
+ transform?: LayerTransform | string;
129
+ /** Effects (filters, pixel effects, copies), applied in order; a kind alone takes its defaults. */
130
+ effects?: Effect | EffectKind | (Effect | EffectKind)[];
131
+ /** Layers whose luminance masks this one (white shows). */
132
+ mask?: Layer[];
133
+ /** Layers cut out of this one (knockout type and marks, punched holes). */
134
+ knockout?: Layer[];
135
+ /** Layers this one shows only inside. */
136
+ clip?: Layer[];
137
+ /** Draw the layer in a box: the print `'trim'` or `'safe'` area, or fractions of the canvas. */
138
+ area?: 'bleed' | 'trim' | 'safe' | Region;
90
139
  }
91
140
 
92
141
  export interface SolidLayer extends BaseLayer {
@@ -196,10 +245,15 @@ export interface StripesLayer extends BaseLayer {
196
245
 
197
246
  export interface ImageLayer extends BaseLayer {
198
247
  type: 'image';
199
- /** A local file path, a `data:` URI, or bytes. Never fetched from the network. */
200
- src: string | Uint8Array;
248
+ /** A local file path, a `data:` URI, bytes, or a URL (fetched in browsers and Workers; in Node only with `configure({ network: true })` or a loader). */
249
+ src: string | URL | Uint8Array | ArrayBuffer;
201
250
  /** Default `'cover'`. */
202
- fit?: 'cover' | 'contain';
251
+ fit?: 'cover' | 'contain' | 'stretch';
252
+ /** An explicit box in px or `'%'` (instead of `region`). */
253
+ x?: Length | `${number}%`;
254
+ y?: Length | `${number}%`;
255
+ width?: Length | `${number}%`;
256
+ height?: Length | `${number}%`;
203
257
  /** Crop focus: an anchor, or (with sharp) `'attention'` / `'entropy'`. Default `'center'`. */
204
258
  focus?: Anchor | 'attention' | 'entropy';
205
259
  /** Default the whole canvas. */
@@ -311,12 +365,19 @@ export interface IconLayer extends BaseLayer {
311
365
  svg?: string;
312
366
  /** A bare path `d` (filled with the colour). */
313
367
  path?: string;
314
- /** For `path`. Default `[0, 0, 24, 24]`. */
368
+ /** For `path`. Default `[0, 0, 24, 24]` (`[0, 0, 512, 512]` for `markup`). */
315
369
  viewBox?: [number, number, number, number];
370
+ /** Inner SVG markup (a mark's `<path>`s on a 512 grid), filled with `color`. */
371
+ markup?: string;
372
+ /** Top-left corner in px (a mark at `translate(x y) scale(size / 512)`), instead of anchoring. */
373
+ x?: Length | `${number}%`;
374
+ y?: Length | `${number}%`;
316
375
  /** Default `'24vmin'` (`'15vmin'` on a tile). */
317
376
  size?: Length;
318
377
  /** Default the accent on a tile, else `palette.text`. */
319
378
  color?: Color;
379
+ /** A paint instead of `color`: a gradient, tile or preset laid over the icon's grid. */
380
+ fill?: Paint;
320
381
  /** Override stroke icons' `stroke-width` (in icon units). */
321
382
  strokeWidth?: number;
322
383
  /** Default `'center'`. */
@@ -347,10 +408,60 @@ export interface TextLabel {
347
408
  gap?: Length;
348
409
  }
349
410
 
350
- export interface TextLayer extends BaseLayer {
411
+ export interface TextLayer extends Omit<BaseLayer, 'transform'> {
351
412
  type: 'text';
352
- content: string;
353
- /** Family. Default `'Inter'` (bundled). */
413
+ /** The text; an array is one line per item. (Optional with `spans`.) */
414
+ content?: string | string[];
415
+ /** First baseline x (px or `'%'`): places the text like SVG `<text x y>`, `align` acting as the anchor. */
416
+ x?: Length | `${number}%`;
417
+ y?: Length | `${number}%`;
418
+ /** Justify every line to this width (`textLength`). */
419
+ width?: Length;
420
+ /** Justify every line to `maxWidth`. */
421
+ justify?: boolean;
422
+ /** Default `'spacingAndGlyphs'` (scale the glyphs); `'spacing'` opens the letter-spacing. */
423
+ lengthAdjust?: 'spacingAndGlyphs' | 'spacing';
424
+ /** `'width'`: the block fills the width; `'lines'`: each line does; `'words'`: a word a line, the widest filling the width; `'wrap'`: wrap at the width, then fill it; `'shrink'`: shrink before wrapping. */
425
+ fit?: 'width' | 'lines' | 'words' | 'wrap' | 'shrink';
426
+ /** `'words'`: a word a line. */
427
+ split?: 'words';
428
+ /** Shrink the block to this height. */
429
+ maxHeight?: Length;
430
+ /** Mixed runs on the same lines, each with its own font, weight, size, colour (instead of `content`). */
431
+ spans?: (TextSpan | string)[];
432
+ /** Per-glyph scale, shift, turn and colour (arrays cycle). */
433
+ glyphs?: GlyphControl;
434
+ /** A family (or list) for characters the font lacks; then fonts registered with `fallback: true`, then built-in symbols. */
435
+ fallback?: string | string[];
436
+ /** Dashes on the stroke (`stroke`): `[on, off]` lengths, `'dashed'` or `'dotted'`. */
437
+ dash?: 'dashed' | 'dotted' | Length[];
438
+ /** Block extrusion: a depth, or its options. */
439
+ extrude?: Length | { depth?: Length; angle?: number; offset?: [Length, Length]; steps?: number; color?: Color; to?: Color };
440
+ /** `color: 'auto'` takes the dark colour above this backdrop luma (0–1). Default 0.72. */
441
+ autoThreshold?: number;
442
+ /** Stretch about the first baseline: `[sx, sy]`, or `sy`. */
443
+ stretch?: number | [number, number];
444
+ /** Variable-font axes: `{ wdth: 50, SOFT: 100, WONK: 1, opsz: 144 }`. */
445
+ axes?: Record<string, number>;
446
+ /** Outline colour (with `fill: 'none'` for outline type). */
447
+ stroke?: Color;
448
+ strokeWidth?: Length;
449
+ /** Keylines under the fill, widest first (a preset paint brings its own). */
450
+ strokes?: { color: Color; width: Length; fill?: Color; dash?: 'dashed' | 'dotted' | Length[] }[];
451
+ fill?: Paint;
452
+ /** Set the text along a path. */
453
+ path?: TextPath;
454
+ /** Along the path: 0–1, or px. Default 0.5 (centred). */
455
+ startOffset?: number | Length;
456
+ /** Space the text to fill the path (a ring); a length leaves that gap. */
457
+ fitPath?: boolean | Length;
458
+ /** Shift off the path. */
459
+ dy?: Length;
460
+ /** `'live'`: `<text>` with the font embedded by `@font-face` (or linked by URL). Default `'outline'`. */
461
+ render?: 'outline' | 'live';
462
+ /** For `color: 'auto'`. Default `['#ffffff', '#0b0b0b']`. */
463
+ autoColors?: [Color, Color];
464
+ /** Family. Default `'Inter'` (bundled). `'pixel'`: the built-in 5×7 face. */
354
465
  font?: string;
355
466
  /** Default 600. */
356
467
  weight?: number;
@@ -363,8 +474,8 @@ export interface TextLayer extends BaseLayer {
363
474
  lineHeight?: number;
364
475
  /** Em. Default -0.02. */
365
476
  letterSpacing?: number;
366
- /** Default `palette.text`. */
367
- color?: Color;
477
+ /** Default `palette.text`. `'auto'`: white or near-black, whichever reads better on what's drawn underneath. */
478
+ color?: Color | 'auto';
368
479
  /** Default from the anchor. */
369
480
  align?: 'left' | 'center' | 'right';
370
481
  /** Default `'bottom-left'`. */
@@ -381,7 +492,8 @@ export interface TextLayer extends BaseLayer {
381
492
  ellipsis?: string;
382
493
  /** Even out line lengths. Default true. */
383
494
  balance?: boolean;
384
- transform?: 'none' | 'uppercase' | 'lowercase';
495
+ /** The case (`'uppercase'`, `'lowercase'`), or a geometric transform. */
496
+ transform?: 'none' | 'uppercase' | 'lowercase' | LayerTransform;
385
497
  /** A small line above (a string, or its options). */
386
498
  label?: string | TextLabel;
387
499
  /** A pill or box behind the block. */
@@ -601,13 +713,15 @@ export interface RuleLayer extends BaseLayer {
601
713
  }
602
714
 
603
715
  export type Layer =
716
+ | GroupLayer | ShapeLayer | SvgLayer | TableLayer | BarcodeLayer | PerforationLayer | TearLayer | TextureLayer
604
717
  | InkLayer | DitherLayer | ContourLayer | WaveformLayer | TilesLayer | RuleLayer
605
718
  | DiagramLayer | PaperLayer | ScribbleLayer
606
719
  | SolidLayer | LinearLayer | RadialLayer | MeshLayer | StripesLayer | ImageLayer
607
- | GrainLayer | PatternLayer | ShapesLayer | IconLayer | TextLayer | FrameLayer;
720
+ | GrainLayer | PatternLayer | ShapesLayer | IconLayer | TextLayer | FrameLayer
721
+ | RepeatLayer | DefsLayer | UseLayer;
608
722
 
609
723
  /** Built-in recipe names. */
610
- export type RecipeName = 'aurora' | 'gridTitle' | 'iconTile' | 'bigType' | 'stripes' | 'duotone' | 'mark' | 'diagram' | 'cutout' | 'riso' | 'dither' | 'contour' | 'waveform' | 'bauhaus' | 'issue' | 'photo';
724
+ export type RecipeName = 'aurora' | 'gridTitle' | 'iconTile' | 'bigType' | 'stripes' | 'duotone' | 'mark' | 'diagram' | 'cutout' | 'riso' | 'dither' | 'contour' | 'waveform' | 'bauhaus' | 'issue' | 'photo' | 'brand';
611
725
 
612
726
  /** What a recipe function receives besides the options. */
613
727
  export interface RecipeContext {
@@ -619,6 +733,12 @@ export interface RecipeContext {
619
733
  u: number;
620
734
  /** The cover's seed. */
621
735
  seed?: string;
736
+ /** The safe box (a format's safe zone, a print piece's trim less its safe margin, else the canvas). */
737
+ safeX?: number; safeY?: number; safeWidth?: number; safeHeight?: number;
738
+ /** Print: bleed and safe margin in px, the trim size, px per mm, dpi. */
739
+ bleed?: number; safe?: number; trimWidth?: number; trimHeight?: number; mm?: number; dpi?: number;
740
+ /** The format's name, when one was given. */
741
+ format?: string;
622
742
  }
623
743
 
624
744
  /** A custom recipe. JSON recipes defined with {@link defineRecipe} also carry `recipeName` and `json`. */
@@ -645,6 +765,9 @@ export type RecipeValue =
645
765
  | { ramp: RecipeValue[] }
646
766
  | { taper: [RecipeValue, RecipeValue] }
647
767
  | { reverse: RecipeValue }
768
+ | { join: [RecipeValue] | [RecipeValue, RecipeValue] } | { upper: RecipeValue } | { lower: RecipeValue }
769
+ | { slice: [RecipeValue, RecipeValue] | [RecipeValue, RecipeValue, RecipeValue] }
770
+ | { keyframes: [number, RecipeValue][]; ease?: 'linear' | 'in' | 'out' | 'inOut' | 'step'; loop?: boolean | 'pingpong' }
648
771
  | { [key: string]: RecipeValue };
649
772
 
650
773
  /** A condition: an option name (set?), `{ name: value }` (equals; `mode` is the palette's), `not`, `all`, `any`. */
@@ -677,6 +800,10 @@ export interface RecipeJson {
677
800
  output?: { lossless?: boolean };
678
801
  /** Named values, resolved in order; referenced as `"$name"`. */
679
802
  vars?: Record<string, RecipeValue>;
803
+ /** The pages of a two-sided piece; layers pick theirs with `when: { page: 'back' }`. */
804
+ pages?: string[];
805
+ /** Fonts the recipe uses: a path beside the recipe file, or a URL. */
806
+ fonts?: { name: string; src: string; weight?: number | [number, number]; style?: 'normal' | 'italic'; axes?: Record<string, number>; fallback?: boolean }[];
680
807
  layers: RecipeJsonLayer[];
681
808
  }
682
809
 
@@ -742,6 +869,23 @@ export interface RecipeOptions {
742
869
  show?: 'blur';
743
870
  /** `mark` icon colour. `'auto'` (default): white whenever it clears 3:1 against everything under the icon, else near-black. */
744
871
  iconColor?: 'auto' | 'white' | 'black' | Color;
872
+ /** Brand and event data, for recipes that read them (`{name}`, `{lineup.0}`). */
873
+ name?: string;
874
+ tagline?: string;
875
+ /** A mark: `<path>` markup on a 512 grid. */
876
+ mark?: string;
877
+ photo?: string | Uint8Array | URL;
878
+ /** The photo's subject cut out (transparent, in the same frame). */
879
+ cutout?: string | Uint8Array | URL;
880
+ lines?: string[];
881
+ lineup?: string[];
882
+ dates?: string[];
883
+ tracklist?: (string | string[] | Record<string, string>)[];
884
+ venue?: string;
885
+ address?: string;
886
+ time?: string;
887
+ price?: string;
888
+ meta?: unknown;
745
889
  /** Any recipe layer by id: `mesh`, `grid`, `grain`, `bg`, `glow`, `rings`, `wash`, `stripes`, `spot`, `scrim`… */
746
890
  [layerId: string]: unknown;
747
891
  }
@@ -768,6 +912,28 @@ export interface CoverOptions extends RecipeOptions {
768
912
  radius?: Length;
769
913
  /** Preferred encoding, overriding the recipe's. */
770
914
  output?: { lossless?: boolean };
915
+ /** A named format (`'release'`, `'instagram-story'`, `'business-card'`, …; see `FORMATS`) or a custom size with a unit. */
916
+ format?: FormatName | { width: number; height: number; unit?: 'px' | 'mm' | 'in'; bleed?: number; safe?: number; zones?: Zones };
917
+ /** Print: dots per inch. Default 300. */
918
+ dpi?: number;
919
+ /** Print: bleed in mm (default the format's, usually 3), or `false`. */
920
+ bleed?: number | false;
921
+ /** Print: safe margin inside the trim, mm. */
922
+ safe?: number;
923
+ /** Print: crop marks in a slug round the bleed. */
924
+ cropMarks?: boolean;
925
+ /** Print: draw the die line (`CutContour`). */
926
+ cutLine?: boolean;
927
+ /** Print: the die-cut shape. */
928
+ cut?: Cut;
929
+ /** Draw the format's safe zones over the art (for previews). */
930
+ overlay?: boolean;
931
+ /** Which page of a two-sided piece to draw. Default `'front'`. */
932
+ page?: string;
933
+ /** `'live'`: every text layer as `<text>` with `@font-face`. */
934
+ textRender?: 'outline' | 'live';
935
+ /** Seconds: the length of an animated cover's loop (keyframes with `loop` wrap at it). */
936
+ loop?: number;
771
937
  }
772
938
 
773
939
  /** A resolved cover (from {@link cover}). */
@@ -782,6 +948,17 @@ export interface Cover {
782
948
  radius?: Length;
783
949
  /** Preferred encoding (from the recipe, or set here). */
784
950
  output?: { lossless?: boolean };
951
+ /** Print set-up, when the format is a print size. */
952
+ print?: PrintInfo;
953
+ /** The format's name. */
954
+ format?: string;
955
+ /** The format's safe zones, px. */
956
+ zones?: Zones;
957
+ page?: string;
958
+ pages?: string[];
959
+ overlay?: boolean;
960
+ loop?: number;
961
+ textRender?: 'outline' | 'live';
785
962
  }
786
963
 
787
964
  export interface RenderImageOptions {
@@ -821,10 +998,19 @@ export interface IconSet {
821
998
  /** Resolve a cover's size and palette and expand its recipe. Pure and synchronous. */
822
999
  export function cover(options: CoverOptions | Cover): Cover;
823
1000
 
824
- /** Render to an SVG string (text and icons as outlines; byte-identical for the same input). */
825
- export function renderSvg(input: CoverOptions | Cover): Promise<string>;
1001
+ /** Render to an SVG string (text and icons as outlines; byte-identical for the same input). `t`: the time to draw, in seconds, for keyframed layers. */
1002
+ export function renderSvg(input: CoverOptions | Cover, options?: { t?: number }): Promise<string>;
1003
+
1004
+ /** Render each page of a two-sided piece: `{ front, back }`. */
1005
+ export function renderPages(input: CoverOptions, options?: { pages?: string[] }): Promise<Record<string, string>>;
1006
+
1007
+ /** Render to an image in the browser (a canvas; no sharp). */
1008
+ export function renderBlob(input: CoverOptions | Cover, options?: RasterizeOptions): Promise<Blob>;
1009
+
1010
+ /** Register a font for every cover. */
1011
+ export function registerFont(font: FontSource): void;
826
1012
 
827
- /** Render to image bytes with sharp (a peer dependency). */
1013
+ /** Render to image bytes with sharp (a peer dependency; Node). */
828
1014
  export function renderImage(input: CoverOptions | Cover, options?: RenderImageOptions): Promise<Buffer>;
829
1015
 
830
1016
  /** The content-hash cache key for a cover and output options (40 hex chars). */