@pixerate/editor 0.192.0 → 0.194.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 (56) hide show
  1. package/dist/animation-editor/index.cjs +56 -56
  2. package/dist/animation-editor/index.js +2 -2
  3. package/dist/{chunk-ZQGZWFPU.cjs → chunk-3T5VCDIV.cjs} +97 -5
  4. package/dist/{chunk-RQ2EM5TY.js → chunk-5OC22IEO.js} +97 -5
  5. package/dist/{chunk-EGZM52EO.cjs → chunk-BRKVNXDZ.cjs} +354 -125
  6. package/dist/{chunk-XCDGM7TD.cjs → chunk-FR2Q2LVF.cjs} +35 -35
  7. package/dist/{chunk-LPIKXPBM.js → chunk-GHCXLQ47.js} +262 -33
  8. package/dist/{chunk-VRXXAXUT.js → chunk-MLC7HUHO.js} +1 -1
  9. package/dist/{chunk-2UR4OX3B.cjs → chunk-UYXL6ZWQ.cjs} +114 -97
  10. package/dist/{chunk-TXYXF32N.js → chunk-ZIDUPSP4.js} +35 -18
  11. package/dist/image-editor/controller.d.cts +16 -6
  12. package/dist/image-editor/controller.d.ts +16 -6
  13. package/dist/image-editor/index.cjs +945 -831
  14. package/dist/image-editor/index.d.cts +1 -0
  15. package/dist/image-editor/index.d.ts +1 -0
  16. package/dist/image-editor/index.js +181 -67
  17. package/dist/image-editor/layers/model.d.cts +7 -2
  18. package/dist/image-editor/layers/model.d.ts +7 -2
  19. package/dist/image-editor/layers/serialize.d.cts +2 -0
  20. package/dist/image-editor/layers/serialize.d.ts +2 -0
  21. package/dist/image-editor/layers/shape-type-ui.d.cts +8 -2
  22. package/dist/image-editor/layers/shape-type-ui.d.ts +8 -2
  23. package/dist/image-editor/layers/text.d.cts +6 -2
  24. package/dist/image-editor/layers/text.d.ts +6 -2
  25. package/dist/image-editor/psd/type.d.cts +2 -1
  26. package/dist/image-editor/psd/type.d.ts +2 -1
  27. package/dist/image-editor/tool-groups.d.cts +35 -0
  28. package/dist/image-editor/tool-groups.d.ts +35 -0
  29. package/dist/image-editor/tools.d.cts +1 -1
  30. package/dist/image-editor/tools.d.ts +1 -1
  31. package/dist/image-editor/type-tools.d.cts +2 -2
  32. package/dist/image-editor/type-tools.d.ts +2 -2
  33. package/dist/paint-editor/index.cjs +7 -7
  34. package/dist/paint-editor/index.js +2 -2
  35. package/dist/text/layout.d.cts +93 -12
  36. package/dist/text/layout.d.ts +93 -12
  37. package/dist/text/opentype.d.cts +13 -3
  38. package/dist/text/opentype.d.ts +13 -3
  39. package/dist/text/shapers.d.cts +12 -0
  40. package/dist/text/shapers.d.ts +12 -0
  41. package/dist/text/types.d.cts +36 -2
  42. package/dist/text/types.d.ts +36 -2
  43. package/dist/vector/controller.d.cts +8 -0
  44. package/dist/vector/controller.d.ts +8 -0
  45. package/dist/vector/index.cjs +1039 -967
  46. package/dist/vector/index.js +90 -18
  47. package/dist/vector/text.d.cts +5 -3
  48. package/dist/vector/text.d.ts +5 -3
  49. package/dist/vector/textPanel.d.cts +6 -0
  50. package/dist/vector/textPanel.d.ts +6 -0
  51. package/dist/vector/types.d.cts +9 -0
  52. package/dist/vector/types.d.ts +9 -0
  53. package/package.json +1 -1
  54. package/skills/pixerate-image-editor/SKILL.md +4 -3
  55. package/skills/pixerate-image-editor/references/shapes-and-type.md +17 -5
  56. package/skills/pixerate-vector/SKILL.md +9 -3
@@ -61,6 +61,7 @@ import {
61
61
  WARP_STYLES,
62
62
  applyMatrix,
63
63
  bezierToSubpaths,
64
+ drawTextFragment,
64
65
  ellipseSubpath,
65
66
  evalWarpMesh,
66
67
  expandVectorCorners,
@@ -70,7 +71,6 @@ import {
70
71
  identityWarpMesh,
71
72
  invertMatrix,
72
73
  isIdentityMatrix,
73
- isTextSpace,
74
74
  isValidWarpMesh,
75
75
  layoutText,
76
76
  mapWarpMeshPoints,
@@ -87,9 +87,10 @@ import {
87
87
  textDecorations,
88
88
  textFragmentOutline,
89
89
  textRectPath,
90
+ transformTextRect,
90
91
  translateMatrix,
91
92
  warpPresetMesh
92
- } from "./chunk-LPIKXPBM.js";
93
+ } from "./chunk-GHCXLQ47.js";
93
94
  import {
94
95
  bezierPathBounds,
95
96
  outlineStroke,
@@ -2923,6 +2924,7 @@ function combineCoverage(a, b, op, n) {
2923
2924
  }
2924
2925
 
2925
2926
  // src/text/types.ts
2927
+ var TEXT_ORIENTATIONS = ["horizontal", "vertical"];
2926
2928
  var TEXT_ALIGNS = ["left", "center", "right", "justify"];
2927
2929
 
2928
2930
  // src/image-editor/layers/model.ts
@@ -4301,9 +4303,9 @@ function inkBounds(layout) {
4301
4303
  let b = null;
4302
4304
  for (const l of layout.lines) {
4303
4305
  for (const f of l.fragments) {
4304
- const pad = f.style.fontSize * 0.5;
4306
+ const pad = f.style.fontSize * (l.matrix ? 1 : 0.5);
4305
4307
  const w = f.carets[f.carets.length - 1];
4306
- const r = { x: f.x - pad, y: f.y - l.ascent - pad, width: w + 2 * pad, height: l.ascent + l.descent + 2 * pad };
4308
+ const r = transformTextRect(l.matrix, { x: f.x - pad, y: l.baseline - l.ascent - pad + Math.min(0, f.y - l.baseline), width: w + 2 * pad, height: l.ascent + l.descent + 2 * pad + Math.abs(f.y - l.baseline) });
4307
4309
  if (!b) b = r;
4308
4310
  else {
4309
4311
  const x0 = Math.min(b.x, r.x);
@@ -4389,9 +4391,7 @@ function renderOnCanvas(layer, shaper, region, env) {
4389
4391
  const [r, g, b] = rgbOf(f.style.color);
4390
4392
  ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
4391
4393
  ctx.globalAlpha = f.style.opacity ?? 1;
4392
- const s = layer.text.slice(f.start, f.end);
4393
- if (!f.spaced) shaper.draw(ctx, s, f.font, f.x, f.y);
4394
- else for (let k = 0; k < s.length; k++) if (!isTextSpace(s[k])) shaper.draw(ctx, s[k], f.font, f.x + f.carets[k], f.y);
4394
+ drawTextFragment(ctx, shaper, layer.text, f);
4395
4395
  for (const d of textDecorations(f, l)) ctx.fillRect(d.x, d.y, d.width, d.height);
4396
4396
  }
4397
4397
  }
@@ -4426,7 +4426,7 @@ function num(v, path, min, max) {
4426
4426
  if (!isNum2(v) || v < min || v > max) fail3(`${path} must be a number from ${min} to ${max}, got ${JSON.stringify(v)}.`);
4427
4427
  return v;
4428
4428
  }
4429
- var TEXT_STYLE_FIELDS = ["fontFamily", "fontWeight", "italic", "fontSize", "color", "opacity", "tracking", "leading", "baselineShift", "underline", "strikethrough", "postScriptName"];
4429
+ var TEXT_STYLE_FIELDS = ["fontFamily", "fontWeight", "italic", "fontSize", "color", "opacity", "tracking", "leading", "baselineShift", "underline", "strikethrough", "upright", "postScriptName"];
4430
4430
  function patchTextStyle(base, patch, path = "style") {
4431
4431
  const out = { ...base };
4432
4432
  const o = out;
@@ -4452,6 +4452,7 @@ function patchTextStyle(base, patch, path = "style") {
4452
4452
  case "italic":
4453
4453
  case "underline":
4454
4454
  case "strikethrough":
4455
+ case "upright":
4455
4456
  if (typeof v !== "boolean" && v !== null) fail3(`${path}.${k} must be true or false.`);
4456
4457
  if (v) o[k] = true;
4457
4458
  else delete o[k];
@@ -4530,6 +4531,10 @@ function typeLayerName(text3) {
4530
4531
  const line = text3.split("\n").find((l) => l.trim()) ?? "";
4531
4532
  return line.trim().slice(0, 32) || "Text";
4532
4533
  }
4534
+ function textOrientation(v, path = "orientation") {
4535
+ if (!TEXT_ORIENTATIONS.includes(v)) fail3(`${path} must be ${TEXT_ORIENTATIONS.join(" or ")}, got ${JSON.stringify(v)}.`);
4536
+ return v;
4537
+ }
4533
4538
  function createTypeLayer(init, env, id = newLayerId(), lenient = false) {
4534
4539
  const text3 = normalizeText(init.text);
4535
4540
  const layer = {
@@ -4543,6 +4548,7 @@ function createTypeLayer(init, env, id = newLayerId(), lenient = false) {
4543
4548
  runs: fitRuns(init.runs, text3.length, DEFAULT_TEXT_STYLE),
4544
4549
  paragraphs: fitParagraphRuns(init.paragraphs ?? [{ length: text3.length, style: {} }], text3),
4545
4550
  frame: init.frame,
4551
+ ...init.orientation === "vertical" ? { orientation: "vertical" } : {},
4546
4552
  surface: emptySurface(4),
4547
4553
  offset: { x: 0, y: 0 }
4548
4554
  };
@@ -4557,6 +4563,7 @@ function updateTypeLayer(layers, id, change, env, action = "edit it") {
4557
4563
  const patch = change(current);
4558
4564
  const next = { ...current, ...patch };
4559
4565
  if (!("annotation" in patch)) delete next.annotation;
4566
+ if (next.orientation !== "vertical") delete next.orientation;
4560
4567
  if ("text" in patch || "runs" in patch) next.runs = fitRuns(next.runs, next.text.length, current.runs[0]?.style ?? DEFAULT_TEXT_STYLE);
4561
4568
  if (!next.runs.length) next.runs = [{ length: 0, style: styleAt(current.runs, 0, DEFAULT_TEXT_STYLE) }];
4562
4569
  if ("text" in patch || "paragraphs" in patch) next.paragraphs = fitParagraphRuns(next.paragraphs, next.text);
@@ -4586,7 +4593,7 @@ function typeFrame(layer, shaper) {
4586
4593
  return { x: b.x, y: b.y, width: Math.max(b.width, 1), height: Math.max(b.height, 1) };
4587
4594
  }
4588
4595
  function typeAsPixelLayer(layer) {
4589
- const { text: _t, runs: _r, paragraphs: _p, frame: _f, transform: _m, antiAlias: _a, rendering: _g, missingFonts: _mf, annotation: _an, psd: _psd, type: _ty, ...rest } = layer;
4596
+ const { text: _t, runs: _r, paragraphs: _p, frame: _f, orientation: _o, transform: _m, antiAlias: _a, rendering: _g, missingFonts: _mf, annotation: _an, psd: _psd, type: _ty, ...rest } = layer;
4590
4597
  return { ...rest, type: "pixel", surface: layer.surface, offset: { x: 0, y: 0 } };
4591
4598
  }
4592
4599
  function toTextSpace(layer, p) {
@@ -5952,6 +5959,7 @@ function serializeType(layer, base, ctx) {
5952
5959
  paragraphs: JSON.parse(JSON.stringify(layer.paragraphs)),
5953
5960
  frame: { ...layer.frame }
5954
5961
  };
5962
+ if (layer.orientation === "vertical") out.orientation = "vertical";
5955
5963
  if (layer.transform) out.transform = [...layer.transform];
5956
5964
  if (layer.antiAlias === false) out.antiAlias = false;
5957
5965
  if (layer.fillOpacity !== void 0 && layer.fillOpacity !== 1) out.fillOpacity = layer.fillOpacity;
@@ -5998,6 +6006,7 @@ function readType(r, base, ctx) {
5998
6006
  };
5999
6007
  if (!layer.runs.length) layer.runs = [{ length: 0, style: runs[0]?.style ?? DEFAULT_TEXT_STYLE }];
6000
6008
  if (m) layer.transform = m;
6009
+ if (r.orientation === "vertical") layer.orientation = "vertical";
6001
6010
  if (r.antiAlias === false) layer.antiAlias = false;
6002
6011
  if (typeof r.fillOpacity === "number" && r.fillOpacity !== 1) layer.fillOpacity = num2(r.fillOpacity, 0, 1, 1);
6003
6012
  if (Array.isArray(r.missingFonts)) layer.missingFonts = r.missingFonts.filter((x) => typeof x === "string");
@@ -8444,7 +8453,7 @@ function merged(levels) {
8444
8453
  }
8445
8454
  return E.dict(items);
8446
8455
  }
8447
- function charStyle(d, fonts, notes) {
8456
+ function charStyle(d, fonts, notes, vertical = false) {
8448
8457
  const n = (k) => eNum(eGet(d, k));
8449
8458
  const flag = (k) => eBool(eGet(d, k));
8450
8459
  const s = { ...DEFAULT_TEXT_STYLE };
@@ -8469,6 +8478,8 @@ function charStyle(d, fonts, notes) {
8469
8478
  if (shift) s.baselineShift = shift;
8470
8479
  if (flag("Underline")) s.underline = true;
8471
8480
  if (flag("Strikethrough")) s.strikethrough = true;
8481
+ if (vertical && n("BaselineDirection") === 1) s.upright = true;
8482
+ if (vertical && n("BaselineDirection") === 3) notes.add("tate-chu-yoko (horizontal-in-vertical) text is set as the rest of the column");
8472
8483
  if (flag("FauxBold")) notes.add("faux bold is drawn as the regular weight");
8473
8484
  if (flag("FauxItalic")) notes.add("faux italic is drawn upright");
8474
8485
  if ((n("HorizontalScale") ?? 1) !== 1 || (n("VerticalScale") ?? 1) !== 1) notes.add("horizontal and vertical character scaling is drawn at 100 %");
@@ -8523,7 +8534,7 @@ function typeFromTySh(data, notes) {
8523
8534
  const anti = descGet(t.text, "AntA");
8524
8535
  const antiAlias = !(anti?.type === "enum" && (anti.value === "Anno" || anti.value === "antiAliasNone"));
8525
8536
  const ornt = descGet(t.text, "Ornt");
8526
- if (ornt?.type === "enum" && ornt.value === "Vrtc") notes.add("vertical type is laid out horizontally");
8537
+ const vertical = ornt?.type === "enum" && ornt.value === "Vrtc";
8527
8538
  if (t.warp) {
8528
8539
  const style = descGet(t.warp, "warpStyle");
8529
8540
  if (style?.type === "enum" && style.value !== "warpNone") notes.add("warped text is drawn without its warp when edited");
@@ -8578,7 +8589,7 @@ function typeFromTySh(data, notes) {
8578
8589
  const paraDefault = paraBases[paraAt(at)]?.charDefault;
8579
8590
  at += slens[i] ?? 0;
8580
8591
  const d = merged([eGet(sheet, "StyleSheetData"), eGet(own, "StyleSheetData"), eGet(paraDefault, "StyleSheetData"), eGet(normalStyle, "StyleSheetData")]);
8581
- return { length: slens[i] ?? 0, style: charStyle(d, fonts, notes) };
8592
+ return { length: slens[i] ?? 0, style: charStyle(d, fonts, notes, vertical) };
8582
8593
  });
8583
8594
  }
8584
8595
  const shape = eArray(ePath(engine, ["EngineDict", "Rendered", "Shapes", "Children"]))[0];
@@ -8595,15 +8606,16 @@ function typeFromTySh(data, notes) {
8595
8606
  runs: fitRuns(runs, text3.length, DEFAULT_TEXT_STYLE),
8596
8607
  paragraphs: fitParagraphRuns(paragraphs, text3),
8597
8608
  frame,
8609
+ orientation: vertical ? "vertical" : "horizontal",
8598
8610
  transform,
8599
8611
  antiAlias,
8600
8612
  fonts: [...new Set(fonts.filter(Boolean))]
8601
8613
  };
8602
8614
  }
8603
8615
  function typeSignature(layer) {
8604
- return JSON.stringify([layer.text, layer.runs, layer.paragraphs, layer.frame, layer.transform ?? null, layer.antiAlias ?? true]);
8616
+ return JSON.stringify([layer.text, layer.runs, layer.paragraphs, layer.frame, layer.transform ?? null, layer.antiAlias ?? true, ...layer.orientation === "vertical" ? ["vertical"] : []]);
8605
8617
  }
8606
- function styleSheetData(s, font) {
8618
+ function styleSheetData(s, font, vertical = false) {
8607
8619
  const [r, g, b] = [1, 3, 5].map((i) => parseInt(s.color.slice(i, i + 2), 16) / 255);
8608
8620
  return E.dict([
8609
8621
  ["Font", E.int(font)],
@@ -8621,6 +8633,7 @@ function styleSheetData(s, font) {
8621
8633
  ["FontCaps", E.int(0)],
8622
8634
  ["Underline", E.bool(Boolean(s.underline))],
8623
8635
  ["Strikethrough", E.bool(Boolean(s.strikethrough))],
8636
+ ["BaselineDirection", E.int(vertical && s.upright ? 1 : 2)],
8624
8637
  ["Ligatures", E.bool(true)],
8625
8638
  ["DLigatures", E.bool(false)],
8626
8639
  ["FillFlag", E.bool(true)],
@@ -8668,10 +8681,11 @@ function buildEngineData(layer, template) {
8668
8681
  return fonts.length - 1;
8669
8682
  };
8670
8683
  const runs = fitRuns(layer.runs, layer.text.length, DEFAULT_TEXT_STYLE);
8684
+ const vertical = layer.orientation === "vertical";
8671
8685
  const sruns = [];
8672
8686
  const slens = [];
8673
8687
  runs.forEach((r, i) => {
8674
- sruns.push(E.dict([["StyleSheet", E.dict([["StyleSheetData", styleSheetData(r.style, fontIndex(postScriptFor(r.style)))]])]]));
8688
+ sruns.push(E.dict([["StyleSheet", E.dict([["StyleSheetData", styleSheetData(r.style, fontIndex(postScriptFor(r.style)), vertical)]])]]));
8675
8689
  slens.push(E.int(r.length + (i === runs.length - 1 ? 1 : 0)));
8676
8690
  });
8677
8691
  if (!runs.length) {
@@ -8718,10 +8732,10 @@ function buildEngineData(layer, template) {
8718
8732
  const child = E.dict([
8719
8733
  ["ShapeType", E.int(shapeType)],
8720
8734
  ["Procession", E.int(0)],
8721
- ["Lines", E.dict([["WritingDirection", E.int(0)], ["Children", E.array([])]])],
8735
+ ["Lines", E.dict([["WritingDirection", E.int(vertical ? 2 : 0)], ["Children", E.array([])]])],
8722
8736
  ["Cookie", E.dict([["Photoshop", photoshop]])]
8723
8737
  ]);
8724
- eSet(dict, "Rendered", E.dict([["Version", E.int(1)], ["Shapes", E.dict([["WritingDirection", E.int(0)], ["Children", E.array([child])]])]]));
8738
+ eSet(dict, "Rendered", E.dict([["Version", E.int(1)], ["Shapes", E.dict([["WritingDirection", E.int(vertical ? 2 : 0)], ["Children", E.array([child])]])]]));
8725
8739
  const fontSet = E.array(fonts.map(fontEntry));
8726
8740
  for (const key of ["ResourceDict", "DocumentResources"]) {
8727
8741
  let rd = eGet(e, key);
@@ -8776,7 +8790,7 @@ function tyShForLayer(layer) {
8776
8790
  };
8777
8791
  set("Txt ", { type: "TEXT", value: layer.text.replace(/\n/g, "\r") });
8778
8792
  if (!descGet(text3, "textGridding")) set("textGridding", { type: "enum", typeId: "textGridding", value: "None" });
8779
- set("Ornt", { type: "enum", typeId: "Ornt", value: "Hrzn" });
8793
+ set("Ornt", { type: "enum", typeId: "Ornt", value: layer.orientation === "vertical" ? "Vrtc" : "Hrzn" });
8780
8794
  set("AntA", { type: "enum", typeId: "Annt", value: layer.antiAlias === false ? "Anno" : "AnSm" });
8781
8795
  const [l, t, r, b] = inkBounds2(layer);
8782
8796
  const rect = (cls) => ({
@@ -10594,6 +10608,7 @@ function importType(ctx, rec, name, notes, props) {
10594
10608
  rendering: "stored"
10595
10609
  };
10596
10610
  if (!data.antiAlias) layer.antiAlias = false;
10611
+ if (data.orientation === "vertical") layer.orientation = "vertical";
10597
10612
  layer.psd = { tySh: block.data.slice(), signature: typeSignature(layer) };
10598
10613
  const fill = u8Block(rec, "iOpa");
10599
10614
  if (fill !== void 0 && fill < 255) layer.fillOpacity = percent(fill);
@@ -12141,6 +12156,7 @@ export {
12141
12156
  removeAlpha,
12142
12157
  flattenPath,
12143
12158
  rasterizeCoverage,
12159
+ TEXT_ORIENTATIONS,
12144
12160
  TEXT_ALIGNS,
12145
12161
  MAX_EFFECT_ANIMATION_MS,
12146
12162
  SHAPE_OPERATIONS,
@@ -12246,6 +12262,7 @@ export {
12246
12262
  requireTypeLayer,
12247
12263
  normalizeText,
12248
12264
  typeLayerName,
12265
+ textOrientation,
12249
12266
  createTypeLayer,
12250
12267
  updateTypeLayer,
12251
12268
  replaceTextRange,
@@ -12,7 +12,7 @@ import { type RefineEdgeParams } from './selection/refine.cjs';
12
12
  import { type LayerMaskProps } from './selection/layer-mask.cjs';
13
13
  import { type AlphaChannel } from './selection/channels.cjs';
14
14
  import type { AdjustmentLayerMask } from './types.cjs';
15
- import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TypeLayer, type ImageEffectAnimation } from './layers/model.cjs';
15
+ import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TextOrientation, type TypeLayer, type ImageEffectAnimation } from './layers/model.cjs';
16
16
  import { type TextLayout, type TextShaper } from './layers/text.cjs';
17
17
  import type { VectorSubpath } from '../vector/types.cjs';
18
18
  import { EffectsSlot } from '../effects/helpers.cjs';
@@ -282,8 +282,6 @@ export interface ShapeLayerInfo {
282
282
  /** A new type layer: its text, where it goes, and its character and paragraph style. */
283
283
  /** A Type Mask: text laid out like `TextLayerInit` (no layer is made), selected by `mode`. */
284
284
  export interface TypeMaskInit extends Omit<TextLayerInit, keyof LayerPlacement | 'name' | 'opacity' | 'blend'> {
285
- /** Horizontal (default) or vertical: the characters stacked top to bottom, centred on `x`. */
286
- orientation?: 'horizontal' | 'vertical';
287
285
  mode?: SelectionMode;
288
286
  /** Feather radius in px (default 0). */
289
287
  feather?: number;
@@ -304,6 +302,11 @@ export interface TextLayerInit extends LayerPlacement {
304
302
  width: number;
305
303
  height: number;
306
304
  };
305
+ /**
306
+ * Default `horizontal`. `vertical`: columns running down, right to left (Photoshop's Vertical Type
307
+ * tool); (`x`, `y`) is then the top of the first column's centre line, or the box's top-left.
308
+ */
309
+ orientation?: TextOrientation;
307
310
  /** Character style of the whole text (default 24 px sans-serif, the foreground colour). */
308
311
  style?: Partial<TextStyle>;
309
312
  paragraph?: ParagraphStyle;
@@ -329,9 +332,10 @@ export interface TypeLayerInfo {
329
332
  style: ParagraphStyle;
330
333
  }>;
331
334
  frame: TypeLayer['frame'];
335
+ orientation: TextOrientation;
332
336
  /** Text space → document. */
333
337
  transform: AffineMatrix;
334
- /** Laid-out lines: text range, baseline (text space) and width; empty without a shaper. */
338
+ /** Laid-out lines (vertical text: columns): text range, baseline (line space: the column's centre for vertical text) and length; empty without a shaper. */
335
339
  lines: Array<{
336
340
  start: number;
337
341
  end: number;
@@ -1817,8 +1821,8 @@ export declare class ImageEditorController {
1817
1821
  /**
1818
1822
  * Type Mask (horizontal or vertical): a selection in the shape of `text`, laid out and drawn as
1819
1823
  * `addTextLayer` would place it (font, size, tracking, box, rotation), combined with the current
1820
- * selection by `mode`. Vertical type stacks the characters top to bottom, centred on `x`. No
1821
- * layer is added. One undo step. Returns whether anything is selected.
1824
+ * selection by `mode`. Vertical type is laid out as vertical type layers are: columns down from
1825
+ * (`x`, `y`), right to left, CJK upright and Latin turned. No layer is added. One undo step. Returns whether anything is selected.
1822
1826
  */
1823
1827
  selectTextShape(init: TypeMaskInit): boolean;
1824
1828
  /** A type layer laid out and drawn from `init` (not added): the shared part of `addTextLayer` and the type masks. */
@@ -1853,6 +1857,12 @@ export declare class ImageEditorController {
1853
1857
  width: number;
1854
1858
  height: number;
1855
1859
  }, commitToHistory?: boolean): TypeLayerInfo;
1860
+ /**
1861
+ * Horizontal ↔ vertical type (Type › Orientation): the text, styles, frame and origin stay; the
1862
+ * lines become columns running down, stacked right to left, or back. One undo step. Returns the
1863
+ * layer.
1864
+ */
1865
+ setTextOrientation(id: string, orientation: TextOrientation, commitToHistory?: boolean): TypeLayerInfo;
1856
1866
  /**
1857
1867
  * Convert to Shape: the text's glyph outlines become a shape layer with the same id, name and
1858
1868
  * layer properties (needs a shaper that gives outlines). The fill is the first run's colour;
@@ -12,7 +12,7 @@ import { type RefineEdgeParams } from './selection/refine.js';
12
12
  import { type LayerMaskProps } from './selection/layer-mask.js';
13
13
  import { type AlphaChannel } from './selection/channels.js';
14
14
  import type { AdjustmentLayerMask } from './types.js';
15
- import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TypeLayer, type ImageEffectAnimation } from './layers/model.js';
15
+ import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TextOrientation, type TypeLayer, type ImageEffectAnimation } from './layers/model.js';
16
16
  import { type TextLayout, type TextShaper } from './layers/text.js';
17
17
  import type { VectorSubpath } from '../vector/types.js';
18
18
  import { EffectsSlot } from '../effects/helpers.js';
@@ -282,8 +282,6 @@ export interface ShapeLayerInfo {
282
282
  /** A new type layer: its text, where it goes, and its character and paragraph style. */
283
283
  /** A Type Mask: text laid out like `TextLayerInit` (no layer is made), selected by `mode`. */
284
284
  export interface TypeMaskInit extends Omit<TextLayerInit, keyof LayerPlacement | 'name' | 'opacity' | 'blend'> {
285
- /** Horizontal (default) or vertical: the characters stacked top to bottom, centred on `x`. */
286
- orientation?: 'horizontal' | 'vertical';
287
285
  mode?: SelectionMode;
288
286
  /** Feather radius in px (default 0). */
289
287
  feather?: number;
@@ -304,6 +302,11 @@ export interface TextLayerInit extends LayerPlacement {
304
302
  width: number;
305
303
  height: number;
306
304
  };
305
+ /**
306
+ * Default `horizontal`. `vertical`: columns running down, right to left (Photoshop's Vertical Type
307
+ * tool); (`x`, `y`) is then the top of the first column's centre line, or the box's top-left.
308
+ */
309
+ orientation?: TextOrientation;
307
310
  /** Character style of the whole text (default 24 px sans-serif, the foreground colour). */
308
311
  style?: Partial<TextStyle>;
309
312
  paragraph?: ParagraphStyle;
@@ -329,9 +332,10 @@ export interface TypeLayerInfo {
329
332
  style: ParagraphStyle;
330
333
  }>;
331
334
  frame: TypeLayer['frame'];
335
+ orientation: TextOrientation;
332
336
  /** Text space → document. */
333
337
  transform: AffineMatrix;
334
- /** Laid-out lines: text range, baseline (text space) and width; empty without a shaper. */
338
+ /** Laid-out lines (vertical text: columns): text range, baseline (line space: the column's centre for vertical text) and length; empty without a shaper. */
335
339
  lines: Array<{
336
340
  start: number;
337
341
  end: number;
@@ -1817,8 +1821,8 @@ export declare class ImageEditorController {
1817
1821
  /**
1818
1822
  * Type Mask (horizontal or vertical): a selection in the shape of `text`, laid out and drawn as
1819
1823
  * `addTextLayer` would place it (font, size, tracking, box, rotation), combined with the current
1820
- * selection by `mode`. Vertical type stacks the characters top to bottom, centred on `x`. No
1821
- * layer is added. One undo step. Returns whether anything is selected.
1824
+ * selection by `mode`. Vertical type is laid out as vertical type layers are: columns down from
1825
+ * (`x`, `y`), right to left, CJK upright and Latin turned. No layer is added. One undo step. Returns whether anything is selected.
1822
1826
  */
1823
1827
  selectTextShape(init: TypeMaskInit): boolean;
1824
1828
  /** A type layer laid out and drawn from `init` (not added): the shared part of `addTextLayer` and the type masks. */
@@ -1853,6 +1857,12 @@ export declare class ImageEditorController {
1853
1857
  width: number;
1854
1858
  height: number;
1855
1859
  }, commitToHistory?: boolean): TypeLayerInfo;
1860
+ /**
1861
+ * Horizontal ↔ vertical type (Type › Orientation): the text, styles, frame and origin stay; the
1862
+ * lines become columns running down, stacked right to left, or back. One undo step. Returns the
1863
+ * layer.
1864
+ */
1865
+ setTextOrientation(id: string, orientation: TextOrientation, commitToHistory?: boolean): TypeLayerInfo;
1856
1866
  /**
1857
1867
  * Convert to Shape: the text's glyph outlines become a shape layer with the same id, name and
1858
1868
  * layer properties (needs a shaper that gives outlines). The fill is the first run's colour;