@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,199 @@
1
+ /**
2
+ * The options every layer takes on top of its own: `area`, `effects`,
3
+ * `transform`, `mask`, `knockout` and `clip`.
4
+ *
5
+ * @module render/wrap
6
+ */
7
+
8
+ import { attrs, esc, num } from '../svg.js';
9
+ import { applyEffects, transformAttr, rasterEffect, isRaster, RESOLVED, maskImage } from '../effects/index.js';
10
+ import { transformMatrix, stretchOf, compose, IDENTITY } from '../effects/transform.js';
11
+ import { renderGroup } from './group.js';
12
+ import { scaledReport } from './report.js';
13
+ import { contentBox } from './content.js';
14
+
15
+ /** Layer options handled here, for every layer type. */
16
+ export const WRAP_KEYS = ['effects', 'mask', 'knockout', 'clip', 'area'];
17
+
18
+ /** Text-case keywords (the text layer's `transform` is its case when it's one of these). */
19
+ export const CASES = new Set(['uppercase', 'lowercase', 'none']);
20
+
21
+ /**
22
+ * Whether a layer uses the common 1.1 options (so 1.0 layers take exactly
23
+ * the 1.0 path).
24
+ *
25
+ * @param {any} layer
26
+ * @returns {boolean}
27
+ */
28
+ export function wrapped(layer) {
29
+ if (WRAP_KEYS.some((k) => layer[k] != null)) return true;
30
+ return isGeometric(layer);
31
+ }
32
+
33
+ /** @param {any} layer @returns {boolean} Whether `transform` is geometry (not a text case). */
34
+ const isGeometric = (layer) => layer.transform != null && !(layer.type === 'text' && typeof layer.transform === 'string' && CASES.has(layer.transform));
35
+
36
+ /**
37
+ * A layer with `area`, `effects`, `transform`, `mask`, `knockout` or
38
+ * `clip`. Blend and opacity go on the outermost group, so a masked or
39
+ * filtered layer still blends with what's under it. On a `group` with a
40
+ * `viewBox`, effects and masks work in the group's own grid.
41
+ *
42
+ * @param {any} layer
43
+ * @param {any} ctx
44
+ * @param {{ c: import('../index.js').Cover }} scope
45
+ * @param {(layer: any, ctx: any) => Promise<{ defs: string, body: string }>} renderOne - Draws the layer itself.
46
+ * @returns {Promise<{ defs: string, body: string }>}
47
+ */
48
+ export async function renderWrapped(layer, ctx, scope, renderOne) {
49
+ const inner = { ...layer, opacity: undefined, blend: undefined, effects: undefined, mask: undefined, knockout: undefined, clip: undefined, area: undefined };
50
+ const geometric = isGeometric(layer);
51
+ if (geometric) inner.transform = undefined;
52
+ const grid = layer.type === 'group' && layer.viewBox != null;
53
+
54
+ const box = areaBox(layer.area, ctx, scope);
55
+ /** @type {any} */
56
+ let lctx = box ? { ...ctx, width: box.width, height: box.height, canvas: { width: box.width, height: box.height }, inset: undefined, matrix: compose(ctx.matrix ?? IDENTITY, [1, 0, 0, 1, box.x, box.y]) } : ctx;
57
+ // What's drawn inside a transform is drawn that much larger (images resample for it, lint measures it).
58
+ if (geometric) {
59
+ const t = transformMatrix(layer.transform, lctx.canvas);
60
+ const k = stretchOf(t);
61
+ lctx = { ...lctx, matrix: compose(lctx.matrix ?? IDENTITY, t) };
62
+ if (k.max !== 1 || k.mean !== 1) Object.assign(lctx, { scale: (lctx.scale ?? 1) * k.mean, res: (lctx.res ?? 1) * k.max, report: scaledReport(lctx.report, k.mean) });
63
+ }
64
+
65
+ let { defs, body } = grid
66
+ ? await renderGroup(inner, lctx, (/** @type {any} */ part, /** @type {any} */ gctx) => decorate(part, layer, gctx))
67
+ : await decorateEffects(await renderOne(inner, lctx), layer.effects, lctx);
68
+ if (geometric) {
69
+ const t = transformAttr(layer.transform, lctx.canvas);
70
+ if (t) body = `<g transform="${t}">${body}</g>`;
71
+ }
72
+ if (box) body = `<g transform="translate(${num(box.x)} ${num(box.y)})">${body}</g>`;
73
+ if (!grid) ({ defs, body } = await decorateMasks({ defs, body }, layer, ctx));
74
+ const a = attrs({ opacity: layer.opacity != null && layer.opacity < 1 ? Math.max(0, layer.opacity) : undefined, style: layer.blend && layer.blend !== 'normal' ? `mix-blend-mode:${esc(layer.blend)}` : undefined });
75
+ if (a) body = `<g${a}>${body}</g>`;
76
+ return { defs, body };
77
+ }
78
+
79
+ /**
80
+ * `area`: the box a layer is drawn in (the trim, the safe area, or
81
+ * fractions of the canvas), or null for the whole canvas.
82
+ *
83
+ * @param {any} a
84
+ * @param {any} ctx
85
+ * @param {{ c: import('../index.js').Cover }} scope
86
+ * @returns {{ x: number, y: number, width: number, height: number } | null}
87
+ */
88
+ function areaBox(a, ctx, scope) {
89
+ if (!a || a === 'bleed') return null;
90
+ if (a === 'trim' || a === 'safe') {
91
+ const print = /** @type {any} */ (scope.c).print;
92
+ const inset = print ? print.bleed + (a === 'safe' ? print.safe : 0) : 0;
93
+ return { x: inset, y: inset, width: ctx.width - 2 * inset, height: ctx.height - 2 * inset };
94
+ }
95
+ return { x: (a.x ?? 0) * ctx.width, y: (a.y ?? 0) * ctx.height, width: (a.width ?? 1 - (a.x ?? 0)) * ctx.width, height: (a.height ?? 1 - (a.y ?? 0)) * ctx.height };
96
+ }
97
+
98
+ /**
99
+ * A layer's effects, then its masks, round its markup.
100
+ *
101
+ * @param {{ defs: string, body: string }} m
102
+ * @param {any} layer
103
+ * @param {any} ctx
104
+ * @returns {Promise<{ defs: string, body: string }>}
105
+ */
106
+ export async function decorate(m, layer, ctx) {
107
+ return decorateMasks(await decorateEffects(m, layer.effects, ctx), layer, ctx);
108
+ }
109
+
110
+ /**
111
+ * Effects, in order. Pixel effects are computed on pixels where something
112
+ * can draw them (sharp, a canvas) and copy effects drawn as copies; the
113
+ * rest are SVG filters. An effect's layer `mask` is drawn first (as
114
+ * markup, and as an image for a filter to mix through).
115
+ *
116
+ * @param {{ defs: string, body: string }} m
117
+ * @param {any} list
118
+ * @param {any} ctx
119
+ * @returns {Promise<{ defs: string, body: string }>}
120
+ */
121
+ async function decorateEffects(m, list, ctx) {
122
+ if (!list) return m;
123
+ const effects = await Promise.all((Array.isArray(list) ? list : [list]).map((e, i) => withMask(typeof e === 'string' ? { kind: e } : e, ctx, i)));
124
+ let { defs, body } = m;
125
+ const filters = async (/** @type {number} */ from, /** @type {number} */ to) => {
126
+ if (to <= from) return;
127
+ // In renderImage, filters work over what the layer paints, not the whole canvas.
128
+ const out = applyEffects(body, effects.slice(from, to), ctx, from, await contentBox({ defs, body }, ctx), defs);
129
+ defs += out.defs;
130
+ body = out.body;
131
+ };
132
+ let from = 0;
133
+ for (let i = 0; i < effects.length; i++) {
134
+ const fx = effects[i];
135
+ if (!isRaster(fx, ctx)) continue;
136
+ await filters(from, i);
137
+ from = i + 1;
138
+ const raster = await rasterEffect(fx, { defs, body }, ctx, i);
139
+ if (raster) ({ defs, body } = raster);
140
+ else await filters(i, i + 1);
141
+ }
142
+ await filters(from, effects.length);
143
+ return { defs, body };
144
+ }
145
+
146
+ /**
147
+ * An effect with a layer `mask`: the mask's layers drawn (kept on the
148
+ * mask), and for a filter effect drawn as an image too, where something
149
+ * can draw pixels. Other effects come back as they are.
150
+ *
151
+ * @param {any} fx
152
+ * @param {any} ctx
153
+ * @param {number} i
154
+ * @returns {Promise<any>}
155
+ */
156
+ async function withMask(fx, ctx, i) {
157
+ if (!fx?.mask?.layers || !ctx.stack) return fx;
158
+ const parts = await ctx.stack(fx.mask.layers, { tag: `fm${i}` });
159
+ const mask = { ...fx.mask, [RESOLVED]: { defs: parts.map((/** @type {any} */ p) => p.defs).join(''), body: parts.map((/** @type {any} */ p) => p.body).join('') } };
160
+ if (!isRaster(fx, ctx)) mask[RESOLVED].image = await maskImage(mask, ctx, fx.pad ?? 0.3, fx.kind === 'motion' ? fx.angle ?? 0 : 0);
161
+ return { ...fx, mask };
162
+ }
163
+
164
+ /**
165
+ * Masks: sub-stacks drawn as a luminance mask. `knockout` cuts its layers
166
+ * out of this one; `clip` shows this one only where they paint; `mask`
167
+ * uses their luminance as drawn (a white-to-black gradient fades).
168
+ *
169
+ * @param {{ defs: string, body: string }} m
170
+ * @param {any} layer
171
+ * @param {any} ctx
172
+ * @returns {Promise<{ defs: string, body: string }>}
173
+ */
174
+ async function decorateMasks(m, layer, ctx) {
175
+ let { defs, body } = m;
176
+ const masks = [['mask', layer.mask], ['knockout', layer.knockout], ['clip', layer.clip]].filter(([, v]) => v != null);
177
+ for (const [kind, list] of masks) {
178
+ const sub = await ctx.stack(Array.isArray(list) ? list : [list], { tag: `${kind[0]}` });
179
+ const mid = `${ctx.id}${kind[0]}m`;
180
+ const fid = `${ctx.id}${kind[0]}f`;
181
+ const subDefs = sub.map((/** @type {any} */ p) => p.defs).join('');
182
+ let subBody = sub.map((/** @type {any} */ p) => p.body).join('');
183
+ const pad = Math.max(ctx.width, ctx.height);
184
+ const region = { x: -pad, y: -pad, width: ctx.width + 2 * pad, height: ctx.height + 2 * pad };
185
+ let mdefs = subDefs;
186
+ if (kind === 'knockout' || kind === 'clip') {
187
+ // Whatever the sub-layers paint becomes black (cut) or white (kept), keeping their alpha
188
+ // (in renderImage, over just what they paint).
189
+ const v = kind === 'knockout' ? 0 : 1;
190
+ const painted = await contentBox({ defs: subDefs, body: subBody }, ctx);
191
+ mdefs += `<filter${attrs({ id: fid, filterUnits: 'userSpaceOnUse', ...(painted ?? region), 'color-interpolation-filters': 'sRGB' })}><feColorMatrix type="matrix" values="0 0 0 0 ${v} 0 0 0 0 ${v} 0 0 0 0 ${v} 0 0 0 1 0"/></filter>`;
192
+ subBody = `<g filter="url(#${fid})">${subBody}</g>`;
193
+ if (kind === 'knockout') subBody = `<rect${attrs({ ...region, fill: '#fff' })}/>${subBody}`;
194
+ }
195
+ defs += `${mdefs}<mask${attrs({ id: mid, maskUnits: 'userSpaceOnUse', ...region })}>${subBody}</mask>`;
196
+ body = `<g mask="url(#${mid})">${body}</g>`;
197
+ }
198
+ return { defs, body };
199
+ }