@arraypress/covers 1.1.0 → 1.4.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 (259) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +18 -809
  3. package/package.json +3 -1
  4. package/recipes/flyers/afterglow-motion.json +540 -0
  5. package/recipes/flyers/afterglow.json +70 -0
  6. package/recipes/flyers/arcfield-mirror-motion.json +397 -0
  7. package/recipes/flyers/arcfield-mirror.json +364 -0
  8. package/recipes/flyers/bauhaus-oktav.json +319 -0
  9. package/recipes/flyers/bauhaus-ritmo.json +735 -0
  10. package/recipes/flyers/bauhaus-volta.json +394 -0
  11. package/recipes/flyers/bloomfall.json +99 -0
  12. package/recipes/flyers/drag-solenne-motion.json +444 -0
  13. package/recipes/flyers/drag-solenne.json +426 -0
  14. package/recipes/flyers/fennick-patches.json +874 -0
  15. package/recipes/flyers/ferro-pass.json +398 -0
  16. package/recipes/flyers/ferrova.json +812 -0
  17. package/recipes/flyers/grid-poster.json +91 -0
  18. package/recipes/flyers/grid-rest-rhythm.json +125 -0
  19. package/recipes/flyers/groovy-honeycomb.json +816 -0
  20. package/recipes/flyers/groovy-marigold.json +479 -0
  21. package/recipes/flyers/groovy-peachfuzz.json +974 -0
  22. package/recipes/flyers/halden-orbit-motion.json +381 -0
  23. package/recipes/flyers/halden-orbit.json +356 -0
  24. package/recipes/flyers/halvard.json +120 -0
  25. package/recipes/flyers/kairo-weekender.json +696 -0
  26. package/recipes/flyers/linger-motion.json +518 -0
  27. package/recipes/flyers/linger.json +67 -0
  28. package/recipes/flyers/little-orchard.json +105 -0
  29. package/recipes/flyers/lofi-hexmoth.json +912 -0
  30. package/recipes/flyers/lofi-lowbit-motion.json +888 -0
  31. package/recipes/flyers/lofi-lowbit.json +880 -0
  32. package/recipes/flyers/lofi-nullpoint.json +3013 -0
  33. package/recipes/flyers/logofolio.json +60 -0
  34. package/recipes/flyers/lumio-stack-motion.json +648 -0
  35. package/recipes/flyers/lumio-stack.json +438 -0
  36. package/recipes/flyers/magazine-cover.json +168 -0
  37. package/recipes/flyers/magazine-extended.json +269 -0
  38. package/recipes/flyers/magazine-knockout.json +40 -0
  39. package/recipes/flyers/northfold-varsity.json +618 -0
  40. package/recipes/flyers/open-studio.json +69 -0
  41. package/recipes/flyers/orrin-merch.json +715 -0
  42. package/recipes/flyers/oxyde.json +113 -0
  43. package/recipes/flyers/pixel-mosaic.json +58 -0
  44. package/recipes/flyers/quiet-fields.json +610 -0
  45. package/recipes/flyers/scrapbook-halcyon.json +948 -0
  46. package/recipes/flyers/solenne-ticket.json +730 -0
  47. package/recipes/flyers/solvane-motion.json +576 -0
  48. package/recipes/flyers/solvane.json +98 -0
  49. package/recipes/flyers/subduct.json +99 -0
  50. package/recipes/flyers/sunvale-lineup.json +584 -0
  51. package/recipes/flyers/tidewell.json +91 -0
  52. package/recipes/flyers/tour-kairo.json +535 -0
  53. package/recipes/flyers/velora-stickers.json +885 -0
  54. package/recipes/flyers/wheatpaste-black.json +313 -0
  55. package/recipes/flyers/wheatpaste-mark.json +198 -0
  56. package/recipes/flyers/wheatpaste-torn.json +114 -0
  57. package/recipes/flyers/wheatpaste-wall.json +984 -0
  58. package/recipes/flyers/wheatpaste-words.json +233 -0
  59. package/recipes/release/aura.json +137 -0
  60. package/recipes/release/chrome.json +433 -0
  61. package/recipes/release/collage.json +372 -0
  62. package/recipes/release/duotone.json +113 -0
  63. package/recipes/release/film-still.json +102 -0
  64. package/recipes/release/headline.json +70 -0
  65. package/recipes/release/instant.json +170 -0
  66. package/recipes/release/label.json +364 -0
  67. package/recipes/release/liquid-glass.json +301 -0
  68. package/recipes/release/odd-tile-dark.json +688 -0
  69. package/recipes/release/odd-tile-dense.json +1056 -0
  70. package/recipes/release/odd-tile-shift.json +541 -0
  71. package/recipes/release/odd-tile-split.json +571 -0
  72. package/recipes/release/odd-tile.json +633 -0
  73. package/recipes/release/off-edge.json +96 -0
  74. package/recipes/release/plinth.json +794 -0
  75. package/recipes/release/riso.json +108 -0
  76. package/recipes/release/series/fluid-label.json +561 -0
  77. package/recipes/release/series/fluid-pixel.json +334 -0
  78. package/recipes/release/series/hairline.json +305 -0
  79. package/recipes/release/series/halo.json +282 -0
  80. package/recipes/release/series/mark-crop.json +135 -0
  81. package/recipes/release/series/mark-window.json +150 -0
  82. package/recipes/release/series/photo-label.json +475 -0
  83. package/recipes/release/series/smear-field.json +345 -0
  84. package/recipes/release/series/smear.json +202 -0
  85. package/recipes/release/series/spin.json +172 -0
  86. package/recipes/release/series/split-ghost.json +230 -0
  87. package/recipes/release/series/zoom.json +168 -0
  88. package/recipes/release/small-print.json +114 -0
  89. package/recipes/release/stack.json +156 -0
  90. package/recipes/release/swiss.json +117 -0
  91. package/schema/recipe.schema.json +638 -69
  92. package/src/animate/index.d.ts +9 -19
  93. package/src/animate/index.js +6 -111
  94. package/src/animate/keyframes.d.ts +21 -0
  95. package/src/animate/keyframes.js +119 -0
  96. package/src/animate/marquee.d.ts +30 -0
  97. package/src/animate/marquee.js +93 -0
  98. package/src/animate/morph.d.ts +8 -0
  99. package/src/animate/morph.js +99 -0
  100. package/src/animate/motion.d.ts +59 -0
  101. package/src/animate/motion.js +63 -0
  102. package/src/animate/sheet.d.ts +19 -0
  103. package/src/animate/sheet.js +61 -0
  104. package/src/animate/timing.d.ts +28 -0
  105. package/src/animate/timing.js +146 -0
  106. package/src/animate/tracks.d.ts +18 -0
  107. package/src/animate/tracks.js +139 -0
  108. package/src/diagram.js +4 -3
  109. package/src/extract.js +2 -2
  110. package/src/fonts.js +1 -2
  111. package/src/geometry.js +4 -34
  112. package/src/grid.js +1 -1
  113. package/src/handmade.js +9 -7
  114. package/src/icons.js +1 -2
  115. package/src/index.d.ts +47 -54
  116. package/src/index.js +5 -5
  117. package/src/inter-data.js +1 -1
  118. package/src/json-recipe.js +28 -7
  119. package/src/layer-options.js +26 -4
  120. package/src/layers/barcode.js +3 -3
  121. package/src/layers/geometry.js +5 -5
  122. package/src/layers/index.d.ts +6 -0
  123. package/src/layers/index.js +3 -0
  124. package/src/layers/mark-geometry.d.ts +15 -0
  125. package/src/layers/mark-geometry.js +46 -0
  126. package/src/layers/marks.d.ts +25 -0
  127. package/src/layers/marks.js +102 -0
  128. package/src/layers/mosaic.d.ts +32 -0
  129. package/src/layers/mosaic.js +155 -0
  130. package/src/layers/perforation.js +3 -3
  131. package/src/layers/repeat.js +2 -2
  132. package/src/layers/shape.d.ts +2 -2
  133. package/src/layers/shape.js +5 -5
  134. package/src/layers/soft.js +2 -2
  135. package/src/layers/svg.js +2 -3
  136. package/src/layers/table.js +3 -3
  137. package/src/layers/tear.d.ts +1 -1
  138. package/src/layers/tear.js +4 -4
  139. package/src/layers/texture.js +3 -3
  140. package/src/layers/util.js +15 -2
  141. package/src/layers.js +17 -32
  142. package/src/{effects/paint.js → paint/index.js} +5 -5
  143. package/src/{effects/paint-presets.d.ts → paint/presets.d.ts} +1 -1
  144. package/src/{effects/paint-presets.js → paint/presets.js} +1 -1
  145. package/src/{effects → paint}/tile.d.ts +1 -1
  146. package/src/{effects → paint}/tile.js +4 -4
  147. package/src/palette.js +1 -1
  148. package/src/platform/files.d.ts +6 -0
  149. package/src/platform/files.js +41 -0
  150. package/src/platform/harfbuzz.js +1 -1
  151. package/src/platform/index.d.ts +3 -3
  152. package/src/platform/index.js +6 -4
  153. package/src/platform/load.js +1 -2
  154. package/src/print.js +9 -7
  155. package/src/recipes.js +1 -1
  156. package/src/render/backdrop.js +2 -2
  157. package/src/render/grain.js +1 -1
  158. package/src/render/group.js +3 -3
  159. package/src/render/key.js +1 -2
  160. package/src/{effects → render}/transform.d.ts +2 -1
  161. package/src/{effects → render}/transform.js +17 -5
  162. package/src/render/use.d.ts +1 -1
  163. package/src/render/use.js +2 -2
  164. package/src/render/wrap.js +5 -60
  165. package/src/render.js +55 -18
  166. package/src/text.js +1 -1
  167. package/src/type/auto-color.js +1 -1
  168. package/src/type/fallback.js +1 -1
  169. package/src/type/glyphs.js +2 -2
  170. package/src/type/line.js +1 -1
  171. package/src/type/live.js +1 -1
  172. package/src/type/path.js +2 -2
  173. package/src/type/pixel.d.ts +0 -2
  174. package/src/type/pixel.js +3 -33
  175. package/src/type/spans.d.ts +5 -1
  176. package/src/type/spans.js +28 -5
  177. package/src/type/symbols.d.ts +1 -1
  178. package/src/type/symbols.js +20 -2
  179. package/src/type/typeset.js +25 -10
  180. package/src/version.js +1 -1
  181. package/src/color.js +0 -246
  182. package/src/effects/ascii.d.ts +0 -7
  183. package/src/effects/ascii.js +0 -93
  184. package/src/effects/buffer.d.ts +0 -60
  185. package/src/effects/buffer.js +0 -472
  186. package/src/effects/detail.d.ts +0 -12
  187. package/src/effects/detail.js +0 -124
  188. package/src/effects/digital.d.ts +0 -14
  189. package/src/effects/digital.js +0 -216
  190. package/src/effects/distort.d.ts +0 -20
  191. package/src/effects/distort.js +0 -314
  192. package/src/effects/dither.d.ts +0 -8
  193. package/src/effects/dither.js +0 -147
  194. package/src/effects/drop-shadow.d.ts +0 -4
  195. package/src/effects/drop-shadow.js +0 -45
  196. package/src/effects/extrude.d.ts +0 -5
  197. package/src/effects/extrude.js +0 -55
  198. package/src/effects/film.d.ts +0 -11
  199. package/src/effects/film.js +0 -204
  200. package/src/effects/glow.d.ts +0 -8
  201. package/src/effects/glow.js +0 -56
  202. package/src/effects/grade.d.ts +0 -30
  203. package/src/effects/grade.js +0 -274
  204. package/src/effects/halftone.d.ts +0 -8
  205. package/src/effects/halftone.js +0 -95
  206. package/src/effects/index.d.ts +0 -125
  207. package/src/effects/index.js +0 -277
  208. package/src/effects/ink.d.ts +0 -12
  209. package/src/effects/ink.js +0 -70
  210. package/src/effects/lens.d.ts +0 -14
  211. package/src/effects/lens.js +0 -277
  212. package/src/effects/light.d.ts +0 -10
  213. package/src/effects/light.js +0 -59
  214. package/src/effects/looks.d.ts +0 -9
  215. package/src/effects/looks.js +0 -74
  216. package/src/effects/mask.d.ts +0 -52
  217. package/src/effects/mask.js +0 -314
  218. package/src/effects/motion.d.ts +0 -6
  219. package/src/effects/motion.js +0 -60
  220. package/src/effects/newsprint.d.ts +0 -6
  221. package/src/effects/newsprint.js +0 -89
  222. package/src/effects/optics.d.ts +0 -10
  223. package/src/effects/optics.js +0 -213
  224. package/src/effects/options.d.ts +0 -6
  225. package/src/effects/options.js +0 -106
  226. package/src/effects/pixels.d.ts +0 -24
  227. package/src/effects/pixels.js +0 -138
  228. package/src/effects/polar.d.ts +0 -10
  229. package/src/effects/polar.js +0 -265
  230. package/src/effects/region.d.ts +0 -8
  231. package/src/effects/region.js +0 -136
  232. package/src/effects/round-border.d.ts +0 -5
  233. package/src/effects/round-border.js +0 -62
  234. package/src/effects/screen.d.ts +0 -14
  235. package/src/effects/screen.js +0 -212
  236. package/src/effects/sort.d.ts +0 -8
  237. package/src/effects/sort.js +0 -125
  238. package/src/effects/symmetry.d.ts +0 -6
  239. package/src/effects/symmetry.js +0 -74
  240. package/src/effects/tone.d.ts +0 -16
  241. package/src/effects/tone.js +0 -117
  242. package/src/effects/util.d.ts +0 -16
  243. package/src/effects/util.js +0 -85
  244. package/src/platform/bytes.d.ts +0 -22
  245. package/src/platform/bytes.js +0 -103
  246. package/src/platform/compress.d.ts +0 -12
  247. package/src/platform/compress.js +0 -81
  248. package/src/platform/node.d.ts +0 -26
  249. package/src/platform/node.js +0 -86
  250. package/src/png.js +0 -126
  251. package/src/raster/index.d.ts +0 -24
  252. package/src/raster/index.js +0 -171
  253. package/src/render/content.d.ts +0 -5
  254. package/src/render/content.js +0 -58
  255. package/src/render/tiles.d.ts +0 -6
  256. package/src/render/tiles.js +0 -77
  257. package/src/rng.js +0 -95
  258. package/src/svg.js +0 -69
  259. /package/src/{effects/paint.d.ts → paint/index.d.ts} +0 -0
@@ -1,22 +0,0 @@
1
- // Byte helpers without Buffer.
2
-
3
- /** Bytes as a Uint8Array view. */
4
- export function u8(v: Uint8Array | ArrayBuffer | ArrayBufferView): Uint8Array;
5
- /** Whether a value is bytes. */
6
- export function isBytes(v: unknown): v is Uint8Array | ArrayBuffer;
7
- /** Base64-encode bytes. */
8
- export function toBase64(bytes: Uint8Array | ArrayBuffer): string;
9
- /** Decode base64. */
10
- export function fromBase64(s: string): Uint8Array;
11
- /** Each byte as one character. */
12
- export function latin1(b: Uint8Array): string;
13
- /** Decode UTF-8. */
14
- export function utf8(b: Uint8Array | ArrayBuffer): string;
15
- /** Encode UTF-8. */
16
- export function encodeUtf8(s: string): Uint8Array;
17
- /** Join byte arrays. */
18
- export function concat(parts: Uint8Array[]): Uint8Array;
19
- /** A `data:` URI's bytes, or undefined when it isn't one. */
20
- export function fromDataUri(uri: string): Uint8Array | undefined;
21
- /** A hex digest with Web Crypto. */
22
- export function hexDigest(algo: 'SHA-1' | 'SHA-256', data: Uint8Array | string): Promise<string>;
@@ -1,103 +0,0 @@
1
- /**
2
- * Byte helpers that work the same in Node, browsers and Workers (no
3
- * `Buffer`). Where Node's `Buffer` is present it's used for speed; the
4
- * output is identical either way.
5
- *
6
- * @module bytes
7
- */
8
-
9
- const B = /** @type {any} */ (globalThis).Buffer;
10
-
11
- /**
12
- * Bytes as a `Uint8Array` view.
13
- *
14
- * @param {Uint8Array | ArrayBuffer | ArrayBufferView} v
15
- * @returns {Uint8Array}
16
- */
17
- export function u8(v) {
18
- if (v instanceof Uint8Array) return v;
19
- if (v instanceof ArrayBuffer) return new Uint8Array(v);
20
- if (ArrayBuffer.isView(v)) return new Uint8Array(v.buffer, v.byteOffset, v.byteLength);
21
- throw new TypeError('Expected bytes (Uint8Array or ArrayBuffer)');
22
- }
23
-
24
- /** @param {unknown} v @returns {v is Uint8Array | ArrayBuffer} */
25
- export const isBytes = (v) => v instanceof Uint8Array || v instanceof ArrayBuffer || ArrayBuffer.isView(v);
26
-
27
- /**
28
- * Base64-encode bytes.
29
- *
30
- * @param {Uint8Array | ArrayBuffer} bytes
31
- * @returns {string}
32
- */
33
- export function toBase64(bytes) {
34
- const b = u8(bytes);
35
- if (B) return B.from(b.buffer, b.byteOffset, b.byteLength).toString('base64');
36
- let out = '';
37
- for (let i = 0; i < b.length; i += 0x8000) out += String.fromCharCode.apply(null, /** @type {any} */ (b.subarray(i, i + 0x8000)));
38
- return btoa(out);
39
- }
40
-
41
- /**
42
- * Decode base64.
43
- *
44
- * @param {string} s
45
- * @returns {Uint8Array}
46
- */
47
- export function fromBase64(s) {
48
- if (B) return new Uint8Array(B.from(s, 'base64'));
49
- const bin = atob(s.replace(/\s+/g, ''));
50
- const out = new Uint8Array(bin.length);
51
- for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
52
- return out;
53
- }
54
-
55
- /** @param {Uint8Array} b @returns {string} Each byte as one char. */
56
- export function latin1(b) {
57
- let s = '';
58
- for (let i = 0; i < b.length; i++) s += String.fromCharCode(b[i]);
59
- return s;
60
- }
61
-
62
- /** @param {Uint8Array | ArrayBuffer} b @returns {string} */
63
- export const utf8 = (b) => new TextDecoder().decode(u8(b));
64
-
65
- /** @param {string} s @returns {Uint8Array} */
66
- export const encodeUtf8 = (s) => new TextEncoder().encode(s);
67
-
68
- /**
69
- * Join byte arrays.
70
- *
71
- * @param {Uint8Array[]} parts
72
- * @returns {Uint8Array}
73
- */
74
- export function concat(parts) {
75
- const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
76
- let o = 0;
77
- for (const p of parts) { out.set(p, o); o += p.length; }
78
- return out;
79
- }
80
-
81
- /**
82
- * Decode a `data:` URI.
83
- *
84
- * @param {string} uri
85
- * @returns {Uint8Array | undefined} Its bytes, or undefined when it isn't one.
86
- */
87
- export function fromDataUri(uri) {
88
- const m = /^data:([^,]*?),(.*)$/s.exec(uri);
89
- if (!m) return undefined;
90
- return /;base64$/i.test(m[1]) ? fromBase64(m[2]) : encodeUtf8(decodeURIComponent(m[2]));
91
- }
92
-
93
- /**
94
- * Hex digest with Web Crypto (Node 20+, browsers, Workers).
95
- *
96
- * @param {'SHA-1' | 'SHA-256'} algo
97
- * @param {Uint8Array | string} data
98
- * @returns {Promise<string>}
99
- */
100
- export async function hexDigest(algo, data) {
101
- const buf = await globalThis.crypto.subtle.digest(algo, typeof data === 'string' ? encodeUtf8(data) : u8(data));
102
- return Array.from(new Uint8Array(buf), (x) => x.toString(16).padStart(2, '0')).join('');
103
- }
@@ -1,12 +0,0 @@
1
- // zlib on every platform.
2
-
3
- /** zlib-deflate synchronously (Node), else undefined. */
4
- export function deflateSync(data: Uint8Array): Uint8Array | undefined;
5
- /** zlib-deflate (Node's zlib, else CompressionStream). */
6
- export function deflate(data: Uint8Array): Promise<Uint8Array>;
7
- /** zlib-inflate. */
8
- export function inflate(data: Uint8Array): Promise<Uint8Array>;
9
- /** Brotli-decompress synchronously (Node), else undefined. */
10
- export function unbrotliSync(data: Uint8Array): Uint8Array | undefined;
11
- /** Brotli-decompress (Node's zlib, else DecompressionStream('brotli') where there is one), else undefined. */
12
- export function unbrotli(data: Uint8Array): Promise<Uint8Array | undefined>;
@@ -1,81 +0,0 @@
1
- /**
2
- * zlib deflate and inflate: Node's zlib, else `CompressionStream` and
3
- * `DecompressionStream` (browsers, Workers). For PNG textures and WOFF.
4
- *
5
- * @module platform/compress
6
- */
7
-
8
- import { node } from './node.js';
9
-
10
- /**
11
- * zlib-deflate synchronously, when the platform can (Node).
12
- *
13
- * @param {Uint8Array} data
14
- * @returns {Uint8Array | undefined}
15
- */
16
- export function deflateSync(data) {
17
- const z = node('zlib');
18
- return z ? z.deflateSync(data, { level: 9 }) : undefined;
19
- }
20
-
21
- /**
22
- * zlib-deflate: Node's zlib, else `CompressionStream` (browsers, Workers).
23
- * The two may differ in bytes but both are valid zlib streams.
24
- *
25
- * @param {Uint8Array} data
26
- * @returns {Promise<Uint8Array>}
27
- */
28
- export async function deflate(data) {
29
- return deflateSync(data) ?? stream(data, new CompressionStream('deflate'));
30
- }
31
-
32
- /**
33
- * zlib-inflate (WOFF tables).
34
- *
35
- * @param {Uint8Array} data
36
- * @returns {Promise<Uint8Array>}
37
- */
38
- export async function inflate(data) {
39
- const z = node('zlib');
40
- return z ? z.inflateSync(data) : stream(data, new DecompressionStream('deflate'));
41
- }
42
-
43
- /**
44
- * @param {Uint8Array} data
45
- * @param {TransformStream} ts
46
- * @returns {Promise<Uint8Array>}
47
- */
48
- async function stream(data, ts) {
49
- const out = new Response(new Blob([/** @type {any} */ (data)]).stream().pipeThrough(ts));
50
- return new Uint8Array(await out.arrayBuffer());
51
- }
52
-
53
- /**
54
- * Brotli-decompress synchronously, when the platform can (Node's zlib).
55
- *
56
- * @param {Uint8Array} data
57
- * @returns {Uint8Array | undefined}
58
- */
59
- export function unbrotliSync(data) {
60
- const z = node('zlib');
61
- if (!z?.brotliDecompressSync) return undefined;
62
- const out = z.brotliDecompressSync(data);
63
- return new Uint8Array(out.buffer, out.byteOffset, out.byteLength);
64
- }
65
-
66
- /**
67
- * Brotli-decompress (WOFF2): Node's zlib, else `DecompressionStream('brotli')`
68
- * where the runtime has it. Undefined where neither can.
69
- *
70
- * @param {Uint8Array} data
71
- * @returns {Promise<Uint8Array | undefined>}
72
- */
73
- export async function unbrotli(data) {
74
- const sync = unbrotliSync(data);
75
- if (sync) return sync;
76
- try {
77
- return await stream(data, new DecompressionStream(/** @type {any} */ ('brotli')));
78
- } catch {
79
- return undefined;
80
- }
81
- }
@@ -1,26 +0,0 @@
1
- // Node's built-ins, without importing node:.
2
-
3
- /** Node modules covers uses. */
4
- export interface NodeModules {
5
- fs?: typeof import('node:fs');
6
- fsp?: typeof import('node:fs/promises');
7
- zlib?: typeof import('node:zlib');
8
- path?: typeof import('node:path');
9
- module?: typeof import('node:module');
10
- url?: typeof import('node:url');
11
- }
12
-
13
- /** The global `process`, where there is one. */
14
- export const proc: NodeJS.Process | undefined;
15
- /** This module was loaded from disk. */
16
- export const FROM_DISK: boolean;
17
- /** A Node built-in, when there is one. */
18
- export function node<K extends 'fs' | 'fs/promises' | 'zlib' | 'path' | 'module' | 'url'>(name: K): K extends 'fs' ? typeof import('node:fs') : K extends 'fs/promises' ? typeof import('node:fs/promises') : K extends 'zlib' ? typeof import('node:zlib') : K extends 'path' ? typeof import('node:path') : K extends 'module' ? typeof import('node:module') : typeof import('node:url') | undefined;
19
- /** Install Node's modules (what `@arraypress/covers/node` does). */
20
- export function installNode(mods: NodeModules): void;
21
- /** Whether this is Node with a file system. */
22
- export function isNode(): boolean;
23
- /** Read a text file synchronously (Node). */
24
- export function readTextSync(path: string | URL): string;
25
- /** A file shipped with the package, from disk (Node), or undefined. */
26
- export function packageFileSync(rel: string): Uint8Array | undefined;
@@ -1,86 +0,0 @@
1
- /**
2
- * Node's built-ins, found without importing `node:` (so bundles for
3
- * browsers and Workers never see them): `process.getBuiltinModule` on Node
4
- * 20.16+, or the modules `@arraypress/covers/node` installs.
5
- *
6
- * @module platform/node
7
- */
8
-
9
- /**
10
- * @typedef {object} NodeModules
11
- * @property {any} [fs] - `node:fs`.
12
- * @property {any} [fsp] - `node:fs/promises`.
13
- * @property {any} [zlib] - `node:zlib`.
14
- * @property {any} [path] - `node:path`.
15
- * @property {any} [module] - `node:module`.
16
- * @property {any} [url] - `node:url`.
17
- */
18
-
19
- /** @type {NodeModules} */
20
- let nodeModules = {};
21
-
22
- /** The global `process`, where there is one. */
23
- export const proc = /** @type {any} */ (globalThis).process;
24
- /** Built-in lookup without a static import (Node 20.16+, Deno, Bun). */
25
- const builtin = typeof proc?.getBuiltinModule === 'function' && proc.versions?.node ? proc.getBuiltinModule.bind(proc) : undefined;
26
- /** This module was loaded from disk (Node, Deno, Bun), not a bundle or a page. */
27
- export const FROM_DISK = String(import.meta.url).startsWith('file:');
28
-
29
- /**
30
- * A Node built-in, when there is one.
31
- *
32
- * @param {'fs' | 'fs/promises' | 'zlib' | 'path' | 'module' | 'url'} name
33
- * @returns {any}
34
- */
35
- export function node(name) {
36
- const key = name === 'fs/promises' ? 'fsp' : name;
37
- const have = /** @type {any} */ (nodeModules)[key];
38
- if (have) return have;
39
- if (!builtin) return undefined;
40
- try {
41
- return (/** @type {any} */ (nodeModules)[key] = builtin(`node:${name}`));
42
- } catch {
43
- return undefined;
44
- }
45
- }
46
-
47
- /**
48
- * Install Node's modules (what `@arraypress/covers/node` does).
49
- *
50
- * @param {NodeModules} mods
51
- * @returns {void}
52
- */
53
- export function installNode(mods) {
54
- nodeModules = { ...nodeModules, ...mods };
55
- }
56
-
57
- /** Whether this looks like Node with a file system we can read. */
58
- export const isNode = () => Boolean(node('fs')) && Boolean(proc?.versions?.node);
59
-
60
- /**
61
- * Read a text file synchronously (Node only; `loadRecipe`).
62
- *
63
- * @param {string | URL} path
64
- * @returns {string}
65
- */
66
- export function readTextSync(path) {
67
- const fs = node('fs');
68
- if (!fs) throw new Error('Reading a file synchronously needs Node. In a browser or Worker, fetch the JSON and pass it to defineRecipe().');
69
- return fs.readFileSync(path, 'utf8');
70
- }
71
-
72
- /**
73
- * Read a file that ships with this package (Node, from disk), or undefined.
74
- *
75
- * @param {string} rel - Relative to the package root (`'fonts/inter-latin-400.woff'`).
76
- * @returns {Uint8Array | undefined}
77
- */
78
- export function packageFileSync(rel) {
79
- const fs = node('fs');
80
- if (!fs || !FROM_DISK) return undefined;
81
- try {
82
- return fs.readFileSync(new URL(`../../${rel}`, import.meta.url));
83
- } catch {
84
- return undefined;
85
- }
86
- }
package/src/png.js DELETED
@@ -1,126 +0,0 @@
1
- /**
2
- * A minimal PNG encoder (8-bit greyscale, RGB or RGBA; no filtering), so
3
- * layers that are pixels by nature (dithers, print textures) can embed
4
- * themselves in an SVG without sharp.
5
- *
6
- * @module png
7
- */
8
-
9
- import { deflateSync, deflate } from './platform/index.js';
10
- import { concat, toBase64 } from './platform/bytes.js';
11
-
12
- /** CRC-32 table (PNG's polynomial). */
13
- const CRC = (() => {
14
- const t = new Uint32Array(256);
15
- for (let n = 0; n < 256; n++) {
16
- let c = n;
17
- for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
18
- t[n] = c >>> 0;
19
- }
20
- return t;
21
- })();
22
-
23
- /** @param {Uint8Array} bytes @returns {number} */
24
- function crc32(bytes) {
25
- let c = 0xffffffff;
26
- for (const b of bytes) c = CRC[(c ^ b) & 0xff] ^ (c >>> 8);
27
- return (c ^ 0xffffffff) >>> 0;
28
- }
29
-
30
- /** @param {string} type @param {Uint8Array} data @returns {Uint8Array} */
31
- function chunk(type, data) {
32
- const out = new Uint8Array(12 + data.length);
33
- const v = new DataView(out.buffer);
34
- v.setUint32(0, data.length);
35
- for (let i = 0; i < 4; i++) out[4 + i] = type.charCodeAt(i);
36
- out.set(data, 8);
37
- v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
38
- return out;
39
- }
40
-
41
- /**
42
- * The filtered scanlines and header of a PNG.
43
- *
44
- * @param {Uint8Array} pixels @param {number} width @param {number} height @param {1 | 3 | 4} channels
45
- * @returns {{ raw: Uint8Array, ihdr: Uint8Array }}
46
- */
47
- function scanlines(pixels, width, height, channels) {
48
- const stride = width * channels;
49
- const raw = new Uint8Array((stride + 1) * height);
50
- for (let y = 0; y < height; y++) {
51
- raw[y * (stride + 1)] = 0;
52
- raw.set(pixels.subarray(y * stride, y * stride + stride), y * (stride + 1) + 1);
53
- }
54
- const ihdr = new Uint8Array(13);
55
- const v = new DataView(ihdr.buffer);
56
- v.setUint32(0, width);
57
- v.setUint32(4, height);
58
- ihdr[8] = 8;
59
- ihdr[9] = /** @type {Record<number, number>} */ ({ 1: 0, 3: 2, 4: 6 })[channels];
60
- return { raw, ihdr };
61
- }
62
-
63
- const SIGNATURE = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
64
-
65
- /**
66
- * zlib "stored" blocks: valid, uncompressed. The synchronous fallback where
67
- * the platform has no synchronous deflate (browsers, Workers).
68
- *
69
- * @param {Uint8Array} data
70
- * @returns {Uint8Array}
71
- */
72
- function stored(data) {
73
- const parts = [new Uint8Array([0x78, 0x01])];
74
- for (let i = 0; i < data.length || i === 0; i += 65535) {
75
- const block = data.subarray(i, i + 65535);
76
- const last = i + 65535 >= data.length ? 1 : 0;
77
- parts.push(new Uint8Array([last, block.length & 255, block.length >> 8, ~block.length & 255, (~block.length >> 8) & 255]), block);
78
- if (last) break;
79
- }
80
- let a = 1;
81
- let b = 0;
82
- for (const x of data) { a = (a + x) % 65521; b = (b + a) % 65521; }
83
- const adler = new Uint8Array(4);
84
- new DataView(adler.buffer).setUint32(0, ((b << 16) | a) >>> 0);
85
- parts.push(adler);
86
- return concat(parts);
87
- }
88
-
89
- /**
90
- * Encode pixels as a PNG (synchronously: zlib in Node, stored blocks where
91
- * there's no synchronous deflate; {@link encodePngAsync} compresses
92
- * everywhere).
93
- *
94
- * @param {Uint8Array} pixels - Row-major samples.
95
- * @param {number} width
96
- * @param {number} height
97
- * @param {1 | 3 | 4} [channels=4] - Grey, RGB or RGBA.
98
- * @returns {Uint8Array} PNG bytes.
99
- *
100
- * @example
101
- * encodePng(new Uint8Array([0, 255]), 2, 1, 1); // a black and a white pixel
102
- */
103
- export function encodePng(pixels, width, height, channels = 4) {
104
- const { raw, ihdr } = scanlines(pixels, width, height, channels);
105
- return concat([SIGNATURE, chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw) ?? stored(raw)), chunk('IEND', new Uint8Array(0))]);
106
- }
107
-
108
- /**
109
- * Encode pixels as a PNG, compressed on every platform (Node's zlib, else
110
- * `CompressionStream`).
111
- *
112
- * @param {Uint8Array} pixels @param {number} width @param {number} height @param {1 | 3 | 4} [channels=4]
113
- * @returns {Promise<Uint8Array>}
114
- */
115
- export async function encodePngAsync(pixels, width, height, channels = 4) {
116
- const { raw, ihdr } = scanlines(pixels, width, height, channels);
117
- return concat([SIGNATURE, chunk('IHDR', ihdr), chunk('IDAT', await deflate(raw)), chunk('IEND', new Uint8Array(0))]);
118
- }
119
-
120
- /**
121
- * A PNG as a data URI.
122
- *
123
- * @param {Uint8Array} png
124
- * @returns {string}
125
- */
126
- export const pngUri = (png) => `data:image/png;base64,${toBase64(png)}`;
@@ -1,24 +0,0 @@
1
- // Rasterising in the browser.
2
-
3
- /** Browser raster options. */
4
- export interface RasterizeOptions {
5
- /** Default `'png'`. */
6
- format?: 'png' | 'jpeg' | 'webp';
7
- /** 0–1 (JPEG, WebP). Default 0.9. */
8
- quality?: number;
9
- /** Output width or height, px (the other follows), or a `scale`. */
10
- width?: number;
11
- height?: number;
12
- scale?: number;
13
- /** A colour under the art (JPEG defaults to the page colour). */
14
- background?: string;
15
- }
16
-
17
- /** Rasterise an SVG string in a browser page. */
18
- export function rasterize(svg: string, options?: RasterizeOptions): Promise<Blob>;
19
- /** RGBA pixels of an SVG at a size: sharp in Node, a canvas in a page, undefined elsewhere. */
20
- export function pixelsOf(svg: string, w: number, h: number, sharp?: unknown): Promise<Uint8Array | undefined>;
21
- /** Encode RGBA pixels for embedding: JPEG where opaque (unless `lossless`), else PNG. */
22
- export function encodePixels(data: Uint8Array | Uint8ClampedArray, w: number, h: number, o?: { opaque?: boolean; lossless?: boolean; quality?: number }, sharp?: unknown): Promise<{ mime: 'image/jpeg' | 'image/png'; bytes: Uint8Array }>;
23
- /** Pixels through a real JPEG encoder and back; undefined where none is available. */
24
- export function jpegRoundTrip(data: Uint8ClampedArray, w: number, h: number, quality: number, subsample: boolean, sharp?: unknown): Promise<Uint8ClampedArray | undefined>;
@@ -1,171 +0,0 @@
1
- /**
2
- * Rasterising without sharp: an SVG drawn into a canvas in the browser,
3
- * encoded as PNG, JPEG or WebP. `renderImage()` stays the Node path (sharp,
4
- * with its grain and padded-blur handling); this is the browser one.
5
- *
6
- * The SVG is decoded by the browser as an `<img>` (so it needs a page:
7
- * `document` and `Image`), drawn onto an `OffscreenCanvas` when there is
8
- * one (else a `<canvas>`), and encoded with `convertToBlob` / `toBlob`.
9
- *
10
- * @module raster
11
- */
12
-
13
- const MIMES = /** @type {const} */ ({ png: 'image/png', jpeg: 'image/jpeg', webp: 'image/webp' });
14
-
15
- /**
16
- * Decode an SVG string into a drawable image (browser).
17
- *
18
- * @param {string} svg
19
- * @returns {Promise<HTMLImageElement>}
20
- */
21
- async function decodeSvg(svg) {
22
- const g = /** @type {any} */ (globalThis);
23
- if (typeof g.Image !== 'function' || typeof g.document === 'undefined') {
24
- throw new Error('rasterize() needs a browser page (Image and canvas). In Node use renderImage(); in a Worker return the SVG, or rasterise it with a wasm renderer.');
25
- }
26
- const url = URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
27
- try {
28
- const img = new g.Image();
29
- img.decoding = 'sync';
30
- img.src = url;
31
- await img.decode();
32
- return img;
33
- } finally {
34
- // Revoked after decode: the decoded image keeps its pixels.
35
- setTimeout(() => URL.revokeObjectURL(url), 0);
36
- }
37
- }
38
-
39
- /**
40
- * A 2D canvas of a size.
41
- *
42
- * @param {number} w @param {number} h
43
- * @returns {{ canvas: any, ctx: CanvasRenderingContext2D }}
44
- */
45
- function canvasOf(w, h) {
46
- const g = /** @type {any} */ (globalThis);
47
- const canvas = typeof g.OffscreenCanvas === 'function' ? new g.OffscreenCanvas(w, h) : Object.assign(g.document.createElement('canvas'), { width: w, height: h });
48
- return { canvas, ctx: canvas.getContext('2d') };
49
- }
50
-
51
- /**
52
- * Rasterise an SVG in the browser.
53
- *
54
- * @param {string} svg - An SVG document (from `renderSvg`).
55
- * @param {{ format?: 'png' | 'jpeg' | 'webp', quality?: number, width?: number, height?: number, scale?: number, background?: string }} [options]
56
- * @returns {Promise<Blob>}
57
- *
58
- * @example
59
- * const blob = await rasterize(await renderSvg(cover), { format: 'webp', quality: 0.9 });
60
- * img.src = URL.createObjectURL(blob);
61
- */
62
- export async function rasterize(svg, options = {}) {
63
- const format = options.format ?? 'png';
64
- if (!Object.hasOwn(MIMES, format)) throw new TypeError(`Unknown format "${format}" (png, jpeg, webp)`);
65
- const vw = Number(/\bwidth="([\d.]+)"/.exec(svg)?.[1] ?? 1200);
66
- const vh = Number(/\bheight="([\d.]+)"/.exec(svg)?.[1] ?? 1200);
67
- const scale = options.scale ?? (options.width ? options.width / vw : options.height ? options.height / vh : 1);
68
- const w = Math.max(1, Math.round(vw * scale));
69
- const h = Math.max(1, Math.round(vh * scale));
70
- const img = await decodeSvg(svg);
71
- const { canvas, ctx } = canvasOf(w, h);
72
- if (format === 'jpeg' || options.background) {
73
- ctx.fillStyle = options.background ?? /fill="(#[0-9a-f]{6})"/i.exec(svg)?.[1] ?? '#ffffff';
74
- ctx.fillRect(0, 0, w, h);
75
- }
76
- ctx.drawImage(img, 0, 0, w, h);
77
- const type = MIMES[format];
78
- const quality = options.quality ?? (format === 'png' ? undefined : 0.9);
79
- if (typeof canvas.convertToBlob === 'function') return canvas.convertToBlob({ type, quality });
80
- return new Promise((resolve, reject) => canvas.toBlob((/** @type {Blob | null} */ b) => (b ? resolve(b) : reject(new Error('Canvas encoding failed'))), type, quality));
81
- }
82
-
83
- /**
84
- * RGBA pixels of an SVG at a size: sharp in Node, a canvas in a page.
85
- * Undefined where neither is available (a Worker).
86
- *
87
- * @param {string} svg
88
- * @param {number} w
89
- * @param {number} h
90
- * @param {any} [sharp] - The sharp module, when loaded.
91
- * @returns {Promise<Uint8Array | undefined>}
92
- */
93
- export async function pixelsOf(svg, w, h, sharp) {
94
- if (sharp) {
95
- const out = await sharp(new TextEncoder().encode(svg)).resize(w, h, { fit: 'fill' }).ensureAlpha().raw().toBuffer();
96
- return new Uint8Array(out.buffer, out.byteOffset, out.byteLength);
97
- }
98
- const g = /** @type {any} */ (globalThis);
99
- if (typeof g.Image !== 'function' || typeof g.document === 'undefined') return undefined;
100
- const img = await decodeSvg(svg);
101
- const { ctx } = canvasOf(w, h);
102
- ctx.drawImage(img, 0, 0, w, h);
103
- return new Uint8Array(ctx.getImageData(0, 0, w, h).data.buffer);
104
- }
105
-
106
- /**
107
- * Encode RGBA pixels for embedding: JPEG where they're opaque (smaller),
108
- * PNG where they aren't or where `lossless` (flat colour, crisp cells).
109
- * sharp in Node, a canvas in a page, else the in-library PNG encoder.
110
- *
111
- * @param {Uint8Array | Uint8ClampedArray} data - RGBA, unpremultiplied.
112
- * @param {number} w
113
- * @param {number} h
114
- * @param {{ opaque?: boolean, lossless?: boolean, quality?: number }} [o]
115
- * @param {any} [sharp]
116
- * @returns {Promise<{ mime: 'image/jpeg' | 'image/png', bytes: Uint8Array }>}
117
- */
118
- export async function encodePixels(data, w, h, o = {}, sharp) {
119
- const jpeg = Boolean(o.opaque) && !o.lossless;
120
- const quality = o.quality ?? 92;
121
- if (sharp) {
122
- let img = sharp(data, { raw: { width: w, height: h, channels: 4 } });
123
- if (o.opaque) img = img.removeAlpha();
124
- const out = jpeg ? await img.jpeg({ quality, chromaSubsampling: '4:4:4' }).toBuffer() : await img.png({ compressionLevel: 6 }).toBuffer();
125
- return { mime: jpeg ? 'image/jpeg' : 'image/png', bytes: new Uint8Array(out.buffer, out.byteOffset, out.byteLength) };
126
- }
127
- const g = /** @type {any} */ (globalThis);
128
- if (typeof g.OffscreenCanvas === 'function' || typeof g.document !== 'undefined') {
129
- const { canvas, ctx } = canvasOf(w, h);
130
- ctx.putImageData(new g.ImageData(new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength), w, h), 0, 0);
131
- const type = jpeg ? 'image/jpeg' : 'image/png';
132
- const blob = typeof canvas.convertToBlob === 'function'
133
- ? await canvas.convertToBlob({ type, quality: quality / 100 })
134
- : await new Promise((resolve, reject) => canvas.toBlob((/** @type {Blob | null} */ b) => (b ? resolve(b) : reject(new Error('Canvas encoding failed'))), type, quality / 100));
135
- return { mime: type, bytes: new Uint8Array(await blob.arrayBuffer()) };
136
- }
137
- const { encodePngAsync } = await import('../png.js');
138
- return { mime: 'image/png', bytes: await encodePngAsync(new Uint8Array(data.buffer, data.byteOffset, data.byteLength), w, h, 4) };
139
- }
140
-
141
- /**
142
- * Pixels through a real JPEG encoder and back (compression artefacts):
143
- * libjpeg through sharp in Node, the browser's own encoder in a page.
144
- * Undefined where neither is available.
145
- *
146
- * @param {Uint8ClampedArray} data - RGBA (alpha ignored).
147
- * @param {number} w
148
- * @param {number} h
149
- * @param {number} quality - 1–100.
150
- * @param {boolean} subsample - 4:2:0 chroma (blocky colour).
151
- * @param {any} [sharp]
152
- * @returns {Promise<Uint8ClampedArray | undefined>} RGBA, opaque.
153
- */
154
- export async function jpegRoundTrip(data, w, h, quality, subsample, sharp) {
155
- if (sharp) {
156
- const jpg = await sharp(data, { raw: { width: w, height: h, channels: 4 } }).removeAlpha().jpeg({ quality, chromaSubsampling: subsample ? '4:2:0' : '4:4:4' }).toBuffer();
157
- const out = await sharp(jpg).ensureAlpha().raw().toBuffer();
158
- return new Uint8ClampedArray(out.buffer, out.byteOffset, out.byteLength);
159
- }
160
- const g = /** @type {any} */ (globalThis);
161
- if (typeof g.createImageBitmap !== 'function' || (typeof g.OffscreenCanvas !== 'function' && typeof g.document === 'undefined')) return undefined;
162
- const { canvas, ctx } = canvasOf(w, h);
163
- ctx.putImageData(new g.ImageData(new Uint8ClampedArray(data), w, h), 0, 0);
164
- const blob = typeof canvas.convertToBlob === 'function'
165
- ? await canvas.convertToBlob({ type: 'image/jpeg', quality: quality / 100 })
166
- : await new Promise((resolve) => canvas.toBlob(resolve, 'image/jpeg', quality / 100));
167
- const bitmap = await g.createImageBitmap(blob);
168
- ctx.clearRect(0, 0, w, h);
169
- ctx.drawImage(bitmap, 0, 0);
170
- return new Uint8ClampedArray(ctx.getImageData(0, 0, w, h).data.buffer);
171
- }
@@ -1,5 +0,0 @@
1
- // Where a layer paints, for content-sized filter regions.
2
- import type { Markup, RenderContext } from './context.js';
3
-
4
- /** The box a layer's markup paints, in its units (drawn small with sharp); undefined when it can't be probed. */
5
- export function contentBox(m: Markup, ctx: RenderContext): Promise<{ x: number; y: number; width: number; height: number } | undefined>;