@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.
- package/CHANGELOG.md +76 -0
- package/README.md +296 -11
- package/package.json +33 -5
- package/recipes/brand.json +1143 -0
- package/schema/recipe.schema.json +7300 -770
- package/src/animate/index.d.ts +19 -0
- package/src/animate/index.js +116 -0
- package/src/builtins.js +4 -0
- package/src/effects/ascii.d.ts +7 -0
- package/src/effects/ascii.js +93 -0
- package/src/effects/buffer.d.ts +60 -0
- package/src/effects/buffer.js +472 -0
- package/src/effects/detail.d.ts +12 -0
- package/src/effects/detail.js +124 -0
- package/src/effects/digital.d.ts +14 -0
- package/src/effects/digital.js +216 -0
- package/src/effects/distort.d.ts +20 -0
- package/src/effects/distort.js +314 -0
- package/src/effects/dither.d.ts +8 -0
- package/src/effects/dither.js +147 -0
- package/src/effects/drop-shadow.d.ts +4 -0
- package/src/effects/drop-shadow.js +45 -0
- package/src/effects/extrude.d.ts +5 -0
- package/src/effects/extrude.js +55 -0
- package/src/effects/film.d.ts +11 -0
- package/src/effects/film.js +204 -0
- package/src/effects/glow.d.ts +8 -0
- package/src/effects/glow.js +56 -0
- package/src/effects/grade.d.ts +30 -0
- package/src/effects/grade.js +274 -0
- package/src/effects/halftone.d.ts +8 -0
- package/src/effects/halftone.js +95 -0
- package/src/effects/index.d.ts +125 -0
- package/src/effects/index.js +277 -0
- package/src/effects/ink.d.ts +12 -0
- package/src/effects/ink.js +70 -0
- package/src/effects/lens.d.ts +14 -0
- package/src/effects/lens.js +277 -0
- package/src/effects/light.d.ts +10 -0
- package/src/effects/light.js +59 -0
- package/src/effects/looks.d.ts +9 -0
- package/src/effects/looks.js +74 -0
- package/src/effects/mask.d.ts +52 -0
- package/src/effects/mask.js +314 -0
- package/src/effects/motion.d.ts +6 -0
- package/src/effects/motion.js +60 -0
- package/src/effects/newsprint.d.ts +6 -0
- package/src/effects/newsprint.js +89 -0
- package/src/effects/optics.d.ts +10 -0
- package/src/effects/optics.js +213 -0
- package/src/effects/options.d.ts +6 -0
- package/src/effects/options.js +106 -0
- package/src/effects/paint-presets.d.ts +10 -0
- package/src/effects/paint-presets.js +47 -0
- package/src/effects/paint.d.ts +49 -0
- package/src/effects/paint.js +66 -0
- package/src/effects/pixels.d.ts +24 -0
- package/src/effects/pixels.js +138 -0
- package/src/effects/polar.d.ts +10 -0
- package/src/effects/polar.js +265 -0
- package/src/effects/region.d.ts +8 -0
- package/src/effects/region.js +136 -0
- package/src/effects/round-border.d.ts +5 -0
- package/src/effects/round-border.js +62 -0
- package/src/effects/screen.d.ts +14 -0
- package/src/effects/screen.js +212 -0
- package/src/effects/sort.d.ts +8 -0
- package/src/effects/sort.js +125 -0
- package/src/effects/symmetry.d.ts +6 -0
- package/src/effects/symmetry.js +74 -0
- package/src/effects/tile.d.ts +7 -0
- package/src/effects/tile.js +55 -0
- package/src/effects/tone.d.ts +16 -0
- package/src/effects/tone.js +117 -0
- package/src/effects/transform.d.ts +35 -0
- package/src/effects/transform.js +127 -0
- package/src/effects/util.d.ts +16 -0
- package/src/effects/util.js +85 -0
- package/src/extract.js +4 -3
- package/src/fonts.d.ts +55 -0
- package/src/fonts.js +246 -53
- package/src/formats/cut.d.ts +10 -0
- package/src/formats/cut.js +77 -0
- package/src/formats/index.d.ts +6 -0
- package/src/formats/index.js +9 -0
- package/src/formats/presets.d.ts +41 -0
- package/src/formats/presets.js +89 -0
- package/src/formats/print.d.ts +25 -0
- package/src/formats/print.js +117 -0
- package/src/formats/zones.d.ts +10 -0
- package/src/formats/zones.js +64 -0
- package/src/geometry.js +1 -1
- package/src/icons.js +43 -9
- package/src/index.d.ts +205 -19
- package/src/index.js +12 -4
- package/src/inter-data.js +27 -0
- package/src/json-recipe.js +181 -23
- package/src/kit/index.d.ts +20 -0
- package/src/kit/index.js +58 -0
- package/src/layer-options.js +23 -4
- package/src/layers/barcode.d.ts +19 -0
- package/src/layers/barcode.js +63 -0
- package/src/layers/code128.d.ts +4 -0
- package/src/layers/code128.js +28 -0
- package/src/layers/expr.d.ts +6 -0
- package/src/layers/expr.js +121 -0
- package/src/layers/geometry.d.ts +6 -0
- package/src/layers/geometry.js +238 -0
- package/src/layers/index.d.ts +18 -0
- package/src/layers/index.js +15 -0
- package/src/layers/perforation.d.ts +14 -0
- package/src/layers/perforation.js +47 -0
- package/src/layers/repeat.d.ts +29 -0
- package/src/layers/repeat.js +90 -0
- package/src/layers/shape.d.ts +46 -0
- package/src/layers/shape.js +115 -0
- package/src/layers/soft.d.ts +5 -0
- package/src/layers/soft.js +43 -0
- package/src/layers/svg.d.ts +23 -0
- package/src/layers/svg.js +109 -0
- package/src/layers/table.d.ts +25 -0
- package/src/layers/table.js +80 -0
- package/src/layers/tear.d.ts +21 -0
- package/src/layers/tear.js +74 -0
- package/src/layers/texture.d.ts +17 -0
- package/src/layers/texture.js +106 -0
- package/src/layers/util.d.ts +8 -0
- package/src/layers/util.js +48 -0
- package/src/layers.js +125 -41
- package/src/lint/image-size.d.ts +4 -0
- package/src/lint/image-size.js +34 -0
- package/src/lint/index.d.ts +16 -0
- package/src/lint/index.js +127 -0
- package/src/lint/rules.d.ts +5 -0
- package/src/lint/rules.js +19 -0
- package/src/node.d.ts +2 -0
- package/src/node.js +20 -0
- package/src/platform/bytes.d.ts +22 -0
- package/src/platform/bytes.js +103 -0
- package/src/platform/compress.d.ts +12 -0
- package/src/platform/compress.js +81 -0
- package/src/platform/harfbuzz.d.ts +9 -0
- package/src/platform/harfbuzz.js +80 -0
- package/src/platform/index.d.ts +8 -0
- package/src/platform/index.js +13 -0
- package/src/platform/load.d.ts +6 -0
- package/src/platform/load.js +72 -0
- package/src/platform/no-harfbuzz.d.ts +2 -0
- package/src/platform/no-harfbuzz.js +11 -0
- package/src/platform/node.d.ts +26 -0
- package/src/platform/node.js +86 -0
- package/src/platform/settings.d.ts +28 -0
- package/src/platform/settings.js +51 -0
- package/src/png.js +77 -28
- package/src/print.js +8 -7
- package/src/raster/index.d.ts +24 -0
- package/src/raster/index.js +171 -0
- package/src/recipes.js +4 -3
- package/src/render/backdrop.d.ts +6 -0
- package/src/render/backdrop.js +55 -0
- package/src/render/content.d.ts +5 -0
- package/src/render/content.js +58 -0
- package/src/render/context.d.ts +82 -0
- package/src/render/grain.d.ts +7 -0
- package/src/render/grain.js +113 -0
- package/src/render/group.d.ts +23 -0
- package/src/render/group.js +67 -0
- package/src/render/key.d.ts +5 -0
- package/src/render/key.js +91 -0
- package/src/render/report.d.ts +5 -0
- package/src/render/report.js +19 -0
- package/src/render/tiles.d.ts +6 -0
- package/src/render/tiles.js +77 -0
- package/src/render/use.d.ts +27 -0
- package/src/render/use.js +73 -0
- package/src/render/wrap.d.ts +14 -0
- package/src/render/wrap.js +199 -0
- package/src/render.js +303 -212
- package/src/text.d.ts +29 -0
- package/src/text.js +22 -15
- package/src/type/auto-color.d.ts +9 -0
- package/src/type/auto-color.js +44 -0
- package/src/type/fallback.d.ts +11 -0
- package/src/type/fallback.js +92 -0
- package/src/type/glyphs.d.ts +24 -0
- package/src/type/glyphs.js +87 -0
- package/src/type/index.d.ts +5 -0
- package/src/type/index.js +10 -0
- package/src/type/kern.d.ts +4 -0
- package/src/type/kern.js +144 -0
- package/src/type/layout-ref.d.ts +10 -0
- package/src/type/layout-ref.js +74 -0
- package/src/type/line.d.ts +11 -0
- package/src/type/line.js +105 -0
- package/src/type/live.d.ts +6 -0
- package/src/type/live.js +39 -0
- package/src/type/name.d.ts +4 -0
- package/src/type/name.js +32 -0
- package/src/type/path.d.ts +18 -0
- package/src/type/path.js +89 -0
- package/src/type/pixel.d.ts +9 -0
- package/src/type/pixel.js +115 -0
- package/src/type/sfnt.d.ts +20 -0
- package/src/type/sfnt.js +258 -0
- package/src/type/spans.d.ts +33 -0
- package/src/type/spans.js +138 -0
- package/src/type/symbols.d.ts +7 -0
- package/src/type/symbols.js +89 -0
- package/src/type/typeset.d.ts +10 -0
- package/src/type/typeset.js +352 -0
- package/src/type/woff2.d.ts +6 -0
- package/src/type/woff2.js +301 -0
- 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
|
+
}
|