@arraypress/covers 0.0.0-stage → 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 (245) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/LICENSE +29 -0
  3. package/README.md +846 -2
  4. package/fonts/OFL.txt +93 -0
  5. package/fonts/inter-latin-400.woff +0 -0
  6. package/fonts/inter-latin-500.woff +0 -0
  7. package/fonts/inter-latin-600.woff +0 -0
  8. package/fonts/inter-latin-700.woff +0 -0
  9. package/package.json +124 -4
  10. package/recipes/aurora.json +132 -0
  11. package/recipes/bauhaus.json +113 -0
  12. package/recipes/bigType.json +103 -0
  13. package/recipes/brand.json +1143 -0
  14. package/recipes/contour.json +109 -0
  15. package/recipes/cutout.json +191 -0
  16. package/recipes/diagram.json +92 -0
  17. package/recipes/dither.json +170 -0
  18. package/recipes/duotone.json +75 -0
  19. package/recipes/gridTitle.json +136 -0
  20. package/recipes/iconTile.json +214 -0
  21. package/recipes/issue.json +221 -0
  22. package/recipes/mark.json +170 -0
  23. package/recipes/photo.json +156 -0
  24. package/recipes/riso.json +387 -0
  25. package/recipes/stripes.json +206 -0
  26. package/recipes/waveform.json +154 -0
  27. package/schema/recipe.schema.json +8822 -0
  28. package/src/animate/index.d.ts +19 -0
  29. package/src/animate/index.js +116 -0
  30. package/src/astro.d.ts +31 -0
  31. package/src/astro.js +86 -0
  32. package/src/builtins.js +4 -0
  33. package/src/color.js +246 -0
  34. package/src/diagram.js +248 -0
  35. package/src/effects/ascii.d.ts +7 -0
  36. package/src/effects/ascii.js +93 -0
  37. package/src/effects/buffer.d.ts +60 -0
  38. package/src/effects/buffer.js +472 -0
  39. package/src/effects/detail.d.ts +12 -0
  40. package/src/effects/detail.js +124 -0
  41. package/src/effects/digital.d.ts +14 -0
  42. package/src/effects/digital.js +216 -0
  43. package/src/effects/distort.d.ts +20 -0
  44. package/src/effects/distort.js +314 -0
  45. package/src/effects/dither.d.ts +8 -0
  46. package/src/effects/dither.js +147 -0
  47. package/src/effects/drop-shadow.d.ts +4 -0
  48. package/src/effects/drop-shadow.js +45 -0
  49. package/src/effects/extrude.d.ts +5 -0
  50. package/src/effects/extrude.js +55 -0
  51. package/src/effects/film.d.ts +11 -0
  52. package/src/effects/film.js +204 -0
  53. package/src/effects/glow.d.ts +8 -0
  54. package/src/effects/glow.js +56 -0
  55. package/src/effects/grade.d.ts +30 -0
  56. package/src/effects/grade.js +274 -0
  57. package/src/effects/halftone.d.ts +8 -0
  58. package/src/effects/halftone.js +95 -0
  59. package/src/effects/index.d.ts +125 -0
  60. package/src/effects/index.js +277 -0
  61. package/src/effects/ink.d.ts +12 -0
  62. package/src/effects/ink.js +70 -0
  63. package/src/effects/lens.d.ts +14 -0
  64. package/src/effects/lens.js +277 -0
  65. package/src/effects/light.d.ts +10 -0
  66. package/src/effects/light.js +59 -0
  67. package/src/effects/looks.d.ts +9 -0
  68. package/src/effects/looks.js +74 -0
  69. package/src/effects/mask.d.ts +52 -0
  70. package/src/effects/mask.js +314 -0
  71. package/src/effects/motion.d.ts +6 -0
  72. package/src/effects/motion.js +60 -0
  73. package/src/effects/newsprint.d.ts +6 -0
  74. package/src/effects/newsprint.js +89 -0
  75. package/src/effects/optics.d.ts +10 -0
  76. package/src/effects/optics.js +213 -0
  77. package/src/effects/options.d.ts +6 -0
  78. package/src/effects/options.js +106 -0
  79. package/src/effects/paint-presets.d.ts +10 -0
  80. package/src/effects/paint-presets.js +47 -0
  81. package/src/effects/paint.d.ts +49 -0
  82. package/src/effects/paint.js +66 -0
  83. package/src/effects/pixels.d.ts +24 -0
  84. package/src/effects/pixels.js +138 -0
  85. package/src/effects/polar.d.ts +10 -0
  86. package/src/effects/polar.js +265 -0
  87. package/src/effects/region.d.ts +8 -0
  88. package/src/effects/region.js +136 -0
  89. package/src/effects/round-border.d.ts +5 -0
  90. package/src/effects/round-border.js +62 -0
  91. package/src/effects/screen.d.ts +14 -0
  92. package/src/effects/screen.js +212 -0
  93. package/src/effects/sort.d.ts +8 -0
  94. package/src/effects/sort.js +125 -0
  95. package/src/effects/symmetry.d.ts +6 -0
  96. package/src/effects/symmetry.js +74 -0
  97. package/src/effects/tile.d.ts +7 -0
  98. package/src/effects/tile.js +55 -0
  99. package/src/effects/tone.d.ts +16 -0
  100. package/src/effects/tone.js +117 -0
  101. package/src/effects/transform.d.ts +35 -0
  102. package/src/effects/transform.js +127 -0
  103. package/src/effects/util.d.ts +16 -0
  104. package/src/effects/util.js +85 -0
  105. package/src/extract.js +91 -0
  106. package/src/flatten.js +234 -0
  107. package/src/fonts.d.ts +55 -0
  108. package/src/fonts.js +393 -0
  109. package/src/formats/cut.d.ts +10 -0
  110. package/src/formats/cut.js +77 -0
  111. package/src/formats/index.d.ts +6 -0
  112. package/src/formats/index.js +9 -0
  113. package/src/formats/presets.d.ts +41 -0
  114. package/src/formats/presets.js +89 -0
  115. package/src/formats/print.d.ts +25 -0
  116. package/src/formats/print.js +117 -0
  117. package/src/formats/zones.d.ts +10 -0
  118. package/src/formats/zones.js +64 -0
  119. package/src/geometry.js +187 -0
  120. package/src/grid.js +41 -0
  121. package/src/handmade.js +253 -0
  122. package/src/icons.js +191 -0
  123. package/src/index.d.ts +1083 -0
  124. package/src/index.js +27 -0
  125. package/src/inter-data.js +27 -0
  126. package/src/json-recipe.js +616 -0
  127. package/src/kit/index.d.ts +20 -0
  128. package/src/kit/index.js +58 -0
  129. package/src/layer-options.js +56 -0
  130. package/src/layers/barcode.d.ts +19 -0
  131. package/src/layers/barcode.js +63 -0
  132. package/src/layers/code128.d.ts +4 -0
  133. package/src/layers/code128.js +28 -0
  134. package/src/layers/expr.d.ts +6 -0
  135. package/src/layers/expr.js +121 -0
  136. package/src/layers/geometry.d.ts +6 -0
  137. package/src/layers/geometry.js +238 -0
  138. package/src/layers/index.d.ts +18 -0
  139. package/src/layers/index.js +15 -0
  140. package/src/layers/perforation.d.ts +14 -0
  141. package/src/layers/perforation.js +47 -0
  142. package/src/layers/repeat.d.ts +29 -0
  143. package/src/layers/repeat.js +90 -0
  144. package/src/layers/shape.d.ts +46 -0
  145. package/src/layers/shape.js +115 -0
  146. package/src/layers/soft.d.ts +5 -0
  147. package/src/layers/soft.js +43 -0
  148. package/src/layers/svg.d.ts +23 -0
  149. package/src/layers/svg.js +109 -0
  150. package/src/layers/table.d.ts +25 -0
  151. package/src/layers/table.js +80 -0
  152. package/src/layers/tear.d.ts +21 -0
  153. package/src/layers/tear.js +74 -0
  154. package/src/layers/texture.d.ts +17 -0
  155. package/src/layers/texture.js +106 -0
  156. package/src/layers/util.d.ts +8 -0
  157. package/src/layers/util.js +48 -0
  158. package/src/layers.js +1056 -0
  159. package/src/lint/image-size.d.ts +4 -0
  160. package/src/lint/image-size.js +34 -0
  161. package/src/lint/index.d.ts +16 -0
  162. package/src/lint/index.js +127 -0
  163. package/src/lint/rules.d.ts +5 -0
  164. package/src/lint/rules.js +19 -0
  165. package/src/node.d.ts +2 -0
  166. package/src/node.js +20 -0
  167. package/src/palette.js +185 -0
  168. package/src/platform/bytes.d.ts +22 -0
  169. package/src/platform/bytes.js +103 -0
  170. package/src/platform/compress.d.ts +12 -0
  171. package/src/platform/compress.js +81 -0
  172. package/src/platform/harfbuzz.d.ts +9 -0
  173. package/src/platform/harfbuzz.js +80 -0
  174. package/src/platform/index.d.ts +8 -0
  175. package/src/platform/index.js +13 -0
  176. package/src/platform/load.d.ts +6 -0
  177. package/src/platform/load.js +72 -0
  178. package/src/platform/no-harfbuzz.d.ts +2 -0
  179. package/src/platform/no-harfbuzz.js +11 -0
  180. package/src/platform/node.d.ts +26 -0
  181. package/src/platform/node.js +86 -0
  182. package/src/platform/settings.d.ts +28 -0
  183. package/src/platform/settings.js +51 -0
  184. package/src/png.js +126 -0
  185. package/src/print.js +659 -0
  186. package/src/raster/index.d.ts +24 -0
  187. package/src/raster/index.js +171 -0
  188. package/src/recipes.js +79 -0
  189. package/src/render/backdrop.d.ts +6 -0
  190. package/src/render/backdrop.js +55 -0
  191. package/src/render/content.d.ts +5 -0
  192. package/src/render/content.js +58 -0
  193. package/src/render/context.d.ts +82 -0
  194. package/src/render/grain.d.ts +7 -0
  195. package/src/render/grain.js +113 -0
  196. package/src/render/group.d.ts +23 -0
  197. package/src/render/group.js +67 -0
  198. package/src/render/key.d.ts +5 -0
  199. package/src/render/key.js +91 -0
  200. package/src/render/report.d.ts +5 -0
  201. package/src/render/report.js +19 -0
  202. package/src/render/tiles.d.ts +6 -0
  203. package/src/render/tiles.js +77 -0
  204. package/src/render/use.d.ts +27 -0
  205. package/src/render/use.js +73 -0
  206. package/src/render/wrap.d.ts +14 -0
  207. package/src/render/wrap.js +199 -0
  208. package/src/render.js +553 -0
  209. package/src/rng.js +95 -0
  210. package/src/svg.js +69 -0
  211. package/src/text.d.ts +29 -0
  212. package/src/text.js +237 -0
  213. package/src/type/auto-color.d.ts +9 -0
  214. package/src/type/auto-color.js +44 -0
  215. package/src/type/fallback.d.ts +11 -0
  216. package/src/type/fallback.js +92 -0
  217. package/src/type/glyphs.d.ts +24 -0
  218. package/src/type/glyphs.js +87 -0
  219. package/src/type/index.d.ts +5 -0
  220. package/src/type/index.js +10 -0
  221. package/src/type/kern.d.ts +4 -0
  222. package/src/type/kern.js +144 -0
  223. package/src/type/layout-ref.d.ts +10 -0
  224. package/src/type/layout-ref.js +74 -0
  225. package/src/type/line.d.ts +11 -0
  226. package/src/type/line.js +105 -0
  227. package/src/type/live.d.ts +6 -0
  228. package/src/type/live.js +39 -0
  229. package/src/type/name.d.ts +4 -0
  230. package/src/type/name.js +32 -0
  231. package/src/type/path.d.ts +18 -0
  232. package/src/type/path.js +89 -0
  233. package/src/type/pixel.d.ts +9 -0
  234. package/src/type/pixel.js +115 -0
  235. package/src/type/sfnt.d.ts +20 -0
  236. package/src/type/sfnt.js +258 -0
  237. package/src/type/spans.d.ts +33 -0
  238. package/src/type/spans.js +138 -0
  239. package/src/type/symbols.d.ts +7 -0
  240. package/src/type/symbols.js +89 -0
  241. package/src/type/typeset.d.ts +10 -0
  242. package/src/type/typeset.js +352 -0
  243. package/src/type/woff2.d.ts +6 -0
  244. package/src/type/woff2.js +301 -0
  245. package/src/version.js +4 -0
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Pair kerning from a TrueType font: GPOS `kern` feature pair adjustments
3
+ * (formats 1 and 2, through extension lookups; lookups add up), else the
4
+ * legacy `kern` table.
5
+ *
6
+ * @module type/kern
7
+ */
8
+
9
+ /**
10
+ * Pair kerning: GPOS `kern` feature pair adjustments (formats 1 and 2,
11
+ * through extension lookups), else the legacy `kern` table.
12
+ *
13
+ * @param {DataView} v
14
+ * @param {Record<string, number>} tables
15
+ * @returns {(a: number, b: number) => number} X advance adjustment, font units.
16
+ */
17
+ export function pairKerning(v, tables) {
18
+ const u16 = (/** @type {number} */ o) => v.getUint16(o);
19
+ const i16 = (/** @type {number} */ o) => v.getInt16(o);
20
+ /** Pair lookups, each a list of subtables (the first that covers a pair applies; lookups add up). @type {((a: number, b: number) => number | undefined)[][]} */
21
+ const lookupsList = [];
22
+ /** @type {((a: number, b: number) => number | undefined)[]} */
23
+ let subtables = [];
24
+
25
+ const coverage = (/** @type {number} */ o) => {
26
+ const fmt = u16(o);
27
+ /** @type {Map<number, number>} */
28
+ const m = new Map();
29
+ if (fmt === 1) for (let i = 0, n = u16(o + 2); i < n; i++) m.set(u16(o + 4 + i * 2), i);
30
+ else for (let i = 0, n = u16(o + 2); i < n; i++) {
31
+ const r = o + 4 + i * 6;
32
+ for (let g = u16(r), idx = u16(r + 4); g <= u16(r + 2); g++, idx++) m.set(g, idx);
33
+ }
34
+ return m;
35
+ };
36
+ const classDef = (/** @type {number} */ o) => {
37
+ const fmt = u16(o);
38
+ /** @type {Map<number, number>} */
39
+ const m = new Map();
40
+ if (fmt === 1) { const s = u16(o + 2); for (let i = 0, n = u16(o + 4); i < n; i++) m.set(s + i, u16(o + 6 + i * 2)); }
41
+ else for (let i = 0, n = u16(o + 2); i < n; i++) { const r = o + 4 + i * 6; for (let g = u16(r); g <= u16(r + 2); g++) m.set(g, u16(r + 4)); }
42
+ return m;
43
+ };
44
+ const bits = (/** @type {number} */ f) => { let c = 0; for (let x = f; x; x >>= 1) c += x & 1; return c * 2; };
45
+ // XAdvance of a value record (or XPlacement, the rare alternative).
46
+ const xadv = (/** @type {number} */ o, /** @type {number} */ fmt) => {
47
+ let p = o;
48
+ let x = 0;
49
+ if (fmt & 1) { x += i16(p); p += 2; }
50
+ if (fmt & 2) p += 2;
51
+ if (fmt & 4) x += i16(p);
52
+ return x;
53
+ };
54
+
55
+ const pairPos = (/** @type {number} */ st) => {
56
+ const fmt = u16(st);
57
+ const cov = coverage(st + u16(st + 2));
58
+ const vf1 = u16(st + 4);
59
+ const vf2 = u16(st + 6);
60
+ const s1 = bits(vf1);
61
+ const s2 = bits(vf2);
62
+ if (fmt === 1) {
63
+ const sets = st + 10;
64
+ subtables.push((a, b) => {
65
+ const idx = cov.get(a);
66
+ if (idx === undefined) return undefined;
67
+ const ps = st + u16(sets + idx * 2);
68
+ const n = u16(ps);
69
+ const rec = 2 + s1 + s2;
70
+ let lo = 0;
71
+ let hi = n - 1;
72
+ while (lo <= hi) {
73
+ const mid = (lo + hi) >> 1;
74
+ const r = ps + 2 + mid * rec;
75
+ const g = u16(r);
76
+ if (g === b) return xadv(r + 2, vf1);
77
+ if (g < b) lo = mid + 1; else hi = mid - 1;
78
+ }
79
+ return undefined;
80
+ });
81
+ } else if (fmt === 2) {
82
+ const c1 = classDef(st + u16(st + 8));
83
+ const c2 = classDef(st + u16(st + 10));
84
+ const n2 = u16(st + 14);
85
+ const rec = s1 + s2;
86
+ subtables.push((a, b) => {
87
+ if (!cov.has(a)) return undefined;
88
+ const r = st + 16 + ((c1.get(a) ?? 0) * n2 + (c2.get(b) ?? 0)) * rec;
89
+ return xadv(r, vf1);
90
+ });
91
+ }
92
+ };
93
+
94
+ const gpos = tables.GPOS;
95
+ if (gpos != null) {
96
+ const features = gpos + u16(gpos + 6);
97
+ const lookups = gpos + u16(gpos + 8);
98
+ /** @type {Set<number>} */
99
+ const wanted = new Set();
100
+ for (let i = 0, n = u16(features); i < n; i++) {
101
+ const r = features + 2 + i * 6;
102
+ if (String.fromCharCode(v.getUint8(r), v.getUint8(r + 1), v.getUint8(r + 2), v.getUint8(r + 3)) !== 'kern') continue;
103
+ const f = features + u16(r + 4);
104
+ for (let k = 0, m = u16(f + 2); k < m; k++) wanted.add(u16(f + 4 + k * 2));
105
+ }
106
+ for (const li of [...wanted].sort((a, b) => a - b)) {
107
+ subtables = [];
108
+ lookupsList.push(subtables);
109
+ const l = lookups + u16(lookups + 2 + li * 2);
110
+ const type = u16(l);
111
+ for (let k = 0, m = u16(l + 4); k < m; k++) {
112
+ let st = l + u16(l + 6 + k * 2);
113
+ let t = type;
114
+ if (t === 9) { t = u16(st + 2); st += v.getUint32(st + 4); }
115
+ if (t === 2) pairPos(st);
116
+ }
117
+ }
118
+ }
119
+ if (!lookupsList.some((l) => l.length) && tables.kern != null && u16(tables.kern) === 0) {
120
+ subtables = [];
121
+ lookupsList.push(subtables);
122
+ const k = tables.kern;
123
+ const st = k + 4;
124
+ if (u16(st + 4) >> 8 === 0) {
125
+ /** @type {Map<number, number>} */
126
+ const pairs = new Map();
127
+ for (let i = 0, n = u16(st + 6); i < n; i++) {
128
+ const r = st + 14 + i * 6;
129
+ pairs.set(u16(r) * 65536 + u16(r + 2), i16(r + 4));
130
+ }
131
+ subtables.push((a, b) => pairs.get(a * 65536 + b));
132
+ }
133
+ }
134
+ return (a, b) => {
135
+ let total = 0;
136
+ for (const list of lookupsList) {
137
+ for (const s of list) {
138
+ const x = s(a, b);
139
+ if (x !== undefined) { total += x; break; }
140
+ }
141
+ }
142
+ return total;
143
+ };
144
+ }
@@ -0,0 +1,10 @@
1
+ // Text layout later layers can read: {layout.<id>.<field>}.
2
+
3
+ /** Fields a reference can read. */
4
+ export const LAYOUT_FIELDS: readonly ('x' | 'y' | 'top' | 'bottom' | 'left' | 'right' | 'width' | 'height' | 'baseline' | 'size' | 'lines')[];
5
+ /** Whether a value holds a layout reference (outside nested layers). */
6
+ export function hasLayoutRef(v: unknown): boolean;
7
+ /** A layer with its layout references replaced by numbers. */
8
+ export function resolveLayoutRefs<T>(v: T, layout: Map<string, Record<string, number>>): T;
9
+ /** Record a text block's layout for later layers. */
10
+ export function recordLayout(ctx: { layout?: Map<string, Record<string, number>> }, id: string | undefined, box: { x: number; y: number; width: number; height: number }, more: { x: number; y: number; baseline: number; size: number; lines: number }): void;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Text layout other layers can read: a text layer with an `id` records
3
+ * where its block landed, and any later layer can use `{layout.<id>.<field>}`
4
+ * (optionally `+ n` or `- n`) as a number: `bottom`, `top`, `left`,
5
+ * `right`, `width`, `height`, `x`, `y` (the first baseline), `baseline`
6
+ * (the last one), `size` (as fitted) and `lines`. Units are the text
7
+ * layer's own (a group's grid inside a group).
8
+ *
9
+ * @module type/layout-ref
10
+ */
11
+
12
+ /** A layout reference, whole or inside a string. */
13
+ const TOKEN = /\{layout\.([\w-]+)\.(\w+)\s*(?:([+-])\s*([\d.]+))?\}/g;
14
+
15
+ /** Fields a reference can read. */
16
+ export const LAYOUT_FIELDS = Object.freeze(['x', 'y', 'top', 'bottom', 'left', 'right', 'width', 'height', 'baseline', 'size', 'lines']);
17
+
18
+ /**
19
+ * Whether a value holds a layout reference (layers nested in it aside: they
20
+ * resolve when they're drawn).
21
+ *
22
+ * @param {unknown} v
23
+ * @returns {boolean}
24
+ */
25
+ export function hasLayoutRef(v) {
26
+ if (typeof v === 'string') return v.includes('{layout.');
27
+ if (Array.isArray(v)) return v.some(hasLayoutRef);
28
+ if (v && typeof v === 'object' && !(v instanceof Uint8Array)) return Object.entries(v).some(([k, x]) => !NESTED.has(k) && hasLayoutRef(x));
29
+ return false;
30
+ }
31
+
32
+ /** Options holding nested layers. */
33
+ const NESTED = new Set(['layers', 'mask', 'knockout', 'clip']);
34
+
35
+ /**
36
+ * A layer with its layout references replaced by numbers.
37
+ *
38
+ * @param {any} v
39
+ * @param {Map<string, Record<string, number>>} layout
40
+ * @returns {any}
41
+ * @throws {TypeError} On a reference to a text layer not drawn yet, or an unknown field.
42
+ */
43
+ export function resolveLayoutRefs(v, layout) {
44
+ if (typeof v === 'string') {
45
+ const one = new RegExp(`^${TOKEN.source}$`).exec(v.trim());
46
+ if (one) return value(one, layout);
47
+ return v.replace(TOKEN, (...m) => String(value(/** @type {any} */ (m), layout)));
48
+ }
49
+ if (Array.isArray(v)) return v.map((x) => resolveLayoutRefs(x, layout));
50
+ if (v && typeof v === 'object' && !(v instanceof Uint8Array)) return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, NESTED.has(k) ? x : resolveLayoutRefs(x, layout)]));
51
+ return v;
52
+ }
53
+
54
+ /** @param {string[]} m @param {Map<string, Record<string, number>>} layout @returns {number} */
55
+ function value(m, layout) {
56
+ const [, id, field, sign, n] = m;
57
+ const box = layout.get(id);
58
+ if (!box) throw new TypeError(`{layout.${id}.${field}}: no text layer "${id}" drawn before this one`);
59
+ if (!LAYOUT_FIELDS.includes(field)) throw new TypeError(`{layout.${id}.${field}}: unknown field (${LAYOUT_FIELDS.join(', ')})`);
60
+ return box[field] + (sign ? (sign === '-' ? -1 : 1) * Number(n) : 0);
61
+ }
62
+
63
+ /**
64
+ * Record a text block's layout for later layers.
65
+ *
66
+ * @param {any} ctx
67
+ * @param {string | undefined} id
68
+ * @param {{ x: number, y: number, width: number, height: number }} box - The block, from cap height to last baseline.
69
+ * @param {{ x: number, y: number, baseline: number, size: number, lines: number }} more
70
+ */
71
+ export function recordLayout(ctx, id, box, more) {
72
+ if (!id || !ctx.layout) return;
73
+ ctx.layout.set(id, { ...more, top: box.y, bottom: box.y + box.height, left: box.x, right: box.x + box.width, width: box.width, height: box.height });
74
+ }
@@ -0,0 +1,11 @@
1
+ // Setting one line of type.
2
+ import type { LoadedFont } from '../fonts.js';
3
+ import type { Shaped } from '../text.js';
4
+ import type { RenderContext } from '../render/context.js';
5
+
6
+ /** A face for a family, weight, style and variable axes (`'pixel'`: the built-in 5×7 face). */
7
+ export function faceFor(ctx: RenderContext, o: { font?: string; weight?: number; style?: string; axes?: Record<string, number> }): Promise<LoadedFont>;
8
+ /** Glyph outlines of a run through a map from glyph px (y down, baseline 0) to canvas px. */
9
+ export function mappedPath(face: LoadedFont, run: Shaped, size: number, map: (gx: number, gy: number, glyph: Shaped['glyphs'][number]) => [number, number], only?: (glyph: Shaped['glyphs'][number]) => boolean): string;
10
+ /** Shape, justify (`width`) and draw one line anchored at `x` by `align`. */
11
+ export function setLine(o: { face: LoadedFont; text: string; size: number; tracking: number; x: number; y: number; align: 'left' | 'center' | 'right'; width?: number; adjust?: 'spacingAndGlyphs' | 'spacing'; shape?: (tracking: number) => Shaped; only?: (glyph: Shaped['glyphs'][number]) => boolean }): { d: string; width: number; left: number; run: Shaped; scaleX: number };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Setting one line of type: a face for a layer, glyph outlines through any
3
+ * affine map, and a line justified to a width (`textLength`).
4
+ *
5
+ * @module type/line
6
+ */
7
+
8
+ import { num } from '../svg.js';
9
+ import { loadFont, pickFont, variationsFor } from '../fonts.js';
10
+ import { shape, outline } from '../text.js';
11
+ import { pixelFace } from './pixel.js';
12
+
13
+ /**
14
+ * A face for a layer's family, weight, style and axes (`'pixel'` is the
15
+ * built-in 5×7 face, unless a font of that name is registered).
16
+ *
17
+ * @param {import('../layers.js').RenderContext} ctx
18
+ * @param {{ font?: string, weight?: number, style?: string, axes?: Record<string, number> }} o
19
+ * @returns {Promise<import('../fonts.js').LoadedFont>}
20
+ */
21
+ export async function faceFor(ctx, o) {
22
+ if (o.font === 'pixel' && !ctx.fonts.some((f) => f.name === 'pixel')) return pixelFace();
23
+ const weight = o.weight ?? 600;
24
+ const source = pickFont(ctx.fonts, o.font ?? 'Inter', weight, o.style);
25
+ return loadFont(source, variationsFor(source, weight, o.axes));
26
+ }
27
+
28
+ /**
29
+ * Glyph outlines of a shaped run, through an affine map: glyph point
30
+ * (gx, gy) in px (y down, baseline 0) goes to `map(gx, gy)`.
31
+ *
32
+ * @param {import('../fonts.js').LoadedFont} face
33
+ * @param {import('../text.js').Shaped} run
34
+ * @param {number} size
35
+ * @param {(gx: number, gy: number, glyph: { x: number, y: number }) => [number, number]} map
36
+ * @param {(glyph: any) => boolean} [only] - Draw only these glyphs (one paint at a time).
37
+ * @returns {string}
38
+ */
39
+ export function mappedPath(face, run, size, map, only) {
40
+ const parts = [];
41
+ for (const g of run.glyphs) {
42
+ if (only && !only(g)) continue;
43
+ const f = g.face ?? face;
44
+ const s = (g.size ?? size) / f.upem;
45
+ const t = g.t;
46
+ let coords = [];
47
+ let even = true;
48
+ let px = 0;
49
+ for (const c of outline(f, g.gid)) {
50
+ if (typeof c === 'string') {
51
+ if (coords.length) parts.push(coords.join(' '));
52
+ parts.push(c);
53
+ coords = [];
54
+ even = true;
55
+ } else if (even) {
56
+ px = c;
57
+ even = false;
58
+ } else {
59
+ // A glyph's own transform (per-glyph scale, turn, shift) is about its pen position.
60
+ const gx = px * s;
61
+ const gy = -c * s;
62
+ const [X, Y] = t ? map(g.x + t[0] * gx + t[2] * gy + t[4], g.y + t[1] * gx + t[3] * gy + t[5], g) : map(g.x + gx, g.y + gy, g);
63
+ coords.push(num(X), num(Y));
64
+ even = true;
65
+ }
66
+ }
67
+ if (coords.length) parts.push(coords.join(' '));
68
+ }
69
+ return parts.join('');
70
+ }
71
+
72
+ /**
73
+ * Set one line: shape, justify and draw.
74
+ *
75
+ * @param {object} o
76
+ * @param {import('../fonts.js').LoadedFont} o.face
77
+ * @param {string} o.text
78
+ * @param {number} o.size
79
+ * @param {number} o.tracking - Letter-spacing, px.
80
+ * @param {number} o.x - Anchor x (start, middle or end by `align`).
81
+ * @param {number} o.y - Baseline.
82
+ * @param {'left' | 'center' | 'right'} o.align
83
+ * @param {number} [o.width] - Justify to this width.
84
+ * @param {'spacingAndGlyphs' | 'spacing'} [o.adjust]
85
+ * @param {(tracking: number) => import('../text.js').Shaped} [o.shape] - Shapes the line at a tracking (fallback faces, spans, per-glyph control); default the face alone.
86
+ * @param {(glyph: any) => boolean} [o.only] - Draw only these glyphs.
87
+ * @returns {{ d: string, width: number, left: number, run: import('../text.js').Shaped, scaleX: number }}
88
+ */
89
+ export function setLine(o) {
90
+ const shapeAt = o.shape ?? ((/** @type {number} */ tracking) => shape(o.face, o.text, o.size, tracking));
91
+ let run = shapeAt(o.tracking);
92
+ let scaleX = 1;
93
+ if (o.width && run.width > 0) {
94
+ if (o.adjust === 'spacing') {
95
+ const gaps = Math.max(1, new Set(o.face.shapeRun(o.text).map((g) => g.cluster)).size - 1);
96
+ run = shapeAt(o.tracking + (o.width - run.width) / gaps);
97
+ } else {
98
+ scaleX = o.width / run.width;
99
+ }
100
+ }
101
+ const w = run.width * scaleX;
102
+ const left = o.align === 'center' ? o.x - w / 2 : o.align === 'right' ? o.x - w : o.x;
103
+ const d = mappedPath(o.face, run, o.size, (gx, gy) => [left + gx * scaleX, o.y + gy], o.only);
104
+ return { d, width: w, left, run, scaleX };
105
+ }
@@ -0,0 +1,6 @@
1
+ // Live text: @font-face rules.
2
+ import type { LoadedFont } from '../fonts.js';
3
+ import type { RenderContext } from '../render/context.js';
4
+
5
+ /** The face's `@font-face` rule (empty when this document has it already) and its family name. */
6
+ export function fontFace(face: LoadedFont, ctx: RenderContext): Promise<{ family: string; css: string }>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Live text: the `@font-face` rule for a face, embedded (or linked by URL)
3
+ * once per document.
4
+ *
5
+ * @module type/live
6
+ */
7
+
8
+ import { fontBytes } from '../fonts.js';
9
+ import { toBase64 } from '../platform/bytes.js';
10
+
11
+ /** Faces embedded in this document already (live text). @type {WeakMap<object, Set<string>>} */
12
+ const embedded = new WeakMap();
13
+
14
+ /**
15
+ * `@font-face` for a face, once per document.
16
+ *
17
+ * @param {import('../fonts.js').LoadedFont} face
18
+ * @param {import('../layers.js').RenderContext} ctx
19
+ * @returns {Promise<{ family: string, css: string }>}
20
+ */
21
+ export async function fontFace(face, ctx) {
22
+ const src = face.source;
23
+ const family = `covers-${src.name}-${Array.isArray(src.weight) ? 'var' : src.weight ?? 400}-${src.style ?? 'normal'}`.replace(/[^\w-]/g, '_');
24
+ const doc = /** @type {any} */ (ctx).doc ?? ctx;
25
+ let seen = embedded.get(doc);
26
+ if (!seen) embedded.set(doc, (seen = new Set()));
27
+ if (seen.has(family)) return { family, css: '' };
28
+ seen.add(family);
29
+ let url;
30
+ if (src.data == null && typeof src.src === 'string' && !src.src.startsWith('covers:')) url = src.src;
31
+ else {
32
+ const bytes = await fontBytes(src);
33
+ const tag = new DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0);
34
+ const mime = tag === 0x774f4646 ? 'font/woff' : tag === 0x774f4632 ? 'font/woff2' : tag === 0x4f54544f ? 'font/otf' : 'font/ttf';
35
+ url = `data:${mime};base64,${toBase64(bytes)}`;
36
+ }
37
+ const weight = Array.isArray(src.weight) ? `${src.weight[0]} ${src.weight[1]}` : src.weight ?? 400;
38
+ return { family, css: `@font-face{font-family:"${family}";src:url("${url}");font-weight:${weight};font-style:${src.style ?? 'normal'}}` };
39
+ }
@@ -0,0 +1,4 @@
1
+ // A font's family name.
2
+
3
+ /** The typographic family (name ID 16), else the family (1), else ''. */
4
+ export function familyName(v: DataView, bytes: Uint8Array, base: number | undefined): string;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * A font's family name from its `name` table.
3
+ *
4
+ * @module type/name
5
+ */
6
+
7
+ /**
8
+ * The family name from the `name` table (typographic family, else family).
9
+ *
10
+ * @param {DataView} v @param {Uint8Array} bytes @param {number | undefined} base
11
+ * @returns {string}
12
+ */
13
+ export function familyName(v, bytes, base) {
14
+ if (base == null) return '';
15
+ const count = v.getUint16(base + 2);
16
+ const strings = base + v.getUint16(base + 4);
17
+ /** @type {Record<number, string>} */
18
+ const found = {};
19
+ for (let i = 0; i < count; i++) {
20
+ const r = base + 6 + i * 12;
21
+ const pid = v.getUint16(r);
22
+ const id = v.getUint16(r + 6);
23
+ if ((id !== 1 && id !== 16) || found[id]) continue;
24
+ const len = v.getUint16(r + 8);
25
+ const off = strings + v.getUint16(r + 10);
26
+ let s = '';
27
+ if (pid === 3 || pid === 0) for (let k = 0; k < len; k += 2) s += String.fromCharCode(v.getUint16(off + k));
28
+ else for (let k = 0; k < len; k++) s += String.fromCharCode(bytes[off + k]);
29
+ found[id] = s;
30
+ }
31
+ return found[16] ?? found[1] ?? '';
32
+ }
@@ -0,0 +1,18 @@
1
+ // Paths for text.
2
+ import type { Length } from '../index.js';
3
+
4
+ /** A path for text: an arc, circle, ellipse or wave, or any path data. */
5
+ export type TextPath = string | { d: string } | {
6
+ kind?: 'circle' | 'arc' | 'ellipse' | 'wave';
7
+ cx?: Length | `${number}%`; cy?: Length | `${number}%`; r?: Length; rx?: Length; ry?: Length;
8
+ /** Degrees, 0 at 12 o'clock, clockwise. */
9
+ start?: number; end?: number;
10
+ direction?: 'cw' | 'ccw'; side?: 'outside' | 'inside';
11
+ x1?: Length; x2?: Length; y?: Length; amplitude?: Length; wavelength?: Length; phase?: number;
12
+ };
13
+
14
+
15
+ /** A text path as path data, px. */
16
+ export function textPathD(p: TextPath, canvas: { width: number; height: number }): string;
17
+ /** A sampler along path data: point and unit tangent at a distance (continuing straight past the ends). */
18
+ export function pathSampler(d: string): { length: number; at: (s: number) => { x: number; y: number; tx: number; ty: number } };
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Paths for text: circles, arcs, ellipses and waves as path data, and a
3
+ * sampler that gives a point and tangent at any distance along a path.
4
+ *
5
+ * @module type/path
6
+ */
7
+
8
+ import { num } from '../svg.js';
9
+ import { len } from '../geometry.js';
10
+ import { flattenPath } from '../flatten.js';
11
+ import { pos } from '../layers/util.js';
12
+
13
+ /**
14
+ * A text path as path data, in px.
15
+ *
16
+ * @param {any} p - `{ kind: 'circle' | 'arc' | 'ellipse' | 'wave', … }` or `{ d }`.
17
+ * @param {{ width: number, height: number }} canvas
18
+ * @returns {string}
19
+ */
20
+ export function textPathD(p, canvas) {
21
+ const X = (/** @type {any} */ v, /** @type {any} */ f) => pos(v ?? f, 'x', canvas);
22
+ const Y = (/** @type {any} */ v, /** @type {any} */ f) => pos(v ?? f, 'y', canvas);
23
+ if (typeof p === 'string') return p;
24
+ if (p.d) return p.d;
25
+ const kind = p.kind ?? 'circle';
26
+ if (kind === 'circle' || kind === 'ellipse' || kind === 'arc') {
27
+ const cx = X(p.cx, '50%');
28
+ const cy = Y(p.cy, '50%');
29
+ const rx = len(p.rx ?? p.r, canvas, '30vmin');
30
+ const ry = len(p.ry ?? p.r, canvas, '30vmin');
31
+ // Angles: 0 is 12 o'clock, clockwise. A circle starts at its `start` and goes all the way round.
32
+ const ccw = p.direction === 'ccw' || p.side === 'inside';
33
+ const a0 = p.start ?? (kind === 'arc' ? -90 : 180);
34
+ const sweep = kind === 'arc' ? (p.end ?? 90) - a0 : 360;
35
+ const n = Math.max(24, Math.ceil(Math.abs(sweep) / 2));
36
+ const pts = [];
37
+ for (let i = 0; i <= n; i++) {
38
+ const a = ((a0 + (ccw ? -1 : 1) * Math.abs(sweep) * (i / n)) * Math.PI) / 180;
39
+ pts.push(`${i ? 'L' : 'M'}${num(cx + rx * Math.sin(a), 3)} ${num(cy - ry * Math.cos(a), 3)}`);
40
+ }
41
+ return pts.join('');
42
+ }
43
+ if (kind === 'wave') {
44
+ const x1 = X(p.x1, 0);
45
+ const x2 = X(p.x2, '100%');
46
+ const y = Y(p.y, '50%');
47
+ const amp = len(p.amplitude, canvas, '2vmin');
48
+ const wl = len(p.wavelength, canvas, '40vmin');
49
+ const ph = p.phase ?? 0;
50
+ const pts = [];
51
+ for (let x = x1, i = 0; x <= x2 + 0.01; x += 2, i++) pts.push(`${i ? 'L' : 'M'}${num(x, 3)} ${num(y + Math.sin(((x - x1) / wl) * Math.PI * 2 + ph) * amp, 3)}`);
52
+ return pts.join('');
53
+ }
54
+ throw new TypeError(`Unknown text path kind "${kind}" (circle, arc, ellipse, wave, or { d })`);
55
+ }
56
+
57
+ /**
58
+ * A sampler along path data: point and tangent at a distance.
59
+ *
60
+ * @param {string} d
61
+ * @returns {{ length: number, at: (s: number) => { x: number, y: number, tx: number, ty: number } }}
62
+ */
63
+ export function pathSampler(d) {
64
+ const subs = flattenPath(d, 1);
65
+ const pts = subs.flatMap((s) => s.points);
66
+ if (pts.length < 2) throw new TypeError('A text path needs at least two points');
67
+ const cum = [0];
68
+ for (let i = 1; i < pts.length; i++) cum.push(cum[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]));
69
+ const length = cum[cum.length - 1];
70
+ return {
71
+ length,
72
+ at(s) {
73
+ const t = Math.max(0, Math.min(length, s));
74
+ let lo = 0;
75
+ let hi = cum.length - 1;
76
+ while (hi - lo > 1) { const mid = (lo + hi) >> 1; if (cum[mid] <= t) lo = mid; else hi = mid; }
77
+ const seg = cum[hi] - cum[lo] || 1;
78
+ const f = (t - cum[lo]) / seg;
79
+ const [ax, ay] = pts[lo];
80
+ const [bx, by] = pts[hi];
81
+ const dl = Math.hypot(bx - ax, by - ay) || 1;
82
+ // Beyond the ends, keep going along the end tangent.
83
+ const over = s - t;
84
+ const tx = (bx - ax) / dl;
85
+ const ty = (by - ay) / dl;
86
+ return { x: ax + (bx - ax) * f + tx * over, y: ay + (by - ay) * f + ty * over, tx, ty };
87
+ },
88
+ };
89
+ }
@@ -0,0 +1,9 @@
1
+ // The built-in 5×7 pixel face.
2
+ import type { LoadedFont } from '../fonts.js';
3
+
4
+ /** The pixel face (`font: 'pixel'`): A–Z, 0–9 and basic punctuation, as merged cell paths. */
5
+ export function pixelFace(): LoadedFont;
6
+ /** The characters it draws (lowercase is drawn as capitals). */
7
+ export const PIXEL_CHARS: string;
8
+ /** A glyph's 5×7 bitmap (35 cells row by row, `#` inked), or undefined. */
9
+ export function pixelBitmap(ch: string): string | undefined;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * A built-in 5×7 pixel face (`font: 'pixel'`): A–Z, 0–9 and basic
3
+ * punctuation, each glyph a set of square cells merged into row runs. It
4
+ * is a face like any other (layout, fitting, justification, text on a
5
+ * path, per-glyph control all work), drawn as paths.
6
+ *
7
+ * Metrics, in a 1000-unit em: a cell is 100 units, capitals are 7 cells
8
+ * (700), the advance is 6 cells (a cell's gap after each glyph), a space
9
+ * is 4 cells. Lowercase is drawn as capitals.
10
+ *
11
+ * @module type/pixel
12
+ */
13
+
14
+ /** Glyph bitmaps, 5 wide × 7 tall, `#` inked. */
15
+ const BITMAPS = /** @type {Record<string, string>} */ ({
16
+ A: '.###.#...##...#######...##...##...#', B: '####.#...##...#####.#...##...#####.', C: '.###.#...##....#....#....#...#.###.',
17
+ D: '####.#...##...##...##...##...#####.', E: '######....#....####.#....#....#####', F: '######....#....####.#....#....#....',
18
+ G: '.###.#...##....#.####...##...#.####', H: '#...##...##...#######...##...##...#', I: '.###...#....#....#....#....#...###.',
19
+ J: '..###...#....#....#....#.#..#..##..', K: '#...##..#.#.#..##...#.#..#..#.#...#', L: '#....#....#....#....#....#....#####',
20
+ M: '#...###.###.#.##.#.##...##...##...#', N: '#...##...###..##.#.##..###...##...#', O: '.###.#...##...##...##...##...#.###.',
21
+ P: '####.#...##...#####.#....#....#....', Q: '.###.#...##...##...##.#.##..#..##.#', R: '####.#...##...#####.#.#..#..#.#...#',
22
+ S: '.#####....#.....###.....#....#####.', T: '#####..#....#....#....#....#....#..', U: '#...##...##...##...##...##...#.###.',
23
+ V: '#...##...##...##...##...#.#.#...#..', W: '#...##...##...##.#.##.#.##.#.#.#.#.', X: '#...##...#.#.#...#...#.#.#...##...#',
24
+ Y: '#...##...#.#.#...#....#....#....#..', Z: '#####....#...#...#...#...#....#####', '0': '.###.#...##..###.#.###..##...#.###.',
25
+ '1': '..#...##....#....#....#....#...###.', '2': '.###.#...#....#...#...#...#...#####', '3': '#####...#...#.....#.....##...#.###.',
26
+ '4': '...#...##..#.#.#..#.#####...#....#.', '5': '######....####.....#....##...#.###.', '6': '..##..#...#....####.#...##...#.###.',
27
+ '7': '#####....#...#...#...#....#....#...', '8': '.###.#...##...#.###.#...##...#.###.', '9': '.###.#...##...#.####....#...#..##..',
28
+ '.': '..........................##...##..', ',': '.....................##....#...#...', '!': '..#....#....#....#....#.........#..',
29
+ '?': '.###.#...#....#...#...#.........#..', '-': '................###................', ':': '......##...##........##...##.......',
30
+ ';': '......##...##........##....#...#...', "'": '..#....#...#.......................', '"': '.#.#..#.#..#.#.....................',
31
+ '/': '.........#...#...#...#...#.........', '&': '.##..#..#.#.#...#...#.#.##..#..##.#', '+': '.......#....#..#####..#....#.......',
32
+ '=': '..........#####.....#####..........', '(': '...#...#...#....#....#.....#.....#.', ')': '.#.....#.....#....#....#...#...#...',
33
+ '#': '.#.#..#.#.#####.#.#.#####.#.#..#.#.', '@': '.###.#...##.####.#.##.####.....###.', '%': '##...##..#...#...#...#...#..##...##',
34
+ '*': '.......#..#.#.#.###.#.#.#..#.......', _: '..............................#####', '<': '...#...#...#...#.....#.....#.....#.',
35
+ '>': '.#.....#.....#.....#...#...#...#...', '[': '.###..#....#....#....#....#....###.', ']': '.###....#....#....#....#....#..###.',
36
+ '|': '..#....#....#....#....#....#....#..', '$': '..#...#####.#...###...#.#####...#..', '·': '.................#.................',
37
+ });
38
+
39
+ /** Cell size, font units. */
40
+ const CELL = 100;
41
+
42
+ /** Glyph id per character (1-based; 0 is missing), and back. */
43
+ const CHARS = Object.keys(BITMAPS);
44
+ const GID = new Map(CHARS.map((c, i) => [c, i + 1]));
45
+ const SPACE = CHARS.length + 1;
46
+
47
+ /**
48
+ * A bitmap's cells as path data, row runs merged: font units, y up,
49
+ * baseline 0.
50
+ *
51
+ * @param {string} bits - 35 cells, row by row from the top.
52
+ * @returns {string}
53
+ */
54
+ function cellsPath(bits) {
55
+ let d = '';
56
+ for (let row = 0; row < 7; row++) {
57
+ const top = (7 - row) * CELL;
58
+ for (let col = 0; col < 5; col++) {
59
+ if (bits[row * 5 + col] !== '#') continue;
60
+ let end = col;
61
+ while (end + 1 < 5 && bits[row * 5 + end + 1] === '#') end++;
62
+ const x0 = col * CELL;
63
+ const x1 = (end + 1) * CELL;
64
+ d += `M${x0},${top}L${x1},${top}L${x1},${top - CELL}L${x0},${top - CELL}Z`;
65
+ col = end;
66
+ }
67
+ }
68
+ return d;
69
+ }
70
+
71
+ /** @type {Map<number, string>} */
72
+ const PATHS = new Map(CHARS.map((c, i) => [i + 1, cellsPath(BITMAPS[c])]));
73
+
74
+ /** @type {any} */
75
+ let face;
76
+
77
+ /**
78
+ * The pixel face, shaped and outlined in JavaScript (no font file).
79
+ *
80
+ * @returns {import('../fonts.js').LoadedFont}
81
+ */
82
+ export function pixelFace() {
83
+ face ??= {
84
+ name: 'pixel',
85
+ weight: 400,
86
+ style: 'normal',
87
+ font: null,
88
+ upem: 1000,
89
+ ascender: 800,
90
+ descender: -200,
91
+ capHeight: 7 * CELL,
92
+ paths: new Map(),
93
+ axes: {},
94
+ variations: {},
95
+ source: { name: 'pixel', src: 'covers:pixel', weight: 400 },
96
+ glyphToPath: (/** @type {number} */ gid) => PATHS.get(gid) ?? '',
97
+ shapeRun: (/** @type {string} */ text) => Array.from(text).map((ch, i) => {
98
+ const c = BITMAPS[ch] ? ch : ch.toUpperCase();
99
+ const gid = ch === ' ' ? SPACE : GID.get(c) ?? 0;
100
+ return { gid, cluster: i, xAdvance: ch === ' ' ? 4 * CELL : 6 * CELL, xOffset: 0, yOffset: 0 };
101
+ }),
102
+ };
103
+ return face;
104
+ }
105
+
106
+ /** Characters the pixel face draws. */
107
+ export const PIXEL_CHARS = Object.freeze(CHARS.join(''));
108
+
109
+ /**
110
+ * A glyph's 5×7 bitmap (35 cells row by row, `#` inked), or undefined.
111
+ *
112
+ * @param {string} ch
113
+ * @returns {string | undefined}
114
+ */
115
+ export const pixelBitmap = (ch) => (Object.hasOwn(BITMAPS, ch.toUpperCase()) ? BITMAPS[ch.toUpperCase()] : undefined);