@pixerate/editor 0.111.0 → 0.113.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 (38) hide show
  1. package/THIRD_PARTY_NOTICES.md +6 -0
  2. package/dist/chunk-4KLT7ZYS.cjs +781 -0
  3. package/dist/chunk-4KLT7ZYS.cjs.map +1 -0
  4. package/dist/{chunk-HHZ6ZJTW.js → chunk-CZOXTEMN.js} +1772 -456
  5. package/dist/chunk-CZOXTEMN.js.map +1 -0
  6. package/dist/chunk-LPYOIVLA.js +781 -0
  7. package/dist/chunk-LPYOIVLA.js.map +1 -0
  8. package/dist/{chunk-PHUMLSCW.cjs → chunk-YGIMK4NX.cjs} +1810 -494
  9. package/dist/chunk-YGIMK4NX.cjs.map +1 -0
  10. package/dist/{export-DoDhCszr.d.cts → export-CEXEzqBf.d.cts} +150 -18
  11. package/dist/{export-DBNVwb6h.d.ts → export-DIkoK8-r.d.ts} +150 -18
  12. package/dist/image-editor/index.cjs +1922 -788
  13. package/dist/image-editor/index.cjs.map +1 -1
  14. package/dist/image-editor/index.d.cts +233 -9
  15. package/dist/image-editor/index.d.ts +233 -9
  16. package/dist/image-editor/index.js +1208 -74
  17. package/dist/image-editor/index.js.map +1 -1
  18. package/dist/paint/index.cjs +61 -2
  19. package/dist/paint/index.cjs.map +1 -1
  20. package/dist/paint/index.d.cts +29 -2
  21. package/dist/paint/index.d.ts +29 -2
  22. package/dist/paint/index.js +60 -1
  23. package/dist/paint-editor/index.cjs +6 -6
  24. package/dist/paint-editor/index.d.cts +3 -3
  25. package/dist/paint-editor/index.d.ts +3 -3
  26. package/dist/paint-editor/index.js +1 -1
  27. package/dist/{blending-DXl06NSx.d.ts → retouch-BXmeSCaB.d.ts} +218 -1
  28. package/dist/{blending-aOE6Y3UE.d.cts → retouch-CEIKR22W.d.cts} +218 -1
  29. package/package.json +1 -1
  30. package/skills/pixerate-image-editor/SKILL.md +30 -6
  31. package/skills/pixerate-image-editor/references/retouching.md +36 -0
  32. package/skills/pixerate-vector/SKILL.md +102 -5
  33. package/dist/chunk-FTBZPI77.cjs +0 -1
  34. package/dist/chunk-FTBZPI77.cjs.map +0 -1
  35. package/dist/chunk-HHZ6ZJTW.js.map +0 -1
  36. package/dist/chunk-PHUMLSCW.cjs.map +0 -1
  37. package/dist/chunk-VKK5VS2T.js +0 -1
  38. package/dist/chunk-VKK5VS2T.js.map +0 -1
@@ -522,20 +522,20 @@ function hitTestCrop(canvasPt, state, vpWidth, vpHeight, handleThreshold = 14, c
522
522
  }
523
523
  function hitTestCropRect(canvasPt, rect, handleThreshold) {
524
524
  const { x, y, width: w, height: h } = rect;
525
- const px = canvasPt.x;
525
+ const px2 = canvasPt.x;
526
526
  const py = canvasPt.y;
527
527
  const dist = (x1, y1, x2, y2) => Math.hypot(x1 - x2, y1 - y2);
528
- if (dist(px, py, x, y) <= handleThreshold) return "nw";
529
- if (dist(px, py, x + w, y) <= handleThreshold) return "ne";
530
- if (dist(px, py, x + w, y + h) <= handleThreshold) return "se";
531
- if (dist(px, py, x, y + h) <= handleThreshold) return "sw";
532
- const nearX = px >= x - handleThreshold && px <= x + w + handleThreshold;
528
+ if (dist(px2, py, x, y) <= handleThreshold) return "nw";
529
+ if (dist(px2, py, x + w, y) <= handleThreshold) return "ne";
530
+ if (dist(px2, py, x + w, y + h) <= handleThreshold) return "se";
531
+ if (dist(px2, py, x, y + h) <= handleThreshold) return "sw";
532
+ const nearX = px2 >= x - handleThreshold && px2 <= x + w + handleThreshold;
533
533
  const nearY = py >= y - handleThreshold && py <= y + h + handleThreshold;
534
534
  if (nearX && Math.abs(py - y) <= handleThreshold) return "n";
535
535
  if (nearX && Math.abs(py - (y + h)) <= handleThreshold) return "s";
536
- if (nearY && Math.abs(px - x) <= handleThreshold) return "w";
537
- if (nearY && Math.abs(px - (x + w)) <= handleThreshold) return "e";
538
- if (px >= x && px <= x + w && py >= y && py <= y + h) {
536
+ if (nearY && Math.abs(px2 - x) <= handleThreshold) return "w";
537
+ if (nearY && Math.abs(px2 - (x + w)) <= handleThreshold) return "e";
538
+ if (px2 >= x && px2 <= x + w && py >= y && py <= y + h) {
539
539
  return "inside";
540
540
  }
541
541
  return null;
@@ -2146,8 +2146,8 @@ function applyFastBoxBlur(imageData, radius) {
2146
2146
  let r = 0, g = 0, b = 0, a = 0;
2147
2147
  let count = 0;
2148
2148
  for (let k = -radius; k <= radius; k++) {
2149
- const px = Math.min(w - 1, Math.max(0, x + k));
2150
- const idx = (y * w + px) * 4;
2149
+ const px2 = Math.min(w - 1, Math.max(0, x + k));
2150
+ const idx = (y * w + px2) * 4;
2151
2151
  r += src[idx];
2152
2152
  g += src[idx + 1];
2153
2153
  b += src[idx + 2];
@@ -3129,9 +3129,9 @@ function updateLayerFilter(layers, id, filterId, changes) {
3129
3129
  if (i === -1) fail(`Layer "${id}" has no filter "${filterId}". Filters: ${(layer.filters ?? []).map((f) => f.id).join(", ") || "none"}.`);
3130
3130
  const current = layer.filters[i];
3131
3131
  if ("kind" in changes && changes.kind !== current.kind) fail("A filter kind cannot change; remove it and add another.");
3132
- const merged = normalizeFilter({ ...current, ...changes, kind: current.kind }, current.id);
3132
+ const merged2 = normalizeFilter({ ...current, ...changes, kind: current.kind }, current.id);
3133
3133
  const filters = [...layer.filters];
3134
- filters[i] = merged;
3134
+ filters[i] = merged2;
3135
3135
  return mapLayer(layers, id, () => ({ ...layer, filters }));
3136
3136
  }
3137
3137
  function removeLayerFilter(layers, id, filterId) {
@@ -3418,7 +3418,8 @@ function shapeGeometry(layer) {
3418
3418
  bounds = { x: x0, y: y0, width: Math.max(bounds.x + bounds.width, b.x + b.width) - x0, height: Math.max(bounds.y + bounds.height, b.y + b.height) - y0 };
3419
3419
  }
3420
3420
  };
3421
- if (layer.fill) for (const f of fills) add(flatBounds(f.flat));
3421
+ if (layer.fill && layer.inverted) add({ x: -1e7, y: -1e7, width: 2e7, height: 2e7 });
3422
+ else if (layer.fill) for (const f of fills) add(flatBounds(f.flat));
3422
3423
  if (stroke) add(flatBounds(stroke));
3423
3424
  const g = shapeGeometryBounds(layer) ?? { x: 0, y: 0, width: 0, height: 0 };
3424
3425
  const frame = { x: Math.floor(g.x), y: Math.floor(g.y), width: Math.max(1, Math.ceil(g.x + g.width) - Math.floor(g.x)), height: Math.max(1, Math.ceil(g.y + g.height) - Math.floor(g.y)) };
@@ -3426,8 +3427,14 @@ function shapeGeometry(layer) {
3426
3427
  geometryCache.set(layer, geo);
3427
3428
  return geo;
3428
3429
  }
3429
- function fillCoverage(geo, rect, rule) {
3430
+ function fillCoverage(geo, rect, rule, inverted = false) {
3430
3431
  const n = rect.width * rect.height;
3432
+ if (inverted) {
3433
+ const area = fillCoverage(geo, rect, rule);
3434
+ const out = new Float32Array(n);
3435
+ for (let i = 0; i < n; i++) out[i] = 1 - (area ? area[i] : 0);
3436
+ return out;
3437
+ }
3431
3438
  let acc = null;
3432
3439
  geo.fills.forEach((f, i) => {
3433
3440
  const cov = f.flat.length > 1 ? rasterizeCoverageSampled(f.flat, rect, rule) : rasterizeCoverage(f.flat, rect, rule);
@@ -3465,7 +3472,7 @@ function renderShapeRect(layer, rect, env, ctx = paintContext(env)) {
3465
3472
  const n = rect.width * rect.height;
3466
3473
  const rule = layer.fillRule ?? "nonzero";
3467
3474
  const needArea = Boolean(layer.fill) || geo.stroke !== null && geo.align !== "center";
3468
- const area = needArea ? fillCoverage(geo, rect, rule) : null;
3475
+ const area = needArea ? fillCoverage(geo, rect, rule, Boolean(layer.inverted && layer.fill)) : null;
3469
3476
  let sCov = geo.stroke ? rasterizeCoverageSampled(geo.stroke, rect, "nonzero") : null;
3470
3477
  if (sCov && geo.align === "inside") sCov = area ? combineCoverage(sCov, area, "intersect", n) : null;
3471
3478
  else if (sCov && geo.align === "outside" && area) sCov = combineCoverage(sCov, area, "subtract", n);
@@ -3522,9 +3529,9 @@ function renderShape(layer, env, options = {}) {
3522
3529
  const tiles = /* @__PURE__ */ new Map();
3523
3530
  for (const { tx, ty } of tilesInRect(region)) {
3524
3531
  const rect = { x: tx * TILE, y: ty * TILE, width: TILE, height: TILE };
3525
- const px = renderShapeRect(layer, rect, env, ctx);
3526
- if (!px) continue;
3527
- const tile = finishTile(px, 4, 0);
3532
+ const px2 = renderShapeRect(layer, rect, env, ctx);
3533
+ if (!px2) continue;
3534
+ const tile = finishTile(px2, 4, 0);
3528
3535
  if (tile) tiles.set(tileKey(tx, ty), tile);
3529
3536
  }
3530
3537
  return new TiledSurface(4, tiles, 0);
@@ -3755,6 +3762,7 @@ function createShapeLayer(init, env, id = newLayerId()) {
3755
3762
  offset: { x: 0, y: 0 }
3756
3763
  };
3757
3764
  if (init.fillRule && init.fillRule !== "nonzero") layer.fillRule = init.fillRule;
3765
+ if (init.inverted) layer.inverted = true;
3758
3766
  return rerenderShape(layer, env);
3759
3767
  }
3760
3768
  function defaultShapeName(c) {
@@ -3807,7 +3815,7 @@ function mergeShapeComponents(components, fillRule = "nonzero") {
3807
3815
  return [{ op: "combine", subpaths }];
3808
3816
  }
3809
3817
  function shapeAsPixelLayer(layer) {
3810
- const { components: _c, fillRule: _r, fill: _f, stroke: _s, annotation: _a, psd: _p, type: _t, ...rest } = layer;
3818
+ const { components: _c, fillRule: _r, inverted: _i, fill: _f, stroke: _s, annotation: _a, psd: _p, type: _t, ...rest } = layer;
3811
3819
  return { ...rest, type: "pixel", surface: layer.surface, offset: { x: 0, y: 0 } };
3812
3820
  }
3813
3821
  function annotationPaint(color, opacity) {
@@ -4302,16 +4310,16 @@ function renderOutlines(layer, shaper, region) {
4302
4310
  }
4303
4311
  }
4304
4312
  if (!any) continue;
4305
- const px = new Uint8ClampedArray(n * 4);
4313
+ const px2 = new Uint8ClampedArray(n * 4);
4306
4314
  for (let i = 0; i < n * 4; i += 4) {
4307
4315
  const a = Math.round(acc[i + 3] * 255);
4308
4316
  if (!a) continue;
4309
- px[i] = Math.round(acc[i]);
4310
- px[i + 1] = Math.round(acc[i + 1]);
4311
- px[i + 2] = Math.round(acc[i + 2]);
4312
- px[i + 3] = a;
4317
+ px2[i] = Math.round(acc[i]);
4318
+ px2[i + 1] = Math.round(acc[i + 1]);
4319
+ px2[i + 2] = Math.round(acc[i + 2]);
4320
+ px2[i + 3] = a;
4313
4321
  }
4314
- const tile = finishTile(px, 4, 0);
4322
+ const tile = finishTile(px2, 4, 0);
4315
4323
  if (tile) tiles.set(tileKey(tx, ty), tile);
4316
4324
  }
4317
4325
  return new TiledSurface(4, tiles, 0);
@@ -5581,7 +5589,7 @@ function updateSmartFilter(layers, id, filterId, changes, canvas) {
5581
5589
  const i = requireFilter(layer, filterId);
5582
5590
  const current = layer.smartFilters.filters[i];
5583
5591
  if ("kind" in changes && changes.kind !== current.kind) fail4("A smart filter kind cannot change; remove it and add another.");
5584
- let merged;
5592
+ let merged2;
5585
5593
  if (current.kind === "psd") {
5586
5594
  const extra = Object.keys(changes).filter((k) => !["enabled", "blend", "opacity", "kind"].includes(k));
5587
5595
  if (extra.length) fail4(`"${current.name}" is a Photoshop filter we keep but do not model; only enabled, blend and opacity change. Unknown: ${extra.join(", ")}.`);
@@ -5589,14 +5597,14 @@ function updateSmartFilter(layers, id, filterId, changes, canvas) {
5589
5597
  if (!isBlendMode(blend) || blend === "pigment") fail4(`Unknown blend mode "${String(blend)}" for a smart filter.`);
5590
5598
  const opacity = changes.opacity ?? current.opacity;
5591
5599
  if (!(typeof opacity === "number" && opacity >= 0 && opacity <= 1)) fail4("opacity must be a number from 0 to 1.");
5592
- merged = { ...current, enabled: changes.enabled === void 0 ? current.enabled : Boolean(changes.enabled), blend, opacity };
5600
+ merged2 = { ...current, enabled: changes.enabled === void 0 ? current.enabled : Boolean(changes.enabled), blend, opacity };
5593
5601
  } else {
5594
- merged = normalizeSmartFilter({ ...current, ...changes, kind: current.kind }, current.id);
5602
+ merged2 = normalizeSmartFilter({ ...current, ...changes, kind: current.kind }, current.id);
5595
5603
  }
5596
5604
  const filters = [...layer.smartFilters.filters];
5597
- filters[i] = merged;
5605
+ filters[i] = merged2;
5598
5606
  const next = withStack(layer, { ...layer.smartFilters, filters }, canvas);
5599
- return { layers: mapLayer(layers, id, () => next), layer: next, filter: merged };
5607
+ return { layers: mapLayer(layers, id, () => next), layer: next, filter: merged2 };
5600
5608
  }
5601
5609
  function removeSmartFilter(layers, id, filterId, canvas) {
5602
5610
  const layer = editableSmart(layers, id);
@@ -5782,6 +5790,7 @@ function serializeType(layer, base, ctx) {
5782
5790
  out.psd = {};
5783
5791
  if (layer.psd.tySh) out.psd.tySh = bytesToBase64(layer.psd.tySh);
5784
5792
  if (layer.psd.blocks?.length) out.psd.blocks = layer.psd.blocks.map((b) => ({ key: b.key, data: bytesToBase64(b.data) }));
5793
+ if (layer.psd.signature) out.psd.signature = layer.psd.signature;
5785
5794
  }
5786
5795
  return out;
5787
5796
  }
@@ -5820,6 +5829,7 @@ function readType(r, base, ctx) {
5820
5829
  const psd = {};
5821
5830
  if (typeof r.psd.tySh === "string") psd.tySh = base64ToBytes(r.psd.tySh);
5822
5831
  if (Array.isArray(r.psd.blocks)) psd.blocks = r.psd.blocks.filter((b) => b && typeof b.key === "string" && typeof b.data === "string").map((b) => ({ key: b.key, data: base64ToBytes(b.data) }));
5832
+ if (typeof r.psd.signature === "string") psd.signature = r.psd.signature;
5823
5833
  if (psd.tySh || psd.blocks?.length) layer.psd = psd;
5824
5834
  }
5825
5835
  if (typeof r.asset === "string") return { ...layer, surface: readSurface(r.asset, r.x, r.y, 4, 0, ctx, label), rendering: "stored" };
@@ -5843,9 +5853,10 @@ function serializeShape(layer, base) {
5843
5853
  stroke: layer.stroke ? JSON.parse(JSON.stringify(layer.stroke)) : null
5844
5854
  };
5845
5855
  if (layer.fillRule === "evenodd") out.fillRule = "evenodd";
5856
+ if (layer.inverted) out.inverted = true;
5846
5857
  if (layer.fillOpacity !== void 0 && layer.fillOpacity !== 1) out.fillOpacity = layer.fillOpacity;
5847
5858
  if (layer.annotation) out.annotation = JSON.parse(JSON.stringify(layer.annotation));
5848
- if (layer.psd?.blocks?.length) out.psd = { blocks: layer.psd.blocks.map((b) => ({ key: b.key, data: bytesToBase64(b.data) })) };
5859
+ if (layer.psd?.blocks?.length) out.psd = { blocks: layer.psd.blocks.map((b) => ({ key: b.key, data: bytesToBase64(b.data) })), ...layer.psd.signature ? { signature: layer.psd.signature } : {} };
5849
5860
  return out;
5850
5861
  }
5851
5862
  function readShape(r, base, ctx) {
@@ -5872,13 +5883,14 @@ function readShape(r, base, ctx) {
5872
5883
  stroke: r.stroke === null || r.stroke === void 0 ? null : normalizeShapeStroke(r.stroke, "stroke")
5873
5884
  };
5874
5885
  }
5875
- const layer = createShapeLayer({ ...data, fillRule: r.fillRule === "evenodd" ? "evenodd" : "nonzero" }, env, base.id);
5886
+ const layer = createShapeLayer({ ...data, fillRule: r.fillRule === "evenodd" ? "evenodd" : "nonzero", inverted: r.inverted === true && !a }, env, base.id);
5876
5887
  const out = { ...layer, ...base, type: "shape", components: layer.components, fill: layer.fill, stroke: layer.stroke, surface: layer.surface, offset: { x: 0, y: 0 } };
5877
5888
  if (typeof r.fillOpacity === "number" && r.fillOpacity !== 1) out.fillOpacity = num2(r.fillOpacity, 0, 1, 1);
5878
5889
  if (a && shapeFromAnnotation(a)) out.annotation = a;
5879
5890
  if (r.psd && Array.isArray(r.psd.blocks)) {
5880
5891
  const blocks = r.psd.blocks.filter((b) => b && typeof b.key === "string" && typeof b.data === "string").map((b) => ({ key: b.key, data: base64ToBytes(b.data) }));
5881
5892
  if (blocks.length) out.psd = { blocks };
5893
+ if (out.psd && typeof r.psd.signature === "string") out.psd.signature = r.psd.signature;
5882
5894
  }
5883
5895
  return out;
5884
5896
  } catch (e) {
@@ -6035,8 +6047,8 @@ function readSurface(ref, x, y, channels, defaultValue, ctx, what) {
6035
6047
  return emptySurface(channels, defaultValue);
6036
6048
  }
6037
6049
  try {
6038
- const px = (ctx.decodeAsset ?? decodePngDataUrl)(url, channels);
6039
- return surfaceFromPixels(px, channels, Math.round(num2(x, -1e7, 1e7, 0)), Math.round(num2(y, -1e7, 1e7, 0)), defaultValue);
6050
+ const px2 = (ctx.decodeAsset ?? decodePngDataUrl)(url, channels);
6051
+ return surfaceFromPixels(px2, channels, Math.round(num2(x, -1e7, 1e7, 0)), Math.round(num2(y, -1e7, 1e7, 0)), defaultValue);
6040
6052
  } catch (err) {
6041
6053
  ctx.problems.push(`${what}: asset "${ref}" could not be decoded (${err.message})`);
6042
6054
  return emptySurface(channels, defaultValue);
@@ -6358,8 +6370,8 @@ var PsdReader = class _PsdReader {
6358
6370
  return v;
6359
6371
  }
6360
6372
  /** A length field: 8 bytes in PSB where the format says so, else 4. */
6361
- len(long4) {
6362
- return long4 ? this.u64() : this.u32();
6373
+ len(long5) {
6374
+ return long5 ? this.u64() : this.u32();
6363
6375
  }
6364
6376
  bytes(n) {
6365
6377
  this.need(n, `${n} bytes`);
@@ -6466,11 +6478,11 @@ var PsdWriter = class {
6466
6478
  this.length += n;
6467
6479
  }
6468
6480
  /** Reserves a length field; `endLen` fills it with the bytes written since. */
6469
- beginLen(long4) {
6481
+ beginLen(long5) {
6470
6482
  const at = this.length;
6471
- if (long4) this.u64(0);
6483
+ if (long5) this.u64(0);
6472
6484
  else this.u32(0);
6473
- return { at, long: long4 };
6485
+ return { at, long: long5 };
6474
6486
  }
6475
6487
  endLen(mark) {
6476
6488
  const n = this.length - mark.at - (mark.long ? 8 : 4);
@@ -6515,162 +6527,6 @@ function encodeLegacyName(s) {
6515
6527
  return Uint8Array.from(out);
6516
6528
  }
6517
6529
 
6518
- // src/image-editor/psd/compression.ts
6519
- var MAX_DECODED_BYTES = 2 ** 31;
6520
- function rowBytes(width, depth) {
6521
- return depth === 1 ? Math.ceil(width / 8) : width * Math.max(1, depth >> 3);
6522
- }
6523
- function decodedLength(l) {
6524
- const total = l.planes * l.height * rowBytes(l.width, l.depth);
6525
- if (total > MAX_DECODED_BYTES) throw new PsdError(`Decoded channel data of ${total} bytes is larger than the ${MAX_DECODED_BYTES}-byte limit.`);
6526
- return total;
6527
- }
6528
- function packbitsBound(n) {
6529
- return n + Math.ceil(n / 128);
6530
- }
6531
- function packbitsEncode(src, out, at) {
6532
- const n = src.length;
6533
- let i = 0;
6534
- while (i < n) {
6535
- let run = 1;
6536
- while (i + run < n && run < 128 && src[i + run] === src[i]) run++;
6537
- if (run >= 2) {
6538
- out[at++] = 257 - run & 255;
6539
- out[at++] = src[i];
6540
- i += run;
6541
- continue;
6542
- }
6543
- const start = i;
6544
- let len = 0;
6545
- while (i < n && len < 128) {
6546
- if (i + 2 < n && src[i] === src[i + 1] && src[i] === src[i + 2]) break;
6547
- i++;
6548
- len++;
6549
- }
6550
- out[at++] = len - 1;
6551
- out.set(src.subarray(start, start + len), at);
6552
- at += len;
6553
- }
6554
- return at;
6555
- }
6556
- function packbitsDecodeInto(src, expected, out, at) {
6557
- const target = at + expected;
6558
- let i = 0;
6559
- while (at < target) {
6560
- if (i >= src.length) throw new PsdError("PackBits row ended early.");
6561
- const h = src[i++];
6562
- if (h < 128) {
6563
- const len = h + 1;
6564
- if (i + len > src.length) throw new PsdError("PackBits literal is truncated.");
6565
- if (at + len > target) throw new PsdError("PackBits literal overflows its row.");
6566
- out.set(src.subarray(i, i + len), at);
6567
- at += len;
6568
- i += len;
6569
- } else if (h !== 128) {
6570
- const len = 257 - h;
6571
- if (i >= src.length) throw new PsdError("PackBits run is truncated.");
6572
- const b = src[i++];
6573
- if (at + len > target) throw new PsdError("PackBits run overflows its row.");
6574
- out.fill(b, at, at + len);
6575
- at += len;
6576
- }
6577
- }
6578
- return at;
6579
- }
6580
- function inflateChecked(data, expected) {
6581
- let out;
6582
- try {
6583
- out = inflate(data, { maxOutput: expected + (1 << 20), sizeHint: expected, verify: false });
6584
- } catch (e) {
6585
- throw new PsdError(`ZIP channel data: ${e instanceof ZlibError || e instanceof Error ? e.message : String(e)}`);
6586
- }
6587
- if (out.length < expected) throw new PsdError(`ZIP stream produced ${out.length} of ${expected} bytes.`);
6588
- return out.length === expected ? out : out.subarray(0, expected);
6589
- }
6590
- function unpredict(buf, l) {
6591
- const rb = rowBytes(l.width, l.depth);
6592
- if (rb === 0) return;
6593
- const w = l.width;
6594
- const scratch = l.depth === 32 ? new Uint8Array(rb) : null;
6595
- for (let o = 0; o + rb <= buf.length; o += rb) {
6596
- if (l.depth === 1 || l.depth === 8) {
6597
- for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
6598
- } else if (l.depth === 16) {
6599
- for (let i = 1; i < w; i++) {
6600
- const a = buf[o + 2 * i] << 8 | buf[o + 2 * i + 1];
6601
- const b = buf[o + 2 * i - 2] << 8 | buf[o + 2 * i - 1];
6602
- const v = a + b & 65535;
6603
- buf[o + 2 * i] = v >> 8;
6604
- buf[o + 2 * i + 1] = v & 255;
6605
- }
6606
- } else if (l.depth === 32) {
6607
- for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
6608
- scratch.set(buf.subarray(o, o + rb));
6609
- for (let i = 0; i < w; i++) for (let k = 0; k < 4; k++) buf[o + 4 * i + k] = scratch[k * w + i];
6610
- } else {
6611
- throw new PsdError(`ZIP prediction is not defined for depth ${l.depth}.`);
6612
- }
6613
- }
6614
- }
6615
- function decodeRle(r, l, total) {
6616
- const rows = l.planes * l.height;
6617
- const rb = rowBytes(l.width, l.depth);
6618
- r.checkCount(rows, l.psb ? 4 : 2, "RLE row counts");
6619
- const counts = new Array(rows);
6620
- for (let i = 0; i < rows; i++) counts[i] = l.psb ? r.u32() : r.u16();
6621
- if (total > r.remaining * 130) throw new PsdError("RLE data is too short for its declared size.");
6622
- const out = new Uint8Array(total);
6623
- let at = 0;
6624
- for (const c of counts) at = packbitsDecodeInto(r.bytes(c), rb, out, at);
6625
- return out;
6626
- }
6627
- function decodePlanes(compression, data, l) {
6628
- const total = decodedLength(l);
6629
- switch (compression) {
6630
- case 0:
6631
- if (data.length < total) throw new PsdError(`Raw channel data has ${data.length} of ${total} bytes.`);
6632
- return data.slice(0, total);
6633
- case 1:
6634
- return decodeRle(new PsdReader(data), l, total);
6635
- case 2:
6636
- return inflateChecked(data, total).slice();
6637
- case 3: {
6638
- const v = inflateChecked(data, total).slice();
6639
- unpredict(v, l);
6640
- return v;
6641
- }
6642
- default:
6643
- throw new PsdError(`Unsupported compression method ${compression}.`);
6644
- }
6645
- }
6646
- function encodeRle(decoded2, l) {
6647
- const rows = l.planes * l.height;
6648
- const rb = rowBytes(l.width, l.depth);
6649
- const cs = l.psb ? 4 : 2;
6650
- const packed = new Uint8Array(rows * packbitsBound(rb));
6651
- let at = 0;
6652
- const counts = new Uint8Array(rows * cs);
6653
- for (let row = 0; row < rows; row++) {
6654
- const start = at;
6655
- at = packbitsEncode(decoded2.subarray(row * rb, (row + 1) * rb), packed, at);
6656
- const n = at - start;
6657
- if (cs === 2) {
6658
- if (n > 65535) throw new PsdError("An RLE row is longer than 65535 bytes; write a PSB instead.");
6659
- counts[row * 2] = n >> 8;
6660
- counts[row * 2 + 1] = n & 255;
6661
- } else {
6662
- counts[row * 4] = n >>> 24;
6663
- counts[row * 4 + 1] = n >>> 16 & 255;
6664
- counts[row * 4 + 2] = n >>> 8 & 255;
6665
- counts[row * 4 + 3] = n & 255;
6666
- }
6667
- }
6668
- const out = new Uint8Array(counts.length + at);
6669
- out.set(counts);
6670
- out.set(packed.subarray(0, at), counts.length);
6671
- return out;
6672
- }
6673
-
6674
6530
  // src/image-editor/psd/descriptor.ts
6675
6531
  var MAX_DEPTH = 64;
6676
6532
  var STRING_IDS = /* @__PURE__ */ new Set(["warp", "list", "time"]);
@@ -6786,12 +6642,12 @@ function readValue(r, depth) {
6786
6642
  case "UntF":
6787
6643
  return { type, unit: r.ascii(4), value: r.f64() };
6788
6644
  case "UnFl": {
6789
- const unit3 = r.ascii(4);
6645
+ const unit4 = r.ascii(4);
6790
6646
  const n = r.u32();
6791
6647
  r.checkCount(n, 8, "Unit float list");
6792
6648
  const values = [];
6793
6649
  for (let i = 0; i < n; i++) values.push(r.f64());
6794
- return { type, unit: unit3, values };
6650
+ return { type, unit: unit4, values };
6795
6651
  }
6796
6652
  case "TEXT":
6797
6653
  return { type, value: r.unicode() };
@@ -7244,11 +7100,11 @@ function writeAdjustmentBlock(kind, params) {
7244
7100
  case "blackWhite": {
7245
7101
  const p = params;
7246
7102
  const tint = p.tint ? hexToRgb(p.tint) : BW_TINT;
7247
- const dbl3 = (v) => ({ type: "doub", value: clamp2(v, 0, 1) * 255 });
7103
+ const dbl4 = (v) => ({ type: "doub", value: clamp2(v, 0, 1) * 255 });
7248
7104
  const items = BW_KEYS.map(([field, key]) => [key, long(clamp2(p[field], -200, 300))]);
7249
7105
  items.push(
7250
7106
  ["useTint", { type: "bool", value: p.tint !== null }],
7251
- ["tintColor", { type: "Objc", value: descriptor("RGBC", [["Rd ", dbl3(tint[0])], ["Grn ", dbl3(tint[1])], ["Bl ", dbl3(tint[2])]]) }],
7107
+ ["tintColor", { type: "Objc", value: descriptor("RGBC", [["Rd ", dbl4(tint[0])], ["Grn ", dbl4(tint[1])], ["Bl ", dbl4(tint[2])]]) }],
7252
7108
  ["bwPresetKind", { type: "long", value: 1 }],
7253
7109
  ["blackAndWhitePresetFileName", { type: "TEXT", value: "" }]
7254
7110
  );
@@ -7270,83 +7126,1423 @@ function writeAdjustmentBlock(kind, params) {
7270
7126
  throw new Error(`Unknown adjustment kind "${kind}".`);
7271
7127
  }
7272
7128
 
7273
- // src/image-editor/psd/smart.ts
7274
- var dbl = (value) => ({ type: "doub", value });
7129
+ // src/image-editor/psd/paths.ts
7130
+ var RECORD = 26;
7131
+ var fixed = (v) => v / 16777216;
7132
+ var toFixed = (v) => Math.max(-2147483648, Math.min(2147483647, Math.round(v * 16777216)));
7133
+ function readPathRecords(data) {
7134
+ const r = new PsdReader(data);
7135
+ const subpaths = [];
7136
+ let initialFill = false;
7137
+ let remaining = 0;
7138
+ while (r.remaining >= RECORD) {
7139
+ const selector = r.u16();
7140
+ const body = r.sub(24);
7141
+ if (selector === 0 || selector === 3) {
7142
+ remaining = body.u16();
7143
+ subpaths.push({ closed: selector === 0, operation: body.i16(), knots: [] });
7144
+ } else if (selector === 1 || selector === 2 || selector === 4 || selector === 5) {
7145
+ const closed = selector <= 2;
7146
+ const last = subpaths[subpaths.length - 1];
7147
+ if (remaining === 0 && (!last || last.closed !== closed)) subpaths.push({ closed, operation: -1, knots: [] });
7148
+ remaining = Math.max(0, remaining - 1);
7149
+ const pt = () => {
7150
+ const y = fixed(body.i32());
7151
+ const x = fixed(body.i32());
7152
+ return { x, y };
7153
+ };
7154
+ const pre = pt();
7155
+ const anchor = pt();
7156
+ const post = pt();
7157
+ subpaths[subpaths.length - 1].knots.push({ linked: selector === 1 || selector === 4, pre, anchor, post });
7158
+ } else if (selector === 8) {
7159
+ initialFill = body.u16() !== 0;
7160
+ }
7161
+ }
7162
+ return { subpaths, initialFill };
7163
+ }
7164
+ function writePathRecords(path) {
7165
+ const w = new PsdWriter();
7166
+ w.u16(6);
7167
+ w.zeros(24);
7168
+ w.u16(8);
7169
+ w.u16(path.initialFill ? 1 : 0);
7170
+ w.zeros(22);
7171
+ for (const s of path.subpaths) {
7172
+ w.u16(s.closed ? 0 : 3);
7173
+ w.u16(Math.min(s.knots.length, 65535));
7174
+ w.i16(s.operation);
7175
+ w.u16(1);
7176
+ w.zeros(18);
7177
+ for (const k of s.knots.slice(0, 65535)) {
7178
+ w.u16(s.closed ? k.linked ? 1 : 2 : k.linked ? 4 : 5);
7179
+ for (const p of [k.pre, k.anchor, k.post]) {
7180
+ w.i32(toFixed(p.y));
7181
+ w.i32(toFixed(p.x));
7182
+ }
7183
+ }
7184
+ }
7185
+ return w.result();
7186
+ }
7187
+ var VMSK_INVERT = 1;
7188
+ function readVectorBlock(data) {
7189
+ if (data.length < 8) return null;
7190
+ const r = new PsdReader(data);
7191
+ const version = r.u32();
7192
+ const flags = r.u32();
7193
+ return { version, flags, path: readPathRecords(data.subarray(8)) };
7194
+ }
7195
+ function writeVectorBlock(path, flags = 0) {
7196
+ const body = writePathRecords(path);
7197
+ const w = new PsdWriter();
7198
+ w.u32(3);
7199
+ w.u32(flags);
7200
+ w.bytes(body);
7201
+ return w.result();
7202
+ }
7203
+ var OP_TO_PSD = { combine: 1, subtract: 2, intersect: 3, exclude: 0 };
7204
+ var OP_FROM_PSD = (v) => v === 0 ? "exclude" : v === 2 ? "subtract" : v === 3 ? "intersect" : "combine";
7205
+ function componentsFromVectorBlock(data, width, height) {
7206
+ const block = readVectorBlock(data);
7207
+ if (!block) return null;
7208
+ const pt = (p) => ({ x: p.x * width, y: p.y * height });
7209
+ const comps = [];
7210
+ for (const s of block.path.subpaths) {
7211
+ if (!s.knots.length) continue;
7212
+ const anchors = s.knots.map((k) => {
7213
+ const a = pt(k.anchor);
7214
+ const pre = pt(k.pre);
7215
+ const post = pt(k.post);
7216
+ const out = { x: a.x, y: a.y };
7217
+ if (pre.x !== a.x || pre.y !== a.y) out.in = pre;
7218
+ if (post.x !== a.x || post.y !== a.y) out.out = post;
7219
+ if (k.linked && (out.in || out.out)) out.type = "smooth";
7220
+ return out;
7221
+ });
7222
+ const sp = { closed: s.closed, anchors };
7223
+ if (s.operation === -1 && comps.length) comps[comps.length - 1].subpaths.push(sp);
7224
+ else comps.push({ op: s.operation, subpaths: [sp] });
7225
+ }
7226
+ let inverted = comps.length ? comps[0].op === 2 : block.path.initialFill;
7227
+ const components = comps.map((c, i) => {
7228
+ let op = i === 0 ? "combine" : OP_FROM_PSD(c.op);
7229
+ if (inverted && i > 0) op = op === "combine" ? "subtract" : op === "subtract" ? "combine" : op;
7230
+ return { op, subpaths: c.subpaths };
7231
+ });
7232
+ if (block.flags & VMSK_INVERT) inverted = !inverted;
7233
+ return { components, inverted, flags: block.flags };
7234
+ }
7235
+ function vectorBlockForComponents(components, inverted, width, height, flags = 0) {
7236
+ const w = Math.max(1, width);
7237
+ const h = Math.max(1, height);
7238
+ const fr = (p) => ({ x: p.x / w, y: p.y / h });
7239
+ const subpaths = [];
7240
+ components.forEach((c, ci) => {
7241
+ expandVectorCorners(c.subpaths).forEach((sp, si) => {
7242
+ if (!sp.anchors.length) return;
7243
+ subpaths.push({
7244
+ closed: sp.closed,
7245
+ operation: si > 0 ? -1 : ci === 0 ? 1 : OP_TO_PSD[c.op],
7246
+ knots: sp.anchors.map((a) => ({
7247
+ linked: a.type === "smooth" || a.type === "symmetric",
7248
+ pre: fr(a.in ?? a),
7249
+ anchor: fr(a),
7250
+ post: fr(a.out ?? a)
7251
+ }))
7252
+ });
7253
+ });
7254
+ });
7255
+ return writeVectorBlock({ subpaths, initialFill: subpaths.length === 0 && inverted }, flags & ~VMSK_INVERT | (inverted && subpaths.length ? VMSK_INVERT : 0));
7256
+ }
7257
+
7258
+ // src/image-editor/psd/shapes.ts
7259
+ var SHAPE_PSD_KEYS = ["vsms", "vmsk", "vscg", "vstk", "vogk", "SoCo", "GdFl", "PtFl"];
7275
7260
  var unit = (u, value) => ({ type: "UntF", unit: u, value });
7261
+ var dbl = (value) => ({ type: "doub", value });
7276
7262
  var long2 = (value) => ({ type: "long", value: Math.round(value) });
7277
7263
  var boolV = (value) => ({ type: "bool", value });
7278
7264
  var enumV = (typeId, value) => ({ type: "enum", typeId, value });
7279
- var textV = (value) => ({ type: "TEXT", value });
7280
7265
  var obj = (d) => ({ type: "Objc", value: d });
7281
- var text = (d, key) => {
7282
- const v = descGet(d, key);
7283
- return v?.type === "TEXT" ? v.value : void 0;
7284
- };
7285
7266
  var enumOf = (d, key) => {
7286
7267
  const v = descGet(d, key);
7287
7268
  return v?.type === "enum" ? v.value : void 0;
7288
7269
  };
7289
- var numbers = (d, key) => {
7290
- const v = descGet(d, key);
7291
- if (v?.type !== "VlLs") return null;
7292
- const out = [];
7293
- for (const it of v.items) {
7294
- if (it.type === "doub" || it.type === "UntF" || it.type === "long") out.push(it.value);
7295
- else return null;
7270
+ function read(data) {
7271
+ try {
7272
+ return parseVersionedDescriptor(data);
7273
+ } catch {
7274
+ return null;
7296
7275
  }
7297
- return out;
7298
- };
7299
- function setItem(d, key, v) {
7300
- const i = d.items.findIndex(([k]) => k === key);
7301
- if (i >= 0) d.items[i] = [key, v];
7302
- else d.items.push([key, v]);
7303
- }
7304
- function cloneDescriptor(d) {
7305
- return parseVersionedDescriptor(versionedDescriptorBytes(d));
7306
7276
  }
7307
- function psdUuid(seed) {
7308
- if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(seed)) return seed;
7309
- let hex = "";
7310
- for (let k = 0; k < 4; k++) {
7311
- let h = (2166136261 ^ k * 2654435769) >>> 0;
7312
- for (let i = 0; i < seed.length; i++) h = Math.imul(h ^ seed.charCodeAt(i), 16777619) >>> 0;
7313
- hex += h.toString(16).padStart(8, "0");
7277
+ function px(v, dpi) {
7278
+ if (!v) return void 0;
7279
+ if (v.type === "UntF") {
7280
+ if (v.unit === "#Pnt") return v.value * dpi / 72;
7281
+ if (v.unit === "#Mlm") return v.value * dpi / 25.4;
7282
+ return v.value;
7314
7283
  }
7315
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
7284
+ if (v.type === "doub" || v.type === "long") return v.value;
7285
+ return void 0;
7316
7286
  }
7317
- var LINKED_FILE_KEYS = ["lnk2", "lnk3", "lnkD", "lnkE"];
7318
- function readLinkedItem(item) {
7319
- const r = new PsdReader(item);
7320
- const kind = r.ascii(4);
7321
- const version = r.u32();
7322
- const n = r.u8();
7323
- const uuid = new TextDecoder().decode(r.bytes(n));
7324
- const name = r.unicode();
7325
- const fileType = r.ascii(4);
7326
- r.skip(4);
7327
- const len = r.u64();
7328
- if (r.u8() !== 0) readVersionedDescriptor(r);
7329
- if (kind === "liFD") return { kind, uuid, name, fileType, bytes: r.bytes(Math.min(len, r.remaining)) };
7330
- let path;
7331
- if (kind === "liFE") {
7332
- try {
7333
- const d = readVersionedDescriptor(r);
7334
- path = text(d, "fullPath") ?? text(d, "originalPath") ?? text(d, "relPath");
7335
- } catch {
7336
- path = void 0;
7337
- }
7338
- void version;
7287
+ function paintFromContent(key, d, notes) {
7288
+ if (key === "SoCo") return { type: "color", color: colorOf(descObject(d, "Clr ")) };
7289
+ if (key === "GdFl") return { type: "gradient", gradient: gradientOf(d, notes) };
7290
+ if (key === "PtFl") {
7291
+ const ref = patternRefOf(d);
7292
+ const place = placementOf(d);
7293
+ const scale = (descNumber(d, "Scl ") ?? 100) / 100;
7294
+ const p = { type: "pattern", patternId: ref.patternId, name: ref.name, scale: Math.max(0.01, Math.min(10, scale)) };
7295
+ if (place.angle) p.angle = place.angle;
7296
+ if (!place.link) p.link = false;
7297
+ if (place.phase.x || place.phase.y) p.phase = place.phase;
7298
+ return p;
7339
7299
  }
7340
- return { kind, uuid, name, fileType, bytes: new Uint8Array(0), path: path ?? name };
7300
+ return null;
7341
7301
  }
7342
- function parseLinkedFiles(data) {
7343
- const out = [];
7344
- let at = 0;
7345
- while (at + 8 <= data.length) {
7346
- const r = new PsdReader(data, at);
7347
- const len = r.u64();
7348
- if (!(len > 0) || at + 8 + len > data.length) break;
7349
- try {
7302
+ function contentOfPaint(p) {
7303
+ if (p.type === "color") return { key: "SoCo", desc: descriptor("null", [["Clr ", obj(colorDesc(p.color))]]) };
7304
+ if (p.type === "gradient") return { key: "GdFl", desc: descriptor("null", gradientItems(p.gradient)) };
7305
+ return {
7306
+ key: "PtFl",
7307
+ desc: descriptor("null", [["Ptrn", patternRefDesc(p.patternId, p.name)], ["Scl ", unit("#Prc", p.scale * 100)], ...placementItems(p.angle ?? 0, p.link ?? true, p.phase ?? { x: 0, y: 0 })])
7308
+ };
7309
+ }
7310
+ function paintFromVscg(data, notes) {
7311
+ if (data.length < 8) return null;
7312
+ const key = String.fromCharCode(...data.subarray(0, 4));
7313
+ const d = read(data.subarray(4));
7314
+ return d ? paintFromContent(key, d, notes) : null;
7315
+ }
7316
+ function vscgBytes(p) {
7317
+ const { key, desc } = contentOfPaint(p);
7318
+ const body = versionedDescriptorBytes(desc);
7319
+ const out = new Uint8Array(4 + body.length);
7320
+ for (let i = 0; i < 4; i++) out[i] = key.charCodeAt(i);
7321
+ out.set(body, 4);
7322
+ return out;
7323
+ }
7324
+ function classOfContent(d) {
7325
+ if (d.classId === "solidColorLayer" || descGet(d, "Clr ")) return "SoCo";
7326
+ if (d.classId === "gradientLayer" || descGet(d, "Grad")) return "GdFl";
7327
+ return "PtFl";
7328
+ }
7329
+ function strokeFromVstk(data, dpi, notes) {
7330
+ const d = read(data);
7331
+ if (!d) return null;
7332
+ const fillEnabled = descBool(d, "fillEnabled") !== false;
7333
+ if (descBool(d, "strokeEnabled") !== true) return { stroke: null, fillEnabled };
7334
+ const content = descObject(d, "strokeStyleContent");
7335
+ const paint = (content ? paintFromContent(classOfContent(content), content, notes) : null) ?? { type: "color", color: "#000000" };
7336
+ const opacity = (descNumber(d, "strokeStyleOpacity") ?? 100) / 100;
7337
+ const stroke = { width: px(descGet(d, "strokeStyleLineWidth"), dpi) ?? 1, paint: opacity < 1 ? { ...paint, opacity } : paint };
7338
+ const cap = enumOf(d, "strokeStyleLineCapType");
7339
+ if (cap === "strokeStyleRoundCap") stroke.cap = "round";
7340
+ else if (cap === "strokeStyleSquareCap") stroke.cap = "square";
7341
+ const join = enumOf(d, "strokeStyleLineJoinType");
7342
+ if (join === "strokeStyleRoundJoin") stroke.join = "round";
7343
+ else if (join === "strokeStyleBevelJoin") stroke.join = "bevel";
7344
+ const align = enumOf(d, "strokeStyleLineAlignment");
7345
+ if (align === "strokeStyleAlignInside") stroke.align = "inside";
7346
+ else if (align === "strokeStyleAlignOutside") stroke.align = "outside";
7347
+ const miter = descNumber(d, "strokeStyleMiterLimit");
7348
+ if (miter !== void 0 && miter !== 100) stroke.miterLimit = Math.max(1, Math.min(500, miter));
7349
+ const dashes = descGet(d, "strokeStyleLineDashSet");
7350
+ if (dashes?.type === "VlLs") {
7351
+ const list2 = dashes.items.map((v) => v.type === "UntF" || v.type === "doub" || v.type === "long" ? v.value : NaN).filter((v) => Number.isFinite(v) && v >= 0);
7352
+ if (list2.length && list2.some((v) => v > 0)) stroke.dashes = list2;
7353
+ }
7354
+ const offset = descNumber(d, "strokeStyleLineDashOffset");
7355
+ if (offset) stroke.dashOffset = offset;
7356
+ if (enumOf(d, "strokeStyleBlendMode") && enumOf(d, "strokeStyleBlendMode") !== "Nrml" && enumOf(d, "strokeStyleBlendMode") !== "normal") notes.add("a shape stroke blend mode other than Normal became Normal");
7357
+ return { stroke, fillEnabled };
7358
+ }
7359
+ function vstkBytes(stroke, fillEnabled, dpi) {
7360
+ const s = stroke ?? { width: 3, paint: { type: "color", color: "#000000" } };
7361
+ const { key, desc } = contentOfPaint(s.paint);
7362
+ const content = { ...desc, classId: key === "SoCo" ? "solidColorLayer" : key === "GdFl" ? "gradientLayer" : "patternLayer" };
7363
+ const d = descriptor("strokeStyle", [
7364
+ ["strokeStyleVersion", long2(2)],
7365
+ ["strokeEnabled", boolV(Boolean(stroke))],
7366
+ ["fillEnabled", boolV(fillEnabled)],
7367
+ ["strokeStyleLineWidth", unit("#Pxl", s.width)],
7368
+ ["strokeStyleLineDashOffset", unit("#Pnt", s.dashOffset ?? 0)],
7369
+ ["strokeStyleMiterLimit", dbl(s.miterLimit ?? 100)],
7370
+ ["strokeStyleLineCapType", enumV("strokeStyleLineCapType", s.cap === "round" ? "strokeStyleRoundCap" : s.cap === "square" ? "strokeStyleSquareCap" : "strokeStyleButtCap")],
7371
+ ["strokeStyleLineJoinType", enumV("strokeStyleLineJoinType", s.join === "round" ? "strokeStyleRoundJoin" : s.join === "bevel" ? "strokeStyleBevelJoin" : "strokeStyleMiterJoin")],
7372
+ ["strokeStyleLineAlignment", enumV("strokeStyleLineAlignment", s.align === "inside" ? "strokeStyleAlignInside" : s.align === "outside" ? "strokeStyleAlignOutside" : "strokeStyleAlignCenter")],
7373
+ ["strokeStyleScaleLock", boolV(false)],
7374
+ ["strokeStyleStrokeAdjust", boolV(false)],
7375
+ ["strokeStyleLineDashSet", { type: "VlLs", items: (s.dashes ?? []).map((v) => unit("#Nne", v)) }],
7376
+ ["strokeStyleBlendMode", enumV("BlnM", "Nrml")],
7377
+ ["strokeStyleOpacity", unit("#Prc", (s.paint.opacity ?? 1) * 100)],
7378
+ ["strokeStyleContent", obj(content)],
7379
+ ["strokeStyleResolution", dbl(dpi)]
7380
+ ]);
7381
+ return versionedDescriptorBytes(d);
7382
+ }
7383
+ function rectOf(o) {
7384
+ const b = descObject(o, "keyOriginShapeBBox");
7385
+ if (!b) return null;
7386
+ const t = descNumber(b, "Top ");
7387
+ const l = descNumber(b, "Left");
7388
+ const bt = descNumber(b, "Btom");
7389
+ const r = descNumber(b, "Rght");
7390
+ if ([t, l, bt, r].some((v) => v === void 0 || !Number.isFinite(v))) return null;
7391
+ return { x: l, y: t, width: r - l, height: bt - t };
7392
+ }
7393
+ function pointOf(o, key) {
7394
+ const p = descObject(o, key);
7395
+ const x = p && descNumber(p, "Hrzn");
7396
+ const y = p && descNumber(p, "Vrtc");
7397
+ return x !== void 0 && y !== void 0 && Number.isFinite(x) && Number.isFinite(y) ? { x, y } : null;
7398
+ }
7399
+ function liveShapesFromVogk(data) {
7400
+ const out = /* @__PURE__ */ new Map();
7401
+ if (data.length < 8) return out;
7402
+ const d = read(data.subarray(4));
7403
+ const list2 = d && descGet(d, "keyDescriptorList");
7404
+ if (!list2 || list2.type !== "VlLs") return out;
7405
+ list2.items.forEach((item, i) => {
7406
+ if (item.type !== "Objc") return;
7407
+ const o = item.value;
7408
+ if (descBool(o, "keyShapeInvalidated") === true) return;
7409
+ const index = descNumber(o, "keyOriginIndex") ?? i;
7410
+ let transform;
7411
+ const t = descObject(o, "Trnf");
7412
+ if (t) {
7413
+ const g = (k, dv) => descNumber(t, k) ?? dv;
7414
+ const m = [g("xx", 1), g("xy", 0), g("yx", 0), g("yy", 1), g("tx", 0), g("ty", 0)];
7415
+ if (!(m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0)) transform = m;
7416
+ }
7417
+ const tt = transform ? { transform } : {};
7418
+ const type = descNumber(o, "keyOriginType");
7419
+ let live = null;
7420
+ const r = rectOf(o);
7421
+ if ((type === 1 || type === 2) && r) {
7422
+ const rr = type === 2 ? descObject(o, "keyOriginRRectRadii") : void 0;
7423
+ const g = (k) => rr ? descNumber(rr, k) ?? 0 : 0;
7424
+ const radii = [g("topLeft"), g("topRight"), g("bottomRight"), g("bottomLeft")];
7425
+ live = { kind: "rect", ...r, ...radii.some((v) => v > 0) ? { radii } : {}, ...tt };
7426
+ } else if (type === 5 && r) live = { kind: "ellipse", ...r, ...tt };
7427
+ else if (type === 4) {
7428
+ const a = pointOf(o, "keyOriginLineStart");
7429
+ const b = pointOf(o, "keyOriginLineEnd");
7430
+ if (a && b) {
7431
+ const weight = descNumber(o, "keyOriginLineWeight") ?? 1;
7432
+ const line = { kind: "line", x1: a.x, y1: a.y, x2: b.x, y2: b.y, weight, ...tt };
7433
+ const head = () => {
7434
+ const w = (descNumber(o, "keyOriginLineArrWdth") ?? 500) / 100 * weight;
7435
+ const l = (descNumber(o, "keyOriginLineArrLngth") ?? 1e3) / 100 * weight;
7436
+ const c = (descNumber(o, "keyOriginLineArrConc") ?? 0) / 100;
7437
+ return c ? { width: w, length: l, concavity: Math.max(-0.5, Math.min(0.5, c)) } : { width: w, length: l };
7438
+ };
7439
+ if (descBool(o, "keyOriginLineArrowSt") === true) line.startArrow = head();
7440
+ if (descBool(o, "keyOriginLineArrowEnd") === true) line.endArrow = head();
7441
+ live = line;
7442
+ }
7443
+ }
7444
+ if (live) out.set(index, live);
7445
+ });
7446
+ return out;
7447
+ }
7448
+ function vogkBytes(components, dpi) {
7449
+ const bbox = (r) => obj(
7450
+ descriptor("unitRect", [
7451
+ ["unitValueQuadVersion", long2(1)],
7452
+ ["Top ", unit("#Pxl", r.y)],
7453
+ ["Left", unit("#Pxl", r.x)],
7454
+ ["Btom", unit("#Pxl", r.y + r.height)],
7455
+ ["Rght", unit("#Pxl", r.x + r.width)]
7456
+ ])
7457
+ );
7458
+ const items = [];
7459
+ components.forEach((c, index) => {
7460
+ const live = c.live;
7461
+ if (!live || live.kind === "polygon" || live.kind === "star") return;
7462
+ const base = (type) => [
7463
+ ["keyOriginType", long2(type)],
7464
+ ["keyOriginResolution", dbl(dpi)]
7465
+ ];
7466
+ let o;
7467
+ if (live.kind === "rect") {
7468
+ const radii = live.radii ?? [0, 0, 0, 0];
7469
+ if (radii.every((v) => v === 0)) o = [...base(1), ["keyOriginShapeBBox", bbox(live)]];
7470
+ else
7471
+ o = [
7472
+ ...base(2),
7473
+ [
7474
+ "keyOriginRRectRadii",
7475
+ obj(
7476
+ descriptor("radii", [
7477
+ ["unitValueQuadVersion", long2(1)],
7478
+ ["topRight", unit("#Pxl", radii[1])],
7479
+ ["topLeft", unit("#Pxl", radii[0])],
7480
+ ["bottomLeft", unit("#Pxl", radii[3])],
7481
+ ["bottomRight", unit("#Pxl", radii[2])]
7482
+ ])
7483
+ )
7484
+ ],
7485
+ ["keyOriginShapeBBox", bbox(live)]
7486
+ ];
7487
+ } else if (live.kind === "ellipse") o = [...base(5), ["keyOriginShapeBBox", bbox(live)]];
7488
+ else {
7489
+ const pnt = (x, y) => obj(descriptor("Pnt ", [["Hrzn", dbl(x)], ["Vrtc", dbl(y)]]));
7490
+ const head = live.endArrow ?? live.startArrow;
7491
+ const w = live.weight || 1;
7492
+ o = [
7493
+ ...base(4),
7494
+ ["keyOriginShapeBBox", bbox({ x: Math.min(live.x1, live.x2), y: Math.min(live.y1, live.y2), width: Math.abs(live.x2 - live.x1), height: Math.abs(live.y2 - live.y1) })],
7495
+ ["keyOriginLineEnd", pnt(live.x2, live.y2)],
7496
+ ["keyOriginLineStart", pnt(live.x1, live.y1)],
7497
+ ["keyOriginLineWeight", dbl(live.weight)],
7498
+ ["keyOriginLineArrowSt", boolV(Boolean(live.startArrow))],
7499
+ ["keyOriginLineArrowEnd", boolV(Boolean(live.endArrow))],
7500
+ ["keyOriginLineArrWdth", dbl(head ? head.width / w * 100 : 0)],
7501
+ ["keyOriginLineArrLngth", dbl(head ? head.length / w * 100 : 0)],
7502
+ ["keyOriginLineArrConc", long2(head?.concavity ? head.concavity * 100 : 0)]
7503
+ ];
7504
+ }
7505
+ const m = live.transform;
7506
+ if (m) {
7507
+ o.push([
7508
+ "Trnf",
7509
+ obj(
7510
+ descriptor("Trnf", [
7511
+ ["xx", dbl(m[0])],
7512
+ ["xy", dbl(m[1])],
7513
+ ["yx", dbl(m[2])],
7514
+ ["yy", dbl(m[3])],
7515
+ ["tx", dbl(m[4])],
7516
+ ["ty", dbl(m[5])]
7517
+ ])
7518
+ )
7519
+ ]);
7520
+ }
7521
+ o.push(["keyOriginIndex", long2(index)]);
7522
+ items.push(obj(descriptor("null", o)));
7523
+ });
7524
+ if (!items.length) return null;
7525
+ const body = versionedDescriptorBytes(descriptor("null", [["keyDescriptorList", { type: "VlLs", items }]]));
7526
+ const out = new Uint8Array(4 + body.length);
7527
+ new DataView(out.buffer).setUint32(0, 1);
7528
+ out.set(body, 4);
7529
+ return out;
7530
+ }
7531
+ function shapeSignature(layer) {
7532
+ return JSON.stringify([layer.components, layer.fill, layer.stroke, layer.fillRule ?? "nonzero", Boolean(layer.inverted)]);
7533
+ }
7534
+ function shapeFromBlocks(blocks, width, height, dpi, notes) {
7535
+ const block = (k) => blocks.find((b) => b.key === k)?.data;
7536
+ const vector = block("vsms") ?? block("vmsk");
7537
+ const path = vector ? componentsFromVectorBlock(vector, width, height) : null;
7538
+ if (!path) return null;
7539
+ let fill = null;
7540
+ const vscg = block("vscg");
7541
+ if (vscg) fill = paintFromVscg(vscg, notes);
7542
+ for (const key of ["SoCo", "GdFl", "PtFl"]) {
7543
+ if (fill) break;
7544
+ const data = block(key);
7545
+ const d = data && (() => {
7546
+ try {
7547
+ return parseVersionedDescriptor(data);
7548
+ } catch {
7549
+ return null;
7550
+ }
7551
+ })();
7552
+ if (d) fill = paintFromContent(key, d, notes);
7553
+ }
7554
+ let stroke = null;
7555
+ const vstk = block("vstk");
7556
+ if (vstk) {
7557
+ const s = strokeFromVstk(vstk, dpi, notes);
7558
+ if (s) {
7559
+ stroke = s.stroke;
7560
+ if (!s.fillEnabled) fill = null;
7561
+ }
7562
+ }
7563
+ const vogk = block("vogk");
7564
+ const live = vogk ? liveShapesFromVogk(vogk) : /* @__PURE__ */ new Map();
7565
+ const components = path.components.map((c, i) => {
7566
+ const l = live.get(i);
7567
+ return l && sameExtent(liveShapeSubpaths(l), c) ? { ...c, live: l } : c;
7568
+ });
7569
+ return { components, inverted: path.inverted, fill, stroke };
7570
+ }
7571
+ function sameExtent(sub, c) {
7572
+ const ext = (list2) => {
7573
+ let x0 = Infinity;
7574
+ let y0 = Infinity;
7575
+ let x1 = -Infinity;
7576
+ let y1 = -Infinity;
7577
+ for (const sp of list2) for (const a2 of sp.anchors) {
7578
+ x0 = Math.min(x0, a2.x);
7579
+ y0 = Math.min(y0, a2.y);
7580
+ x1 = Math.max(x1, a2.x);
7581
+ y1 = Math.max(y1, a2.y);
7582
+ }
7583
+ return [x0, y0, x1, y1];
7584
+ };
7585
+ const a = ext(sub);
7586
+ const b = ext(c.subpaths);
7587
+ return a.every((v, i) => Math.abs(v - b[i]) < 1);
7588
+ }
7589
+ function shapeBlocks(layer, width, height, dpi, notes) {
7590
+ const kept = layer.psd?.blocks;
7591
+ if (kept?.length && layer.psd?.signature === shapeSignature(layer)) return kept.map((b) => ({ key: b.key, data: b.data }));
7592
+ const out = [];
7593
+ out.push({ key: "vsms", data: vectorBlockForComponents(layer.components, Boolean(layer.inverted), width, height) });
7594
+ if (layer.fillRule === "evenodd" && layer.components.some((c) => c.subpaths.length > 1)) notes.add("Even-odd fill rules were written as nonzero (Photoshop fills shapes nonzero)");
7595
+ const fill = layer.fill ?? { type: "color", color: "#000000" };
7596
+ if (layer.fill?.opacity !== void 0 && layer.fill.opacity < 1) notes.add("Shape fill opacity has no PSD equivalent and was written as 100 % (use the layer fill opacity)");
7597
+ out.push({ key: "vscg", data: vscgBytes(fill) });
7598
+ if (layer.stroke || !layer.fill) out.push({ key: "vstk", data: vstkBytes(layer.stroke, Boolean(layer.fill), dpi) });
7599
+ const vogk = vogkBytes(layer.components, dpi);
7600
+ if (vogk) out.push({ key: "vogk", data: vogk });
7601
+ if (layer.components.some((c) => c.live?.kind === "line" && (c.live.startArrow && c.live.endArrow && JSON.stringify(c.live.startArrow) !== JSON.stringify(c.live.endArrow) || false))) {
7602
+ notes.add("Lines with different start and end arrowheads were written with one arrowhead size (Photoshop has one)");
7603
+ }
7604
+ return out;
7605
+ }
7606
+
7607
+ // src/image-editor/psd/engine-data.ts
7608
+ var EngineDataError = class extends Error {
7609
+ constructor(message) {
7610
+ super(`EngineData: ${message}`);
7611
+ this.name = "EngineDataError";
7612
+ }
7613
+ };
7614
+ var MAX_DEPTH2 = 64;
7615
+ var isWs = (c) => c === 32 || c === 9 || c === 13 || c === 10 || c === 0;
7616
+ var isDelim = (c) => isWs(c) || c === 91 || c === 93 || c === 60 || c === 62 || c === 40 || c === 47;
7617
+ function parseEngineData(b) {
7618
+ let i = 0;
7619
+ const ws = () => {
7620
+ while (i < b.length && isWs(b[i])) i++;
7621
+ };
7622
+ const name = () => {
7623
+ i++;
7624
+ const start = i;
7625
+ while (i < b.length && !isDelim(b[i])) i++;
7626
+ let s = "";
7627
+ for (let k = start; k < i; k++) s += String.fromCharCode(b[k]);
7628
+ return s;
7629
+ };
7630
+ const value = (depth) => {
7631
+ if (depth > MAX_DEPTH2) throw new EngineDataError("nesting too deep");
7632
+ ws();
7633
+ if (i >= b.length) throw new EngineDataError("unexpected end");
7634
+ const c = b[i];
7635
+ if (c === 60 && b[i + 1] === 60) {
7636
+ i += 2;
7637
+ const items = [];
7638
+ for (; ; ) {
7639
+ ws();
7640
+ if (b[i] === 62 && b[i + 1] === 62) {
7641
+ i += 2;
7642
+ return { t: "dict", items };
7643
+ }
7644
+ if (b[i] !== 47) throw new EngineDataError(i >= b.length ? "unterminated dictionary" : `expected a key at byte ${i}`);
7645
+ const k = name();
7646
+ items.push([k, value(depth + 1)]);
7647
+ }
7648
+ }
7649
+ if (c === 91) {
7650
+ i++;
7651
+ const items = [];
7652
+ for (; ; ) {
7653
+ ws();
7654
+ if (i >= b.length) throw new EngineDataError("unterminated array");
7655
+ if (b[i] === 93) {
7656
+ i++;
7657
+ return { t: "array", items };
7658
+ }
7659
+ items.push(value(depth + 1));
7660
+ }
7661
+ }
7662
+ if (c === 40) {
7663
+ i++;
7664
+ const raw = [];
7665
+ for (; ; ) {
7666
+ if (i >= b.length) throw new EngineDataError("unterminated string");
7667
+ const x = b[i];
7668
+ if (x === 92) {
7669
+ if (i + 1 < b.length) raw.push(b[i + 1]);
7670
+ i += 2;
7671
+ } else if (x === 41) {
7672
+ i++;
7673
+ break;
7674
+ } else {
7675
+ raw.push(x);
7676
+ i++;
7677
+ }
7678
+ }
7679
+ return { t: "string", value: decodeString(raw) };
7680
+ }
7681
+ if (c === 47) return { t: "name", value: name() };
7682
+ const start = i;
7683
+ while (i < b.length && !isDelim(b[i])) i++;
7684
+ let tok = "";
7685
+ for (let k = start; k < i; k++) tok += String.fromCharCode(b[k]);
7686
+ if (tok === "true" || tok === "false") return { t: "bool", value: tok === "true" };
7687
+ if (!tok) throw new EngineDataError(`unexpected byte at ${i}`);
7688
+ const n = Number(tok);
7689
+ if (!Number.isFinite(n)) throw new EngineDataError(`bad number "${tok}"`);
7690
+ return /[.eE]/.test(tok) ? { t: "real", value: n } : { t: "int", value: n };
7691
+ };
7692
+ ws();
7693
+ if (b[i] === 47) {
7694
+ const items = [];
7695
+ while (i < b.length) {
7696
+ ws();
7697
+ if (i >= b.length) break;
7698
+ if (b[i] !== 47) throw new EngineDataError(`expected a key at byte ${i}`);
7699
+ const k = name();
7700
+ items.push([k, value(1)]);
7701
+ ws();
7702
+ }
7703
+ return { t: "dict", items };
7704
+ }
7705
+ return value(0);
7706
+ }
7707
+ function decodeString(raw) {
7708
+ if (raw.length >= 2 && raw[0] === 254 && raw[1] === 255) {
7709
+ let s = "";
7710
+ for (let k = 2; k + 1 < raw.length; k += 2) s += String.fromCharCode(raw[k] << 8 | raw[k + 1]);
7711
+ return s;
7712
+ }
7713
+ return String.fromCharCode(...raw);
7714
+ }
7715
+ var isContainer = (v) => v.t === "dict" || v.t === "array" && v.items.some((x) => x.t === "dict" || x.t === "array");
7716
+ function formatReal(r) {
7717
+ if (!Number.isFinite(r)) return "0.0";
7718
+ if (Number.isInteger(r) && Math.abs(r) < 1e15) return r.toFixed(1);
7719
+ let s = r.toFixed(5);
7720
+ while (s.endsWith("0")) s = s.slice(0, -1);
7721
+ if (s === "0." || s === "-0.") return "0.0";
7722
+ if (s.endsWith(".")) s += "0";
7723
+ return s.replace(/^(-?)0\./, "$1.");
7724
+ }
7725
+ function writeEngineData(v) {
7726
+ const out = [10, 10];
7727
+ const push = (s) => {
7728
+ for (let k = 0; k < s.length; k++) out.push(s.charCodeAt(k) & 255);
7729
+ };
7730
+ const indent = (n) => {
7731
+ for (let k = 0; k < n; k++) out.push(9);
7732
+ };
7733
+ const write = (x, depth) => {
7734
+ switch (x.t) {
7735
+ case "dict":
7736
+ push("<<\n");
7737
+ for (const [k, val] of x.items) {
7738
+ indent(depth + 1);
7739
+ push(`/${k}`);
7740
+ if (isContainer(val)) {
7741
+ push("\n");
7742
+ indent(depth + 1);
7743
+ } else push(" ");
7744
+ write(val, depth + 1);
7745
+ push("\n");
7746
+ }
7747
+ indent(depth);
7748
+ push(">>");
7749
+ break;
7750
+ case "array":
7751
+ if (isContainer(x)) {
7752
+ push("[\n");
7753
+ for (const item of x.items) {
7754
+ indent(depth);
7755
+ write(item, depth);
7756
+ push("\n");
7757
+ }
7758
+ indent(depth);
7759
+ push("]");
7760
+ } else {
7761
+ push("[");
7762
+ for (const item of x.items) {
7763
+ push(" ");
7764
+ write(item, depth);
7765
+ }
7766
+ push(" ]");
7767
+ }
7768
+ break;
7769
+ case "string": {
7770
+ out.push(40);
7771
+ const raw = [254, 255];
7772
+ for (let k = 0; k < x.value.length; k++) {
7773
+ const u = x.value.charCodeAt(k);
7774
+ raw.push(u >> 8, u & 255);
7775
+ }
7776
+ for (const c of raw) {
7777
+ if (c === 40 || c === 41 || c === 92) out.push(92);
7778
+ out.push(c);
7779
+ }
7780
+ out.push(41);
7781
+ break;
7782
+ }
7783
+ case "int":
7784
+ push(String(Math.round(x.value)));
7785
+ break;
7786
+ case "real":
7787
+ push(formatReal(x.value));
7788
+ break;
7789
+ case "bool":
7790
+ push(x.value ? "true" : "false");
7791
+ break;
7792
+ case "name":
7793
+ push(`/${x.value}`);
7794
+ break;
7795
+ }
7796
+ };
7797
+ write(v, 0);
7798
+ out.push(10);
7799
+ return Uint8Array.from(out);
7800
+ }
7801
+ var E = {
7802
+ dict: (items = []) => ({ t: "dict", items }),
7803
+ array: (items) => ({ t: "array", items }),
7804
+ str: (value) => ({ t: "string", value }),
7805
+ int: (value) => ({ t: "int", value }),
7806
+ real: (value) => ({ t: "real", value }),
7807
+ bool: (value) => ({ t: "bool", value })
7808
+ };
7809
+ function eGet(v, key) {
7810
+ return v?.t === "dict" ? v.items.find(([k]) => k === key)?.[1] : void 0;
7811
+ }
7812
+ function ePath(v, keys) {
7813
+ return keys.reduce((at, k) => eGet(at, k), v);
7814
+ }
7815
+ function eNum(v) {
7816
+ return v && (v.t === "int" || v.t === "real") ? v.value : void 0;
7817
+ }
7818
+ function eBool(v) {
7819
+ if (!v) return void 0;
7820
+ if (v.t === "bool") return v.value;
7821
+ if (v.t === "int") return v.value !== 0;
7822
+ return void 0;
7823
+ }
7824
+ function eStr(v) {
7825
+ return v && (v.t === "string" || v.t === "name") ? v.value : void 0;
7826
+ }
7827
+ function eArray(v) {
7828
+ return v?.t === "array" ? v.items : [];
7829
+ }
7830
+ function eSet(d, key, value) {
7831
+ if (d.t !== "dict") return;
7832
+ const at = d.items.findIndex(([k]) => k === key);
7833
+ if (at >= 0) d.items[at] = [key, value];
7834
+ else d.items.push([key, value]);
7835
+ }
7836
+ function eClone(v) {
7837
+ return JSON.parse(JSON.stringify(v));
7838
+ }
7839
+
7840
+ // src/image-editor/psd/type.ts
7841
+ function parseTySh(data) {
7842
+ try {
7843
+ const r = new PsdReader(data);
7844
+ r.u16();
7845
+ const m = [];
7846
+ for (let k = 0; k < 6; k++) m.push(r.f64());
7847
+ r.u16();
7848
+ const text3 = readVersionedDescriptor(r);
7849
+ let warp = null;
7850
+ if (r.remaining >= 6) {
7851
+ const at = r.pos;
7852
+ try {
7853
+ r.u16();
7854
+ warp = readVersionedDescriptor(r);
7855
+ } catch {
7856
+ r.pos = at;
7857
+ }
7858
+ }
7859
+ return { transform: m, text: text3, warp, tail: r.rest() };
7860
+ } catch {
7861
+ return null;
7862
+ }
7863
+ }
7864
+ function defaultWarp() {
7865
+ return descriptor("warp", [
7866
+ ["warpStyle", { type: "enum", typeId: "warpStyle", value: "warpNone" }],
7867
+ ["warpValue", { type: "doub", value: 0 }],
7868
+ ["warpPerspective", { type: "doub", value: 0 }],
7869
+ ["warpPerspectiveOther", { type: "doub", value: 0 }],
7870
+ ["warpRotate", { type: "enum", typeId: "Ornt", value: "Hrzn" }]
7871
+ ]);
7872
+ }
7873
+ function writeTySh(t) {
7874
+ const w = new PsdWriter();
7875
+ w.u16(1);
7876
+ for (const v of t.transform) w.f64(v);
7877
+ w.u16(50);
7878
+ writeVersionedDescriptor(w, t.text);
7879
+ w.u16(1);
7880
+ writeVersionedDescriptor(w, t.warp ?? defaultWarp());
7881
+ if (t.tail.length >= 16) w.bytes(t.tail);
7882
+ else w.zeros(16);
7883
+ return w.result();
7884
+ }
7885
+ var WEIGHTS = [
7886
+ [/thin|hairline/, 100],
7887
+ [/extralight|ultralight/, 200],
7888
+ [/semibold|demibold|demi/, 600],
7889
+ [/extrabold|ultrabold/, 800],
7890
+ [/black|heavy/, 900],
7891
+ [/bold/, 700],
7892
+ [/medium/, 500],
7893
+ [/light/, 300]
7894
+ ];
7895
+ function fontFromPostScript(ps) {
7896
+ let pro = ps;
7897
+ let style = "";
7898
+ const dash = ps.lastIndexOf("-");
7899
+ if (dash > 0) {
7900
+ pro = ps.slice(0, dash);
7901
+ style = ps.slice(dash + 1);
7902
+ const prev = pro.lastIndexOf("-");
7903
+ if (/^(it|italic|oblique)$/i.test(style) && prev > 0 && /^(thin|hairline|extralight|ultralight|light|regular|roman|book|medium|semibold|demibold|demi|bold|extrabold|ultrabold|heavy|black)$/i.test(pro.slice(prev + 1))) {
7904
+ style = ps.slice(prev + 1);
7905
+ pro = ps.slice(0, prev);
7906
+ }
7907
+ } else {
7908
+ const m = /^(.*?)(Bold|Italic|Light|Medium|Black|Regular|Oblique)+$/.exec(ps);
7909
+ if (m && m[1]) {
7910
+ pro = m[1];
7911
+ style = ps.slice(m[1].length);
7912
+ }
7913
+ }
7914
+ let suffix = "";
7915
+ if (pro.endsWith("Pro")) suffix = " Pro";
7916
+ else if (pro.endsWith("Std")) suffix = " Std";
7917
+ const fam = pro.replace(/(MT|PS|Std|Pro)+$/, "");
7918
+ let family = "";
7919
+ for (let i = 0; i < fam.length; i++) {
7920
+ const c = fam[i];
7921
+ const upper = c >= "A" && c <= "Z";
7922
+ const prevLower = i > 0 && fam[i - 1] >= "a" && fam[i - 1] <= "z";
7923
+ const prevUpper = i > 0 && fam[i - 1] >= "A" && fam[i - 1] <= "Z";
7924
+ const nextLower = i + 1 < fam.length && fam[i + 1] >= "a" && fam[i + 1] <= "z";
7925
+ if (i > 0 && upper && (prevLower || prevUpper && nextLower)) family += " ";
7926
+ family += c;
7927
+ }
7928
+ family += suffix;
7929
+ const s = style.toLowerCase();
7930
+ const weight = WEIGHTS.find(([re]) => re.test(s))?.[1] ?? 400;
7931
+ return { family: family || ps, weight, italic: /italic|oblique|it$/.test(s) };
7932
+ }
7933
+ var STYLE_NAMES = [
7934
+ [100, "Thin"],
7935
+ [200, "ExtraLight"],
7936
+ [300, "Light"],
7937
+ [400, "Regular"],
7938
+ [500, "Medium"],
7939
+ [600, "SemiBold"],
7940
+ [700, "Bold"],
7941
+ [800, "ExtraBold"],
7942
+ [900, "Black"]
7943
+ ];
7944
+ function postScriptFor(s) {
7945
+ if (s.postScriptName) return s.postScriptName;
7946
+ const base = s.fontFamily.replace(/["',]/g, "").replace(/\s+/g, "");
7947
+ const weight = STYLE_NAMES.reduce((best, w) => Math.abs(w[0] - s.fontWeight) < Math.abs(best[0] - s.fontWeight) ? w : best)[1];
7948
+ if (weight === "Regular" && !s.italic) return base;
7949
+ return `${base}-${weight === "Regular" ? "" : weight}${s.italic ? "Italic" : ""}`;
7950
+ }
7951
+ var hex22 = (v) => Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, "0");
7952
+ function merged(levels) {
7953
+ const items = [];
7954
+ for (const l of levels) {
7955
+ if (l?.t !== "dict") continue;
7956
+ for (const [k, v] of l.items) if (!items.some(([o]) => o === k)) items.push([k, v]);
7957
+ }
7958
+ return E.dict(items);
7959
+ }
7960
+ function charStyle(d, fonts, notes) {
7961
+ const n = (k) => eNum(eGet(d, k));
7962
+ const flag = (k) => eBool(eGet(d, k));
7963
+ const s = { ...DEFAULT_TEXT_STYLE };
7964
+ const fi = n("Font");
7965
+ const ps = fi !== void 0 ? fonts[fi] : void 0;
7966
+ if (ps) {
7967
+ const f = fontFromPostScript(ps);
7968
+ s.fontFamily = f.family;
7969
+ s.fontWeight = f.weight;
7970
+ if (f.italic) s.italic = true;
7971
+ s.postScriptName = ps;
7972
+ }
7973
+ const size = n("FontSize");
7974
+ if (size !== void 0 && size > 0) s.fontSize = size;
7975
+ if (flag("AutoLeading") === false) {
7976
+ const lead = n("Leading");
7977
+ if (lead !== void 0 && lead > 0) s.leading = lead;
7978
+ }
7979
+ const tracking = n("Tracking");
7980
+ if (tracking) s.tracking = tracking;
7981
+ const shift = n("BaselineShift");
7982
+ if (shift) s.baselineShift = shift;
7983
+ if (flag("Underline")) s.underline = true;
7984
+ if (flag("Strikethrough")) s.strikethrough = true;
7985
+ if (flag("FauxBold")) notes.add("faux bold is drawn as the regular weight");
7986
+ if (flag("FauxItalic")) notes.add("faux italic is drawn upright");
7987
+ if ((n("HorizontalScale") ?? 1) !== 1 || (n("VerticalScale") ?? 1) !== 1) notes.add("horizontal and vertical character scaling is drawn at 100 %");
7988
+ if ((n("FontCaps") ?? 0) !== 0) notes.add("small caps and all caps are drawn as typed");
7989
+ const color = eGet(d, "FillColor");
7990
+ const values = eArray(eGet(color, "Values")).map((v) => eNum(v) ?? 0);
7991
+ if (values.length === 4) {
7992
+ s.color = `#${hex22(values[1])}${hex22(values[2])}${hex22(values[3])}`;
7993
+ if (values[0] < 1) s.opacity = Math.max(0, values[0]);
7994
+ } else if (values.length === 2) {
7995
+ const g = hex22(values[1]);
7996
+ s.color = `#${g}${g}${g}`;
7997
+ } else if (values.length === 5) {
7998
+ const [c, m, y, k] = values.slice(1);
7999
+ s.color = `#${hex22((1 - c) * (1 - k))}${hex22((1 - m) * (1 - k))}${hex22((1 - y) * (1 - k))}`;
8000
+ notes.add("CMYK text colours were converted to RGB");
8001
+ }
8002
+ return s;
8003
+ }
8004
+ function paraStyle(d) {
8005
+ const n = (k) => eNum(eGet(d, k));
8006
+ const out = {};
8007
+ const j = n("Justification");
8008
+ const align = j === 1 ? "right" : j === 2 ? "center" : j !== void 0 && j >= 3 ? "justify" : "left";
8009
+ if (align !== "left") out.align = align;
8010
+ const set = (k, v) => {
8011
+ if (v) out[k] = v;
8012
+ };
8013
+ set("firstLineIndent", n("FirstLineIndent"));
8014
+ set("startIndent", n("StartIndent"));
8015
+ set("endIndent", n("EndIndent"));
8016
+ set("spaceBefore", n("SpaceBefore"));
8017
+ set("spaceAfter", n("SpaceAfter"));
8018
+ const auto = n("AutoLeading");
8019
+ if (auto !== void 0 && auto > 0 && Math.abs(auto - 1.2) > 1e-6) out.autoLeading = auto;
8020
+ return out;
8021
+ }
8022
+ function typeFromTySh(data, notes) {
8023
+ const t = parseTySh(data);
8024
+ if (!t) return null;
8025
+ const txtV = descGet(t.text, "Txt ");
8026
+ const txt = txtV?.type === "TEXT" ? txtV.value.replace(/\0+$/, "") : void 0;
8027
+ const ed = descGet(t.text, "EngineData");
8028
+ let engine = null;
8029
+ if (ed?.type === "tdta") {
8030
+ try {
8031
+ engine = parseEngineData(ed.data);
8032
+ } catch {
8033
+ notes.add("the text engine data could not be read; the text came in in one style");
8034
+ }
8035
+ }
8036
+ const anti = descGet(t.text, "AntA");
8037
+ const antiAlias = !(anti?.type === "enum" && (anti.value === "Anno" || anti.value === "antiAliasNone"));
8038
+ const ornt = descGet(t.text, "Ornt");
8039
+ if (ornt?.type === "enum" && ornt.value === "Vrtc") notes.add("vertical type is laid out horizontally");
8040
+ if (t.warp) {
8041
+ const style = descGet(t.warp, "warpStyle");
8042
+ if (style?.type === "enum" && style.value !== "warpNone") notes.add("warped text is drawn without its warp when edited");
8043
+ }
8044
+ const edText = eStr(ePath(engine ?? void 0, ["EngineDict", "Editor", "Text"])) ?? "";
8045
+ const text3 = (txt ?? edText.replace(/\r$/, "")).replace(/\r\n?/g, "\n");
8046
+ let runs = [{ length: text3.length, style: { ...DEFAULT_TEXT_STYLE } }];
8047
+ let paragraphs = [{ length: text3.length, style: {} }];
8048
+ let frame = { kind: "point" };
8049
+ let transform = t.transform;
8050
+ const fonts = [];
8051
+ if (engine) {
8052
+ const res = eGet(engine, "ResourceDict") ?? eGet(engine, "DocumentResources");
8053
+ for (const f of eArray(eGet(res, "FontSet"))) fonts.push(eStr(eGet(f, "Name")) ?? "");
8054
+ const styleSet = eArray(eGet(res, "StyleSheetSet"));
8055
+ const paraSet = eArray(eGet(res, "ParagraphSheetSet"));
8056
+ const normalStyle = styleSet[eNum(eGet(res, "TheNormalStyleSheet")) ?? 0] ?? styleSet[0];
8057
+ const normalPara = paraSet[eNum(eGet(res, "TheNormalParagraphSheet")) ?? 0] ?? paraSet[0];
8058
+ const named = (set, sheet) => {
8059
+ const parent = eNum(eGet(sheet, "Parent"));
8060
+ if (parent !== void 0 && set[parent]) return set[parent];
8061
+ const name = eStr(eGet(sheet, "Name"));
8062
+ return name ? set.find((s) => eStr(eGet(s, "Name")) === name) : void 0;
8063
+ };
8064
+ const prun = ePath(engine, ["EngineDict", "ParagraphRun"]);
8065
+ const psheets = eArray(eGet(prun, "RunArray")).map((x) => eGet(x, "ParagraphSheet"));
8066
+ const plens = eArray(eGet(prun, "RunLengthArray")).map((v) => eNum(v) ?? 0);
8067
+ const paraBases = psheets.map((sheet) => {
8068
+ const own = named(paraSet, sheet);
8069
+ const base = merged([eGet(own, "Properties"), eGet(normalPara, "Properties")]);
8070
+ const di = eNum(eGet(sheet, "DefaultStyleSheet")) ?? eNum(eGet(own, "DefaultStyleSheet"));
8071
+ return { base, charDefault: di !== void 0 ? styleSet[di] : void 0 };
8072
+ });
8073
+ if (psheets.length) {
8074
+ paragraphs = psheets.map((sheet, i) => ({ length: plens[i] ?? 0, style: paraStyle(merged([eGet(sheet, "Properties"), paraBases[i].base])) }));
8075
+ }
8076
+ const srun = ePath(engine, ["EngineDict", "StyleRun"]);
8077
+ const ssheets = eArray(eGet(srun, "RunArray")).map((x) => eGet(x, "StyleSheet"));
8078
+ const slens = eArray(eGet(srun, "RunLengthArray")).map((v) => eNum(v) ?? 0);
8079
+ const paraAt = (at) => {
8080
+ let end = 0;
8081
+ for (let i = 0; i < plens.length; i++) {
8082
+ end += plens[i];
8083
+ if (at < end) return i;
8084
+ }
8085
+ return plens.length - 1;
8086
+ };
8087
+ if (ssheets.length) {
8088
+ let at = 0;
8089
+ runs = ssheets.map((sheet, i) => {
8090
+ const own = named(styleSet, sheet);
8091
+ const paraDefault = paraBases[paraAt(at)]?.charDefault;
8092
+ at += slens[i] ?? 0;
8093
+ const d = merged([eGet(sheet, "StyleSheetData"), eGet(own, "StyleSheetData"), eGet(paraDefault, "StyleSheetData"), eGet(normalStyle, "StyleSheetData")]);
8094
+ return { length: slens[i] ?? 0, style: charStyle(d, fonts, notes) };
8095
+ });
8096
+ }
8097
+ const shape = eArray(ePath(engine, ["EngineDict", "Rendered", "Shapes", "Children"]))[0];
8098
+ if (eNum(eGet(shape, "ShapeType")) === 1) {
8099
+ const b = eArray(ePath(shape, ["Cookie", "Photoshop", "BoxBounds"])).map((v) => eNum(v) ?? 0);
8100
+ if (b.length === 4 && b[2] > b[0] && b[3] > b[1]) {
8101
+ frame = { kind: "box", width: b[2] - b[0], height: b[3] - b[1] };
8102
+ transform = multiplyMatrices([1, 0, 0, 1, b[0], b[1]], t.transform);
8103
+ }
8104
+ }
8105
+ }
8106
+ return {
8107
+ text: text3,
8108
+ runs: fitRuns(runs, text3.length, DEFAULT_TEXT_STYLE),
8109
+ paragraphs: fitParagraphRuns(paragraphs, text3),
8110
+ frame,
8111
+ transform,
8112
+ antiAlias,
8113
+ fonts: [...new Set(fonts.filter(Boolean))]
8114
+ };
8115
+ }
8116
+ function typeSignature(layer) {
8117
+ return JSON.stringify([layer.text, layer.runs, layer.paragraphs, layer.frame, layer.transform ?? null, layer.antiAlias ?? true]);
8118
+ }
8119
+ function styleSheetData(s, font) {
8120
+ const [r, g, b] = [1, 3, 5].map((i) => parseInt(s.color.slice(i, i + 2), 16) / 255);
8121
+ return E.dict([
8122
+ ["Font", E.int(font)],
8123
+ ["FontSize", E.real(s.fontSize)],
8124
+ ["FauxBold", E.bool(false)],
8125
+ ["FauxItalic", E.bool(false)],
8126
+ ["AutoLeading", E.bool(s.leading === void 0)],
8127
+ ["Leading", E.real(s.leading ?? 0)],
8128
+ ["HorizontalScale", E.real(1)],
8129
+ ["VerticalScale", E.real(1)],
8130
+ ["Tracking", E.int(Math.round(s.tracking ?? 0))],
8131
+ ["AutoKerning", E.bool(true)],
8132
+ ["Kerning", E.int(0)],
8133
+ ["BaselineShift", E.real(s.baselineShift ?? 0)],
8134
+ ["FontCaps", E.int(0)],
8135
+ ["Underline", E.bool(Boolean(s.underline))],
8136
+ ["Strikethrough", E.bool(Boolean(s.strikethrough))],
8137
+ ["Ligatures", E.bool(true)],
8138
+ ["DLigatures", E.bool(false)],
8139
+ ["FillFlag", E.bool(true)],
8140
+ [
8141
+ "FillColor",
8142
+ E.dict([
8143
+ ["Type", E.int(1)],
8144
+ ["Values", E.array([E.real(s.opacity ?? 1), E.real(r), E.real(g), E.real(b)])]
8145
+ ])
8146
+ ]
8147
+ ]);
8148
+ }
8149
+ function paragraphProperties(p) {
8150
+ const j = p.align === "right" ? 1 : p.align === "center" ? 2 : p.align === "justify" ? 3 : 0;
8151
+ return E.dict([
8152
+ ["Justification", E.int(j)],
8153
+ ["FirstLineIndent", E.real(p.firstLineIndent ?? 0)],
8154
+ ["StartIndent", E.real(p.startIndent ?? 0)],
8155
+ ["EndIndent", E.real(p.endIndent ?? 0)],
8156
+ ["SpaceBefore", E.real(p.spaceBefore ?? 0)],
8157
+ ["SpaceAfter", E.real(p.spaceAfter ?? 0)],
8158
+ ["AutoHyphenate", E.bool(false)],
8159
+ ["AutoLeading", E.real(p.autoLeading ?? 1.2)],
8160
+ ["LeadingType", E.int(0)]
8161
+ ]);
8162
+ }
8163
+ var fontEntry = (name) => E.dict([
8164
+ ["Name", E.str(name)],
8165
+ ["Script", E.int(0)],
8166
+ ["FontType", E.int(1)],
8167
+ ["Synthetic", E.int(0)]
8168
+ ]);
8169
+ function buildEngineData(layer, template) {
8170
+ const e = template?.t === "dict" ? eClone(template) : E.dict([["EngineDict", E.dict()], ["ResourceDict", E.dict()]]);
8171
+ if (eGet(e, "EngineDict")?.t !== "dict") eSet(e, "EngineDict", E.dict());
8172
+ const dict = eGet(e, "EngineDict");
8173
+ const text3 = layer.text.replace(/\n/g, "\r");
8174
+ const edText = `${text3}\r`;
8175
+ const res0 = eGet(e, "ResourceDict") ?? eGet(e, "DocumentResources");
8176
+ const fonts = eArray(eGet(res0, "FontSet")).map((f) => eStr(eGet(f, "Name")) ?? "");
8177
+ const fontIndex = (ps) => {
8178
+ const at = fonts.indexOf(ps);
8179
+ if (at >= 0) return at;
8180
+ fonts.push(ps);
8181
+ return fonts.length - 1;
8182
+ };
8183
+ const runs = fitRuns(layer.runs, layer.text.length, DEFAULT_TEXT_STYLE);
8184
+ const sruns = [];
8185
+ const slens = [];
8186
+ runs.forEach((r, i) => {
8187
+ sruns.push(E.dict([["StyleSheet", E.dict([["StyleSheetData", styleSheetData(r.style, fontIndex(postScriptFor(r.style)))]])]]));
8188
+ slens.push(E.int(r.length + (i === runs.length - 1 ? 1 : 0)));
8189
+ });
8190
+ if (!runs.length) {
8191
+ sruns.push(E.dict([["StyleSheet", E.dict([["StyleSheetData", styleSheetData(DEFAULT_TEXT_STYLE, fontIndex(postScriptFor(DEFAULT_TEXT_STYLE)))]])]]));
8192
+ slens.push(E.int(1));
8193
+ }
8194
+ const paras = fitParagraphRuns(layer.paragraphs, layer.text);
8195
+ const pruns = [];
8196
+ const plens = [];
8197
+ paras.forEach((p, i) => {
8198
+ pruns.push(E.dict([["ParagraphSheet", E.dict([["DefaultStyleSheet", E.int(0)], ["Properties", paragraphProperties(p.style)]])]]));
8199
+ plens.push(E.int(p.length + (i === paras.length - 1 ? 1 : 0)));
8200
+ });
8201
+ const editor = eGet(dict, "Editor") ?? E.dict();
8202
+ eSet(editor, "Text", E.str(edText));
8203
+ eSet(dict, "Editor", editor);
8204
+ const pr = eGet(dict, "ParagraphRun") ?? E.dict();
8205
+ eSet(pr, "RunArray", E.array(pruns));
8206
+ eSet(pr, "RunLengthArray", E.array(plens));
8207
+ if (!eGet(pr, "IsJoinable")) eSet(pr, "IsJoinable", E.int(1));
8208
+ eSet(dict, "ParagraphRun", pr);
8209
+ const sr = eGet(dict, "StyleRun") ?? E.dict();
8210
+ eSet(sr, "RunArray", E.array(sruns));
8211
+ eSet(sr, "RunLengthArray", E.array(slens));
8212
+ if (!eGet(sr, "IsJoinable")) eSet(sr, "IsJoinable", E.int(2));
8213
+ eSet(dict, "StyleRun", sr);
8214
+ eSet(dict, "AntiAlias", E.int(layer.antiAlias === false ? 0 : 3));
8215
+ if (!eGet(dict, "UseFractionalGlyphWidths")) eSet(dict, "UseFractionalGlyphWidths", E.bool(true));
8216
+ const box2 = layer.frame.kind === "box" ? layer.frame : null;
8217
+ const shapeType = box2 ? 1 : 0;
8218
+ const photoshop = E.dict([["ShapeType", E.int(shapeType)]]);
8219
+ if (box2) eSet(photoshop, "BoxBounds", E.array([E.real(0), E.real(0), E.real(box2.width), E.real(box2.height)]));
8220
+ else eSet(photoshop, "PointBase", E.array([E.real(0), E.real(0)]));
8221
+ eSet(
8222
+ photoshop,
8223
+ "Base",
8224
+ E.dict([
8225
+ ["ShapeType", E.int(shapeType)],
8226
+ ["TransformPoint0", E.array([E.real(1), E.real(0)])],
8227
+ ["TransformPoint1", E.array([E.real(0), E.real(1)])],
8228
+ ["TransformPoint2", E.array([E.real(0), E.real(0)])]
8229
+ ])
8230
+ );
8231
+ const child = E.dict([
8232
+ ["ShapeType", E.int(shapeType)],
8233
+ ["Procession", E.int(0)],
8234
+ ["Lines", E.dict([["WritingDirection", E.int(0)], ["Children", E.array([])]])],
8235
+ ["Cookie", E.dict([["Photoshop", photoshop]])]
8236
+ ]);
8237
+ eSet(dict, "Rendered", E.dict([["Version", E.int(1)], ["Shapes", E.dict([["WritingDirection", E.int(0)], ["Children", E.array([child])]])]]));
8238
+ const fontSet = E.array(fonts.map(fontEntry));
8239
+ for (const key of ["ResourceDict", "DocumentResources"]) {
8240
+ let rd = eGet(e, key);
8241
+ if (!rd) {
8242
+ if (key === "DocumentResources") {
8243
+ eSet(e, key, eClone(eGet(e, "ResourceDict") ?? E.dict()));
8244
+ continue;
8245
+ }
8246
+ rd = E.dict();
8247
+ eSet(e, key, rd);
8248
+ }
8249
+ eSet(rd, "FontSet", fontSet);
8250
+ if (!eGet(rd, "StyleSheetSet")) eSet(rd, "StyleSheetSet", E.array([E.dict([["Name", E.str("Normal RGB")], ["StyleSheetData", styleSheetData(runs[0]?.style ?? DEFAULT_TEXT_STYLE, 0)]])]));
8251
+ if (!eGet(rd, "ParagraphSheetSet")) eSet(rd, "ParagraphSheetSet", E.array([E.dict([["Name", E.str("Normal RGB")], ["DefaultStyleSheet", E.int(0)], ["Properties", paragraphProperties({})]])]));
8252
+ if (!eGet(rd, "TheNormalStyleSheet")) eSet(rd, "TheNormalStyleSheet", E.int(0));
8253
+ if (!eGet(rd, "TheNormalParagraphSheet")) eSet(rd, "TheNormalParagraphSheet", E.int(0));
8254
+ }
8255
+ return e;
8256
+ }
8257
+ function inkBounds2(layer) {
8258
+ if (layer.frame.kind === "box") return [0, 0, layer.frame.width, layer.frame.height];
8259
+ const b = contentBounds(layer.surface);
8260
+ if (!b) return [0, 0, 0, 0];
8261
+ const inv = invertMatrix(textMatrix(layer));
8262
+ const pts = [
8263
+ { x: b.x, y: b.y },
8264
+ { x: b.x + b.width, y: b.y },
8265
+ { x: b.x + b.width, y: b.y + b.height },
8266
+ { x: b.x, y: b.y + b.height }
8267
+ ].map((p) => applyMatrix(inv, p));
8268
+ return [Math.min(...pts.map((p) => p.x)), Math.min(...pts.map((p) => p.y)), Math.max(...pts.map((p) => p.x)), Math.max(...pts.map((p) => p.y))];
8269
+ }
8270
+ function tyShForLayer(layer) {
8271
+ const raw = layer.psd?.tySh;
8272
+ if (raw && layer.psd?.signature === typeSignature(layer)) return raw;
8273
+ const old = raw ? parseTySh(raw) : null;
8274
+ const oldEd = old ? descGet(old.text, "EngineData") : void 0;
8275
+ let template = null;
8276
+ if (oldEd?.type === "tdta") {
8277
+ try {
8278
+ template = parseEngineData(oldEd.data);
8279
+ } catch {
8280
+ template = null;
8281
+ }
8282
+ }
8283
+ const engine = buildEngineData(layer, template);
8284
+ const text3 = old ? { ...old.text, items: [...old.text.items] } : descriptor("TxLr");
8285
+ const set = (key, v) => {
8286
+ const at = text3.items.findIndex(([k]) => k === key);
8287
+ if (at >= 0) text3.items[at] = [key, v];
8288
+ else text3.items.push([key, v]);
8289
+ };
8290
+ set("Txt ", { type: "TEXT", value: layer.text.replace(/\n/g, "\r") });
8291
+ if (!descGet(text3, "textGridding")) set("textGridding", { type: "enum", typeId: "textGridding", value: "None" });
8292
+ set("Ornt", { type: "enum", typeId: "Ornt", value: "Hrzn" });
8293
+ set("AntA", { type: "enum", typeId: "Annt", value: layer.antiAlias === false ? "Anno" : "AnSm" });
8294
+ const [l, t, r, b] = inkBounds2(layer);
8295
+ const rect = (cls) => ({
8296
+ type: "Objc",
8297
+ value: descriptor(cls, [
8298
+ ["Left", { type: "UntF", unit: "#Pnt", value: l }],
8299
+ ["Top ", { type: "UntF", unit: "#Pnt", value: t }],
8300
+ ["Rght", { type: "UntF", unit: "#Pnt", value: r }],
8301
+ ["Btom", { type: "UntF", unit: "#Pnt", value: b }]
8302
+ ])
8303
+ });
8304
+ set("bounds", rect("bounds"));
8305
+ set("boundingBox", rect("boundingBox"));
8306
+ if (!descGet(text3, "TextIndex")) set("TextIndex", { type: "long", value: 0 });
8307
+ set("EngineData", { type: "tdta", data: writeEngineData(engine) });
8308
+ const warpStyle = old?.warp ? descGet(old.warp, "warpStyle") : void 0;
8309
+ const warp = old?.warp && warpStyle?.type === "enum" && warpStyle.value === "warpNone" ? old.warp : null;
8310
+ return writeTySh({ transform: textMatrix(layer), text: text3, warp, tail: new Uint8Array(16) });
8311
+ }
8312
+
8313
+ // src/image-editor/psd/compression.ts
8314
+ var MAX_DECODED_BYTES = 2 ** 31;
8315
+ function rowBytes(width, depth) {
8316
+ return depth === 1 ? Math.ceil(width / 8) : width * Math.max(1, depth >> 3);
8317
+ }
8318
+ function decodedLength(l) {
8319
+ const total = l.planes * l.height * rowBytes(l.width, l.depth);
8320
+ if (total > MAX_DECODED_BYTES) throw new PsdError(`Decoded channel data of ${total} bytes is larger than the ${MAX_DECODED_BYTES}-byte limit.`);
8321
+ return total;
8322
+ }
8323
+ function packbitsBound(n) {
8324
+ return n + Math.ceil(n / 128);
8325
+ }
8326
+ function packbitsEncode(src, out, at) {
8327
+ const n = src.length;
8328
+ let i = 0;
8329
+ while (i < n) {
8330
+ let run = 1;
8331
+ while (i + run < n && run < 128 && src[i + run] === src[i]) run++;
8332
+ if (run >= 2) {
8333
+ out[at++] = 257 - run & 255;
8334
+ out[at++] = src[i];
8335
+ i += run;
8336
+ continue;
8337
+ }
8338
+ const start = i;
8339
+ let len = 0;
8340
+ while (i < n && len < 128) {
8341
+ if (i + 2 < n && src[i] === src[i + 1] && src[i] === src[i + 2]) break;
8342
+ i++;
8343
+ len++;
8344
+ }
8345
+ out[at++] = len - 1;
8346
+ out.set(src.subarray(start, start + len), at);
8347
+ at += len;
8348
+ }
8349
+ return at;
8350
+ }
8351
+ function packbitsDecodeInto(src, expected, out, at) {
8352
+ const target = at + expected;
8353
+ let i = 0;
8354
+ while (at < target) {
8355
+ if (i >= src.length) throw new PsdError("PackBits row ended early.");
8356
+ const h = src[i++];
8357
+ if (h < 128) {
8358
+ const len = h + 1;
8359
+ if (i + len > src.length) throw new PsdError("PackBits literal is truncated.");
8360
+ if (at + len > target) throw new PsdError("PackBits literal overflows its row.");
8361
+ out.set(src.subarray(i, i + len), at);
8362
+ at += len;
8363
+ i += len;
8364
+ } else if (h !== 128) {
8365
+ const len = 257 - h;
8366
+ if (i >= src.length) throw new PsdError("PackBits run is truncated.");
8367
+ const b = src[i++];
8368
+ if (at + len > target) throw new PsdError("PackBits run overflows its row.");
8369
+ out.fill(b, at, at + len);
8370
+ at += len;
8371
+ }
8372
+ }
8373
+ return at;
8374
+ }
8375
+ function inflateChecked(data, expected) {
8376
+ let out;
8377
+ try {
8378
+ out = inflate(data, { maxOutput: expected + (1 << 20), sizeHint: expected, verify: false });
8379
+ } catch (e) {
8380
+ throw new PsdError(`ZIP channel data: ${e instanceof ZlibError || e instanceof Error ? e.message : String(e)}`);
8381
+ }
8382
+ if (out.length < expected) throw new PsdError(`ZIP stream produced ${out.length} of ${expected} bytes.`);
8383
+ return out.length === expected ? out : out.subarray(0, expected);
8384
+ }
8385
+ function unpredict(buf, l) {
8386
+ const rb = rowBytes(l.width, l.depth);
8387
+ if (rb === 0) return;
8388
+ const w = l.width;
8389
+ const scratch = l.depth === 32 ? new Uint8Array(rb) : null;
8390
+ for (let o = 0; o + rb <= buf.length; o += rb) {
8391
+ if (l.depth === 1 || l.depth === 8) {
8392
+ for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
8393
+ } else if (l.depth === 16) {
8394
+ for (let i = 1; i < w; i++) {
8395
+ const a = buf[o + 2 * i] << 8 | buf[o + 2 * i + 1];
8396
+ const b = buf[o + 2 * i - 2] << 8 | buf[o + 2 * i - 1];
8397
+ const v = a + b & 65535;
8398
+ buf[o + 2 * i] = v >> 8;
8399
+ buf[o + 2 * i + 1] = v & 255;
8400
+ }
8401
+ } else if (l.depth === 32) {
8402
+ for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
8403
+ scratch.set(buf.subarray(o, o + rb));
8404
+ for (let i = 0; i < w; i++) for (let k = 0; k < 4; k++) buf[o + 4 * i + k] = scratch[k * w + i];
8405
+ } else {
8406
+ throw new PsdError(`ZIP prediction is not defined for depth ${l.depth}.`);
8407
+ }
8408
+ }
8409
+ }
8410
+ function decodeRle(r, l, total) {
8411
+ const rows = l.planes * l.height;
8412
+ const rb = rowBytes(l.width, l.depth);
8413
+ r.checkCount(rows, l.psb ? 4 : 2, "RLE row counts");
8414
+ const counts = new Array(rows);
8415
+ for (let i = 0; i < rows; i++) counts[i] = l.psb ? r.u32() : r.u16();
8416
+ if (total > r.remaining * 130) throw new PsdError("RLE data is too short for its declared size.");
8417
+ const out = new Uint8Array(total);
8418
+ let at = 0;
8419
+ for (const c of counts) at = packbitsDecodeInto(r.bytes(c), rb, out, at);
8420
+ return out;
8421
+ }
8422
+ function decodePlanes(compression, data, l) {
8423
+ const total = decodedLength(l);
8424
+ switch (compression) {
8425
+ case 0:
8426
+ if (data.length < total) throw new PsdError(`Raw channel data has ${data.length} of ${total} bytes.`);
8427
+ return data.slice(0, total);
8428
+ case 1:
8429
+ return decodeRle(new PsdReader(data), l, total);
8430
+ case 2:
8431
+ return inflateChecked(data, total).slice();
8432
+ case 3: {
8433
+ const v = inflateChecked(data, total).slice();
8434
+ unpredict(v, l);
8435
+ return v;
8436
+ }
8437
+ default:
8438
+ throw new PsdError(`Unsupported compression method ${compression}.`);
8439
+ }
8440
+ }
8441
+ function encodeRle(decoded2, l) {
8442
+ const rows = l.planes * l.height;
8443
+ const rb = rowBytes(l.width, l.depth);
8444
+ const cs = l.psb ? 4 : 2;
8445
+ const packed = new Uint8Array(rows * packbitsBound(rb));
8446
+ let at = 0;
8447
+ const counts = new Uint8Array(rows * cs);
8448
+ for (let row = 0; row < rows; row++) {
8449
+ const start = at;
8450
+ at = packbitsEncode(decoded2.subarray(row * rb, (row + 1) * rb), packed, at);
8451
+ const n = at - start;
8452
+ if (cs === 2) {
8453
+ if (n > 65535) throw new PsdError("An RLE row is longer than 65535 bytes; write a PSB instead.");
8454
+ counts[row * 2] = n >> 8;
8455
+ counts[row * 2 + 1] = n & 255;
8456
+ } else {
8457
+ counts[row * 4] = n >>> 24;
8458
+ counts[row * 4 + 1] = n >>> 16 & 255;
8459
+ counts[row * 4 + 2] = n >>> 8 & 255;
8460
+ counts[row * 4 + 3] = n & 255;
8461
+ }
8462
+ }
8463
+ const out = new Uint8Array(counts.length + at);
8464
+ out.set(counts);
8465
+ out.set(packed.subarray(0, at), counts.length);
8466
+ return out;
8467
+ }
8468
+
8469
+ // src/image-editor/psd/smart.ts
8470
+ var dbl2 = (value) => ({ type: "doub", value });
8471
+ var unit2 = (u, value) => ({ type: "UntF", unit: u, value });
8472
+ var long3 = (value) => ({ type: "long", value: Math.round(value) });
8473
+ var boolV2 = (value) => ({ type: "bool", value });
8474
+ var enumV2 = (typeId, value) => ({ type: "enum", typeId, value });
8475
+ var textV = (value) => ({ type: "TEXT", value });
8476
+ var obj2 = (d) => ({ type: "Objc", value: d });
8477
+ var text = (d, key) => {
8478
+ const v = descGet(d, key);
8479
+ return v?.type === "TEXT" ? v.value : void 0;
8480
+ };
8481
+ var enumOf2 = (d, key) => {
8482
+ const v = descGet(d, key);
8483
+ return v?.type === "enum" ? v.value : void 0;
8484
+ };
8485
+ var numbers = (d, key) => {
8486
+ const v = descGet(d, key);
8487
+ if (v?.type !== "VlLs") return null;
8488
+ const out = [];
8489
+ for (const it of v.items) {
8490
+ if (it.type === "doub" || it.type === "UntF" || it.type === "long") out.push(it.value);
8491
+ else return null;
8492
+ }
8493
+ return out;
8494
+ };
8495
+ function setItem(d, key, v) {
8496
+ const i = d.items.findIndex(([k]) => k === key);
8497
+ if (i >= 0) d.items[i] = [key, v];
8498
+ else d.items.push([key, v]);
8499
+ }
8500
+ function cloneDescriptor(d) {
8501
+ return parseVersionedDescriptor(versionedDescriptorBytes(d));
8502
+ }
8503
+ function psdUuid(seed) {
8504
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(seed)) return seed;
8505
+ let hex = "";
8506
+ for (let k = 0; k < 4; k++) {
8507
+ let h = (2166136261 ^ k * 2654435769) >>> 0;
8508
+ for (let i = 0; i < seed.length; i++) h = Math.imul(h ^ seed.charCodeAt(i), 16777619) >>> 0;
8509
+ hex += h.toString(16).padStart(8, "0");
8510
+ }
8511
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
8512
+ }
8513
+ var LINKED_FILE_KEYS = ["lnk2", "lnk3", "lnkD", "lnkE"];
8514
+ function readLinkedItem(item) {
8515
+ const r = new PsdReader(item);
8516
+ const kind = r.ascii(4);
8517
+ const version = r.u32();
8518
+ const n = r.u8();
8519
+ const uuid = new TextDecoder().decode(r.bytes(n));
8520
+ const name = r.unicode();
8521
+ const fileType = r.ascii(4);
8522
+ r.skip(4);
8523
+ const len = r.u64();
8524
+ if (r.u8() !== 0) readVersionedDescriptor(r);
8525
+ if (kind === "liFD") return { kind, uuid, name, fileType, bytes: r.bytes(Math.min(len, r.remaining)) };
8526
+ let path;
8527
+ if (kind === "liFE") {
8528
+ try {
8529
+ const d = readVersionedDescriptor(r);
8530
+ path = text(d, "fullPath") ?? text(d, "originalPath") ?? text(d, "relPath");
8531
+ } catch {
8532
+ path = void 0;
8533
+ }
8534
+ void version;
8535
+ }
8536
+ return { kind, uuid, name, fileType, bytes: new Uint8Array(0), path: path ?? name };
8537
+ }
8538
+ function parseLinkedFiles(data) {
8539
+ const out = [];
8540
+ let at = 0;
8541
+ while (at + 8 <= data.length) {
8542
+ const r = new PsdReader(data, at);
8543
+ const len = r.u64();
8544
+ if (!(len > 0) || at + 8 + len > data.length) break;
8545
+ try {
7350
8546
  const item = readLinkedItem(data.subarray(at + 8, at + 8 + len));
7351
8547
  if (item) out.push(item);
7352
8548
  } catch {
@@ -7509,7 +8705,7 @@ var warpStyleOf = (name) => {
7509
8705
  };
7510
8706
  function warpFromDescriptor(w, contents) {
7511
8707
  const full = { x: 0, y: 0, width: contents.width, height: contents.height };
7512
- const styleName = enumOf(w, "warpStyle");
8708
+ const styleName = enumOf2(w, "warpStyle");
7513
8709
  if (!styleName) return void 0;
7514
8710
  if (styleName === "warpNone") return { mesh: null, frame: full };
7515
8711
  const bd = descObject(w, "bounds");
@@ -7528,9 +8724,9 @@ function warpFromDescriptor(w, contents) {
7528
8724
  const ys = descGet(arr.body, "Vrtc");
7529
8725
  if (xs?.type !== "UnFl" || ys?.type !== "UnFl" || xs.values.length !== 16 || ys.values.length !== 16) return void 0;
7530
8726
  mesh = { us: [0, 1], vs: [0, 1], points: xs.values.map((x, i) => ({ x, y: ys.values[i] })) };
7531
- const px = mesh.points.map((p) => p.x);
8727
+ const px2 = mesh.points.map((p) => p.x);
7532
8728
  const py = mesh.points.map((p) => p.y);
7533
- const f = { x: Math.min(...px), y: Math.min(...py), width: Math.max(...px) - Math.min(...px), height: Math.max(...py) - Math.min(...py) };
8729
+ const f = { x: Math.min(...px2), y: Math.min(...py), width: Math.max(...px2) - Math.min(...px2), height: Math.max(...py) - Math.min(...py) };
7534
8730
  if (f.width > 1e-9 && f.height > 1e-9 && [f.x, f.y, f.width, f.height].every(Number.isFinite)) frame = f;
7535
8731
  } else {
7536
8732
  const style = warpStyleOf(styleName);
@@ -7541,7 +8737,7 @@ function warpFromDescriptor(w, contents) {
7541
8737
  bend: descNumber(w, "warpValue") ?? 0,
7542
8738
  horizontal: descNumber(w, "warpPerspective") ?? 0,
7543
8739
  vertical: descNumber(w, "warpPerspectiveOther") ?? 0,
7544
- orientation: enumOf(w, "warpRotate") === "Vrtc" ? "vertical" : "horizontal"
8740
+ orientation: enumOf2(w, "warpRotate") === "Vrtc" ? "vertical" : "horizontal"
7545
8741
  },
7546
8742
  bounds
7547
8743
  );
@@ -7558,20 +8754,20 @@ function warpDescriptor(mesh, contents) {
7558
8754
  const full = { x: 0, y: 0, width: contents.width, height: contents.height };
7559
8755
  const single = mesh && mesh.us.length === 2 && mesh.vs.length === 2 && mesh.points.length === 16;
7560
8756
  const bounds = descriptor("classFloatRect", [
7561
- ["Top ", dbl(0)],
7562
- ["Left", dbl(0)],
7563
- ["Btom", dbl(contents.height)],
7564
- ["Rght", dbl(contents.width)]
8757
+ ["Top ", dbl2(0)],
8758
+ ["Left", dbl2(0)],
8759
+ ["Btom", dbl2(contents.height)],
8760
+ ["Rght", dbl2(contents.width)]
7565
8761
  ]);
7566
8762
  const items = [
7567
- ["warpStyle", enumV("warpStyle", single ? "warpCustom" : "warpNone")],
7568
- ["warpValue", dbl(0)],
7569
- ["warpPerspective", dbl(0)],
7570
- ["warpPerspectiveOther", dbl(0)],
7571
- ["warpRotate", enumV("Ornt", "Hrzn")],
7572
- ["bounds", obj(bounds)],
7573
- ["uOrder", long2(4)],
7574
- ["vOrder", long2(4)]
8763
+ ["warpStyle", enumV2("warpStyle", single ? "warpCustom" : "warpNone")],
8764
+ ["warpValue", dbl2(0)],
8765
+ ["warpPerspective", dbl2(0)],
8766
+ ["warpPerspectiveOther", dbl2(0)],
8767
+ ["warpRotate", enumV2("Ornt", "Hrzn")],
8768
+ ["bounds", obj2(bounds)],
8769
+ ["uOrder", long3(4)],
8770
+ ["vOrder", long3(4)]
7575
8771
  ];
7576
8772
  let frame = full;
7577
8773
  if (single) {
@@ -7579,10 +8775,10 @@ function warpDescriptor(mesh, contents) {
7579
8775
  ["Hrzn", { type: "UnFl", unit: "#Pxl", values: mesh.points.map((p) => p.x) }],
7580
8776
  ["Vrtc", { type: "UnFl", unit: "#Pxl", values: mesh.points.map((p) => p.y) }]
7581
8777
  ]);
7582
- items.push(["customEnvelopeWarp", obj(descriptor("customEnvelopeWarp", [["meshPoints", { type: "ObAr", prefix: 16, body }]]))]);
7583
- const px = mesh.points.map((p) => p.x);
8778
+ items.push(["customEnvelopeWarp", obj2(descriptor("customEnvelopeWarp", [["meshPoints", { type: "ObAr", prefix: 16, body }]]))]);
8779
+ const px2 = mesh.points.map((p) => p.x);
7584
8780
  const py = mesh.points.map((p) => p.y);
7585
- frame = { x: Math.min(...px), y: Math.min(...py), width: Math.max(...px) - Math.min(...px), height: Math.max(...py) - Math.min(...py) };
8781
+ frame = { x: Math.min(...px2), y: Math.min(...py), width: Math.max(...px2) - Math.min(...px2), height: Math.max(...py) - Math.min(...py) };
7586
8782
  }
7587
8783
  return { warp: descriptor("warp", items), frame, dropped: Boolean(mesh && !single) };
7588
8784
  }
@@ -7679,14 +8875,14 @@ var BOX_BLUR = { classId: "boxblur", id: 840, name: "Box Blur..." };
7679
8875
  function blendOptionsOf(item) {
7680
8876
  const o = descObject(item, "blendOptions");
7681
8877
  const opacity = o ? Math.min(1, Math.max(0, (descNumber(o, "Opct") ?? 100) / 100)) : 1;
7682
- const id = o ? enumOf(o, "Md ") : void 0;
8878
+ const id = o ? enumOf2(o, "Md ") : void 0;
7683
8879
  return { blend: id && blendFromPsdId(id) || "normal", opacity };
7684
8880
  }
7685
8881
  function blendOptions(f) {
7686
- return obj(
8882
+ return obj2(
7687
8883
  descriptor("blendOptions", [
7688
- ["Opct", unit("#Prc", Math.min(1, Math.max(0, f.opacity)) * 100)],
7689
- ["Md ", enumV("BlnM", psdBlendId(f.blend))]
8884
+ ["Opct", unit2("#Prc", Math.min(1, Math.max(0, f.opacity)) * 100)],
8885
+ ["Md ", enumV2("BlnM", psdBlendId(f.blend))]
7690
8886
  ])
7691
8887
  );
7692
8888
  }
@@ -7707,7 +8903,7 @@ function smartFiltersFromPsd(fx, newId, notes) {
7707
8903
  const name = (text(item, "Nm ") ?? "Filter").replace(/&/g, "");
7708
8904
  const neutral = cloneDescriptor(item);
7709
8905
  setItem(neutral, "blendOptions", blendOptions({ blend: "normal", opacity: 1 }));
7710
- setItem(neutral, "enab", boolV(true));
8906
+ setItem(neutral, "enab", boolV2(true));
7711
8907
  filters.push({ id: newId(), kind: "psd", enabled, name, data: versionedDescriptorBytes(neutral), blend, opacity });
7712
8908
  notes.push(`the ${name.replace(/\.\.\.$/, "")} smart filter is kept but not drawn`);
7713
8909
  }
@@ -7719,32 +8915,32 @@ function filterFXDescriptor(stack, dropped) {
7719
8915
  if (f.kind === "psd") {
7720
8916
  const d = parseVersionedDescriptor(f.data);
7721
8917
  setItem(d, "blendOptions", blendOptions(f));
7722
- setItem(d, "enab", boolV(f.enabled));
7723
- list2.push(obj(d));
8918
+ setItem(d, "enab", boolV2(f.enabled));
8919
+ list2.push(obj2(d));
7724
8920
  } else if (f.kind === "blur") {
7725
- const rgb = (v) => obj(descriptor("RGBC", [["Rd ", dbl(v)], ["Grn ", dbl(v)], ["Bl ", dbl(v)]]));
8921
+ const rgb = (v) => obj2(descriptor("RGBC", [["Rd ", dbl2(v)], ["Grn ", dbl2(v)], ["Bl ", dbl2(v)]]));
7726
8922
  list2.push(
7727
- obj(
8923
+ obj2(
7728
8924
  descriptor("filterFX", [
7729
8925
  ["Nm ", textV(BOX_BLUR.name)],
7730
8926
  ["blendOptions", blendOptions(f)],
7731
- ["enab", boolV(f.enabled)],
7732
- ["hasoptions", boolV(true)],
8927
+ ["enab", boolV2(f.enabled)],
8928
+ ["hasoptions", boolV2(true)],
7733
8929
  ["FrgC", rgb(0)],
7734
8930
  ["BckC", rgb(255)],
7735
- ["Fltr", obj(descriptor(BOX_BLUR.classId, [["Rds ", unit("#Pxl", f.radius)]]))],
7736
- ["filterID", long2(BOX_BLUR.id)]
8931
+ ["Fltr", obj2(descriptor(BOX_BLUR.classId, [["Rds ", unit2("#Pxl", f.radius)]]))],
8932
+ ["filterID", long3(BOX_BLUR.id)]
7737
8933
  ])
7738
8934
  )
7739
8935
  );
7740
8936
  } else dropped.push(f.kind);
7741
8937
  }
7742
8938
  return descriptor("filterFXStyle", [
7743
- ["enab", boolV(stack.enabled)],
7744
- ["validAtPosition", boolV(true)],
7745
- ["filterMaskEnable", boolV(stack.mask ? stack.mask.enabled !== false : true)],
7746
- ["filterMaskLinked", boolV(stack.mask ? stack.mask.linked !== false : false)],
7747
- ["filterMaskExtendWithWhite", boolV(true)],
8939
+ ["enab", boolV2(stack.enabled)],
8940
+ ["validAtPosition", boolV2(true)],
8941
+ ["filterMaskEnable", boolV2(stack.mask ? stack.mask.enabled !== false : true)],
8942
+ ["filterMaskLinked", boolV2(stack.mask ? stack.mask.linked !== false : false)],
8943
+ ["filterMaskExtendWithWhite", boolV2(true)],
7748
8944
  ["filterFXList", { type: "VlLs", items: list2 }]
7749
8945
  ]);
7750
8946
  }
@@ -7759,7 +8955,7 @@ function filterStackKey(stack) {
7759
8955
  function soldBytes(s, notes) {
7760
8956
  const { warp, frame, dropped } = warpDescriptor(s.placement.warp, s.size);
7761
8957
  if (dropped) notes.push("a warp of several patches was written without its warp (the layer\u2019s pixels still show it)");
7762
- const quad = { type: "VlLs", items: quadFromCorners(s.placement.corners, frame, s.size).map(dbl) };
8958
+ const quad = { type: "VlLs", items: quadFromCorners(s.placement.corners, frame, s.size).map(dbl2) };
7763
8959
  let d;
7764
8960
  if (s.template) {
7765
8961
  d = cloneDescriptor(s.template);
@@ -7767,31 +8963,31 @@ function soldBytes(s, notes) {
7767
8963
  setItem(d, "placed", textV(s.placed));
7768
8964
  setItem(d, "Trnf", quad);
7769
8965
  setItem(d, "nonAffineTransform", quad);
7770
- setItem(d, "warp", obj(warp));
7771
- setItem(d, "Sz ", obj(descriptor("Pnt ", [["Wdth", dbl(s.size.width)], ["Hght", dbl(s.size.height)]])));
7772
- if (s.filterFX) setItem(d, "filterFX", obj(s.filterFX));
8966
+ setItem(d, "warp", obj2(warp));
8967
+ setItem(d, "Sz ", obj2(descriptor("Pnt ", [["Wdth", dbl2(s.size.width)], ["Hght", dbl2(s.size.height)]])));
8968
+ if (s.filterFX) setItem(d, "filterFX", obj2(s.filterFX));
7773
8969
  else d.items = d.items.filter(([k]) => k !== "filterFX");
7774
8970
  } else {
7775
- const ratio = (n, den) => obj(descriptor("null", [["numerator", long2(n)], ["denominator", long2(den)]]));
8971
+ const ratio = (n, den) => obj2(descriptor("null", [["numerator", long3(n)], ["denominator", long3(den)]]));
7776
8972
  const items = [
7777
8973
  ["Idnt", textV(s.idnt)],
7778
8974
  ["placed", textV(s.placed)],
7779
- ["PgNm", long2(1)],
7780
- ["totalPages", long2(1)],
7781
- ["Crop", long2(1)],
8975
+ ["PgNm", long3(1)],
8976
+ ["totalPages", long3(1)],
8977
+ ["Crop", long3(1)],
7782
8978
  ["frameStep", ratio(0, 600)],
7783
8979
  ["duration", ratio(0, 600)],
7784
- ["frameCount", long2(1)],
7785
- ["Annt", long2(16)],
7786
- ["Type", long2(2)],
8980
+ ["frameCount", long3(1)],
8981
+ ["Annt", long3(16)],
8982
+ ["Type", long3(2)],
7787
8983
  ["Trnf", quad],
7788
8984
  ["nonAffineTransform", quad],
7789
- ["warp", obj(warp)],
7790
- ["Sz ", obj(descriptor("Pnt ", [["Wdth", dbl(s.size.width)], ["Hght", dbl(s.size.height)]]))],
7791
- ["Rslt", unit("#Rsl", 72)]
8985
+ ["warp", obj2(warp)],
8986
+ ["Sz ", obj2(descriptor("Pnt ", [["Wdth", dbl2(s.size.width)], ["Hght", dbl2(s.size.height)]]))],
8987
+ ["Rslt", unit2("#Rsl", 72)]
7792
8988
  ];
7793
- if (s.filterFX) items.push(["filterFX", obj(s.filterFX)]);
7794
- items.push(["comp", long2(-1)], ["compInfo", obj(descriptor("null", [["compID", long2(-1)], ["originalCompID", long2(-1)]]))]);
8989
+ if (s.filterFX) items.push(["filterFX", obj2(s.filterFX)]);
8990
+ items.push(["comp", long3(-1)], ["compInfo", obj2(descriptor("null", [["compID", long3(-1)], ["originalCompID", long3(-1)]]))]);
7795
8991
  d = descriptor("null", items);
7796
8992
  }
7797
8993
  const w = new PsdWriter();
@@ -8011,9 +9207,9 @@ function readLayerInfo(data, psb) {
8011
9207
  const count = r.i16();
8012
9208
  const n = Math.abs(count);
8013
9209
  r.checkCount(n, 34, "Layer records");
8014
- const read = [];
8015
- for (let i = 0; i < n; i++) read.push(readLayerRecord(r, psb));
8016
- for (const { record, lengths } of read) {
9210
+ const read2 = [];
9211
+ for (let i = 0; i < n; i++) read2.push(readLayerRecord(r, psb));
9212
+ for (const { record, lengths } of read2) {
8017
9213
  record.channels.forEach((ch, i) => {
8018
9214
  const len = lengths[i];
8019
9215
  if (len === 0) return;
@@ -8022,7 +9218,7 @@ function readLayerInfo(data, psb) {
8022
9218
  ch.data = r.bytes(len - 2);
8023
9219
  });
8024
9220
  }
8025
- return { layers: read.map((x) => x.record), mergedAlpha: count < 0 };
9221
+ return { layers: read2.map((x) => x.record), mergedAlpha: count < 0 };
8026
9222
  }
8027
9223
  function readPsd(bytes) {
8028
9224
  const r = new PsdReader(bytes);
@@ -8134,11 +9330,11 @@ function writeRect(w, r) {
8134
9330
  }
8135
9331
  function writeBlocks(w, blocks, psb, alignment) {
8136
9332
  for (const b of blocks) {
8137
- const long4 = psb && PSB_LONG_KEYS.has(b.key);
8138
- w.ascii(long4 ? "8B64" : "8BIM");
9333
+ const long5 = psb && PSB_LONG_KEYS.has(b.key);
9334
+ w.ascii(long5 ? "8B64" : "8BIM");
8139
9335
  w.ascii(b.key);
8140
9336
  const pad = (alignment - b.data.length % alignment) % alignment;
8141
- if (long4) w.u64(b.data.length + pad);
9337
+ if (long5) w.u64(b.data.length + pad);
8142
9338
  else w.u32(b.data.length + pad);
8143
9339
  w.bytes(b.data);
8144
9340
  w.zeros(pad);
@@ -8559,7 +9755,7 @@ var PSD_BLEND_KEYS = {
8559
9755
  var BLEND_TO_PSD = Object.fromEntries(
8560
9756
  Object.entries(PSD_BLEND_KEYS).map(([k, v]) => [v, k])
8561
9757
  );
8562
- var FILL_KEYS = ["SoCo", "GdFl", "PtFl"];
9758
+ var FILL_KEYS = ["SoCo", "GdFl", "PtFl", "vscg"];
8563
9759
  var TYPE_KEYS = ["TySh", "tySh"];
8564
9760
  var SMART_KEYS = ["SoLd", "SoLE", "PlLd"];
8565
9761
  var VECTOR_KEYS = ["vmsk", "vsms"];
@@ -8802,6 +9998,24 @@ function importNode(ctx, node, depth) {
8802
9998
  return smart;
8803
9999
  }
8804
10000
  }
10001
+ if (kind === "shape") {
10002
+ const shape = importShape(ctx, rec, name, notes, props);
10003
+ if (shape) {
10004
+ entry.type = "shape";
10005
+ entry.id = shape.id;
10006
+ entry.status = notes.length ? "approximated" : "mapped";
10007
+ return shape;
10008
+ }
10009
+ }
10010
+ if (kind === "type") {
10011
+ const type = importType(ctx, rec, name, notes, props);
10012
+ if (type) {
10013
+ entry.type = "type";
10014
+ entry.id = type.id;
10015
+ entry.status = notes.length ? "approximated" : "mapped";
10016
+ return type;
10017
+ }
10018
+ }
8805
10019
  if (kind !== "pixel") {
8806
10020
  const what = kind === "smart-object" ? "Smart objects" : kind === "type" ? "Type layers" : kind === "shape" ? "Shape layers" : "Fill layers";
8807
10021
  ctx.warn.add(`${what} came in as pixel layers (their stored pixels)`, name);
@@ -8819,6 +10033,69 @@ function importNode(ctx, node, depth) {
8819
10033
  entry.status = notes.length ? "approximated" : "mapped";
8820
10034
  return layer;
8821
10035
  }
10036
+ function importShape(ctx, rec, name, notes, props) {
10037
+ const approx = /* @__PURE__ */ new Set();
10038
+ const { width, height } = ctx.file.header;
10039
+ const data = shapeFromBlocks(rec.blocks, width, height, ctx.dpi, approx);
10040
+ if (!data) return null;
10041
+ for (const n of approx) {
10042
+ ctx.warn.add(`A shape layer was approximated: ${n}`, name);
10043
+ notes.push(n);
10044
+ }
10045
+ const layer = {
10046
+ type: "shape",
10047
+ id: ctx.newId(name),
10048
+ ...props,
10049
+ components: data.components,
10050
+ fill: data.fill,
10051
+ stroke: data.stroke,
10052
+ surface: emptySurface(4),
10053
+ offset: { x: 0, y: 0 }
10054
+ };
10055
+ if (data.inverted) layer.inverted = true;
10056
+ if (!ctx.skipPixels) layer.surface = renderShape(layer, { width, height, pattern: (id) => ctx.patterns.get(id) ?? null });
10057
+ layer.psd = { blocks: rec.blocks.filter((b) => SHAPE_PSD_KEYS.includes(b.key)).map((b) => ({ key: b.key, data: b.data.slice() })), signature: shapeSignature(layer) };
10058
+ const fill = u8Block(rec, "iOpa");
10059
+ if (fill !== void 0 && fill < 255) layer.fillOpacity = percent(fill);
10060
+ const effects = recordEffects(ctx, rec, name, notes, false);
10061
+ if (effects) layer.effects = effects;
10062
+ const mask = recordMask(ctx, rec, name);
10063
+ if (mask) layer.mask = mask;
10064
+ return layer;
10065
+ }
10066
+ function importType(ctx, rec, name, notes, props) {
10067
+ const block = rec.blocks.find((b) => b.key === "TySh" || b.key === "tySh");
10068
+ if (!block || block.key !== "TySh") return null;
10069
+ const approx = /* @__PURE__ */ new Set();
10070
+ const data = typeFromTySh(block.data, approx);
10071
+ if (!data) return null;
10072
+ for (const n of approx) {
10073
+ ctx.warn.add(`A type layer was approximated: ${n}`, name);
10074
+ notes.push(n);
10075
+ }
10076
+ const layer = {
10077
+ type: "type",
10078
+ id: ctx.newId(name),
10079
+ ...props,
10080
+ text: data.text,
10081
+ runs: data.runs,
10082
+ paragraphs: data.paragraphs,
10083
+ frame: data.frame,
10084
+ transform: data.transform,
10085
+ surface: recordPixels(ctx, rec, name),
10086
+ offset: { x: 0, y: 0 },
10087
+ rendering: "stored"
10088
+ };
10089
+ if (!data.antiAlias) layer.antiAlias = false;
10090
+ layer.psd = { tySh: block.data.slice(), signature: typeSignature(layer) };
10091
+ const fill = u8Block(rec, "iOpa");
10092
+ if (fill !== void 0 && fill < 255) layer.fillOpacity = percent(fill);
10093
+ const effects = recordEffects(ctx, rec, name, notes, false);
10094
+ if (effects) layer.effects = effects;
10095
+ const mask = recordMask(ctx, rec, name);
10096
+ if (mask) layer.mask = mask;
10097
+ return layer;
10098
+ }
8822
10099
  function contentsFor(ctx, placed, name, notes) {
8823
10100
  const memo = ctx.contents.get(placed.idnt);
8824
10101
  if (memo !== void 0) return memo;
@@ -8990,7 +10267,8 @@ function psdToLayers(file, options = {}) {
8990
10267
  linked: /* @__PURE__ */ new Map(),
8991
10268
  filterItems: /* @__PURE__ */ new Map(),
8992
10269
  contents: /* @__PURE__ */ new Map(),
8993
- depth: options.depth ?? 0
10270
+ depth: options.depth ?? 0,
10271
+ dpi: resolution(file) ?? 72
8994
10272
  };
8995
10273
  for (const block of file.globalBlocks) {
8996
10274
  if (LINKED_FILE_KEYS.includes(block.key)) {
@@ -9115,7 +10393,7 @@ function psdBlendId(mode) {
9115
10393
  var num3 = (d, key, fallback) => descNumber(d, key) ?? fallback;
9116
10394
  var pct = (d, key, fallback) => num3(d, key, fallback * 100) / 100;
9117
10395
  var bool = (d, key) => descBool(d, key) === true;
9118
- var enumOf2 = (d, key) => {
10396
+ var enumOf3 = (d, key) => {
9119
10397
  const v = descGet(d, key);
9120
10398
  return v?.type === "enum" ? v.value : void 0;
9121
10399
  };
@@ -9129,15 +10407,15 @@ var list = (d, key) => {
9129
10407
  return v.items.flatMap((it) => it.type === "Objc" || it.type === "GlbO" ? [it.value] : []);
9130
10408
  };
9131
10409
  var clamp3 = (v, lo, hi) => Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : lo;
9132
- var unit2 = (u, value) => ({ type: "UntF", unit: u, value });
9133
- var dbl2 = (value) => ({ type: "doub", value });
9134
- var long3 = (value) => ({ type: "long", value: Math.round(value) });
9135
- var boolV2 = (value) => ({ type: "bool", value });
9136
- var enumV2 = (typeId, value) => ({ type: "enum", typeId, value });
10410
+ var unit3 = (u, value) => ({ type: "UntF", unit: u, value });
10411
+ var dbl3 = (value) => ({ type: "doub", value });
10412
+ var long4 = (value) => ({ type: "long", value: Math.round(value) });
10413
+ var boolV3 = (value) => ({ type: "bool", value });
10414
+ var enumV3 = (typeId, value) => ({ type: "enum", typeId, value });
9137
10415
  var textV2 = (value) => ({ type: "TEXT", value });
9138
- var obj2 = (d) => ({ type: "Objc", value: d });
10416
+ var obj3 = (d) => ({ type: "Objc", value: d });
9139
10417
  function blendOf(d, key, fallback, notes) {
9140
- const id = enumOf2(d, key);
10418
+ const id = enumOf3(d, key);
9141
10419
  if (id === void 0) return fallback;
9142
10420
  const m = BLEND_OF.get(id);
9143
10421
  if (m) return m;
@@ -9176,9 +10454,9 @@ function colorOf(d, fallback = "#000000") {
9176
10454
  function colorDesc(hex) {
9177
10455
  const [r, g, b] = parseEffectColor(hex) ?? [0, 0, 0];
9178
10456
  return descriptor("RGBC", [
9179
- ["Rd ", dbl2(r * 255)],
9180
- ["Grn ", dbl2(g * 255)],
9181
- ["Bl ", dbl2(b * 255)]
10457
+ ["Rd ", dbl3(r * 255)],
10458
+ ["Grn ", dbl3(g * 255)],
10459
+ ["Bl ", dbl3(b * 255)]
9182
10460
  ]);
9183
10461
  }
9184
10462
  function contourOf(d, key) {
@@ -9204,14 +10482,14 @@ function contourDesc(c) {
9204
10482
  type: "VlLs",
9205
10483
  items: points.map((p) => {
9206
10484
  const items = [
9207
- ["Hrzn", dbl2(p.input * 255)],
9208
- ["Vrtc", dbl2(p.output * 255)]
10485
+ ["Hrzn", dbl3(p.input * 255)],
10486
+ ["Vrtc", dbl3(p.output * 255)]
9209
10487
  ];
9210
- if (p.corner) items.push(["Cnty", boolV2(false)]);
9211
- return obj2(descriptor("CrPt", items));
10488
+ if (p.corner) items.push(["Cnty", boolV3(false)]);
10489
+ return obj3(descriptor("CrPt", items));
9212
10490
  })
9213
10491
  };
9214
- return obj2(
10492
+ return obj3(
9215
10493
  descriptor("ShpC", [
9216
10494
  ["Nm ", textV2(name)],
9217
10495
  ["Crv ", pts]
@@ -9231,7 +10509,7 @@ function gradientOf(d, notes) {
9231
10509
  { position: 0, color: "#000000" },
9232
10510
  { position: 1, color: "#ffffff" }
9233
10511
  ],
9234
- style: GRADIENT_STYLES.find(([, id]) => id === enumOf2(d, "Type"))?.[0] ?? "linear",
10512
+ style: GRADIENT_STYLES.find(([, id]) => id === enumOf3(d, "Type"))?.[0] ?? "linear",
9235
10513
  angle: clamp3(num3(d, "Angl", 90), -360, 360),
9236
10514
  scale: clamp3(pct(d, "Scl ", 1), 0.1, 1.5),
9237
10515
  reverse: bool(d, "Rvrs"),
@@ -9242,12 +10520,12 @@ function gradientOf(d, notes) {
9242
10520
  if (off) g.offset = { x: clamp3(pct(off, "Hrzn", 0), -1, 1), y: clamp3(pct(off, "Vrtc", 0), -1, 1) };
9243
10521
  const grad = descObject(d, "Grad");
9244
10522
  if (!grad) return g;
9245
- if (enumOf2(grad, "GrdF") === "ClNs") {
10523
+ if (enumOf3(grad, "GrdF") === "ClNs") {
9246
10524
  notes.add("noise gradients became black to white");
9247
10525
  return g;
9248
10526
  }
9249
10527
  const raw = list(grad, "Clrs").map((s) => {
9250
- const type = enumOf2(s, "Type");
10528
+ const type = enumOf3(s, "Type");
9251
10529
  if (type === "FrgC" || type === "BckC") notes.add("foreground / background gradient stops became black / white");
9252
10530
  const color = type === "BckC" ? "#ffffff" : colorOf(descObject(s, "Clr "));
9253
10531
  return { position: clamp3(num3(s, "Lctn", 0) / 4096, 0, 1), color, mid: clamp3(num3(s, "Mdpn", 50) / 100, 0.05, 0.95) };
@@ -9275,12 +10553,12 @@ function gradientItems(g) {
9275
10553
  const clrs = {
9276
10554
  type: "VlLs",
9277
10555
  items: g.stops.map(
9278
- (s) => obj2(
10556
+ (s) => obj3(
9279
10557
  descriptor("Clrt", [
9280
- ["Clr ", obj2(colorDesc(s.color))],
9281
- ["Type", enumV2("Clry", "UsrS")],
9282
- ["Lctn", long3(s.position * 4096)],
9283
- ["Mdpn", long3(50)]
10558
+ ["Clr ", obj3(colorDesc(s.color))],
10559
+ ["Type", enumV3("Clry", "UsrS")],
10560
+ ["Lctn", long4(s.position * 4096)],
10561
+ ["Mdpn", long4(50)]
9284
10562
  ])
9285
10563
  )
9286
10564
  )
@@ -9289,35 +10567,35 @@ function gradientItems(g) {
9289
10567
  const trns = {
9290
10568
  type: "VlLs",
9291
10569
  items: op.map(
9292
- (s) => obj2(
10570
+ (s) => obj3(
9293
10571
  descriptor("TrnS", [
9294
- ["Opct", unit2("#Prc", s.opacity * 100)],
9295
- ["Lctn", long3(s.position * 4096)],
9296
- ["Mdpn", long3(50)]
10572
+ ["Opct", unit3("#Prc", s.opacity * 100)],
10573
+ ["Lctn", long4(s.position * 4096)],
10574
+ ["Mdpn", long4(50)]
9297
10575
  ])
9298
10576
  )
9299
10577
  )
9300
10578
  };
9301
10579
  const grad = descriptor("Grdn", [
9302
10580
  ["Nm ", textV2("Custom")],
9303
- ["GrdF", enumV2("GrdF", "CstS")],
9304
- ["Intr", dbl2(0)],
10581
+ ["GrdF", enumV3("GrdF", "CstS")],
10582
+ ["Intr", dbl3(0)],
9305
10583
  ["Clrs", clrs],
9306
10584
  ["Trns", trns]
9307
10585
  ]);
9308
10586
  return [
9309
- ["Grad", obj2(grad)],
9310
- ["Angl", unit2("#Ang", g.angle)],
9311
- ["Type", enumV2("GrdT", GRADIENT_STYLES.find(([s]) => s === g.style)[1])],
9312
- ["Rvrs", boolV2(g.reverse)],
9313
- ["Algn", boolV2(g.align)],
9314
- ["Scl ", unit2("#Prc", g.scale * 100)],
10587
+ ["Grad", obj3(grad)],
10588
+ ["Angl", unit3("#Ang", g.angle)],
10589
+ ["Type", enumV3("GrdT", GRADIENT_STYLES.find(([s]) => s === g.style)[1])],
10590
+ ["Rvrs", boolV3(g.reverse)],
10591
+ ["Algn", boolV3(g.align)],
10592
+ ["Scl ", unit3("#Prc", g.scale * 100)],
9315
10593
  [
9316
10594
  "Ofst",
9317
- obj2(
10595
+ obj3(
9318
10596
  descriptor("Pnt ", [
9319
- ["Hrzn", unit2("#Prc", g.offset.x * 100)],
9320
- ["Vrtc", unit2("#Prc", g.offset.y * 100)]
10597
+ ["Hrzn", unit3("#Prc", g.offset.x * 100)],
10598
+ ["Vrtc", unit3("#Prc", g.offset.y * 100)]
9321
10599
  ])
9322
10600
  )
9323
10601
  ]
@@ -9328,7 +10606,7 @@ function patternRefOf(d) {
9328
10606
  return { patternId: text2(p, "Idnt") || text2(p, "Nm "), name: text2(p, "Nm ") };
9329
10607
  }
9330
10608
  function patternRefDesc(patternId, name) {
9331
- return obj2(
10609
+ return obj3(
9332
10610
  descriptor("Ptrn", [
9333
10611
  ["Nm ", textV2(name ?? patternId)],
9334
10612
  ["Idnt", textV2(patternId)]
@@ -9345,14 +10623,14 @@ function placementOf(d) {
9345
10623
  }
9346
10624
  function placementItems(angle, link, phase) {
9347
10625
  const out = [];
9348
- if (angle) out.push(["Angl", unit2("#Ang", angle)]);
9349
- out.push(["Algn", boolV2(link)]);
10626
+ if (angle) out.push(["Angl", unit3("#Ang", angle)]);
10627
+ out.push(["Algn", boolV3(link)]);
9350
10628
  out.push([
9351
10629
  "phase",
9352
- obj2(
10630
+ obj3(
9353
10631
  descriptor("Pnt ", [
9354
- ["Hrzn", dbl2(phase.x)],
9355
- ["Vrtc", dbl2(phase.y)]
10632
+ ["Hrzn", dbl3(phase.x)],
10633
+ ["Vrtc", dbl3(phase.y)]
9356
10634
  ])
9357
10635
  )
9358
10636
  ]);
@@ -9401,7 +10679,7 @@ function readOne(kind, d, notes) {
9401
10679
  const base = {
9402
10680
  ...common(d, kind, notes),
9403
10681
  fill,
9404
- technique: enumOf2(d, "GlwT") === "PrBL" ? "precise" : "softer",
10682
+ technique: enumOf3(d, "GlwT") === "PrBL" ? "precise" : "softer",
9405
10683
  size: size("blur", 5),
9406
10684
  contour: contourOf(d, "TrnS"),
9407
10685
  antiAlias: bool(d, "AntA"),
@@ -9410,7 +10688,7 @@ function readOne(kind, d, notes) {
9410
10688
  noise: clamp3(pct(d, "Nose", 0), 0, 1)
9411
10689
  };
9412
10690
  const spread = clamp3(pct(d, "Ckmt", 0), 0, 1);
9413
- return kind === "outer-glow" ? { ...base, kind, spread } : { ...base, kind, choke: spread, source: enumOf2(d, "glwS") === "SrcC" ? "center" : "edge" };
10691
+ return kind === "outer-glow" ? { ...base, kind, spread } : { ...base, kind, choke: spread, source: enumOf3(d, "glwS") === "SrcC" ? "center" : "edge" };
9414
10692
  }
9415
10693
  case "bevel-emboss": {
9416
10694
  const styles = { OtrB: "outer-bevel", InrB: "inner-bevel", Embs: "emboss", PlEb: "pillow-emboss", strokeEmboss: "stroke-emboss" };
@@ -9418,10 +10696,10 @@ function readOne(kind, d, notes) {
9418
10696
  id: newEffectId(),
9419
10697
  enabled: descBool(d, "enab") !== false,
9420
10698
  kind,
9421
- style: styles[enumOf2(d, "bvlS") ?? ""] ?? "inner-bevel",
9422
- technique: enumOf2(d, "bvlT") === "PrBL" ? "chisel-hard" : enumOf2(d, "bvlT") === "Slmt" ? "chisel-soft" : "smooth",
10699
+ style: styles[enumOf3(d, "bvlS") ?? ""] ?? "inner-bevel",
10700
+ technique: enumOf3(d, "bvlT") === "PrBL" ? "chisel-hard" : enumOf3(d, "bvlT") === "Slmt" ? "chisel-soft" : "smooth",
9423
10701
  depth: clamp3(pct(d, "srgR", 1), 0, 10),
9424
- direction: enumOf2(d, "bvlD") === "Out " ? "down" : "up",
10702
+ direction: enumOf3(d, "bvlD") === "Out " ? "down" : "up",
9425
10703
  size: size("blur", 5),
9426
10704
  soften: clamp3(num3(d, "Sftn", 0), 0, 16),
9427
10705
  angle: clamp3(num3(d, "lagl", 120), -360, 360),
@@ -9465,14 +10743,14 @@ function readOne(kind, d, notes) {
9465
10743
  return { ...common(d, kind, notes), kind, patternId: ref.patternId, name: ref.name, scale: clamp3(pct(d, "Scl ", 1), 0.01, 10), angle: place.angle, link: place.link, phase: place.phase };
9466
10744
  }
9467
10745
  case "stroke": {
9468
- const paint = enumOf2(d, "PntT");
10746
+ const paint = enumOf3(d, "PntT");
9469
10747
  let fill;
9470
10748
  if (paint === "GrFl") fill = { type: "gradient", gradient: gradientOf(d, notes) };
9471
10749
  else if (paint === "Ptrn") {
9472
10750
  const ref = patternRefOf(d);
9473
10751
  fill = { type: "pattern", patternId: ref.patternId, name: ref.name, scale: clamp3(pct(d, "Scl ", 1), 0.01, 10) };
9474
10752
  } else fill = { type: "color", color: colorOf(descObject(d, "Clr ")) };
9475
- const pos = enumOf2(d, "Styl");
10753
+ const pos = enumOf3(d, "Styl");
9476
10754
  return { ...common(d, kind, notes), kind, size: size("Sz ", 3), position: pos === "InsF" ? "inside" : pos === "CtrF" ? "center" : "outside", fill };
9477
10755
  }
9478
10756
  }
@@ -9499,11 +10777,11 @@ function parseLfx2(data) {
9499
10777
  }
9500
10778
  function commonItems(e) {
9501
10779
  return [
9502
- ["enab", boolV2(e.enabled)],
9503
- ["present", boolV2(true)],
9504
- ["showInDialog", boolV2(true)],
9505
- ["Md ", enumV2("BlnM", BLEND_ID.get(e.blend) ?? "Nrml")],
9506
- ["Opct", unit2("#Prc", e.opacity * 100)]
10780
+ ["enab", boolV3(e.enabled)],
10781
+ ["present", boolV3(true)],
10782
+ ["showInDialog", boolV3(true)],
10783
+ ["Md ", enumV3("BlnM", BLEND_ID.get(e.blend) ?? "Nrml")],
10784
+ ["Opct", unit3("#Prc", e.opacity * 100)]
9507
10785
  ];
9508
10786
  }
9509
10787
  function writeOne(e) {
@@ -9513,17 +10791,17 @@ function writeOne(e) {
9513
10791
  const inner = e.kind === "inner-shadow";
9514
10792
  const items = [
9515
10793
  ...commonItems(e),
9516
- ["Clr ", obj2(colorDesc(e.color))],
9517
- ["uglg", boolV2(e.useGlobalLight)],
9518
- ["lagl", unit2("#Ang", e.angle)],
9519
- ["Dstn", unit2("#Pxl", e.distance)],
9520
- ["Ckmt", unit2("#Pxl", (inner ? e.choke : e.spread) * 100)],
9521
- ["blur", unit2("#Pxl", e.size)],
9522
- ["Nose", unit2("#Prc", e.noise * 100)],
9523
- ["AntA", boolV2(e.antiAlias)],
10794
+ ["Clr ", obj3(colorDesc(e.color))],
10795
+ ["uglg", boolV3(e.useGlobalLight)],
10796
+ ["lagl", unit3("#Ang", e.angle)],
10797
+ ["Dstn", unit3("#Pxl", e.distance)],
10798
+ ["Ckmt", unit3("#Pxl", (inner ? e.choke : e.spread) * 100)],
10799
+ ["blur", unit3("#Pxl", e.size)],
10800
+ ["Nose", unit3("#Prc", e.noise * 100)],
10801
+ ["AntA", boolV3(e.antiAlias)],
9524
10802
  ["TrnS", contourDesc(e.contour)]
9525
10803
  ];
9526
- if (e.kind === "drop-shadow") items.push(["layerConceals", boolV2(e.knocksOut)]);
10804
+ if (e.kind === "drop-shadow") items.push(["layerConceals", boolV3(e.knocksOut)]);
9527
10805
  return descriptor(inner ? "IrSh" : "DrSh", items);
9528
10806
  }
9529
10807
  case "outer-glow":
@@ -9531,110 +10809,110 @@ function writeOne(e) {
9531
10809
  const inner = e.kind === "inner-glow";
9532
10810
  const items = [...commonItems(e)];
9533
10811
  if (e.fill.type === "gradient") items.push(...gradientItems(e.fill.gradient));
9534
- else items.push(["Clr ", obj2(colorDesc(e.fill.color))]);
10812
+ else items.push(["Clr ", obj3(colorDesc(e.fill.color))]);
9535
10813
  items.push(
9536
- ["GlwT", enumV2("BETE", e.technique === "precise" ? "PrBL" : "SfBL")],
9537
- ["Ckmt", unit2("#Pxl", (inner ? e.choke : e.spread) * 100)],
9538
- ["blur", unit2("#Pxl", e.size)],
9539
- ["Nose", unit2("#Prc", e.noise * 100)],
9540
- ["ShdN", unit2("#Prc", e.jitter * 100)],
9541
- ["AntA", boolV2(e.antiAlias)],
10814
+ ["GlwT", enumV3("BETE", e.technique === "precise" ? "PrBL" : "SfBL")],
10815
+ ["Ckmt", unit3("#Pxl", (inner ? e.choke : e.spread) * 100)],
10816
+ ["blur", unit3("#Pxl", e.size)],
10817
+ ["Nose", unit3("#Prc", e.noise * 100)],
10818
+ ["ShdN", unit3("#Prc", e.jitter * 100)],
10819
+ ["AntA", boolV3(e.antiAlias)],
9542
10820
  ["TrnS", contourDesc(e.contour)],
9543
- ["Inpr", unit2("#Prc", e.range * 100)]
10821
+ ["Inpr", unit3("#Prc", e.range * 100)]
9544
10822
  );
9545
- if (e.kind === "inner-glow") items.push(["glwS", enumV2("IGSr", e.source === "center" ? "SrcC" : "SrcE")]);
10823
+ if (e.kind === "inner-glow") items.push(["glwS", enumV3("IGSr", e.source === "center" ? "SrcC" : "SrcE")]);
9546
10824
  return descriptor(inner ? "IrGl" : "OrGl", items);
9547
10825
  }
9548
10826
  case "bevel-emboss": {
9549
10827
  const style = { "outer-bevel": "OtrB", "inner-bevel": "InrB", emboss: "Embs", "pillow-emboss": "PlEb", "stroke-emboss": "strokeEmboss" }[e.style];
9550
10828
  const tech = { smooth: "SfBL", "chisel-hard": "PrBL", "chisel-soft": "Slmt" }[e.technique];
9551
10829
  const items = [
9552
- ["enab", boolV2(e.enabled)],
9553
- ["present", boolV2(true)],
9554
- ["showInDialog", boolV2(true)],
9555
- ["hglM", enumV2("BlnM", BLEND_ID.get(e.highlightBlend) ?? "Scrn")],
9556
- ["hglC", obj2(colorDesc(e.highlightColor))],
9557
- ["hglO", unit2("#Prc", e.highlightOpacity * 100)],
9558
- ["sdwM", enumV2("BlnM", BLEND_ID.get(e.shadowBlend) ?? "Mltp")],
9559
- ["sdwC", obj2(colorDesc(e.shadowColor))],
9560
- ["sdwO", unit2("#Prc", e.shadowOpacity * 100)],
9561
- ["bvlT", enumV2("bvlT", tech)],
9562
- ["bvlS", enumV2("BESl", style)],
9563
- ["uglg", boolV2(e.useGlobalLight)],
9564
- ["lagl", unit2("#Ang", e.angle)],
9565
- ["Lald", unit2("#Ang", e.altitude)],
9566
- ["srgR", unit2("#Prc", e.depth * 100)],
9567
- ["blur", unit2("#Pxl", e.size)],
9568
- ["bvlD", enumV2("BESs", e.direction === "up" ? "In " : "Out ")],
10830
+ ["enab", boolV3(e.enabled)],
10831
+ ["present", boolV3(true)],
10832
+ ["showInDialog", boolV3(true)],
10833
+ ["hglM", enumV3("BlnM", BLEND_ID.get(e.highlightBlend) ?? "Scrn")],
10834
+ ["hglC", obj3(colorDesc(e.highlightColor))],
10835
+ ["hglO", unit3("#Prc", e.highlightOpacity * 100)],
10836
+ ["sdwM", enumV3("BlnM", BLEND_ID.get(e.shadowBlend) ?? "Mltp")],
10837
+ ["sdwC", obj3(colorDesc(e.shadowColor))],
10838
+ ["sdwO", unit3("#Prc", e.shadowOpacity * 100)],
10839
+ ["bvlT", enumV3("bvlT", tech)],
10840
+ ["bvlS", enumV3("BESl", style)],
10841
+ ["uglg", boolV3(e.useGlobalLight)],
10842
+ ["lagl", unit3("#Ang", e.angle)],
10843
+ ["Lald", unit3("#Ang", e.altitude)],
10844
+ ["srgR", unit3("#Prc", e.depth * 100)],
10845
+ ["blur", unit3("#Pxl", e.size)],
10846
+ ["bvlD", enumV3("BESs", e.direction === "up" ? "In " : "Out ")],
9569
10847
  ["TrnS", contourDesc(e.glossContour)],
9570
- ["Sftn", unit2("#Pxl", e.soften)]
10848
+ ["Sftn", unit3("#Pxl", e.soften)]
9571
10849
  ];
9572
10850
  if (e.contourElement) {
9573
- items.push(["useShape", boolV2(true)], ["MpgS", contourDesc(e.contourElement.contour)], ["AntA", boolV2(e.contourElement.antiAlias)], ["Inpr", unit2("#Prc", e.contourElement.range * 100)]);
9574
- } else items.push(["useShape", boolV2(false)]);
10851
+ items.push(["useShape", boolV3(true)], ["MpgS", contourDesc(e.contourElement.contour)], ["AntA", boolV3(e.contourElement.antiAlias)], ["Inpr", unit3("#Prc", e.contourElement.range * 100)]);
10852
+ } else items.push(["useShape", boolV3(false)]);
9575
10853
  if (e.texture) {
9576
10854
  const t = e.texture;
9577
10855
  items.push(
9578
- ["useTexture", boolV2(true)],
9579
- ["InvT", boolV2(t.invert)],
9580
- ["Scl ", unit2("#Prc", t.scale * 100)],
9581
- ["textureDepth", unit2("#Prc", t.depth * 100)],
10856
+ ["useTexture", boolV3(true)],
10857
+ ["InvT", boolV3(t.invert)],
10858
+ ["Scl ", unit3("#Prc", t.scale * 100)],
10859
+ ["textureDepth", unit3("#Prc", t.depth * 100)],
9582
10860
  ["Ptrn", patternRefDesc(t.patternId, t.name)],
9583
10861
  ...placementItems(0, t.link ?? true, t.phase ?? { x: 0, y: 0 })
9584
10862
  );
9585
- } else items.push(["useTexture", boolV2(false)]);
10863
+ } else items.push(["useTexture", boolV3(false)]);
9586
10864
  return descriptor("ebbl", items);
9587
10865
  }
9588
10866
  case "satin":
9589
10867
  return descriptor("ChFX", [
9590
10868
  ...commonItems(e),
9591
- ["Clr ", obj2(colorDesc(e.color))],
9592
- ["AntA", boolV2(e.antiAlias)],
9593
- ["Invr", boolV2(e.invert)],
9594
- ["lagl", unit2("#Ang", e.angle)],
9595
- ["Dstn", unit2("#Pxl", e.distance)],
9596
- ["blur", unit2("#Pxl", e.size)],
10869
+ ["Clr ", obj3(colorDesc(e.color))],
10870
+ ["AntA", boolV3(e.antiAlias)],
10871
+ ["Invr", boolV3(e.invert)],
10872
+ ["lagl", unit3("#Ang", e.angle)],
10873
+ ["Dstn", unit3("#Pxl", e.distance)],
10874
+ ["blur", unit3("#Pxl", e.size)],
9597
10875
  ["MpgS", contourDesc(e.contour)]
9598
10876
  ]);
9599
10877
  case "color-overlay":
9600
- return descriptor("SoFi", [...commonItems(e), ["Clr ", obj2(colorDesc(e.color))]]);
10878
+ return descriptor("SoFi", [...commonItems(e), ["Clr ", obj3(colorDesc(e.color))]]);
9601
10879
  case "gradient-overlay":
9602
- return descriptor("GrFl", [...commonItems(e), ...gradientItems(e.gradient), ["Dthr", boolV2(e.dither)]]);
10880
+ return descriptor("GrFl", [...commonItems(e), ...gradientItems(e.gradient), ["Dthr", boolV3(e.dither)]]);
9603
10881
  case "pattern-overlay":
9604
10882
  return descriptor("patternFill", [
9605
10883
  ...commonItems(e),
9606
10884
  ["Ptrn", patternRefDesc(e.patternId, e.name)],
9607
- ["Scl ", unit2("#Prc", e.scale * 100)],
10885
+ ["Scl ", unit3("#Prc", e.scale * 100)],
9608
10886
  ...placementItems(e.angle ?? 0, e.link ?? true, e.phase ?? { x: 0, y: 0 })
9609
10887
  ]);
9610
10888
  case "stroke": {
9611
10889
  const items = [
9612
10890
  ...commonItems(e),
9613
- ["Styl", enumV2("FStl", e.position === "inside" ? "InsF" : e.position === "center" ? "CtrF" : "OutF")],
9614
- ["Sz ", unit2("#Pxl", e.size)]
10891
+ ["Styl", enumV3("FStl", e.position === "inside" ? "InsF" : e.position === "center" ? "CtrF" : "OutF")],
10892
+ ["Sz ", unit3("#Pxl", e.size)]
9615
10893
  ];
9616
10894
  const f = e.fill;
9617
- if (f.type === "color") items.push(["PntT", enumV2("FrFl", "SClr")], ["Clr ", obj2(colorDesc(f.color))]);
9618
- else if (f.type === "gradient") items.push(["PntT", enumV2("FrFl", "GrFl")], ...gradientItems(f.gradient));
9619
- else items.push(["PntT", enumV2("FrFl", "Ptrn")], ["Ptrn", patternRefDesc(f.patternId, f.name)], ["Scl ", unit2("#Prc", f.scale * 100)]);
10895
+ if (f.type === "color") items.push(["PntT", enumV3("FrFl", "SClr")], ["Clr ", obj3(colorDesc(f.color))]);
10896
+ else if (f.type === "gradient") items.push(["PntT", enumV3("FrFl", "GrFl")], ...gradientItems(f.gradient));
10897
+ else items.push(["PntT", enumV3("FrFl", "Ptrn")], ["Ptrn", patternRefDesc(f.patternId, f.name)], ["Scl ", unit3("#Prc", f.scale * 100)]);
9620
10898
  return descriptor("FrFX", items);
9621
10899
  }
9622
10900
  }
9623
10901
  }
9624
10902
  function writeLfx2(effects) {
9625
10903
  const items = [
9626
- ["Scl ", unit2("#Prc", 100)],
9627
- ["masterFXSwitch", boolV2(effects.enabled)]
10904
+ ["Scl ", unit3("#Prc", 100)],
10905
+ ["masterFXSwitch", boolV3(effects.enabled)]
9628
10906
  ];
9629
10907
  const scale = effectsScale(effects);
9630
10908
  let dropped = 0;
9631
10909
  for (const [single, multi, kind] of KINDS) {
9632
10910
  const of = effects.items.filter((e) => e.kind === kind).map((e) => writeOne(scaleLayerEffect(e, scale)));
9633
10911
  if (!of.length) continue;
9634
- if (of.length === 1) items.push([single, obj2(of[0])]);
9635
- else if (multi) items.push([multi, { type: "VlLs", items: of.map(obj2) }]);
10912
+ if (of.length === 1) items.push([single, obj3(of[0])]);
10913
+ else if (multi) items.push([multi, { type: "VlLs", items: of.map(obj3) }]);
9636
10914
  else {
9637
- items.push([single, obj2(of[0])]);
10915
+ items.push([single, obj3(of[0])]);
9638
10916
  dropped += of.length - 1;
9639
10917
  }
9640
10918
  }
@@ -9652,7 +10930,7 @@ function parseLrfx(data) {
9652
10930
  const count = r.u16();
9653
10931
  const items = [];
9654
10932
  const notes = /* @__PURE__ */ new Set();
9655
- const fixed = (v) => (v | 0) / 65536;
10933
+ const fixed2 = (v) => (v | 0) / 65536;
9656
10934
  for (let i = 0; i < count; i++) {
9657
10935
  if (new TextDecoder().decode(r.bytes(4)) !== "8BIM") return null;
9658
10936
  const key = new TextDecoder().decode(r.bytes(4));
@@ -9671,10 +10949,10 @@ function parseLrfx(data) {
9671
10949
  };
9672
10950
  if (key === "dsdw" || key === "isdw") {
9673
10951
  b.u32();
9674
- const blur = fixed(b.u32());
10952
+ const blur = fixed2(b.u32());
9675
10953
  b.u32();
9676
- const angle = fixed(b.u32());
9677
- const distance = fixed(b.u32());
10954
+ const angle = fixed2(b.u32());
10955
+ const distance = fixed2(b.u32());
9678
10956
  const color = colorAt(b);
9679
10957
  const blend = blendAt(b);
9680
10958
  const enabled = b.u8() !== 0;
@@ -9684,7 +10962,7 @@ function parseLrfx(data) {
9684
10962
  items.push(key === "dsdw" ? { ...base, kind: "drop-shadow", spread: 0, knocksOut: true } : { ...base, kind: "inner-shadow", choke: 0 });
9685
10963
  } else if (key === "oglw" || key === "iglw") {
9686
10964
  b.u32();
9687
- const blur = fixed(b.u32());
10965
+ const blur = fixed2(b.u32());
9688
10966
  b.u32();
9689
10967
  const color = colorAt(b);
9690
10968
  const blend = blendAt(b);
@@ -9805,8 +11083,8 @@ function smartContentsFile(c) {
9805
11083
  return { name: `${base}.png`, bytes: encodePng(c.width, c.height, data) };
9806
11084
  }
9807
11085
  const doc = c.document;
9808
- const merged = readRegion(contentsSurface(c), { x: 0, y: 0, width: c.width, height: c.height });
9809
- const res = exportPsd({ width: c.width, height: c.height, layers: prepareNestedLayers(doc.layers), merged, patterns: doc.patterns, globalLight: doc.globalLight });
11086
+ const merged2 = readRegion(contentsSurface(c), { x: 0, y: 0, width: c.width, height: c.height });
11087
+ const res = exportPsd({ width: c.width, height: c.height, layers: prepareNestedLayers(doc.layers), merged: merged2, patterns: doc.patterns, globalLight: doc.globalLight });
9810
11088
  return { name: `${base}.${res.psb ? "psb" : "psd"}`, bytes: res.bytes };
9811
11089
  }
9812
11090
  var near = (a, b) => Math.abs(a - b) <= 1e-6 * (1 + Math.abs(a));
@@ -10006,6 +11284,44 @@ function exportPsd(input) {
10006
11284
  records.push(rec);
10007
11285
  return;
10008
11286
  }
11287
+ case "shape":
11288
+ case "type": {
11289
+ const extra = [];
11290
+ if (layer.fillOpacity !== void 0 && layer.fillOpacity < 1) {
11291
+ extra.push({ key: "iOpa", data: Uint8Array.from([Math.round(Math.max(0, layer.fillOpacity) * 255), 0, 0, 0]) });
11292
+ }
11293
+ if (layer.type === "shape") {
11294
+ const losses = /* @__PURE__ */ new Set();
11295
+ for (const b2 of shapeBlocks(layer, width, height, input.resolution ?? 72, losses)) extra.push({ key: b2.key, data: padEven(b2.data) });
11296
+ for (const n2 of losses) note(n2, layer.name);
11297
+ if (layer.fill?.type === "pattern") patternIds2.add(layer.fill.patternId);
11298
+ if (layer.stroke?.paint.type === "pattern") patternIds2.add(layer.stroke.paint.patternId);
11299
+ } else {
11300
+ extra.push({ key: "TySh", data: padEven(tyShForLayer(layer)) });
11301
+ }
11302
+ const rec = { ...common2(layer, extra), rect: EMPTY_RECT, channels: [], blendKey: BLEND_TO_PSD[layer.blend] ?? "norm" };
11303
+ const bounds = contentBounds(layer.surface);
11304
+ if (bounds) {
11305
+ const n2 = bounds.width * bounds.height;
11306
+ const [r2, g2, b2, a2] = rgbaToPlanes(readRegion(layer.surface, bounds), n2);
11307
+ rec.rect = toRect(bounds);
11308
+ rec.channels = [
11309
+ rleChannel(CHANNEL_ALPHA, a2, bounds.width, bounds.height, psb),
11310
+ rleChannel(0, r2, bounds.width, bounds.height, psb),
11311
+ rleChannel(1, g2, bounds.width, bounds.height, psb),
11312
+ rleChannel(2, b2, bounds.width, bounds.height, psb)
11313
+ ];
11314
+ } else {
11315
+ rec.channels = [CHANNEL_ALPHA, 0, 1, 2].map(emptyChannel);
11316
+ }
11317
+ if (layer.mask) {
11318
+ const m = maskChannel(layer.mask, 0, 0, psb);
11319
+ rec.channels.push(m.channel);
11320
+ rec.mask = m.mask;
11321
+ }
11322
+ records.push(rec);
11323
+ return;
11324
+ }
10009
11325
  case "annotation":
10010
11326
  throw new PsdError(`Annotation layer "${layer.name}" must be rasterized before export.`);
10011
11327
  }
@@ -10017,8 +11333,8 @@ function exportPsd(input) {
10017
11333
  const [r, g, b, a] = rgbaToPlanes(matteWhite(input.merged), n);
10018
11334
  const transparent = a.some((v) => v !== 255);
10019
11335
  const planes = transparent ? [r, g, b, a] : [r, g, b];
10020
- const merged = new Uint8Array(n * planes.length);
10021
- planes.forEach((p, i) => merged.set(p, i * n));
11336
+ const merged2 = new Uint8Array(n * planes.length);
11337
+ planes.forEach((p, i) => merged2.set(p, i * n));
10022
11338
  const res = Math.max(1, input.resolution ?? 72);
10023
11339
  const rw = new PsdWriter();
10024
11340
  for (let k = 0; k < 2; k++) {
@@ -10075,7 +11391,7 @@ function exportPsd(input) {
10075
11391
  mergedAlpha: transparent && records.length > 0,
10076
11392
  globalBlocks,
10077
11393
  imageCompression: 1,
10078
- imageData: encodeRle(merged, { planes: planes.length, width, height, depth: 8, psb })
11394
+ imageData: encodeRle(merged2, { planes: planes.length, width, height, depth: 8, psb })
10079
11395
  },
10080
11396
  encodeLegacyName
10081
11397
  );
@@ -10420,8 +11736,6 @@ export {
10420
11736
  deserializeLayer,
10421
11737
  deserializeLayers,
10422
11738
  PsdError,
10423
- decodePlanes,
10424
- encodeRle,
10425
11739
  parseVersionedDescriptor,
10426
11740
  versionedDescriptorBytes,
10427
11741
  ADJUSTMENT_KEYS,
@@ -10431,6 +11745,8 @@ export {
10431
11745
  parseLfx2,
10432
11746
  writeLfx2,
10433
11747
  parseLrfx,
11748
+ decodePlanes,
11749
+ encodeRle,
10434
11750
  BROWSER_IMAGE_TYPES,
10435
11751
  readPsd,
10436
11752
  layerName,
@@ -10448,4 +11764,4 @@ export {
10448
11764
  smartContentsFile,
10449
11765
  exportPsd
10450
11766
  };
10451
- //# sourceMappingURL=chunk-HHZ6ZJTW.js.map
11767
+ //# sourceMappingURL=chunk-CZOXTEMN.js.map