@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.
- package/CHANGELOG.md +60 -0
- package/README.md +18 -809
- package/package.json +3 -1
- package/recipes/flyers/afterglow-motion.json +540 -0
- package/recipes/flyers/afterglow.json +70 -0
- package/recipes/flyers/arcfield-mirror-motion.json +397 -0
- package/recipes/flyers/arcfield-mirror.json +364 -0
- package/recipes/flyers/bauhaus-oktav.json +319 -0
- package/recipes/flyers/bauhaus-ritmo.json +735 -0
- package/recipes/flyers/bauhaus-volta.json +394 -0
- package/recipes/flyers/bloomfall.json +99 -0
- package/recipes/flyers/drag-solenne-motion.json +444 -0
- package/recipes/flyers/drag-solenne.json +426 -0
- package/recipes/flyers/fennick-patches.json +874 -0
- package/recipes/flyers/ferro-pass.json +398 -0
- package/recipes/flyers/ferrova.json +812 -0
- package/recipes/flyers/grid-poster.json +91 -0
- package/recipes/flyers/grid-rest-rhythm.json +125 -0
- package/recipes/flyers/groovy-honeycomb.json +816 -0
- package/recipes/flyers/groovy-marigold.json +479 -0
- package/recipes/flyers/groovy-peachfuzz.json +974 -0
- package/recipes/flyers/halden-orbit-motion.json +381 -0
- package/recipes/flyers/halden-orbit.json +356 -0
- package/recipes/flyers/halvard.json +120 -0
- package/recipes/flyers/kairo-weekender.json +696 -0
- package/recipes/flyers/linger-motion.json +518 -0
- package/recipes/flyers/linger.json +67 -0
- package/recipes/flyers/little-orchard.json +105 -0
- package/recipes/flyers/lofi-hexmoth.json +912 -0
- package/recipes/flyers/lofi-lowbit-motion.json +888 -0
- package/recipes/flyers/lofi-lowbit.json +880 -0
- package/recipes/flyers/lofi-nullpoint.json +3013 -0
- package/recipes/flyers/logofolio.json +60 -0
- package/recipes/flyers/lumio-stack-motion.json +648 -0
- package/recipes/flyers/lumio-stack.json +438 -0
- package/recipes/flyers/magazine-cover.json +168 -0
- package/recipes/flyers/magazine-extended.json +269 -0
- package/recipes/flyers/magazine-knockout.json +40 -0
- package/recipes/flyers/northfold-varsity.json +618 -0
- package/recipes/flyers/open-studio.json +69 -0
- package/recipes/flyers/orrin-merch.json +715 -0
- package/recipes/flyers/oxyde.json +113 -0
- package/recipes/flyers/pixel-mosaic.json +58 -0
- package/recipes/flyers/quiet-fields.json +610 -0
- package/recipes/flyers/scrapbook-halcyon.json +948 -0
- package/recipes/flyers/solenne-ticket.json +730 -0
- package/recipes/flyers/solvane-motion.json +576 -0
- package/recipes/flyers/solvane.json +98 -0
- package/recipes/flyers/subduct.json +99 -0
- package/recipes/flyers/sunvale-lineup.json +584 -0
- package/recipes/flyers/tidewell.json +91 -0
- package/recipes/flyers/tour-kairo.json +535 -0
- package/recipes/flyers/velora-stickers.json +885 -0
- package/recipes/flyers/wheatpaste-black.json +313 -0
- package/recipes/flyers/wheatpaste-mark.json +198 -0
- package/recipes/flyers/wheatpaste-torn.json +114 -0
- package/recipes/flyers/wheatpaste-wall.json +984 -0
- package/recipes/flyers/wheatpaste-words.json +233 -0
- package/recipes/release/aura.json +137 -0
- package/recipes/release/chrome.json +433 -0
- package/recipes/release/collage.json +372 -0
- package/recipes/release/duotone.json +113 -0
- package/recipes/release/film-still.json +102 -0
- package/recipes/release/headline.json +70 -0
- package/recipes/release/instant.json +170 -0
- package/recipes/release/label.json +364 -0
- package/recipes/release/liquid-glass.json +301 -0
- package/recipes/release/odd-tile-dark.json +688 -0
- package/recipes/release/odd-tile-dense.json +1056 -0
- package/recipes/release/odd-tile-shift.json +541 -0
- package/recipes/release/odd-tile-split.json +571 -0
- package/recipes/release/odd-tile.json +633 -0
- package/recipes/release/off-edge.json +96 -0
- package/recipes/release/plinth.json +794 -0
- package/recipes/release/riso.json +108 -0
- package/recipes/release/series/fluid-label.json +561 -0
- package/recipes/release/series/fluid-pixel.json +334 -0
- package/recipes/release/series/hairline.json +305 -0
- package/recipes/release/series/halo.json +282 -0
- package/recipes/release/series/mark-crop.json +135 -0
- package/recipes/release/series/mark-window.json +150 -0
- package/recipes/release/series/photo-label.json +475 -0
- package/recipes/release/series/smear-field.json +345 -0
- package/recipes/release/series/smear.json +202 -0
- package/recipes/release/series/spin.json +172 -0
- package/recipes/release/series/split-ghost.json +230 -0
- package/recipes/release/series/zoom.json +168 -0
- package/recipes/release/small-print.json +114 -0
- package/recipes/release/stack.json +156 -0
- package/recipes/release/swiss.json +117 -0
- package/schema/recipe.schema.json +638 -69
- package/src/animate/index.d.ts +9 -19
- package/src/animate/index.js +6 -111
- package/src/animate/keyframes.d.ts +21 -0
- package/src/animate/keyframes.js +119 -0
- package/src/animate/marquee.d.ts +30 -0
- package/src/animate/marquee.js +93 -0
- package/src/animate/morph.d.ts +8 -0
- package/src/animate/morph.js +99 -0
- package/src/animate/motion.d.ts +59 -0
- package/src/animate/motion.js +63 -0
- package/src/animate/sheet.d.ts +19 -0
- package/src/animate/sheet.js +61 -0
- package/src/animate/timing.d.ts +28 -0
- package/src/animate/timing.js +146 -0
- package/src/animate/tracks.d.ts +18 -0
- package/src/animate/tracks.js +139 -0
- package/src/diagram.js +4 -3
- package/src/extract.js +2 -2
- package/src/fonts.js +1 -2
- package/src/geometry.js +4 -34
- package/src/grid.js +1 -1
- package/src/handmade.js +9 -7
- package/src/icons.js +1 -2
- package/src/index.d.ts +47 -54
- package/src/index.js +5 -5
- package/src/inter-data.js +1 -1
- package/src/json-recipe.js +28 -7
- package/src/layer-options.js +26 -4
- package/src/layers/barcode.js +3 -3
- package/src/layers/geometry.js +5 -5
- package/src/layers/index.d.ts +6 -0
- package/src/layers/index.js +3 -0
- package/src/layers/mark-geometry.d.ts +15 -0
- package/src/layers/mark-geometry.js +46 -0
- package/src/layers/marks.d.ts +25 -0
- package/src/layers/marks.js +102 -0
- package/src/layers/mosaic.d.ts +32 -0
- package/src/layers/mosaic.js +155 -0
- package/src/layers/perforation.js +3 -3
- package/src/layers/repeat.js +2 -2
- package/src/layers/shape.d.ts +2 -2
- package/src/layers/shape.js +5 -5
- package/src/layers/soft.js +2 -2
- package/src/layers/svg.js +2 -3
- package/src/layers/table.js +3 -3
- package/src/layers/tear.d.ts +1 -1
- package/src/layers/tear.js +4 -4
- package/src/layers/texture.js +3 -3
- package/src/layers/util.js +15 -2
- package/src/layers.js +17 -32
- package/src/{effects/paint.js → paint/index.js} +5 -5
- package/src/{effects/paint-presets.d.ts → paint/presets.d.ts} +1 -1
- package/src/{effects/paint-presets.js → paint/presets.js} +1 -1
- package/src/{effects → paint}/tile.d.ts +1 -1
- package/src/{effects → paint}/tile.js +4 -4
- package/src/palette.js +1 -1
- package/src/platform/files.d.ts +6 -0
- package/src/platform/files.js +41 -0
- package/src/platform/harfbuzz.js +1 -1
- package/src/platform/index.d.ts +3 -3
- package/src/platform/index.js +6 -4
- package/src/platform/load.js +1 -2
- package/src/print.js +9 -7
- package/src/recipes.js +1 -1
- package/src/render/backdrop.js +2 -2
- package/src/render/grain.js +1 -1
- package/src/render/group.js +3 -3
- package/src/render/key.js +1 -2
- package/src/{effects → render}/transform.d.ts +2 -1
- package/src/{effects → render}/transform.js +17 -5
- package/src/render/use.d.ts +1 -1
- package/src/render/use.js +2 -2
- package/src/render/wrap.js +5 -60
- package/src/render.js +55 -18
- package/src/text.js +1 -1
- package/src/type/auto-color.js +1 -1
- package/src/type/fallback.js +1 -1
- package/src/type/glyphs.js +2 -2
- package/src/type/line.js +1 -1
- package/src/type/live.js +1 -1
- package/src/type/path.js +2 -2
- package/src/type/pixel.d.ts +0 -2
- package/src/type/pixel.js +3 -33
- package/src/type/spans.d.ts +5 -1
- package/src/type/spans.js +28 -5
- package/src/type/symbols.d.ts +1 -1
- package/src/type/symbols.js +20 -2
- package/src/type/typeset.js +25 -10
- package/src/version.js +1 -1
- package/src/color.js +0 -246
- package/src/effects/ascii.d.ts +0 -7
- package/src/effects/ascii.js +0 -93
- package/src/effects/buffer.d.ts +0 -60
- package/src/effects/buffer.js +0 -472
- package/src/effects/detail.d.ts +0 -12
- package/src/effects/detail.js +0 -124
- package/src/effects/digital.d.ts +0 -14
- package/src/effects/digital.js +0 -216
- package/src/effects/distort.d.ts +0 -20
- package/src/effects/distort.js +0 -314
- package/src/effects/dither.d.ts +0 -8
- package/src/effects/dither.js +0 -147
- package/src/effects/drop-shadow.d.ts +0 -4
- package/src/effects/drop-shadow.js +0 -45
- package/src/effects/extrude.d.ts +0 -5
- package/src/effects/extrude.js +0 -55
- package/src/effects/film.d.ts +0 -11
- package/src/effects/film.js +0 -204
- package/src/effects/glow.d.ts +0 -8
- package/src/effects/glow.js +0 -56
- package/src/effects/grade.d.ts +0 -30
- package/src/effects/grade.js +0 -274
- package/src/effects/halftone.d.ts +0 -8
- package/src/effects/halftone.js +0 -95
- package/src/effects/index.d.ts +0 -125
- package/src/effects/index.js +0 -277
- package/src/effects/ink.d.ts +0 -12
- package/src/effects/ink.js +0 -70
- package/src/effects/lens.d.ts +0 -14
- package/src/effects/lens.js +0 -277
- package/src/effects/light.d.ts +0 -10
- package/src/effects/light.js +0 -59
- package/src/effects/looks.d.ts +0 -9
- package/src/effects/looks.js +0 -74
- package/src/effects/mask.d.ts +0 -52
- package/src/effects/mask.js +0 -314
- package/src/effects/motion.d.ts +0 -6
- package/src/effects/motion.js +0 -60
- package/src/effects/newsprint.d.ts +0 -6
- package/src/effects/newsprint.js +0 -89
- package/src/effects/optics.d.ts +0 -10
- package/src/effects/optics.js +0 -213
- package/src/effects/options.d.ts +0 -6
- package/src/effects/options.js +0 -106
- package/src/effects/pixels.d.ts +0 -24
- package/src/effects/pixels.js +0 -138
- package/src/effects/polar.d.ts +0 -10
- package/src/effects/polar.js +0 -265
- package/src/effects/region.d.ts +0 -8
- package/src/effects/region.js +0 -136
- package/src/effects/round-border.d.ts +0 -5
- package/src/effects/round-border.js +0 -62
- package/src/effects/screen.d.ts +0 -14
- package/src/effects/screen.js +0 -212
- package/src/effects/sort.d.ts +0 -8
- package/src/effects/sort.js +0 -125
- package/src/effects/symmetry.d.ts +0 -6
- package/src/effects/symmetry.js +0 -74
- package/src/effects/tone.d.ts +0 -16
- package/src/effects/tone.js +0 -117
- package/src/effects/util.d.ts +0 -16
- package/src/effects/util.js +0 -85
- package/src/platform/bytes.d.ts +0 -22
- package/src/platform/bytes.js +0 -103
- package/src/platform/compress.d.ts +0 -12
- package/src/platform/compress.js +0 -81
- package/src/platform/node.d.ts +0 -26
- package/src/platform/node.js +0 -86
- package/src/png.js +0 -126
- package/src/raster/index.d.ts +0 -24
- package/src/raster/index.js +0 -171
- package/src/render/content.d.ts +0 -5
- package/src/render/content.js +0 -58
- package/src/render/tiles.d.ts +0 -6
- package/src/render/tiles.js +0 -77
- package/src/rng.js +0 -95
- package/src/svg.js +0 -69
- /package/src/{effects/paint.d.ts → paint/index.d.ts} +0 -0
package/src/index.d.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* WebP / PNG / AVIF / JPEG.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import type { LayerTransform
|
|
9
|
-
import type { Paint } from './
|
|
7
|
+
import type { Effect as AnyEffect, EffectKind, EffectMask as AnyEffectMask, Color, Length, Rng } from '@logomarks/image-effects';
|
|
8
|
+
import type { LayerTransform } from './render/transform.js';
|
|
9
|
+
import type { Paint } from './paint/index.js';
|
|
10
10
|
import type { TextPath } from './type/path.js';
|
|
11
11
|
import type { GroupLayer } from './render/group.js';
|
|
12
12
|
import type { DefsLayer, UseLayer } from './render/use.js';
|
|
@@ -14,31 +14,34 @@ import type { RepeatLayer } from './layers/repeat.js';
|
|
|
14
14
|
import type { TextSpan } from './type/spans.js';
|
|
15
15
|
import type { GlyphControl } from './type/glyphs.js';
|
|
16
16
|
import type { Cut } from './formats/cut.js';
|
|
17
|
-
import type { ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer } from './layers/index.js';
|
|
17
|
+
import type { ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer, MosaicLayer, MarksLayer } from './layers/index.js';
|
|
18
18
|
import type { Zones, PrintInfo, FormatName } from './formats/index.js';
|
|
19
|
+
import type { Motion, MotionPreset, Easing } from './animate/index.js';
|
|
19
20
|
|
|
20
|
-
export type { RepeatLayer, DefsLayer, UseLayer, TextSpan, GlyphControl, Cut };
|
|
21
|
-
export type {
|
|
21
|
+
export type { RepeatLayer, DefsLayer, UseLayer, TextSpan, GlyphControl, Cut, Motion, MotionPreset, Easing };
|
|
22
|
+
export type { Color, Length, Rng, LayerTransform, EffectKind, Paint, TextPath, GroupLayer, ShapeLayer, SvgLayer, TableLayer, BarcodeLayer, PerforationLayer, TearLayer, TextureLayer, MosaicLayer, MarksLayer, Zones, PrintInfo, FormatName };
|
|
22
23
|
export type { ShapeSpec, TableCell } from './layers/index.js';
|
|
23
24
|
export type { Format } from './formats/index.js';
|
|
24
25
|
export type { LintIssue } from './lint/index.js';
|
|
25
26
|
export type { Settings } from './platform/index.js';
|
|
26
27
|
|
|
27
28
|
export { safeBox } from './formats/index.js';
|
|
28
|
-
export { rasterize } from '
|
|
29
|
-
import type { RasterizeOptions } from '
|
|
29
|
+
export { rasterize } from '@logomarks/image-effects/raster';
|
|
30
|
+
import type { RasterizeOptions } from '@logomarks/image-effects/raster';
|
|
30
31
|
export type { RasterizeOptions };
|
|
31
32
|
export { lint } from './lint/index.js';
|
|
32
33
|
export { kit, KIT_FORMATS } from './kit/index.js';
|
|
33
34
|
export { FORMATS } from './formats/index.js';
|
|
34
35
|
export { configure } from './platform/index.js';
|
|
35
|
-
export { EFFECTS, EFFECT_OPTIONS } from '
|
|
36
|
-
export { valueAt } from './animate/index.js';
|
|
36
|
+
export { EFFECTS, EFFECT_OPTIONS } from '@logomarks/image-effects';
|
|
37
|
+
export { valueAt, MOTION_PRESETS } from './animate/index.js';
|
|
38
|
+
export { toHex, toOklch, fromOklch, mixOklab, mixOklch, onBackground, gradientAt } from '@logomarks/image-effects/color';
|
|
39
|
+
export { createRng, hashString } from '@logomarks/image-effects/rng';
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
export type
|
|
41
|
+
/** An effect on a layer (image-effects' effects, with masks and maps given as layers). */
|
|
42
|
+
export type Effect = AnyEffect<Layer>;
|
|
43
|
+
/** Where an effect applies: a gradient, or layers. */
|
|
44
|
+
export type EffectMask = AnyEffectMask<Layer>;
|
|
42
45
|
|
|
43
46
|
/** Where a box sits inside the safe area. */
|
|
44
47
|
export type Anchor =
|
|
@@ -136,6 +139,8 @@ export interface BaseLayer {
|
|
|
136
139
|
clip?: Layer[];
|
|
137
140
|
/** Draw the layer in a box: the print `'trim'` or `'safe'` area, or fractions of the canvas. */
|
|
138
141
|
area?: 'bleed' | 'trim' | 'safe' | Region;
|
|
142
|
+
/** Move in the SVG itself (CSS keyframes, no script): a motion, a preset name, or a list. */
|
|
143
|
+
motion?: Motion | MotionPreset | (Motion | MotionPreset)[];
|
|
139
144
|
}
|
|
140
145
|
|
|
141
146
|
export interface SolidLayer extends BaseLayer {
|
|
@@ -305,8 +310,8 @@ export interface PatternLayer extends BaseLayer {
|
|
|
305
310
|
fade?: 'none' | 'radial' | 'edges' | 'top' | 'bottom' | 'left' | 'right';
|
|
306
311
|
/** Dashed or dotted lines (or `[on, off]` lengths). */
|
|
307
312
|
dash?: 'dashed' | 'dotted' | true | [Length, Length];
|
|
308
|
-
/** Grid / lines: N equal cells (inner lines only) instead of `size
|
|
309
|
-
divisions?: number;
|
|
313
|
+
/** Grid / lines: N equal cells (inner lines only) instead of `size`; `[cols, rows]` for a design grid. */
|
|
314
|
+
divisions?: number | [number, number];
|
|
310
315
|
/** Regions (fractions) the lines break around. */
|
|
311
316
|
gaps?: Region[];
|
|
312
317
|
region?: Region;
|
|
@@ -410,8 +415,8 @@ export interface TextLabel {
|
|
|
410
415
|
|
|
411
416
|
export interface TextLayer extends Omit<BaseLayer, 'transform'> {
|
|
412
417
|
type: 'text';
|
|
413
|
-
/** The text; an array is one line per item. (Optional with `spans`.) */
|
|
414
|
-
content?: string | string[];
|
|
418
|
+
/** The text; an array is one line per item, and an item may be a span (`{ content, bold }`: a headline whose lines change weight). (Optional with `spans`.) */
|
|
419
|
+
content?: string | (string | TextSpan)[];
|
|
415
420
|
/** First baseline x (px or `'%'`): places the text like SVG `<text x y>`, `align` acting as the anchor. */
|
|
416
421
|
x?: Length | `${number}%`;
|
|
417
422
|
y?: Length | `${number}%`;
|
|
@@ -465,6 +470,8 @@ export interface TextLayer extends Omit<BaseLayer, 'transform'> {
|
|
|
465
470
|
font?: string;
|
|
466
471
|
/** Default 600. */
|
|
467
472
|
weight?: number;
|
|
473
|
+
/** The weight of spans and lines marked `bold: true`. Default 700. */
|
|
474
|
+
boldWeight?: number;
|
|
468
475
|
style?: 'normal' | 'italic';
|
|
469
476
|
/** Default 6% of the design unit (`sqrt(width × height)`). */
|
|
470
477
|
size?: Length;
|
|
@@ -713,7 +720,7 @@ export interface RuleLayer extends BaseLayer {
|
|
|
713
720
|
}
|
|
714
721
|
|
|
715
722
|
export type Layer =
|
|
716
|
-
| GroupLayer | ShapeLayer | SvgLayer | TableLayer | BarcodeLayer | PerforationLayer | TearLayer | TextureLayer
|
|
723
|
+
| GroupLayer | ShapeLayer | SvgLayer | TableLayer | BarcodeLayer | PerforationLayer | TearLayer | TextureLayer | MosaicLayer | MarksLayer
|
|
717
724
|
| InkLayer | DitherLayer | ContourLayer | WaveformLayer | TilesLayer | RuleLayer
|
|
718
725
|
| DiagramLayer | PaperLayer | ScribbleLayer
|
|
719
726
|
| SolidLayer | LinearLayer | RadialLayer | MeshLayer | StripesLayer | ImageLayer
|
|
@@ -934,6 +941,10 @@ export interface CoverOptions extends RecipeOptions {
|
|
|
934
941
|
textRender?: 'outline' | 'live';
|
|
935
942
|
/** Seconds: the length of an animated cover's loop (keyframes with `loop` wrap at it). */
|
|
936
943
|
loop?: number;
|
|
944
|
+
/** `false`: `renderSvg` draws a still, no CSS (layers' `motion` at the `poster` time, else at rest). */
|
|
945
|
+
animate?: boolean;
|
|
946
|
+
/** Seconds: the frame stills show (`renderImage`, `renderBlob`, `animate: false`, and an animated SVG under reduced motion). Default: the layers at rest. */
|
|
947
|
+
poster?: number;
|
|
937
948
|
}
|
|
938
949
|
|
|
939
950
|
/** A resolved cover (from {@link cover}). */
|
|
@@ -958,13 +969,16 @@ export interface Cover {
|
|
|
958
969
|
pages?: string[];
|
|
959
970
|
overlay?: boolean;
|
|
960
971
|
loop?: number;
|
|
972
|
+
/** `false` when the cover is drawn as a still. */
|
|
973
|
+
animate?: false;
|
|
974
|
+
poster?: number;
|
|
961
975
|
textRender?: 'outline' | 'live';
|
|
962
976
|
}
|
|
963
977
|
|
|
964
978
|
export interface RenderImageOptions {
|
|
965
979
|
/** Default `'webp'`. */
|
|
966
980
|
format?: 'webp' | 'png' | 'avif' | 'jpeg';
|
|
967
|
-
/** Default 90 for WebP, 72 for AVIF, 88 for JPEG (high enough to keep the grain; see
|
|
981
|
+
/** Default 90 for WebP, 72 for AVIF, 88 for JPEG (high enough to keep the grain; see docs/performance.md). */
|
|
968
982
|
quality?: number;
|
|
969
983
|
/** Lossless WebP / AVIF (default: the recipe's preference, else lossy). */
|
|
970
984
|
lossless?: boolean;
|
|
@@ -974,16 +988,6 @@ export interface RenderImageOptions {
|
|
|
974
988
|
cache?: boolean | string;
|
|
975
989
|
}
|
|
976
990
|
|
|
977
|
-
/** Seeded random stream. */
|
|
978
|
-
export interface Rng {
|
|
979
|
-
next(): number;
|
|
980
|
-
range(min: number, max: number): number;
|
|
981
|
-
int(min: number, max: number): number;
|
|
982
|
-
pick<T>(items: readonly T[]): T;
|
|
983
|
-
shuffle<T>(items: readonly T[]): T[];
|
|
984
|
-
chance(p?: number): boolean;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
991
|
/** An Iconify JSON set (`@iconify-json/<prefix>/icons.json`). */
|
|
988
992
|
export interface IconSet {
|
|
989
993
|
prefix: string;
|
|
@@ -998,13 +1002,23 @@ export interface IconSet {
|
|
|
998
1002
|
/** Resolve a cover's size and palette and expand its recipe. Pure and synchronous. */
|
|
999
1003
|
export function cover(options: CoverOptions | Cover): Cover;
|
|
1000
1004
|
|
|
1001
|
-
/**
|
|
1002
|
-
export
|
|
1005
|
+
/** How `renderSvg` draws. */
|
|
1006
|
+
export interface RenderSvgOptions {
|
|
1007
|
+
/** A still at this time, in seconds, for keyframed layers and `motion` (instead of playing it). */
|
|
1008
|
+
t?: number;
|
|
1009
|
+
/** `'webp'`: embedded photos and pixel effects re-encoded as WebP where smaller (needs sharp). For SVGs shown in browsers; librsvg (sharp) can't draw WebP. */
|
|
1010
|
+
images?: 'webp';
|
|
1011
|
+
/** WebP quality for photos (JPEG sources), 1–100. Default 82. PNG sources go lossless. */
|
|
1012
|
+
quality?: number;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/** Render to an SVG string (text and icons as outlines; byte-identical for the same input). */
|
|
1016
|
+
export function renderSvg(input: CoverOptions | Cover, options?: RenderSvgOptions): Promise<string>;
|
|
1003
1017
|
|
|
1004
1018
|
/** Render each page of a two-sided piece: `{ front, back }`. */
|
|
1005
1019
|
export function renderPages(input: CoverOptions, options?: { pages?: string[] }): Promise<Record<string, string>>;
|
|
1006
1020
|
|
|
1007
|
-
/** Render to an image in the browser (a canvas; no sharp). */
|
|
1021
|
+
/** Render to an image in the browser (a canvas; no sharp): a still, the `poster` frame or the layers at rest. */
|
|
1008
1022
|
export function renderBlob(input: CoverOptions | Cover, options?: RasterizeOptions): Promise<Blob>;
|
|
1009
1023
|
|
|
1010
1024
|
/** Register a font for every cover. */
|
|
@@ -1043,21 +1057,6 @@ export function resolvePalette(input?: PaletteInput | Palette): Palette;
|
|
|
1043
1057
|
/** Derive a palette from a theme's tokens (or just an accent). */
|
|
1044
1058
|
export function derivePalette(tokens: { accent: Color; bg?: Color; text?: Color; colors?: Color[]; mode?: 'light' | 'dark' }): Palette;
|
|
1045
1059
|
|
|
1046
|
-
/** Parse a CSS colour to `#rrggbb`. */
|
|
1047
|
-
export function toHex(color: Color): string;
|
|
1048
|
-
/** A colour in OKLCH (lightness 0–1, chroma, hue in degrees). */
|
|
1049
|
-
export function toOklch(color: Color): { l: number; c: number; h: number };
|
|
1050
|
-
/** OKLCH to `#rrggbb` (chroma reduced to fit sRGB). */
|
|
1051
|
-
export function fromOklch(lch: { l: number; c: number; h: number }): string;
|
|
1052
|
-
/** Mix two colours in OKLab. */
|
|
1053
|
-
export function mixOklab(a: Color, b: Color, t: number): string;
|
|
1054
|
-
/** `prefer` if it clears `ratio` on every background, else the better of white and near-black (3:1 suits icons, WCAG 1.4.11). */
|
|
1055
|
-
export function onBackground(backgrounds: Color | Color[], ratio?: number, prefer?: 'white' | 'black' | Color): string;
|
|
1056
|
-
/** The colour of an eased multi-stop gradient at t (0–1), as the gradient layers draw it. */
|
|
1057
|
-
export function gradientAt(colors: Color[], t: number, ease?: boolean): string;
|
|
1058
|
-
/** Mix two colours in OKLCH (hue the short way; stays vivid). */
|
|
1059
|
-
export function mixOklch(a: Color, b: Color, t: number): string;
|
|
1060
|
-
|
|
1061
1060
|
/** Register an Iconify icon set (no `@iconify-json/*` install needed). */
|
|
1062
1061
|
export function registerIcons(set: IconSet): void;
|
|
1063
1062
|
|
|
@@ -1067,17 +1066,11 @@ export function resolveSize(options?: { width?: number; height?: number; aspect?
|
|
|
1067
1066
|
/** Named aspect ratios (width / height). */
|
|
1068
1067
|
export const ASPECTS: Readonly<{ '1:1': number; '16:9': number; '4:5': number; '3:2': number; '4:3': number; '2:1': number; '9:16': number; og: number }>;
|
|
1069
1068
|
|
|
1070
|
-
/** A seeded random stream (for custom recipes). */
|
|
1071
|
-
export function createRng(seed: string | number): Rng;
|
|
1072
|
-
|
|
1073
1069
|
/** Dominant colours of a photo, most common first (needs sharp). */
|
|
1074
1070
|
export function extractPalette(src: string | Uint8Array, options?: { count?: number }): Promise<string[]>;
|
|
1075
1071
|
|
|
1076
1072
|
/** Classic risograph inks, by name (approximations). */
|
|
1077
1073
|
export const RISO_INKS: Readonly<Record<'pink' | 'blue' | 'yellow' | 'teal' | 'red' | 'green' | 'orange' | 'purple' | 'navy' | 'black', string>>;
|
|
1078
1074
|
|
|
1079
|
-
/** Hash a string to a 32-bit unsigned integer. */
|
|
1080
|
-
export function hashString(str: string, salt?: number): number;
|
|
1081
|
-
|
|
1082
1075
|
/** The layer renderers, by type (for introspection). */
|
|
1083
1076
|
export const layers: Readonly<Record<Layer['type'], (layer: any, context: any) => unknown>>;
|
package/src/index.js
CHANGED
|
@@ -10,18 +10,18 @@ export { cover, renderSvg, renderImage, renderPages, renderBlob, coverKey, safeB
|
|
|
10
10
|
export { recipes } from './recipes.js';
|
|
11
11
|
export { defineRecipe, loadRecipe } from './json-recipe.js';
|
|
12
12
|
export { palettes, resolvePalette, derivePalette } from './palette.js';
|
|
13
|
-
export { toHex, toOklch, fromOklch, mixOklab, mixOklch, onBackground, gradientAt } from '
|
|
13
|
+
export { toHex, toOklch, fromOklch, mixOklab, mixOklch, onBackground, gradientAt } from '@logomarks/image-effects/color';
|
|
14
14
|
export { registerIcons } from './icons.js';
|
|
15
15
|
export { registerFont } from './fonts.js';
|
|
16
16
|
export { configure } from './platform/index.js';
|
|
17
17
|
export { resolveSize, ASPECTS } from './geometry.js';
|
|
18
18
|
export { FORMATS } from './formats/index.js';
|
|
19
|
-
export { createRng, hashString } from '
|
|
19
|
+
export { createRng, hashString } from '@logomarks/image-effects/rng';
|
|
20
20
|
export { LAYERS as layers } from './layers.js';
|
|
21
21
|
export { extractPalette } from './extract.js';
|
|
22
22
|
export { RISO_INKS } from './print.js';
|
|
23
|
-
export { EFFECTS, EFFECT_OPTIONS } from '
|
|
24
|
-
export { rasterize } from '
|
|
23
|
+
export { EFFECTS, EFFECT_OPTIONS } from '@logomarks/image-effects';
|
|
24
|
+
export { rasterize } from '@logomarks/image-effects/raster';
|
|
25
25
|
export { lint } from './lint/index.js';
|
|
26
26
|
export { kit, KIT_FORMATS } from './kit/index.js';
|
|
27
|
-
export { valueAt } from './animate/index.js';
|
|
27
|
+
export { valueAt, MOTION_PRESETS } from './animate/index.js';
|
package/src/inter-data.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated by scripts/generate.mjs from fonts/*.woff. Do not edit.
|
|
2
2
|
// Inter is © The Inter Project Authors, SIL Open Font License 1.1 (fonts/OFL.txt).
|
|
3
3
|
|
|
4
|
-
import { fromBase64 } from './platform/
|
|
4
|
+
import { fromBase64 } from './platform/index.js';
|
|
5
5
|
|
|
6
6
|
/** @type {Record<number, string>} */
|
|
7
7
|
const DATA = {
|
package/src/json-recipe.js
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
* | `{ "gradientUnder": [colors, angle, size] }` | the colours a centred square of `size` covers in a linear gradient |
|
|
32
32
|
* | `{ "pick": [...] }`, `{ "range": [min, max] }`, `{ "int": [min, max] }` | seeded choices |
|
|
33
33
|
* | `{ "add" \| "sub" \| "mul" \| "div" \| "min" \| "max": [a, b, …] }` | arithmetic; lengths like `"54vw"` become pixels |
|
|
34
|
+
* | `{ "round" \| "floor" \| "ceil": x }` | to a whole number (a grid's row count from the canvas) |
|
|
34
35
|
* | `{ "ramp": [from, to, steps, amount?] }` | `steps` colours from `from` toward `to` |
|
|
35
36
|
* | `{ "taper": [ratio, steps] }` | `[1, ratio, ratio², …]` |
|
|
36
37
|
* | `{ "length": value }` | characters in a string (or a number's digits), items in an array |
|
|
@@ -55,15 +56,16 @@
|
|
|
55
56
|
|
|
56
57
|
import { readTextSync, node } from './platform/index.js';
|
|
57
58
|
import { readableText } from '@arraypress/color-utils';
|
|
58
|
-
import { createRng } from '
|
|
59
|
-
import { len
|
|
60
|
-
import {
|
|
59
|
+
import { createRng } from '@logomarks/image-effects/rng';
|
|
60
|
+
import { len } from '@logomarks/image-effects/length';
|
|
61
|
+
import { ANCHORS } from './geometry.js';
|
|
62
|
+
import { toHex, mixOklab, mixOklch, onBackground, gradientAt } from '@logomarks/image-effects/color';
|
|
61
63
|
import { LAYERS, toneColor } from './layers.js';
|
|
62
|
-
import { LAYER_OPTIONS, COMMON } from './layer-options.js';
|
|
63
|
-
import { EFFECT_OPTIONS, EFFECT_COMMON } from '
|
|
64
|
-
import { MASK_GRADIENTS } from './effects/mask.js';
|
|
64
|
+
import { LAYER_OPTIONS, COMMON, MOTION_OPTIONS } from './layer-options.js';
|
|
65
|
+
import { EFFECT_OPTIONS, EFFECT_COMMON, MASK_GRADIENTS } from '@logomarks/image-effects';
|
|
65
66
|
import { shapesOnly } from './layers/svg.js';
|
|
66
67
|
import { LAYOUT_FIELDS } from './type/layout-ref.js';
|
|
68
|
+
import { MOTION_PRESETS } from './animate/timing.js';
|
|
67
69
|
|
|
68
70
|
/** Options every recipe can read without declaring them. */
|
|
69
71
|
const CONTENT = ['title', 'label', 'icon', 'image', 'name', 'mark', 'photo', 'cutout', 'date', 'dates', 'time', 'venue', 'address', 'lines', 'lineup', 'tracklist', 'price', 'meta', 'page'];
|
|
@@ -76,6 +78,7 @@ const CANVAS = ['width', 'height', 'u', 'bleed', 'safe', 'mm', 'dpi', 'trimWidth
|
|
|
76
78
|
const OPS = /** @type {Record<string, [number, number]>} */ ({
|
|
77
79
|
mix: [3, 4], readable: [1, 3], gradientUnder: [3, 3], tone: [2, 2], length: [1, 1], pick: [1, Infinity], range: [2, 2], int: [2, 2],
|
|
78
80
|
add: [1, Infinity], sub: [2, Infinity], mul: [1, Infinity], div: [2, 2], min: [1, Infinity], max: [1, Infinity],
|
|
81
|
+
round: [1, 1], floor: [1, 1], ceil: [1, 1],
|
|
79
82
|
ramp: [3, 4], taper: [2, 2], reverse: [1, 1],
|
|
80
83
|
join: [1, 2], upper: [1, 1], lower: [1, 1], slice: [2, 3],
|
|
81
84
|
});
|
|
@@ -181,7 +184,7 @@ export function loadRecipe(path) {
|
|
|
181
184
|
* @property {Record<string, unknown>} params
|
|
182
185
|
* @property {Record<string, unknown>} vars
|
|
183
186
|
* @property {any} c - The recipe context.
|
|
184
|
-
* @property {() => import('
|
|
187
|
+
* @property {() => import('@logomarks/image-effects/rng').Rng} rng - The current stream.
|
|
185
188
|
*/
|
|
186
189
|
|
|
187
190
|
/**
|
|
@@ -389,6 +392,9 @@ function op(s, name, raw) {
|
|
|
389
392
|
case 'div': { const [a, b] = nums(); return a / b; }
|
|
390
393
|
case 'min': return Math.min(...nums());
|
|
391
394
|
case 'max': return Math.max(...nums());
|
|
395
|
+
case 'round': return Math.round(nums()[0]);
|
|
396
|
+
case 'floor': return Math.floor(nums()[0]);
|
|
397
|
+
case 'ceil': return Math.ceil(nums()[0]);
|
|
392
398
|
case 'ramp': {
|
|
393
399
|
const [from, to, steps, amount = 1] = args;
|
|
394
400
|
return Array.from({ length: steps }, (_, i) => mixOklab(toHex(from), toHex(to), (i / steps) * amount));
|
|
@@ -546,6 +552,8 @@ export function validate(json) {
|
|
|
546
552
|
if (!Object.hasOwn(known, k)) fail(`${path}.${k}`, `"${layer.type}" layers have no option "${k}" (${Object.keys(known).join(', ')})`);
|
|
547
553
|
if (k === 'effects') {
|
|
548
554
|
checkEffects(`${path}.effects`, v);
|
|
555
|
+
} else if (k === 'motion') {
|
|
556
|
+
checkMotion(`${path}.motion`, v);
|
|
549
557
|
} else if (NESTED.includes(k) && Array.isArray(v) && v.every((x) => isPlain(x) && typeof x.type === 'string')) {
|
|
550
558
|
v.forEach((x, j) => checkLayer(`${path}.${k}[${j}]${x.id ? ` (${x.id})` : ''}`, x));
|
|
551
559
|
} else if (k === 'layer' && isPlain(v) && typeof v.type === 'string') {
|
|
@@ -583,6 +591,19 @@ export function validate(json) {
|
|
|
583
591
|
}
|
|
584
592
|
});
|
|
585
593
|
};
|
|
594
|
+
// Motion: known presets and options (values are checked as values).
|
|
595
|
+
const checkMotion = (/** @type {string} */ path, /** @type {any} */ v) => {
|
|
596
|
+
(Array.isArray(v) ? v : [v]).forEach((m, i) => {
|
|
597
|
+
const at = Array.isArray(v) ? `${path}[${i}]` : path;
|
|
598
|
+
const preset = typeof m === 'string' ? m : m?.preset;
|
|
599
|
+
if (typeof preset === 'string' && !/[{$@]/.test(preset) && !Object.hasOwn(MOTION_PRESETS, preset)) fail(at, `unknown motion preset "${preset}" (${Object.keys(MOTION_PRESETS).join(', ')})`);
|
|
600
|
+
if (!isPlain(m)) return checkValue(at, m);
|
|
601
|
+
for (const [k, x] of Object.entries(m)) {
|
|
602
|
+
if (!Object.hasOwn(MOTION_OPTIONS, k)) fail(`${at}.${k}`, `motions have no option "${k}" (${Object.keys(MOTION_OPTIONS).join(', ')})`);
|
|
603
|
+
checkValue(`${at}.${k}`, x);
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
};
|
|
586
607
|
const checkEffectMask = (/** @type {string} */ path, /** @type {any} */ m) => {
|
|
587
608
|
const keys = ['gradient', 'from', 'to', 'center', 'angle', 'aspect', 'invert', 'ease', 'layers'];
|
|
588
609
|
for (const k of Object.keys(m)) if (!keys.includes(k)) fail(`${path}.${k}`, `unknown mask option (${keys.join(', ')})`);
|
package/src/layer-options.js
CHANGED
|
@@ -16,12 +16,12 @@ export const LAYER_OPTIONS = {
|
|
|
16
16
|
stripes: { direction: 'vertical | horizontal | diagonal', angle: 'For diagonal.', count: 'Stripes. Default 8.', widths: 'even | random | taper | [weights]', taper: 'Ratio for widths: taper.', colors: 'Cycled.', gap: 'Length.', radius: 'Length.', region: '{ x, y, width, height } fractions.', regionRadius: 'Length.' },
|
|
17
17
|
image: { x: 'Box left, px or "%" (with y, width, height: an explicit box instead of region).', y: 'Box top.', width: 'Box width.', height: 'Box height.', src: 'A local path, data: URI, URL (where allowed) or bytes ("{image}").', fit: 'cover | contain | stretch', focus: 'An anchor, attention or entropy.', region: 'Fractions.', duotone: 'true or [shadow, highlight].', grayscale: 'boolean', normalize: 'Stretch tones first.', tint: 'A colour or { color, amount, blend }.', blur: 'Length.' },
|
|
18
18
|
grain: { amount: '0–1. Default 0.08.', size: 'Grain px. Default 1.', monochrome: 'Default true.', vary: '"edges": heavier toward the edges (spray paint).' },
|
|
19
|
-
pattern: { kind: 'grid | dots | lines | rings | waves', size: 'Cell. Length.', stroke: 'Length.', color: 'Default @text.', angle: 'For lines.', center: '{ x, y } fractions.', wavelength: 'Length.', amplitude: 'Length.', fade: 'none | radial | edges | top | bottom | left | right', dash: 'dotted | dashed | [on, off] lengths.', divisions: 'Grid/lines: N equal cells (inner lines only) instead of size.', gaps: '[{ x, y, width, height }] fractions the lines break around.', region: 'Fractions.' },
|
|
19
|
+
pattern: { kind: 'grid | dots | lines | rings | waves', size: 'Cell. Length.', stroke: 'Length.', color: 'Default @text.', angle: 'For lines.', center: '{ x, y } fractions.', wavelength: 'Length.', amplitude: 'Length.', fade: 'none | radial | edges | top | bottom | left | right', dash: 'dotted | dashed | [on, off] lengths.', divisions: 'Grid/lines: N equal cells (inner lines only) instead of size; [cols, rows] for a design grid.', gaps: '[{ x, y, width, height }] fractions the lines break around.', region: 'Fractions.' },
|
|
20
20
|
shapes: { kind: 'circle | square | pill | mixed', count: 'Default 6.', size: '[min, max] lengths.', colors: 'Cycled.', placement: 'scatter | row | column', items: '[{ kind, x, y, size, color, rotate, opacity }]', region: 'Fractions.', rotate: 'Tilt squares and pills.', radius: 'Square corner, fraction of size.', outline: 'true or a width.' },
|
|
21
21
|
icon: { fill: 'A gradient, tile or preset paint for the icon (instead of color).', markup: 'Inner SVG markup on a 512 grid (a mark\'s paths); viewBox overrides the grid.', x: 'Left, px (with y: placed by its top-left corner).', y: 'Top, px.', name: 'Iconify name ("lucide:feather") or raw SVG ("{icon}").', svg: 'Raw SVG.', path: 'A path d.', viewBox: '[x, y, w, h] for path.', size: 'Length. Default "24vmin".', color: 'Default the accent on a tile, else @text.', strokeWidth: 'Stroke icons.', anchor: 'One of nine anchors.', position: '{ x, y } fractions.', offset: '{ x, y } lengths.', margin: 'Length.', tile: 'true or { size, radius, color, shadow, border }.' },
|
|
22
|
-
text: { content: 'The text ("{title}").', font: 'Family. Default Inter.', weight: 'Default 600.', style: 'normal | italic', size: 'Length.', minSize: 'Shrink toward this before truncating.', lineHeight: 'Default 1.12.', letterSpacing: 'Em. Default -0.02.', color: 'Default @text.', align: 'left | center | right', anchor: 'Default bottom-left.', position: '{ x, y } fractions.', offset: '{ x, y } lengths.', margin: 'Length.', maxWidth: 'Length.', maxLines: 'Default 3.', ellipsis: "Default '…'.", balance: 'Default true.', transform: 'none | uppercase | lowercase', label: 'A string or { content, size, weight, color, letterSpacing, transform, gap }.', background: 'A colour or { color, radius, padding, opacity }.',
|
|
22
|
+
text: { content: 'The text ("{title}"); an array is a line per item, an item may be a span ({ content, bold }).', font: 'Family. Default Inter.', weight: 'Default 600.', boldWeight: 'The weight of spans and lines marked bold: true. Default 700.', style: 'normal | italic', size: 'Length.', minSize: 'Shrink toward this before truncating.', lineHeight: 'Default 1.12.', letterSpacing: 'Em. Default -0.02.', color: 'Default @text.', align: 'left | center | right', anchor: 'Default bottom-left.', position: '{ x, y } fractions.', offset: '{ x, y } lengths.', margin: 'Length.', maxWidth: 'Length.', maxLines: 'Default 3.', ellipsis: "Default '…'.", balance: 'Default true.', transform: 'none | uppercase | lowercase', label: 'A string or { content, size, weight, color, letterSpacing, transform, gap }.', background: 'A colour or { color, radius, padding, opacity }.',
|
|
23
23
|
x: 'First baseline x, px or "%" (like SVG <text>; align is the text-anchor).', y: 'First baseline y.', width: 'Justify every line to this width (like textLength).', justify: 'Justify every line to maxWidth.', lengthAdjust: 'spacingAndGlyphs (default: scale) | spacing', fit: 'width (the block fills width) | lines (each line fills width) | words (a word a line, the widest filling width) | wrap (wrap at width, then fill it) | shrink', stretch: '[sx, sy] or sy: stretch about the first baseline.', axes: 'Variable-font axes: { wdth: 50, SOFT: 100, WONK: 1, opsz: 144 }.', stroke: 'Outline colour.', strokeWidth: 'Outline width.', strokes: '[{ color, width }]: keylines, widest first, under the fill.', fill: 'A colour, a gradient, or "none" (outline type).', path: 'Text on a path: { kind: circle | arc | ellipse | wave, cx, cy, r, start, end, direction } or { d }.', startOffset: 'Along the path: 0–1 or px. Default 0.5 (centred).', fitPath: 'Space the text to fill the path (a ring); a number leaves that gap.', dy: 'Shift off the path.', render: 'outline (default) | live (<text> with @font-face)', autoColors: '[light, dark] for color: "auto". Default ["#ffffff", "#0b0b0b"].', autoThreshold: 'color: "auto" takes the dark colour above this backdrop luma (0–1). Default 0.72.', lines: 'Reserved.',
|
|
24
|
-
spans: '[{ content, font, weight, style, axes, scale, size, color, letterSpacing, dy }]: mixed runs on the same lines.', glyphs: '{ scale, scaleX, scaleY, dx, dy, rotate, color, origin, advance }: per-glyph arrays (cycled; spaces skipped).', fallback: 'A family (or list) for characters the font lacks; then fonts registered with fallback: true, then the built-in symbols.', split: 'words: a word a line.', dash: 'Dashes on the stroke: [on, off] lengths, dashed or dotted.', extrude: 'A depth, or { depth, angle, color, to, steps }: block extrusion.', maxHeight: 'Shrink the block to this height.' },
|
|
24
|
+
spans: '[{ content, font, weight, bold, style, axes, scale, size, color, letterSpacing, dy }]: mixed runs on the same lines.', glyphs: '{ scale, scaleX, scaleY, dx, dy, rotate, color, origin, advance }: per-glyph arrays (cycled; spaces skipped).', fallback: 'A family (or list) for characters the font lacks; then fonts registered with fallback: true, then the built-in symbols.', split: 'words: a word a line.', dash: 'Dashes on the stroke: [on, off] lengths, dashed or dotted.', extrude: 'A depth, or { depth, angle, color, to, steps }: block extrusion.', maxHeight: 'Shrink the block to this height.' },
|
|
25
25
|
diagram: { icon: 'One icon (Iconify name or raw SVG), drawn as white line-art.', nodes: '[{ icon, shape: square | circle | none, size, brackets }]', layout: 'single | row | triangle | square | stack | hub', arrows: 'true | false | loop | both | cycle', shape: 'For a single icon: none (default) | square | circle.', size: 'Node size. Length.', gap: 'Space between nodes. Length.', stroke: 'Line width. Default "0.3vmin".', color: 'Line colour. Default white.', radius: 'Tile corner radius.', iconScale: 'Icon size inside a tile, 0–1. Default 0.44.', position: '{ x, y } centre, fractions.', grid: 'true or { divisions, opacity, stroke, color, pad }: a dotted grid that breaks around the drawing.' },
|
|
26
26
|
paper: { count: 'Shapes in the collage. Default 4.', kind: 'mixed | rect | square | circle | blob | arch | pill', color: 'Default #faf9f5 (paper).', region: 'Where the collage sits, fractions.', items: '[{ kind, x, y, width, height, size, rotate, color }] placed in the region.', wobble: 'How rough the cut edges are, 0–1. Default 0.5.' },
|
|
27
27
|
scribble: { icon: 'An icon to re-draw by hand (Iconify name or raw SVG).', graph: 'true or { nodes, dot }: a hand-drawn node graph instead.', color: 'Default #faf9f5.', stroke: 'Line width. Default "1.3vmin".', size: 'Length. Default "46vmin".', position: '{ x, y } centre, fractions.', wobble: '0–1. Default 0.5.' },
|
|
@@ -43,14 +43,36 @@ export const LAYER_OPTIONS = {
|
|
|
43
43
|
perforation: { kind: 'holes (default) | dashes | notches', x: 'A vertical line at x.', x1: 'Start x.', y1: 'Start y.', x2: 'End x.', y2: 'End y.', size: 'Hole or notch radius.', gap: 'Spacing.', inset: 'Space before the first hole.', color: 'Default @text (or punch it with knockout).', strokeWidth: 'Dashes width.' },
|
|
44
44
|
tear: { from: '[x, y] where the tear starts (edge to edge).', to: '[x, y] where it ends.', side: 'Which side keeps the sheet: above (default) | below | left | right', color: 'The sheet (a colour, a gradient, or "none" for just the edge).', sheetOpacity: 'The sheet\'s opacity.', step: 'Spacing of the jagged points.', jitter: 'How jagged.', rip: 'false, or { color, width, opacity }: the torn white edge.', fibres: 'false, or { color, width, opacity, length: [min, max], spread }.', ripColor: 'Colour of the rip and fibres. Default #fff6fb.' },
|
|
45
45
|
texture: { kind: 'paper | creases | concrete | speck | wood | folds | dust', color: 'Fibre, aggregate or dust colour.', amount: 'Paper: fibre density.', frequency: 'Noise frequency (number or [x, y]).', octaves: 'Noise octaves.', depth: 'Creases: relief.', azimuth: 'Creases: light direction.', elevation: 'Creases: light height.', x: 'Folds: vertical fold positions, fractions.', y: 'Folds: horizontal fold positions, fractions.', width: 'Folds: band width.', strength: 'Folds: shading.', count: 'Dust: specks.', size: 'Dust: [min, max] radius.', region: 'Clip to { x, y, width, height } (px or "%").' },
|
|
46
|
+
mosaic: { markup: 'The mark\'s paths (512 grid).', x: 'Box x, px or %.', y: 'Box y.', width: 'Box width (default the canvas).', height: 'Box height.', cells: 'Cells across the mark\'s longest side, 4–48. Default 10.', gap: 'Gap between cells, a share of a cell. Default 0.12.', radius: 'Cell corner radius, a share of a cell. Default 0.', drop: 'Share of filled cells left out (seeded), 0–0.9. Default 0.12.', color: 'Default @accent.', accent: 'asterisk | dot: a small glyph in one empty cell beside the mark.', accentColor: 'Default color.' },
|
|
47
|
+
marks: { marks: 'The marks: paths on a 512 grid (or { markup }).', cols: 'Default from the count: 3 × 3 up to 10 × 7.', rows: 'Default from the count.', x: 'Box x, px or %.', y: 'Box y.', width: 'Box width (default the canvas).', height: 'Box height.', size: 'Share of a cell a mark\'s area takes. Default 0.5.', optical: 'false: fit each mark\'s bounds to the same box instead. Default true.', color: 'Default @text.' },
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
/** @type {Record<string, string>} Options every layer takes (besides type, id and when). */
|
|
49
51
|
export const COMMON = {
|
|
50
52
|
opacity: '0–1.', blend: 'A CSS mix-blend-mode.', seed: "This layer's own random stream.",
|
|
51
53
|
transform: '{ translate, rotate, scale, skewX, skewY, mirror: x | y, origin } (on text, a case keyword is still the case).',
|
|
52
|
-
effects: '[{ kind, mask, colorSpace, … }]: filters, pixel effects and copies, in order (see
|
|
54
|
+
effects: '[{ kind, mask, colorSpace, … }]: filters, pixel effects and copies, in order (see docs/effects.md; kinds in EFFECTS).',
|
|
53
55
|
mask: 'Layers whose luminance masks this one (white shows).', knockout: 'Layers cut out of this one.', clip: 'Layers this one shows only inside.',
|
|
54
56
|
area: 'trim | safe | bleed, or { x, y, width, height } fractions: draw the layer in that box.',
|
|
57
|
+
motion: 'A motion, a preset name (drift | spin | breathe | blink | marquee), or a list: plays in the SVG as CSS keyframes (see MOTION_OPTIONS).',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** @type {Record<string, string>} A motion's options. */
|
|
61
|
+
export const MOTION_OPTIONS = {
|
|
62
|
+
preset: 'drift | spin | breathe | blink | marquee: its defaults, under whatever else is set.',
|
|
63
|
+
translate: '[x, y] to move by (px in the layer\'s grid, "%" or lengths), or a list of points [[0, 0], [x, y], …].',
|
|
64
|
+
path: 'Travel a path from where the layer is: path data, { d }, or a text-path kind ({ kind: circle | ellipse | arc | wave, … }).',
|
|
65
|
+
rotate: 'Degrees to turn to, or a list of angles.',
|
|
66
|
+
scale: 'A factor to grow to, or a list.', scaleX: 'Horizontal factor, or a list.', scaleY: 'Vertical factor, or a list.',
|
|
67
|
+
opacity: '0–1 to fade to (times the layer\'s own), or a list.',
|
|
68
|
+
origin: '[x, y] the turn and scale are about (px, "%" or lengths). Default the centre.',
|
|
69
|
+
vary: 'Layer options as lists of stops ({ axes: { wdth: [62, 150] } }): drawn frame by frame, played as outline morphs.',
|
|
70
|
+
frames: 'vary: frames drawn per stop. Default 12.',
|
|
71
|
+
marquee: 'true: text on a path runs along it, one repeat of its words per duration (they must repeat).',
|
|
72
|
+
duration: 'Seconds per iteration. Default 2 (or the preset\'s).',
|
|
73
|
+
delay: 'Seconds before it starts; negative starts it part-way.',
|
|
74
|
+
easing: 'linear | ease | in | out | inOut | step, or [x1, y1, x2, y2]: between each pair of stops.',
|
|
75
|
+
direction: 'normal | reverse | alternate | alternate-reverse',
|
|
76
|
+
iterations: 'A number, or "infinite" (the default).',
|
|
55
77
|
};
|
|
56
78
|
|
package/src/layers/barcode.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @module layers/barcode
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { attrs, num } from '
|
|
8
|
-
import { len } from '
|
|
9
|
-
import { toHex } from '
|
|
7
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
8
|
+
import { len } from '@logomarks/image-effects/length';
|
|
9
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
10
10
|
import { faceFor, setLine } from '../type/line.js';
|
|
11
11
|
import { pos, wrap, contentOf } from './util.js';
|
|
12
12
|
import { code128 } from './code128.js';
|
package/src/layers/geometry.js
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
* @module layers/geometry
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { attrs, num } from '
|
|
10
|
-
import { len } from '
|
|
9
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
10
|
+
import { len } from '@logomarks/image-effects/length';
|
|
11
11
|
import { pos } from './util.js';
|
|
12
12
|
import { cutPath } from '../formats/cut.js';
|
|
13
|
-
import { invert, apply } from '../
|
|
13
|
+
import { invert, apply } from '../render/transform.js';
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -18,7 +18,7 @@ import { invert, apply } from '../effects/transform.js';
|
|
|
18
18
|
*
|
|
19
19
|
* @param {[number, number]} a @param {[number, number]} b
|
|
20
20
|
* @param {{ kind: string, size: number, step: number }} e
|
|
21
|
-
* @param {import('
|
|
21
|
+
* @param {import('@logomarks/image-effects/rng').Rng} rng
|
|
22
22
|
* @returns {[number, number][]} Points from a (exclusive) to b (inclusive).
|
|
23
23
|
*/
|
|
24
24
|
function edgePoints(a, b, e, rng) {
|
|
@@ -211,7 +211,7 @@ export function geometry(s, ctx) {
|
|
|
211
211
|
*
|
|
212
212
|
* @param {{ x: number, y: number, width: number, height: number, cut?: any, mm?: number } | undefined} cut - Trim box and cut, canvas px.
|
|
213
213
|
* @param {number} outset - Grow (or, negative, shrink) the outline, in the layer's units.
|
|
214
|
-
* @param {import('../
|
|
214
|
+
* @param {import('../render/transform.js').Affine | undefined} matrix - Layer units to canvas px.
|
|
215
215
|
* @param {{ width: number, height: number }} canvas
|
|
216
216
|
* @returns {{ d: string, box: { x: number, y: number, width: number, height: number } }}
|
|
217
217
|
*/
|
package/src/layers/index.d.ts
CHANGED
|
@@ -14,5 +14,11 @@ export { tear } from './tear.js';
|
|
|
14
14
|
export type { TearLayer } from './tear.js';
|
|
15
15
|
export { texture } from './texture.js';
|
|
16
16
|
export type { TextureLayer } from './texture.js';
|
|
17
|
+
export { mosaic, mosaicGrid } from './mosaic.js';
|
|
18
|
+
export type { MosaicLayer } from './mosaic.js';
|
|
19
|
+
export { marks, sheetGrid } from './marks.js';
|
|
20
|
+
export type { MarksLayer } from './marks.js';
|
|
21
|
+
export { markGeometry } from './mark-geometry.js';
|
|
22
|
+
export type { MarkGeometry } from './mark-geometry.js';
|
|
17
23
|
export { expandRepeat } from './repeat.js';
|
|
18
24
|
export type { RepeatLayer } from './repeat.js';
|
package/src/layers/index.js
CHANGED
|
@@ -12,4 +12,7 @@ export { code128 } from './code128.js';
|
|
|
12
12
|
export { perforation } from './perforation.js';
|
|
13
13
|
export { tear } from './tear.js';
|
|
14
14
|
export { texture } from './texture.js';
|
|
15
|
+
export { mosaic, mosaicGrid } from './mosaic.js';
|
|
16
|
+
export { marks, sheetGrid } from './marks.js';
|
|
17
|
+
export { markGeometry } from './mark-geometry.js';
|
|
15
18
|
export { expandRepeat } from './repeat.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// A mark's geometry from its markup, read with @logomarks/core.
|
|
2
|
+
import type { FillPath } from '@logomarks/core/raster';
|
|
3
|
+
|
|
4
|
+
/** A mark read from its paths. */
|
|
5
|
+
export interface MarkGeometry {
|
|
6
|
+
/** Its filled paths as rings, with their fill rules. */
|
|
7
|
+
paths: FillPath[];
|
|
8
|
+
/** Its real bounds, grid units. */
|
|
9
|
+
box: { x: number; y: number; width: number; height: number };
|
|
10
|
+
/** The share of its box it inks, 0–1. */
|
|
11
|
+
density: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Read a mark (paths on a 512 grid); null when it draws nothing. */
|
|
15
|
+
export function markGeometry(markup: string): MarkGeometry | null;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A mark's geometry from its markup (paths on a 512 grid), read with
|
|
3
|
+
* `@logomarks/core`: its filled paths as rings, its real bounds, and how
|
|
4
|
+
* much of that box it inks. Shared by the `mosaic` and `marks` layers.
|
|
5
|
+
*
|
|
6
|
+
* @module layers/mark-geometry
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { shapes, rings, bounds } from '@logomarks/core/svg';
|
|
10
|
+
import { pixelGrid, pixelCoverage } from '@logomarks/core/edit';
|
|
11
|
+
|
|
12
|
+
/** Read marks, by markup (a sheet of seventy reads each once). @type {Map<string, MarkGeometry>} */
|
|
13
|
+
const cache = new Map();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {object} MarkGeometry
|
|
17
|
+
* @property {import('@logomarks/core/raster').FillPath[]} paths - Its filled paths as rings, with their fill rules.
|
|
18
|
+
* @property {{ x: number, y: number, width: number, height: number }} box - Its real bounds, grid units.
|
|
19
|
+
* @property {number} density - The share of its box it inks, 0–1.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Read a mark.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} markup - The mark's paths (`<path d="…"/>`, on a 512 grid).
|
|
26
|
+
* @returns {MarkGeometry | null} Null when it draws nothing.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* markGeometry('<path d="M96 96h320v320H96z"/>').box; // { x: 96, y: 96, width: 320, height: 320 }
|
|
30
|
+
*/
|
|
31
|
+
export function markGeometry(markup) {
|
|
32
|
+
if (cache.has(markup)) return /** @type {MarkGeometry} */ (cache.get(markup));
|
|
33
|
+
const filled = shapes(String(markup ?? ''));
|
|
34
|
+
const paths = filled.map((s) => ({ rings: rings(s.segs), evenodd: s.evenodd })).filter((p) => p.rings.length);
|
|
35
|
+
if (!paths.length) return null;
|
|
36
|
+
const b = bounds(filled);
|
|
37
|
+
const box = { x: b.x, y: b.y, width: Math.max(b.w, 1e-6), height: Math.max(b.h, 1e-6) };
|
|
38
|
+
// Ink measured on a 48-cell grid over the box: plenty for a density.
|
|
39
|
+
const grid = pixelGrid(0, box);
|
|
40
|
+
const cov = pixelCoverage(paths, grid);
|
|
41
|
+
const density = cov.reduce((a, v) => a + v, 0) / Math.max(1, cov.length);
|
|
42
|
+
const geo = { paths, box, density };
|
|
43
|
+
if (cache.size > 500) cache.clear();
|
|
44
|
+
cache.set(markup, geo);
|
|
45
|
+
return geo;
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// The `marks` layer.
|
|
2
|
+
import type { Color, Length, BaseLayer } from '../index.js';
|
|
3
|
+
import type { RenderContext, Markup } from '../render/context.js';
|
|
4
|
+
|
|
5
|
+
export interface MarksLayer extends BaseLayer {
|
|
6
|
+
type: 'marks';
|
|
7
|
+
/** The marks: their paths on a 512 grid (or `{ markup }`). */
|
|
8
|
+
marks: (string | { markup: string })[];
|
|
9
|
+
/** Default from the count and the box: 3 × 3 up to 10 × 7. */
|
|
10
|
+
cols?: number;
|
|
11
|
+
rows?: number;
|
|
12
|
+
/** The box the grid is centred in (default the canvas). */
|
|
13
|
+
x?: Length | `${number}%`; y?: Length | `${number}%`; width?: Length | `${number}%`; height?: Length | `${number}%`;
|
|
14
|
+
/** The share of a cell a mark's area takes. Default 0.5. */
|
|
15
|
+
size?: number;
|
|
16
|
+
/** `false`: each mark's bounds fitted to the same box, not optically sized. Default true. */
|
|
17
|
+
optical?: boolean;
|
|
18
|
+
/** Default the palette's text. */
|
|
19
|
+
color?: Color;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Draw a set of marks on an even grid, optically sized the same. */
|
|
23
|
+
export function marks(layer: MarksLayer, ctx: RenderContext): Markup;
|
|
24
|
+
/** Columns and rows for `n` marks in a box of `aspect` (3 × 3 to 10 × 7; given ones kept). */
|
|
25
|
+
export function sheetGrid(n: number, aspect: number, cols?: number, rows?: number): { cols: number; rows: number };
|