@pixerate/editor 0.190.0 → 0.192.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.
@@ -1,4 +1,332 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/text/opentype.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+ var _chunkNM3G4VQTcjs = require('./chunk-NM3G4VQT.cjs');
5
+
6
+ // src/vector/trace.ts
7
+ var VECTOR_TRACE_MAX_PIXELS = 4e6;
8
+ function signedArea(p) {
9
+ let a = 0;
10
+ for (let i = 0; i < p.length; i++) {
11
+ const [x0, y0] = p[i];
12
+ const [x1, y1] = p[(i + 1) % p.length];
13
+ a += x0 * y1 - x1 * y0;
14
+ }
15
+ return a / 2;
16
+ }
17
+ function pointInPoly(p, poly) {
18
+ let inside = false;
19
+ for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
20
+ const [xi, yi] = poly[i];
21
+ const [xj, yj] = poly[j];
22
+ if (yi > p[1] !== yj > p[1] && p[0] < (xj - xi) * (p[1] - yi) / (yj - yi) + xi) inside = !inside;
23
+ }
24
+ return inside;
25
+ }
26
+ function vectorTraceContours(field, w, h, threshold) {
27
+ const gw = w + 2;
28
+ const gh = h + 2;
29
+ const val = (i, j) => i === 0 || j === 0 || i > w || j > h ? -Infinity : field[(j - 1) * w + (i - 1)];
30
+ const inside = (i, j) => val(i, j) >= threshold;
31
+ const pos = (i, j) => [i - 0.5, j - 0.5];
32
+ const cross = (a, b) => {
33
+ const va = val(a[0], a[1]);
34
+ const vb = val(b[0], b[1]);
35
+ let t;
36
+ if (Number.isFinite(va) && Number.isFinite(vb) && Math.abs(vb - va) > 1e-12) t = Math.min(1, Math.max(0, (threshold - va) / (vb - va)));
37
+ else if (Number.isFinite(va)) t = va >= threshold ? 0.5 : 0;
38
+ else t = vb >= threshold ? 0.5 : 1;
39
+ const pa = pos(a[0], a[1]);
40
+ const pb = pos(b[0], b[1]);
41
+ return [pa[0] + (pb[0] - pa[0]) * t, pa[1] + (pb[1] - pa[1]) * t];
42
+ };
43
+ const hid = (i, j) => 2 * (j * gw + i);
44
+ const vid = (i, j) => 2 * (j * gw + i) + 1;
45
+ const next = /* @__PURE__ */ new Map();
46
+ const point = /* @__PURE__ */ new Map();
47
+ for (let j = 0; j < gh - 1; j++) {
48
+ for (let i = 0; i < gw - 1; i++) {
49
+ const tl = inside(i, j);
50
+ const tr = inside(i + 1, j);
51
+ const br = inside(i + 1, j + 1);
52
+ const bl = inside(i, j + 1);
53
+ const code = +tl << 3 | +tr << 2 | +br << 1 | +bl;
54
+ if (code === 0 || code === 15) continue;
55
+ const top = hid(i, j);
56
+ const bottom = hid(i, j + 1);
57
+ const left = vid(i, j);
58
+ const right = vid(i + 1, j);
59
+ const seg = (a, b) => next.set(a, b);
60
+ const mean = () => (val(i, j) + val(i + 1, j) + val(i + 1, j + 1) + val(i, j + 1)) / 4;
61
+ switch (code) {
62
+ case 1:
63
+ seg(left, bottom);
64
+ break;
65
+ case 2:
66
+ seg(bottom, right);
67
+ break;
68
+ case 3:
69
+ seg(left, right);
70
+ break;
71
+ case 4:
72
+ seg(right, top);
73
+ break;
74
+ case 5:
75
+ if (mean() >= threshold) {
76
+ seg(left, top);
77
+ seg(right, bottom);
78
+ } else {
79
+ seg(left, bottom);
80
+ seg(right, top);
81
+ }
82
+ break;
83
+ case 6:
84
+ seg(bottom, top);
85
+ break;
86
+ case 7:
87
+ seg(left, top);
88
+ break;
89
+ case 8:
90
+ seg(top, left);
91
+ break;
92
+ case 9:
93
+ seg(top, bottom);
94
+ break;
95
+ case 10:
96
+ if (mean() >= threshold) {
97
+ seg(top, right);
98
+ seg(bottom, left);
99
+ } else {
100
+ seg(top, left);
101
+ seg(bottom, right);
102
+ }
103
+ break;
104
+ case 11:
105
+ seg(top, right);
106
+ break;
107
+ case 12:
108
+ seg(right, left);
109
+ break;
110
+ case 13:
111
+ seg(right, bottom);
112
+ break;
113
+ case 14:
114
+ seg(bottom, left);
115
+ break;
116
+ }
117
+ const edges = [
118
+ [top, [i, j], [i + 1, j]],
119
+ [bottom, [i, j + 1], [i + 1, j + 1]],
120
+ [left, [i, j], [i, j + 1]],
121
+ [right, [i + 1, j], [i + 1, j + 1]]
122
+ ];
123
+ for (const [e, a, b] of edges) if (inside(a[0], a[1]) !== inside(b[0], b[1]) && !point.has(e)) point.set(e, cross(a, b));
124
+ }
125
+ }
126
+ const out = [];
127
+ for (const start of [...next.keys()].sort((a, b) => a - b)) {
128
+ if (!next.has(start)) continue;
129
+ const poly = [];
130
+ let e = start;
131
+ for (; ; ) {
132
+ const n = next.get(e);
133
+ if (n === void 0) break;
134
+ next.delete(e);
135
+ const p = point.get(e);
136
+ if (p) poly.push(p);
137
+ e = n;
138
+ if (e === start) break;
139
+ }
140
+ if (poly.length >= 3) out.push(poly);
141
+ }
142
+ return out;
143
+ }
144
+ function squareContourCorners(ring) {
145
+ const n = ring.length;
146
+ if (n < 4) return ring;
147
+ const eq = (u, v) => Math.abs(u - v) < 1e-9;
148
+ const out = [];
149
+ for (let i = 0; i < n; i++) {
150
+ const a = ring[i];
151
+ const b = ring[(i + 1) % n];
152
+ const p = ring[(i - 1 + n) % n];
153
+ const q = ring[(i + 2) % n];
154
+ const diagonal = Math.abs(Math.abs(b[0] - a[0]) - 0.5) < 0.02 && Math.abs(Math.abs(b[1] - a[1]) - 0.5) < 0.02;
155
+ const wraps = i === n - 1;
156
+ let corner = null;
157
+ if (diagonal && (!wraps || out[0] === ring[0])) {
158
+ if (eq(p[1], a[1]) && !eq(p[0], a[0]) && eq(q[0], b[0]) && !eq(q[1], b[1])) corner = [b[0], a[1]];
159
+ else if (eq(p[0], a[0]) && !eq(p[1], a[1]) && eq(q[1], b[1]) && !eq(q[0], b[0])) corner = [a[0], b[1]];
160
+ }
161
+ if (!corner) {
162
+ out.push(a);
163
+ continue;
164
+ }
165
+ out.push(corner);
166
+ if (wraps) out.shift();
167
+ else i++;
168
+ }
169
+ if (out.length < 3) return ring;
170
+ const m = out.length;
171
+ for (let k = 0; k < m; k++) {
172
+ const p = out[(k - 1 + m) % m];
173
+ const a = out[k];
174
+ const q = out[(k + 1) % m];
175
+ if (Math.abs((a[0] - p[0]) * (q[1] - a[1]) - (a[1] - p[1]) * (q[0] - a[0])) > 1e-6) return k ? [...out.slice(k), ...out.slice(0, k)] : out;
176
+ }
177
+ return out;
178
+ }
179
+ function loopsOf(field, w, h, threshold, tolerance, minArea, square = false) {
180
+ const loops = [];
181
+ for (const contour of vectorTraceContours(field, w, h, threshold)) {
182
+ const raw = square ? squareContourCorners(contour) : contour;
183
+ const area = signedArea(raw);
184
+ if (Math.abs(area) < Math.max(0, minArea)) continue;
185
+ const simple = _chunkNM3G4VQTcjs.simplifyRing.call(void 0, raw, Math.max(0.01, tolerance));
186
+ if (simple.length < 3) continue;
187
+ loops.push({ raw, simple, area, depth: 0 });
188
+ }
189
+ for (const l of loops) l.depth = loops.filter((o) => o !== l && Math.abs(o.area) > Math.abs(l.area) && pointInPoly(l.simple[0], o.simple)).length;
190
+ return loops.sort((a, b) => a.depth - b.depth || Math.abs(b.area) - Math.abs(a.area));
191
+ }
192
+ function loopSubpath(l, tolerance, roundness) {
193
+ if (roundness <= 0) return { closed: true, anchors: l.simple.map(([x, y]) => ({ x, y })) };
194
+ const cornerAngleDeg = 25 + roundness * 125;
195
+ const cubics = _chunkNM3G4VQTcjs.fitCubics.call(void 0, l.raw, Math.max(0.05, tolerance), {
196
+ closed: true,
197
+ cornerAngleDeg
198
+ });
199
+ if (!cubics.length) return { closed: true, anchors: l.simple.map(([x, y]) => ({ x, y })) };
200
+ const anchors = cubics.map((c, k) => {
201
+ const prev = cubics[(k - 1 + cubics.length) % cubics.length];
202
+ return {
203
+ x: c.p0[0],
204
+ y: c.p0[1],
205
+ in: { x: prev.p2[0], y: prev.p2[1] },
206
+ out: { x: c.p1[0], y: c.p1[1] }
207
+ };
208
+ });
209
+ return { closed: true, anchors };
210
+ }
211
+ function traceField(field, w, h, threshold, o) {
212
+ const tolerance = _nullishCoalesce(o.tolerance, () => ( 1));
213
+ const roundness = Math.min(1, Math.max(0, _nullishCoalesce(o.cornerRoundness, () => ( 0.5))));
214
+ return loopsOf(field, w, h, threshold, tolerance, _nullishCoalesce(o.minArea, () => ( 10)), o.squareCorners === true).map((l) => loopSubpath(l, tolerance, roundness));
215
+ }
216
+ function quantizeVectorTraceColors(pixels, count) {
217
+ const n = pixels.width * pixels.height;
218
+ const d = pixels.data;
219
+ const opaque = [];
220
+ for (let i = 0; i < n; i++) if (d[i * 4 + 3] >= 128) opaque.push(i);
221
+ const boxes = opaque.length ? [opaque] : [];
222
+ const range = (box, c) => {
223
+ let lo = 255;
224
+ let hi = 0;
225
+ for (const i of box) {
226
+ const v = d[i * 4 + c];
227
+ if (v < lo) lo = v;
228
+ if (v > hi) hi = v;
229
+ }
230
+ return hi - lo;
231
+ };
232
+ while (boxes.length < count) {
233
+ let best = -1;
234
+ let bestRange = 0;
235
+ let bestC = 0;
236
+ boxes.forEach((b, k) => {
237
+ if (b.length < 2) return;
238
+ for (let c = 0; c < 3; c++) {
239
+ const r = range(b, c);
240
+ if (r > bestRange) {
241
+ bestRange = r;
242
+ best = k;
243
+ bestC = c;
244
+ }
245
+ }
246
+ });
247
+ if (best < 0) break;
248
+ const box = boxes[best].slice().sort((a, b) => d[a * 4 + bestC] - d[b * 4 + bestC]);
249
+ const key = (k) => d[box[k] * 4 + bestC];
250
+ const v = key(box.length >> 1);
251
+ let mid = box.findIndex((_, k) => key(k) >= v);
252
+ if (mid <= 0) mid = box.findIndex((_, k) => key(k) > v);
253
+ boxes.splice(best, 1, box.slice(0, mid), box.slice(mid));
254
+ }
255
+ const palette = boxes.map((b) => {
256
+ const s = [0, 0, 0];
257
+ for (const i of b) for (let c = 0; c < 3; c++) s[c] += d[i * 4 + c];
258
+ return [Math.round(s[0] / b.length), Math.round(s[1] / b.length), Math.round(s[2] / b.length)];
259
+ });
260
+ const index = new Int16Array(n).fill(-1);
261
+ const counts = palette.map(() => 0);
262
+ for (const i of opaque) {
263
+ let best = 0;
264
+ let bestD = Infinity;
265
+ palette.forEach((p, k) => {
266
+ const dd = (d[i * 4] - p[0]) ** 2 + (d[i * 4 + 1] - p[1]) ** 2 + (d[i * 4 + 2] - p[2]) ** 2;
267
+ if (dd < bestD) {
268
+ bestD = dd;
269
+ best = k;
270
+ }
271
+ });
272
+ index[i] = best;
273
+ counts[best]++;
274
+ }
275
+ return { palette, index, counts };
276
+ }
277
+ var hex = (c) => `#${c.map((v) => Math.round(v).toString(16).padStart(2, "0")).join("")}`;
278
+ function traceVectorImage(pixels, options = {}) {
279
+ const { width: w, height: h, data } = pixels;
280
+ if (!(w > 0 && h > 0) || data.length < w * h * 4) throw new Error("The image has no pixels (or fewer than width \xD7 height \xD7 4 values).");
281
+ if (w * h > VECTOR_TRACE_MAX_PIXELS) throw new Error(`The image has ${w * h} pixels; scale it down to at most ${VECTOR_TRACE_MAX_PIXELS} to trace it.`);
282
+ const n = w * h;
283
+ if ((_nullishCoalesce(options.mode, () => ( "threshold"))) === "threshold") {
284
+ const field = new Float32Array(n);
285
+ const byAlpha = options.channel === "alpha";
286
+ for (let i = 0; i < n; i++) {
287
+ const a = data[i * 4 + 3] / 255;
288
+ field[i] = byAlpha ? a : a * (1 - (0.2126 * data[i * 4] + 0.7152 * data[i * 4 + 1] + 0.0722 * data[i * 4 + 2]) / 255);
289
+ }
290
+ const subpaths = traceField(field, w, h, Math.min(1, Math.max(0, _nullishCoalesce(options.threshold, () => ( 0.5)))), options);
291
+ return subpaths.length ? [
292
+ {
293
+ id: "trace-1",
294
+ type: "path",
295
+ name: "Trace",
296
+ fill: _nullishCoalesce(options.color, () => ( "#000000")),
297
+ stroke: null,
298
+ fillRule: "evenodd",
299
+ subpaths
300
+ }
301
+ ] : [];
302
+ }
303
+ const count = Math.min(32, Math.max(2, Math.round(_nullishCoalesce(options.colors, () => ( 6)))));
304
+ const q = quantizeVectorTraceColors(pixels, count);
305
+ const order = q.palette.map((_, k) => k).filter((k) => q.counts[k] > 0).sort((a, b) => q.counts[b] - q.counts[a]);
306
+ const rank = new Map(order.map((k, r) => [k, r]));
307
+ const out = [];
308
+ order.forEach((k, r) => {
309
+ const field = new Uint8Array(n);
310
+ for (let i = 0; i < n; i++) {
311
+ const ix = q.index[i];
312
+ field[i] = ix >= 0 && rank.get(ix) >= r ? 1 : 0;
313
+ }
314
+ const subpaths = traceField(field, w, h, 0.5, options);
315
+ if (subpaths.length)
316
+ out.push({
317
+ id: `trace-${out.length + 1}`,
318
+ type: "path",
319
+ name: hex(q.palette[k]),
320
+ fill: hex(q.palette[k]),
321
+ stroke: null,
322
+ fillRule: "evenodd",
323
+ subpaths
324
+ });
325
+ });
326
+ return out;
327
+ }
328
+
329
+ // src/text/opentype.ts
2
330
  var fail = (message) => {
3
331
  throw new Error(message);
4
332
  };
@@ -310,22 +638,22 @@ function cffOutlines(view, t, numGlyphs) {
310
638
  const gsubrs = cffIndex(view, strings.end).items;
311
639
  if (!tops.items.length) fail("The font's CFF table has no Top DICT.");
312
640
  const top = cffDict(view, tops.items[0][0], tops.items[0][1]);
313
- const charStrings = cffIndex(view, base + (_nullishCoalesce(_optionalChain([top, 'access', _ => _.get, 'call', _2 => _2(17), 'optionalAccess', _3 => _3[0]]), () => ( fail("The font's CFF table has no CharStrings."))))).items;
641
+ const charStrings = cffIndex(view, base + (_nullishCoalesce(_optionalChain([top, 'access', _2 => _2.get, 'call', _3 => _3(17), 'optionalAccess', _4 => _4[0]]), () => ( fail("The font's CFF table has no CharStrings."))))).items;
314
642
  const privateOf = (dict) => {
315
643
  const pv = dict.get(18);
316
644
  if (!pv || pv.length < 2) return { subrs: [], defaultWidth: 0, nominalWidth: 0 };
317
645
  const [size, off] = pv;
318
646
  const priv = cffDict(view, base + off, base + off + size);
319
- const subrsOff = _optionalChain([priv, 'access', _4 => _4.get, 'call', _5 => _5(19), 'optionalAccess', _6 => _6[0]]);
647
+ const subrsOff = _optionalChain([priv, 'access', _5 => _5.get, 'call', _6 => _6(19), 'optionalAccess', _7 => _7[0]]);
320
648
  return {
321
649
  subrs: subrsOff !== void 0 ? cffIndex(view, base + off + subrsOff).items : [],
322
- defaultWidth: _nullishCoalesce(_optionalChain([priv, 'access', _7 => _7.get, 'call', _8 => _8(20), 'optionalAccess', _9 => _9[0]]), () => ( 0)),
323
- nominalWidth: _nullishCoalesce(_optionalChain([priv, 'access', _10 => _10.get, 'call', _11 => _11(21), 'optionalAccess', _12 => _12[0]]), () => ( 0))
650
+ defaultWidth: _nullishCoalesce(_optionalChain([priv, 'access', _8 => _8.get, 'call', _9 => _9(20), 'optionalAccess', _10 => _10[0]]), () => ( 0)),
651
+ nominalWidth: _nullishCoalesce(_optionalChain([priv, 'access', _11 => _11.get, 'call', _12 => _12(21), 'optionalAccess', _13 => _13[0]]), () => ( 0))
324
652
  };
325
653
  };
326
654
  let fdFor;
327
- const fdArrayOff = _optionalChain([top, 'access', _13 => _13.get, 'call', _14 => _14(1236), 'optionalAccess', _15 => _15[0]]);
328
- const fdSelectOff = _optionalChain([top, 'access', _16 => _16.get, 'call', _17 => _17(1237), 'optionalAccess', _18 => _18[0]]);
655
+ const fdArrayOff = _optionalChain([top, 'access', _14 => _14.get, 'call', _15 => _15(1236), 'optionalAccess', _16 => _16[0]]);
656
+ const fdSelectOff = _optionalChain([top, 'access', _17 => _17.get, 'call', _18 => _18(1237), 'optionalAccess', _19 => _19[0]]);
329
657
  if (fdArrayOff !== void 0 && fdSelectOff !== void 0) {
330
658
  const fds = cffIndex(view, base + fdArrayOff).items.map(([a, b]) => privateOf(cffDict(view, a, b)));
331
659
  const sel = base + fdSelectOff;
@@ -795,7 +1123,7 @@ function parseOpenTypeFont(data, options = {}) {
795
1123
  if (g < 0 || g >= numGlyphs) return 0;
796
1124
  const i = Math.min(g, numHMetrics - 1);
797
1125
  if (i >= 0 && hmtx.offset + i * 4 + 2 <= hmtx.offset + hmtx.length) return view.getUint16(hmtx.offset + i * 4);
798
- return _nullishCoalesce(_optionalChain([cffWidth, 'optionalCall', _19 => _19(g)]), () => ( 0));
1126
+ return _nullishCoalesce(_optionalChain([cffWidth, 'optionalCall', _20 => _20(g)]), () => ( 0));
799
1127
  };
800
1128
  const kernPairs = readKern(view, tables.get("kern"));
801
1129
  const gpos = gposKerning(view, tables.get("GPOS"));
@@ -849,11 +1177,11 @@ function parseOpenTypeFont(data, options = {}) {
849
1177
  kerning,
850
1178
  glyphPath,
851
1179
  getAdvanceWidth(text, fontSize, opts) {
852
- return layout(text, _optionalChain([opts, 'optionalAccess', _20 => _20.kerning]) !== false).width * fontSize / unitsPerEm;
1180
+ return layout(text, _optionalChain([opts, 'optionalAccess', _21 => _21.kerning]) !== false).width * fontSize / unitsPerEm;
853
1181
  },
854
1182
  getPath(text, x, y, fontSize, opts) {
855
1183
  const k = fontSize / unitsPerEm;
856
- const { gs, xs } = layout(text, _optionalChain([opts, 'optionalAccess', _21 => _21.kerning]) !== false);
1184
+ const { gs, xs } = layout(text, _optionalChain([opts, 'optionalAccess', _22 => _22.kerning]) !== false);
857
1185
  const commands = [];
858
1186
  gs.forEach((g, i) => {
859
1187
  const ox = x + xs[i] * k;
@@ -925,4 +1253,9 @@ async function unwoff(view) {
925
1253
 
926
1254
 
927
1255
 
928
- exports.parseOpenTypeFont = parseOpenTypeFont; exports.loadOpenTypeFont = loadOpenTypeFont;
1256
+
1257
+
1258
+
1259
+
1260
+
1261
+ exports.VECTOR_TRACE_MAX_PIXELS = VECTOR_TRACE_MAX_PIXELS; exports.vectorTraceContours = vectorTraceContours; exports.traceField = traceField; exports.quantizeVectorTraceColors = quantizeVectorTraceColors; exports.traceVectorImage = traceVectorImage; exports.parseOpenTypeFont = parseOpenTypeFont; exports.loadOpenTypeFont = loadOpenTypeFont;
@@ -1,4 +1,5 @@
1
1
  import { AdjustmentKind, AdjustmentLayer, AdjustmentParamsMap, Annotation, CreateAnnotationPayload, AspectRatio, CropBox, DepthMaskOptions, ExportOptions, ImageAdjustments, ImageDimensions, ImageEditorFit, ImageEditorOptions, ImageEditorState, ImageEditorTool, ImageTransform, ImagePoint, SerializedImageEditorState } from './types.cjs';
2
+ import { type PathToSelectionOptions, type SelectionToPathOptions } from './selection/path.cjs';
2
3
  import { RenderOptions } from './renderer.cjs';
3
4
  import { type BlendMode } from './blend.cjs';
4
5
  import type { ImageHistogram } from './histogram.cjs';
@@ -279,6 +280,16 @@ export interface ShapeLayerInfo {
279
280
  annotationId?: string;
280
281
  }
281
282
  /** A new type layer: its text, where it goes, and its character and paragraph style. */
283
+ /** A Type Mask: text laid out like `TextLayerInit` (no layer is made), selected by `mode`. */
284
+ export interface TypeMaskInit extends Omit<TextLayerInit, keyof LayerPlacement | 'name' | 'opacity' | 'blend'> {
285
+ /** Horizontal (default) or vertical: the characters stacked top to bottom, centred on `x`. */
286
+ orientation?: 'horizontal' | 'vertical';
287
+ mode?: SelectionMode;
288
+ /** Feather radius in px (default 0). */
289
+ feather?: number;
290
+ /** Soft glyph edges (default true). */
291
+ antiAlias?: boolean;
292
+ }
282
293
  export interface TextLayerInit extends LayerPlacement {
283
294
  text: string;
284
295
  /**
@@ -921,7 +932,28 @@ export declare class ImageEditorController {
921
932
  * hiding everything) or `path` (reveals inside the path given). Replaces an existing vector mask;
922
933
  * the raster mask is kept and both apply. One undo step. Returns the vector mask.
923
934
  */
924
- addVectorMask(id: string, from?: 'reveal-all' | 'hide-all' | 'path', path?: VectorMaskPathInput): VectorMask;
935
+ addVectorMask(id: string, from?: 'reveal-all' | 'hide-all' | 'path' | 'selection', path?: VectorMaskPathInput, trace?: SelectionToPathOptions): VectorMask;
936
+ /** The selection's outline as subpaths; throws when nothing is selected or nothing is left to trace. */
937
+ private traceSelection;
938
+ /**
939
+ * Make Work Path: the selection's outline as closed Bézier subpaths in document pixels, holes
940
+ * included (fill them even-odd), within `tolerance` px. Changes nothing. Throws without a selection.
941
+ */
942
+ selectionToPath(options?: SelectionToPathOptions): {
943
+ subpaths: VectorSubpath[];
944
+ svg: string;
945
+ fillRule: 'evenodd';
946
+ bounds: CropBox | null;
947
+ anchors: number;
948
+ };
949
+ /**
950
+ * Make Selection: fills a path (`shape`, `subpaths` or `components`, document pixels) into a
951
+ * selection combined with the current one by `mode`, with `feather` px and `antiAlias`. One
952
+ * undo step. Returns whether anything is selected.
953
+ */
954
+ pathToSelection(path: VectorMaskPathInput, options?: PathToSelectionOptions & {
955
+ mode?: SelectionMode;
956
+ }): boolean;
925
957
  /** Replaces a vector mask's path (its properties are kept). One undo step. Returns the vector mask. */
926
958
  setVectorMaskPath(id: string, path: VectorMaskPathInput): VectorMask;
927
959
  /** Changes a vector mask's enabled, linked, inverted, density or feather (the path is kept). One undo step. */
@@ -1782,6 +1814,15 @@ export declare class ImageEditorController {
1782
1814
  * text wrapped in `box`. Refused without a text shaper. Returns its id.
1783
1815
  */
1784
1816
  addTextLayer(init: TextLayerInit): string;
1817
+ /**
1818
+ * Type Mask (horizontal or vertical): a selection in the shape of `text`, laid out and drawn as
1819
+ * `addTextLayer` would place it (font, size, tracking, box, rotation), combined with the current
1820
+ * selection by `mode`. Vertical type stacks the characters top to bottom, centred on `x`. No
1821
+ * layer is added. One undo step. Returns whether anything is selected.
1822
+ */
1823
+ selectTextShape(init: TypeMaskInit): boolean;
1824
+ /** A type layer laid out and drawn from `init` (not added): the shared part of `addTextLayer` and the type masks. */
1825
+ private layoutTypeLayer;
1785
1826
  /**
1786
1827
  * Replaces text [start, end) (default all of it) with `text`; inserted text takes the style at
1787
1828
  * `start`. One undo step unless `commitToHistory` is false (typing). Returns the layer.
@@ -1,4 +1,5 @@
1
1
  import { AdjustmentKind, AdjustmentLayer, AdjustmentParamsMap, Annotation, CreateAnnotationPayload, AspectRatio, CropBox, DepthMaskOptions, ExportOptions, ImageAdjustments, ImageDimensions, ImageEditorFit, ImageEditorOptions, ImageEditorState, ImageEditorTool, ImageTransform, ImagePoint, SerializedImageEditorState } from './types.js';
2
+ import { type PathToSelectionOptions, type SelectionToPathOptions } from './selection/path.js';
2
3
  import { RenderOptions } from './renderer.js';
3
4
  import { type BlendMode } from './blend.js';
4
5
  import type { ImageHistogram } from './histogram.js';
@@ -279,6 +280,16 @@ export interface ShapeLayerInfo {
279
280
  annotationId?: string;
280
281
  }
281
282
  /** A new type layer: its text, where it goes, and its character and paragraph style. */
283
+ /** A Type Mask: text laid out like `TextLayerInit` (no layer is made), selected by `mode`. */
284
+ export interface TypeMaskInit extends Omit<TextLayerInit, keyof LayerPlacement | 'name' | 'opacity' | 'blend'> {
285
+ /** Horizontal (default) or vertical: the characters stacked top to bottom, centred on `x`. */
286
+ orientation?: 'horizontal' | 'vertical';
287
+ mode?: SelectionMode;
288
+ /** Feather radius in px (default 0). */
289
+ feather?: number;
290
+ /** Soft glyph edges (default true). */
291
+ antiAlias?: boolean;
292
+ }
282
293
  export interface TextLayerInit extends LayerPlacement {
283
294
  text: string;
284
295
  /**
@@ -921,7 +932,28 @@ export declare class ImageEditorController {
921
932
  * hiding everything) or `path` (reveals inside the path given). Replaces an existing vector mask;
922
933
  * the raster mask is kept and both apply. One undo step. Returns the vector mask.
923
934
  */
924
- addVectorMask(id: string, from?: 'reveal-all' | 'hide-all' | 'path', path?: VectorMaskPathInput): VectorMask;
935
+ addVectorMask(id: string, from?: 'reveal-all' | 'hide-all' | 'path' | 'selection', path?: VectorMaskPathInput, trace?: SelectionToPathOptions): VectorMask;
936
+ /** The selection's outline as subpaths; throws when nothing is selected or nothing is left to trace. */
937
+ private traceSelection;
938
+ /**
939
+ * Make Work Path: the selection's outline as closed Bézier subpaths in document pixels, holes
940
+ * included (fill them even-odd), within `tolerance` px. Changes nothing. Throws without a selection.
941
+ */
942
+ selectionToPath(options?: SelectionToPathOptions): {
943
+ subpaths: VectorSubpath[];
944
+ svg: string;
945
+ fillRule: 'evenodd';
946
+ bounds: CropBox | null;
947
+ anchors: number;
948
+ };
949
+ /**
950
+ * Make Selection: fills a path (`shape`, `subpaths` or `components`, document pixels) into a
951
+ * selection combined with the current one by `mode`, with `feather` px and `antiAlias`. One
952
+ * undo step. Returns whether anything is selected.
953
+ */
954
+ pathToSelection(path: VectorMaskPathInput, options?: PathToSelectionOptions & {
955
+ mode?: SelectionMode;
956
+ }): boolean;
925
957
  /** Replaces a vector mask's path (its properties are kept). One undo step. Returns the vector mask. */
926
958
  setVectorMaskPath(id: string, path: VectorMaskPathInput): VectorMask;
927
959
  /** Changes a vector mask's enabled, linked, inverted, density or feather (the path is kept). One undo step. */
@@ -1782,6 +1814,15 @@ export declare class ImageEditorController {
1782
1814
  * text wrapped in `box`. Refused without a text shaper. Returns its id.
1783
1815
  */
1784
1816
  addTextLayer(init: TextLayerInit): string;
1817
+ /**
1818
+ * Type Mask (horizontal or vertical): a selection in the shape of `text`, laid out and drawn as
1819
+ * `addTextLayer` would place it (font, size, tracking, box, rotation), combined with the current
1820
+ * selection by `mode`. Vertical type stacks the characters top to bottom, centred on `x`. No
1821
+ * layer is added. One undo step. Returns whether anything is selected.
1822
+ */
1823
+ selectTextShape(init: TypeMaskInit): boolean;
1824
+ /** A type layer laid out and drawn from `init` (not added): the shared part of `addTextLayer` and the type masks. */
1825
+ private layoutTypeLayer;
1785
1826
  /**
1786
1827
  * Replaces text [start, end) (default all of it) with `text`; inserted text takes the style at
1787
1828
  * `start`. One undo step unless `commitToHistory` is false (typing). Returns the layer.