@pixerate/editor 0.191.0 → 0.193.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation-editor/index.cjs +56 -56
- package/dist/animation-editor/index.js +2 -2
- package/dist/{chunk-ZQGZWFPU.cjs → chunk-3T5VCDIV.cjs} +97 -5
- package/dist/{chunk-RQ2EM5TY.js → chunk-5OC22IEO.js} +97 -5
- package/dist/{chunk-EGZM52EO.cjs → chunk-BRKVNXDZ.cjs} +354 -125
- package/dist/{chunk-XCDGM7TD.cjs → chunk-FR2Q2LVF.cjs} +35 -35
- package/dist/{chunk-LPIKXPBM.js → chunk-GHCXLQ47.js} +262 -33
- package/dist/{chunk-VRXXAXUT.js → chunk-MLC7HUHO.js} +1 -1
- package/dist/{chunk-2UR4OX3B.cjs → chunk-UYXL6ZWQ.cjs} +114 -97
- package/dist/{chunk-TXYXF32N.js → chunk-ZIDUPSP4.js} +35 -18
- package/dist/image-editor/controller.d.cts +31 -2
- package/dist/image-editor/controller.d.ts +31 -2
- package/dist/image-editor/index.cjs +1064 -923
- package/dist/image-editor/index.js +204 -63
- package/dist/image-editor/layers/model.d.cts +7 -2
- package/dist/image-editor/layers/model.d.ts +7 -2
- package/dist/image-editor/layers/serialize.d.cts +2 -0
- package/dist/image-editor/layers/serialize.d.ts +2 -0
- package/dist/image-editor/layers/shape-type-ui.d.cts +8 -2
- package/dist/image-editor/layers/shape-type-ui.d.ts +8 -2
- package/dist/image-editor/layers/text.d.cts +6 -2
- package/dist/image-editor/layers/text.d.ts +6 -2
- package/dist/image-editor/psd/type.d.cts +2 -1
- package/dist/image-editor/psd/type.d.ts +2 -1
- package/dist/image-editor/selection/tool-options.d.cts +7 -1
- package/dist/image-editor/selection/tool-options.d.ts +7 -1
- package/dist/image-editor/tools.d.cts +1 -1
- package/dist/image-editor/tools.d.ts +1 -1
- package/dist/image-editor/type-tools.d.cts +2 -2
- package/dist/image-editor/type-tools.d.ts +2 -2
- package/dist/paint-editor/index.cjs +7 -7
- package/dist/paint-editor/index.js +2 -2
- package/dist/text/layout.d.cts +93 -12
- package/dist/text/layout.d.ts +93 -12
- package/dist/text/opentype.d.cts +13 -3
- package/dist/text/opentype.d.ts +13 -3
- package/dist/text/shapers.d.cts +12 -0
- package/dist/text/shapers.d.ts +12 -0
- package/dist/text/types.d.cts +36 -2
- package/dist/text/types.d.ts +36 -2
- package/dist/vector/controller.d.cts +8 -0
- package/dist/vector/controller.d.ts +8 -0
- package/dist/vector/index.cjs +1039 -967
- package/dist/vector/index.js +90 -18
- package/dist/vector/text.d.cts +5 -3
- package/dist/vector/text.d.ts +5 -3
- package/dist/vector/textPanel.d.cts +6 -0
- package/dist/vector/textPanel.d.ts +6 -0
- package/dist/vector/types.d.cts +9 -0
- package/dist/vector/types.d.ts +9 -0
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +4 -2
- package/skills/pixerate-image-editor/references/shapes-and-type.md +19 -5
- package/skills/pixerate-image-selection-masking/SKILL.md +2 -0
- package/skills/pixerate-vector/SKILL.md +9 -3
|
@@ -4435,6 +4435,40 @@ var isBreakAfter = (c) => c === " " || c === " " || c === "-" || c === "\u2010"
|
|
|
4435
4435
|
var isTextSpace = (c) => c === " " || c === " ";
|
|
4436
4436
|
var isSpace = isTextSpace;
|
|
4437
4437
|
var isWide = (code) => code >= 11904 && code <= 40959 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65280 && code <= 65519;
|
|
4438
|
+
var isHigh = (code) => code >= 55296 && code <= 56319;
|
|
4439
|
+
var isLow = (code) => code >= 56320 && code <= 57343;
|
|
4440
|
+
var TR = /* @__PURE__ */ new Set([8229, 8230, 12296, 12297, 12298, 12299, 12300, 12301, 12302, 12303, 12304, 12305, 12308, 12309, 12310, 12311, 12312, 12313, 12314, 12315, 12316, 12336, 12448, 12540, 65288, 65289, 65293, 65306, 65307, 65309, 65339, 65341, 65343, 65371, 65372, 65373, 65374]);
|
|
4441
|
+
var TU = /* @__PURE__ */ new Set([12289, 12290, 65292, 65294, 12353, 12355, 12357, 12359, 12361, 12387, 12419, 12421, 12423, 12430, 12437, 12438, 12449, 12451, 12453, 12455, 12457, 12483, 12515, 12517, 12519, 12526, 12533, 12534]);
|
|
4442
|
+
var CORNER = /* @__PURE__ */ new Set([12289, 12290, 65292, 65294]);
|
|
4443
|
+
function verticalOrientationOf(cp) {
|
|
4444
|
+
if (TR.has(cp)) return "Tr";
|
|
4445
|
+
if (TU.has(cp) || cp >= 12784 && cp <= 12799) return "Tu";
|
|
4446
|
+
if (cp >= 4352 && cp <= 4607 || cp >= 11904 && cp <= 19903 || cp >= 19968 && cp <= 40959 || cp >= 40960 && cp <= 42191 || cp >= 43360 && cp <= 43391 || cp >= 44032 && cp <= 55295 || cp >= 63744 && cp <= 64255 || cp >= 65040 && cp <= 65055 || cp >= 65072 && cp <= 65103 || cp >= 65280 && cp <= 65376 || cp >= 65504 && cp <= 65511 || cp >= 126976 && cp <= 129791 || cp >= 131072 && cp <= 262143)
|
|
4447
|
+
return "U";
|
|
4448
|
+
return "R";
|
|
4449
|
+
}
|
|
4450
|
+
var applyMatrix2 = (m, x, y) => m ? { x: m[0] * x + m[2] * y + m[4], y: m[1] * x + m[3] * y + m[5] } : { x, y };
|
|
4451
|
+
function invertMatrix2(m) {
|
|
4452
|
+
const [a, b, c, d, e, f] = m;
|
|
4453
|
+
const det = a * d - b * c || 1;
|
|
4454
|
+
return [d / det, -b / det, -c / det, a / det, (c * f - d * e) / det, (b * e - a * f) / det];
|
|
4455
|
+
}
|
|
4456
|
+
function transformTextRect(m, r) {
|
|
4457
|
+
if (!m) return r;
|
|
4458
|
+
const pts = [applyMatrix2(m, r.x, r.y), applyMatrix2(m, r.x + r.width, r.y), applyMatrix2(m, r.x + r.width, r.y + r.height), applyMatrix2(m, r.x, r.y + r.height)];
|
|
4459
|
+
const x0 = Math.min(...pts.map((p) => p.x));
|
|
4460
|
+
const y0 = Math.min(...pts.map((p) => p.y));
|
|
4461
|
+
return { x: x0, y: y0, width: Math.max(...pts.map((p) => p.x)) - x0, height: Math.max(...pts.map((p) => p.y)) - y0 };
|
|
4462
|
+
}
|
|
4463
|
+
function lineToTextSpace(layout, p) {
|
|
4464
|
+
return applyMatrix2(layout.matrix, p.x, p.y);
|
|
4465
|
+
}
|
|
4466
|
+
function textToLineSpace(layout, p) {
|
|
4467
|
+
return layout.matrix ? applyMatrix2(invertMatrix2(layout.matrix), p.x, p.y) : { x: p.x, y: p.y };
|
|
4468
|
+
}
|
|
4469
|
+
function textLineRect(l) {
|
|
4470
|
+
return transformTextRect(l.matrix, { x: l.x, y: l.baseline - l.ascent, width: l.width, height: l.ascent + l.descent });
|
|
4471
|
+
}
|
|
4438
4472
|
var layoutCache = /* @__PURE__ */ new WeakMap();
|
|
4439
4473
|
function layoutText(layer, shaper) {
|
|
4440
4474
|
let byShaper = layoutCache.get(layer);
|
|
@@ -4450,6 +4484,7 @@ function layoutText(layer, shaper) {
|
|
|
4450
4484
|
}
|
|
4451
4485
|
function computeLayout(layer, shaper) {
|
|
4452
4486
|
const { text, frame } = layer;
|
|
4487
|
+
const vertical = layer.orientation === "vertical";
|
|
4453
4488
|
const runs = fitRuns(layer.runs, text.length, DEFAULT_TEXT_STYLE);
|
|
4454
4489
|
const missing = /* @__PURE__ */ new Set();
|
|
4455
4490
|
const resolved = /* @__PURE__ */ new Map();
|
|
@@ -4465,7 +4500,15 @@ function computeLayout(layer, shaper) {
|
|
|
4465
4500
|
return fam;
|
|
4466
4501
|
};
|
|
4467
4502
|
const resolvedFont = (style) => ({ ...fontOf(style), family: familyOf(fontOf(style)) });
|
|
4503
|
+
const verticalOf = (ch, style) => {
|
|
4504
|
+
const font = resolvedFont(style);
|
|
4505
|
+
if (shaper.vertical) return shaper.vertical(ch, font);
|
|
4506
|
+
const m = shaper.metrics(font);
|
|
4507
|
+
return { advance: m.ascent + m.descent, originY: m.ascent, alternate: false };
|
|
4508
|
+
};
|
|
4468
4509
|
const box = frame.kind === "box" ? frame : null;
|
|
4510
|
+
const inlineSize = box ? vertical ? box.height : box.width : 0;
|
|
4511
|
+
const matrix = vertical ? [0, 1, -1, 0, box ? box.width : 0, 0] : void 0;
|
|
4469
4512
|
const lines = [];
|
|
4470
4513
|
let y = 0;
|
|
4471
4514
|
let first = true;
|
|
@@ -4483,7 +4526,38 @@ function computeLayout(layer, shaper) {
|
|
|
4483
4526
|
const seg = text.slice(i, j);
|
|
4484
4527
|
const carets = shaper.measure(seg, resolvedFont(style));
|
|
4485
4528
|
const track = (style.tracking ?? 0) / 1e3 * style.fontSize;
|
|
4486
|
-
for (let k = 0; k < seg.length; k++)
|
|
4529
|
+
for (let k = 0; k < seg.length; k++) {
|
|
4530
|
+
const index = i + k;
|
|
4531
|
+
const advance = (carets[k + 1] ?? 0) - (carets[k] ?? 0);
|
|
4532
|
+
if (!vertical) {
|
|
4533
|
+
chars.push({ index, ch: seg[k], advance: advance + track, style });
|
|
4534
|
+
continue;
|
|
4535
|
+
}
|
|
4536
|
+
const code = seg.charCodeAt(k);
|
|
4537
|
+
if (isLow(code) && index > 0 && isHigh(text.charCodeAt(index - 1))) {
|
|
4538
|
+
const prev = chars[chars.length - 1];
|
|
4539
|
+
if (prev?.up) {
|
|
4540
|
+
chars.push({ index, ch: seg[k], advance: 0, style, up: prev.up, low: true });
|
|
4541
|
+
} else chars.push({ index, ch: seg[k], advance, style, low: true });
|
|
4542
|
+
continue;
|
|
4543
|
+
}
|
|
4544
|
+
const cp = text.codePointAt(index) ?? code;
|
|
4545
|
+
const kind = verticalOrientationOf(cp);
|
|
4546
|
+
let upright = kind === "U" || kind === "Tu" || kind === "R" && Boolean(style.upright) && !isSpace(seg[k]) && seg[k] !== "\n";
|
|
4547
|
+
let vm = null;
|
|
4548
|
+
const char = cp > 65535 ? text.slice(index, index + 2) : seg[k];
|
|
4549
|
+
if (upright || kind === "Tr") {
|
|
4550
|
+
vm = verticalOf(char, style);
|
|
4551
|
+
if (kind === "Tr") upright = vm.alternate;
|
|
4552
|
+
}
|
|
4553
|
+
if (!upright || !vm) {
|
|
4554
|
+
chars.push({ index, ch: seg[k], advance: advance + track, style });
|
|
4555
|
+
continue;
|
|
4556
|
+
}
|
|
4557
|
+
const corner = kind === "Tu" && !vm.alternate && CORNER.has(cp) ? style.fontSize * 0.55 : 0;
|
|
4558
|
+
const alone = shaper.measure(char, resolvedFont(style));
|
|
4559
|
+
chars.push({ index, ch: seg[k], advance: vm.advance + track, style, up: { width: alone[alone.length - 1] ?? advance, originY: vm.originY, vert: vm.alternate, dx: corner, dy: -corner } });
|
|
4560
|
+
}
|
|
4487
4561
|
i = j;
|
|
4488
4562
|
}
|
|
4489
4563
|
const startIndent = pstyle.startIndent ?? 0;
|
|
@@ -4494,13 +4568,13 @@ function computeLayout(layer, shaper) {
|
|
|
4494
4568
|
else {
|
|
4495
4569
|
let s = 0;
|
|
4496
4570
|
while (s < chars.length) {
|
|
4497
|
-
const avail =
|
|
4571
|
+
const avail = inlineSize - startIndent - endIndent - (breaks.length === 0 ? firstIndent : 0);
|
|
4498
4572
|
let w = 0;
|
|
4499
4573
|
let e = s;
|
|
4500
4574
|
let lastBreak = -1;
|
|
4501
4575
|
while (e < chars.length) {
|
|
4502
4576
|
const c = chars[e];
|
|
4503
|
-
if (!isSpace(c.ch) && w + c.advance > avail && e > s) break;
|
|
4577
|
+
if (!isSpace(c.ch) && w + c.advance > avail && e > s && !c.low) break;
|
|
4504
4578
|
w += c.advance;
|
|
4505
4579
|
if (isBreakAfter(c.ch) || isWide(c.ch.charCodeAt(0)) || e + 1 < chars.length && isWide(chars[e + 1].ch.charCodeAt(0))) lastBreak = e + 1;
|
|
4506
4580
|
e++;
|
|
@@ -4521,9 +4595,14 @@ function computeLayout(layer, shaper) {
|
|
|
4521
4595
|
let descent = 0;
|
|
4522
4596
|
let leading = 0;
|
|
4523
4597
|
for (const st of styles) {
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4598
|
+
if (vertical) {
|
|
4599
|
+
ascent = Math.max(ascent, st.fontSize / 2);
|
|
4600
|
+
descent = ascent;
|
|
4601
|
+
} else {
|
|
4602
|
+
const m = shaper.metrics(resolvedFont(st));
|
|
4603
|
+
ascent = Math.max(ascent, m.ascent);
|
|
4604
|
+
descent = Math.max(descent, m.descent);
|
|
4605
|
+
}
|
|
4527
4606
|
leading = Math.max(leading, st.leading ?? autoLeading * st.fontSize);
|
|
4528
4607
|
}
|
|
4529
4608
|
if (first) {
|
|
@@ -4537,13 +4616,13 @@ function computeLayout(layer, shaper) {
|
|
|
4537
4616
|
let extraPerSpace = 0;
|
|
4538
4617
|
if (box && align === "justify" && !lastLine) {
|
|
4539
4618
|
const spaces = lineChars.slice(0, visibleEnd).filter((c) => isSpace(c.ch)).length;
|
|
4540
|
-
const avail =
|
|
4619
|
+
const avail = inlineSize - startIndent - endIndent - (li === 0 ? firstIndent : 0);
|
|
4541
4620
|
if (spaces > 0 && avail > width) extraPerSpace = (avail - width) / spaces;
|
|
4542
4621
|
}
|
|
4543
4622
|
let x0;
|
|
4544
4623
|
const fullWidth = width + extraPerSpace * lineChars.slice(0, visibleEnd).filter((c) => isSpace(c.ch)).length;
|
|
4545
4624
|
if (box) {
|
|
4546
|
-
const avail =
|
|
4625
|
+
const avail = inlineSize - startIndent - endIndent - (li === 0 ? firstIndent : 0);
|
|
4547
4626
|
x0 = indent + (align === "center" ? (avail - fullWidth) / 2 : align === "right" ? avail - fullWidth : 0);
|
|
4548
4627
|
} else x0 = indent + (align === "center" ? -fullWidth / 2 : align === "right" ? -fullWidth : 0);
|
|
4549
4628
|
const laid = [];
|
|
@@ -4557,27 +4636,57 @@ function computeLayout(layer, shaper) {
|
|
|
4557
4636
|
let k = 0;
|
|
4558
4637
|
while (k < lineChars.length) {
|
|
4559
4638
|
const st = lineChars[k].style;
|
|
4639
|
+
const up = Boolean(lineChars[k].up);
|
|
4560
4640
|
let m = k + 1;
|
|
4561
|
-
while (m < lineChars.length && lineChars[m].style === st) m++;
|
|
4641
|
+
while (m < lineChars.length && lineChars[m].style === st && Boolean(lineChars[m].up) === up) m++;
|
|
4562
4642
|
const font = resolvedFont(st);
|
|
4563
4643
|
const fx = laid[k].x;
|
|
4564
4644
|
const carets = [0];
|
|
4565
4645
|
for (let q = k; q < m; q++) carets.push(laid[q].x + laid[q].advance - fx);
|
|
4566
|
-
|
|
4646
|
+
const shift = st.baselineShift ?? 0;
|
|
4647
|
+
let fy = y - shift;
|
|
4648
|
+
if (vertical && !up) {
|
|
4649
|
+
const hm = shaper.metrics(font);
|
|
4650
|
+
fy = y + (hm.ascent - hm.descent) / 2 - shift;
|
|
4651
|
+
}
|
|
4652
|
+
const fragment = {
|
|
4567
4653
|
start: lineChars[k].index,
|
|
4568
4654
|
end: lineChars[m - 1].index + 1,
|
|
4569
4655
|
style: st,
|
|
4570
4656
|
font,
|
|
4571
4657
|
family: font.family,
|
|
4572
4658
|
x: fx,
|
|
4573
|
-
y:
|
|
4659
|
+
y: fy,
|
|
4574
4660
|
carets,
|
|
4575
|
-
spaced: Boolean(st.tracking) || extraPerSpace > 0
|
|
4576
|
-
}
|
|
4661
|
+
spaced: Boolean(st.tracking) || extraPerSpace > 0 || up
|
|
4662
|
+
};
|
|
4663
|
+
if (vertical) {
|
|
4664
|
+
const glyphs = [];
|
|
4665
|
+
for (let q = k; q < m; q++) {
|
|
4666
|
+
const c = lineChars[q];
|
|
4667
|
+
const lc = laid[q];
|
|
4668
|
+
if (c.low && q > k) {
|
|
4669
|
+
const g = glyphs[glyphs.length - 1];
|
|
4670
|
+
g.length = 2;
|
|
4671
|
+
if (!up) g.advance += lc.advance;
|
|
4672
|
+
continue;
|
|
4673
|
+
}
|
|
4674
|
+
const p = applyMatrix2(matrix, lc.x, fy);
|
|
4675
|
+
if (c.up) {
|
|
4676
|
+
const u = c.up;
|
|
4677
|
+
glyphs.push({ index: c.index, length: 1, x: p.x - u.width / 2 + u.dx, y: p.y + u.originY + u.dy, angle: 0, advance: u.width, ascent: u.originY + u.dy, descent: lc.advance - u.originY - u.dy, vert: u.vert });
|
|
4678
|
+
} else glyphs.push({ index: c.index, length: 1, x: p.x, y: p.y, angle: 90, advance: lc.advance, ascent: fy - (y - ascent), descent: y + descent - fy, vert: false });
|
|
4679
|
+
}
|
|
4680
|
+
fragment.upright = up;
|
|
4681
|
+
fragment.glyphs = glyphs;
|
|
4682
|
+
}
|
|
4683
|
+
fragments.push(fragment);
|
|
4577
4684
|
k = m;
|
|
4578
4685
|
}
|
|
4579
4686
|
const lineStart = lineChars.length ? lineChars[0].index : para.start;
|
|
4580
|
-
|
|
4687
|
+
const line = { start: lineStart, end: lineChars.length ? lineChars[lineChars.length - 1].index + 1 : para.start, baseline: y, ascent, descent, x: x0, width, chars: laid, fragments };
|
|
4688
|
+
if (matrix) line.matrix = matrix;
|
|
4689
|
+
lines.push(line);
|
|
4581
4690
|
});
|
|
4582
4691
|
y += pstyle.spaceAfter ?? 0;
|
|
4583
4692
|
});
|
|
@@ -4594,12 +4703,15 @@ function computeLayout(layer, shaper) {
|
|
|
4594
4703
|
const top = lines.length ? lines[0].baseline - lines[0].ascent : 0;
|
|
4595
4704
|
const last = lines[lines.length - 1];
|
|
4596
4705
|
const bottom = last ? last.baseline + last.descent : 0;
|
|
4597
|
-
bounds = { x: x0, y: top, width: Math.max(0, x1 - x0), height: Math.max(0, bottom - top) };
|
|
4706
|
+
bounds = transformTextRect(matrix, { x: x0, y: top, width: Math.max(0, x1 - x0), height: Math.max(0, bottom - top) });
|
|
4598
4707
|
}
|
|
4599
|
-
|
|
4708
|
+
const out = { lines, bounds, missingFonts: [...missing], orientation: vertical ? "vertical" : "horizontal" };
|
|
4709
|
+
if (matrix) out.matrix = matrix;
|
|
4710
|
+
return out;
|
|
4600
4711
|
}
|
|
4601
|
-
function textIndexAt(layout,
|
|
4712
|
+
function textIndexAt(layout, point) {
|
|
4602
4713
|
if (!layout.lines.length) return 0;
|
|
4714
|
+
const p = textToLineSpace(layout, point);
|
|
4603
4715
|
let line = layout.lines[layout.lines.length - 1];
|
|
4604
4716
|
for (const l of layout.lines) {
|
|
4605
4717
|
if (p.y <= l.baseline + l.descent) {
|
|
@@ -4614,23 +4726,60 @@ function textIndexAt(layout, p) {
|
|
|
4614
4726
|
}
|
|
4615
4727
|
function caretAt(layout, index) {
|
|
4616
4728
|
const lines = layout.lines;
|
|
4617
|
-
|
|
4729
|
+
const caret = (x, l2, line) => {
|
|
4730
|
+
const top = l2.baseline - l2.ascent;
|
|
4731
|
+
const bottom = l2.baseline + l2.descent;
|
|
4732
|
+
return { x, top, bottom, line, from: applyMatrix2(l2.matrix, x, top), to: applyMatrix2(l2.matrix, x, bottom) };
|
|
4733
|
+
};
|
|
4734
|
+
if (!lines.length) return { x: 0, top: 0, bottom: 0, line: 0, from: { x: 0, y: 0 }, to: { x: 0, y: 0 } };
|
|
4618
4735
|
for (let li = 0; li < lines.length; li++) {
|
|
4619
4736
|
const l2 = lines[li];
|
|
4620
4737
|
const next = lines[li + 1];
|
|
4621
4738
|
if (next && index >= next.start && (next.chars.length || index > l2.end || next.start > l2.end)) continue;
|
|
4622
4739
|
const c = l2.chars.find((ch) => ch.index === index);
|
|
4623
4740
|
const prev = l2.chars.find((ch) => ch.index === index - 1);
|
|
4624
|
-
|
|
4625
|
-
return { x, top: l2.baseline - l2.ascent, bottom: l2.baseline + l2.descent, line: li };
|
|
4741
|
+
return caret(c ? c.x : prev ? prev.x + prev.advance : l2.x, l2, li);
|
|
4626
4742
|
}
|
|
4627
4743
|
const l = lines[lines.length - 1];
|
|
4628
|
-
return
|
|
4744
|
+
return caret(l.x + l.width, l, lines.length - 1);
|
|
4745
|
+
}
|
|
4746
|
+
function textSelectionQuads(layout, start, end) {
|
|
4747
|
+
const out = [];
|
|
4748
|
+
if (end <= start) return out;
|
|
4749
|
+
for (const l of layout.lines) {
|
|
4750
|
+
const chars = l.chars.filter((c) => c.index >= start && c.index < end);
|
|
4751
|
+
if (!chars.length) continue;
|
|
4752
|
+
const x0 = chars[0].x;
|
|
4753
|
+
const x1 = chars[chars.length - 1].x + chars[chars.length - 1].advance;
|
|
4754
|
+
const top = l.baseline - l.ascent;
|
|
4755
|
+
const bottom = l.baseline + l.descent;
|
|
4756
|
+
out.push([applyMatrix2(l.matrix, x0, top), applyMatrix2(l.matrix, x1, top), applyMatrix2(l.matrix, x1, bottom), applyMatrix2(l.matrix, x0, bottom)]);
|
|
4757
|
+
}
|
|
4758
|
+
return out;
|
|
4759
|
+
}
|
|
4760
|
+
function textArrowStep(orientation, key) {
|
|
4761
|
+
if (orientation === "vertical") return key === "ArrowUp" ? "previous" : key === "ArrowDown" ? "next" : key === "ArrowRight" ? "line-before" : "line-after";
|
|
4762
|
+
return key === "ArrowLeft" ? "previous" : key === "ArrowRight" ? "next" : key === "ArrowUp" ? "line-before" : "line-after";
|
|
4763
|
+
}
|
|
4764
|
+
function textLineMove(layout, index, dir, length2) {
|
|
4765
|
+
if (!layout.lines.length) return dir < 0 ? 0 : length2;
|
|
4766
|
+
const c = caretAt(layout, index);
|
|
4767
|
+
const target = layout.lines[c.line + dir];
|
|
4768
|
+
if (!target) return dir < 0 ? 0 : length2;
|
|
4769
|
+
const hit = target.chars.find((ch) => c.x < ch.x + ch.advance / 2);
|
|
4770
|
+
const last = target.chars[target.chars.length - 1];
|
|
4771
|
+
return hit ? hit.index : last ? last.index + 1 : target.start;
|
|
4629
4772
|
}
|
|
4630
4773
|
function textDecorations(f, l) {
|
|
4631
4774
|
const out = [];
|
|
4632
4775
|
const t = Math.max(1, f.style.fontSize / 15);
|
|
4633
4776
|
const w = f.carets[f.carets.length - 1];
|
|
4777
|
+
if (l.matrix) {
|
|
4778
|
+
const centre = l.baseline - (f.style.baselineShift ?? 0);
|
|
4779
|
+
if (f.style.underline) out.push(transformTextRect(l.matrix, { x: f.x, y: centre - l.ascent, width: w, height: t }));
|
|
4780
|
+
if (f.style.strikethrough) out.push(transformTextRect(l.matrix, { x: f.x, y: centre - t / 2, width: w, height: t }));
|
|
4781
|
+
return out;
|
|
4782
|
+
}
|
|
4634
4783
|
if (f.style.underline) out.push({ x: f.x, y: f.y + Math.max(t, l.descent * 0.35), width: w, height: t });
|
|
4635
4784
|
if (f.style.strikethrough) out.push({ x: f.x, y: f.y - l.ascent * 0.3 - t / 2, width: w, height: t });
|
|
4636
4785
|
return out;
|
|
@@ -4642,7 +4791,25 @@ var textRectPath = (r) => [
|
|
|
4642
4791
|
{ type: "L", x: r.x, y: r.y + r.height },
|
|
4643
4792
|
{ type: "Z" }
|
|
4644
4793
|
];
|
|
4794
|
+
function turnPath(path, x, y) {
|
|
4795
|
+
return path.map((c) => {
|
|
4796
|
+
if (c.type === "Z") return c;
|
|
4797
|
+
if (c.type === "M" || c.type === "L") return { type: c.type, x: x - c.y, y: y + c.x };
|
|
4798
|
+
if (c.type === "Q") return { type: "Q", x1: x - c.y1, y1: y + c.x1, x: x - c.y, y: y + c.x };
|
|
4799
|
+
return { type: "C", x1: x - c.y1, y1: y + c.x1, x2: x - c.y2, y2: y + c.x2, x: x - c.y, y: y + c.x };
|
|
4800
|
+
});
|
|
4801
|
+
}
|
|
4645
4802
|
function textFragmentOutline(shaper, text, f) {
|
|
4803
|
+
if (f.glyphs) {
|
|
4804
|
+
const out2 = [];
|
|
4805
|
+
for (const g of f.glyphs) {
|
|
4806
|
+
const ch = text.slice(g.index, g.index + g.length);
|
|
4807
|
+
if (isSpace(ch)) continue;
|
|
4808
|
+
const opts = g.vert ? { vertical: true } : void 0;
|
|
4809
|
+
out2.push(...g.angle ? turnPath(shaper.outline(ch, f.font, 0, 0, opts), g.x, g.y) : shaper.outline(ch, f.font, g.x, g.y, opts));
|
|
4810
|
+
}
|
|
4811
|
+
return out2;
|
|
4812
|
+
}
|
|
4646
4813
|
const s = text.slice(f.start, f.end);
|
|
4647
4814
|
if (!f.spaced) return shaper.outline(s, f.font, f.x, f.y);
|
|
4648
4815
|
const out = [];
|
|
@@ -4652,6 +4819,28 @@ function textFragmentOutline(shaper, text, f) {
|
|
|
4652
4819
|
}
|
|
4653
4820
|
return out;
|
|
4654
4821
|
}
|
|
4822
|
+
function drawTextFragment(ctx, shaper, text, f) {
|
|
4823
|
+
if (f.glyphs) {
|
|
4824
|
+
for (const g of f.glyphs) {
|
|
4825
|
+
const ch = text.slice(g.index, g.index + g.length);
|
|
4826
|
+
if (isSpace(ch)) continue;
|
|
4827
|
+
const opts = g.vert ? { vertical: true } : void 0;
|
|
4828
|
+
if (!g.angle) {
|
|
4829
|
+
shaper.draw(ctx, ch, f.font, g.x, g.y, opts);
|
|
4830
|
+
continue;
|
|
4831
|
+
}
|
|
4832
|
+
ctx.save();
|
|
4833
|
+
ctx.translate(g.x, g.y);
|
|
4834
|
+
ctx.rotate(Math.PI / 2);
|
|
4835
|
+
shaper.draw(ctx, ch, f.font, 0, 0, opts);
|
|
4836
|
+
ctx.restore();
|
|
4837
|
+
}
|
|
4838
|
+
return;
|
|
4839
|
+
}
|
|
4840
|
+
const s = text.slice(f.start, f.end);
|
|
4841
|
+
if (!f.spaced) shaper.draw(ctx, s, f.font, f.x, f.y);
|
|
4842
|
+
else for (let k = 0; k < s.length; k++) if (!isSpace(s[k])) shaper.draw(ctx, s[k], f.font, f.x + f.carets[k], f.y);
|
|
4843
|
+
}
|
|
4655
4844
|
|
|
4656
4845
|
// src/text/shapers.ts
|
|
4657
4846
|
var GENERIC = /* @__PURE__ */ new Set(["serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded", "math", "emoji", "fangsong"]);
|
|
@@ -4797,8 +4986,15 @@ function createOutlineTextShaper(fonts, options = {}) {
|
|
|
4797
4986
|
for (let i = 1; i <= text.length; i++) out.push(face.getAdvanceWidth(text.slice(0, i), font.size, { kerning: true }));
|
|
4798
4987
|
return out;
|
|
4799
4988
|
},
|
|
4800
|
-
outline(text, font, x, y) {
|
|
4801
|
-
return toPath(pick(font).entry.face.getPath(text, x, y, font.size, { kerning: true }).commands);
|
|
4989
|
+
outline(text, font, x, y, opts) {
|
|
4990
|
+
return toPath(pick(font).entry.face.getPath(text, x, y, font.size, opts?.vertical ? { kerning: true, vertical: true } : { kerning: true }).commands);
|
|
4991
|
+
},
|
|
4992
|
+
vertical(char, font) {
|
|
4993
|
+
const { face } = pick(font).entry;
|
|
4994
|
+
const k = font.size / face.unitsPerEm;
|
|
4995
|
+
const v2 = face.getVerticalMetrics?.(char);
|
|
4996
|
+
if (v2) return { advance: v2.advance * k, originY: v2.originY * k, alternate: v2.alternate };
|
|
4997
|
+
return { advance: (face.ascender - face.descender) * k, originY: face.ascender * k, alternate: false };
|
|
4802
4998
|
},
|
|
4803
4999
|
families() {
|
|
4804
5000
|
return [...new Set(entries.map((e) => e.family))];
|
|
@@ -4860,7 +5056,7 @@ function vectorTextFamilies() {
|
|
|
4860
5056
|
return getVectorTextShaper().families?.() ?? ["sans-serif", "serif", "monospace"];
|
|
4861
5057
|
}
|
|
4862
5058
|
var DEFAULT_VECTOR_FONT = { fontFamily: "sans-serif", fontWeight: 400, fontSize: 24 };
|
|
4863
|
-
var VECTOR_CHARACTER_STYLE_KEYS = ["fontFamily", "fontWeight", "italic", "fontSize", "tracking", "leading", "baselineShift", "underline", "strikethrough"];
|
|
5059
|
+
var VECTOR_CHARACTER_STYLE_KEYS = ["fontFamily", "fontWeight", "italic", "fontSize", "tracking", "leading", "baselineShift", "underline", "strikethrough", "upright"];
|
|
4864
5060
|
var VECTOR_PARAGRAPH_STYLE_KEYS = ["align", "autoLeading", "firstLineIndent", "startIndent", "endIndent", "spaceBefore", "spaceAfter"];
|
|
4865
5061
|
var VectorTextError = class extends Error {
|
|
4866
5062
|
};
|
|
@@ -5005,7 +5201,8 @@ function textBlock(node) {
|
|
|
5005
5201
|
return { length: r.end - r.start, style: { ...r.style, color: `#${i}` } };
|
|
5006
5202
|
}),
|
|
5007
5203
|
paragraphs: [{ length: text.length, style: { ...node.paragraph ?? {}, ...node.frame.kind === "path" ? { align: "left" } : {} } }],
|
|
5008
|
-
frame: node.frame.kind === "area" ? { kind: "box", width: Math.max(0, node.frame.width), height: Math.max(0, node.frame.height) } : { kind: "point" }
|
|
5204
|
+
frame: node.frame.kind === "area" ? { kind: "box", width: Math.max(0, node.frame.width), height: Math.max(0, node.frame.height) } : { kind: "point" },
|
|
5205
|
+
...node.orientation === "vertical" && node.frame.kind !== "path" ? { orientation: "vertical" } : {}
|
|
5009
5206
|
};
|
|
5010
5207
|
}
|
|
5011
5208
|
var charAt = (text, i) => {
|
|
@@ -5039,13 +5236,21 @@ function computeLayout2(node, shaper) {
|
|
|
5039
5236
|
};
|
|
5040
5237
|
if (frame.kind !== "path") {
|
|
5041
5238
|
text.lines.forEach((l, li) => {
|
|
5042
|
-
if (frame.kind === "area" && li > 0 && l.baseline + l.descent > frame.height + 1e-6) {
|
|
5239
|
+
if (frame.kind === "area" && li > 0 && l.baseline + l.descent > (l.matrix ? frame.width : frame.height) + 1e-6) {
|
|
5043
5240
|
overflow = true;
|
|
5044
5241
|
return;
|
|
5045
5242
|
}
|
|
5046
|
-
|
|
5243
|
+
if (l.matrix) {
|
|
5244
|
+
const top = lineToTextSpace(text, { x: l.x, y: l.baseline });
|
|
5245
|
+
lines.push({ start: l.start, end: l.end, baseline: origin.x + top.x, x: origin.y + top.y, width: l.width });
|
|
5246
|
+
} else lines.push({ start: l.start, end: l.end, baseline: origin.y + l.baseline, x: origin.x + l.x, width: l.width });
|
|
5047
5247
|
for (const f of l.fragments) {
|
|
5048
5248
|
const from = glyphs.length;
|
|
5249
|
+
if (f.glyphs) {
|
|
5250
|
+
for (const g of f.glyphs) glyphs.push({ index: g.index, char: block.text.slice(g.index, g.index + g.length), x: origin.x + g.x, y: origin.y + g.y, angle: g.angle, advance: g.advance, ascent: g.ascent, descent: g.descent, fragment: fragments.length });
|
|
5251
|
+
fragments.push({ style: styleOf(f), font: f.font, ...fillOf(f) !== void 0 ? { fill: fillOf(f) } : {}, from, to: glyphs.length, line: lines.length - 1, laid: { fragment: f, line: l } });
|
|
5252
|
+
continue;
|
|
5253
|
+
}
|
|
5049
5254
|
for (let k = 0; k < f.end - f.start; k++) {
|
|
5050
5255
|
const index = f.start + k;
|
|
5051
5256
|
const ch = block.text[index];
|
|
@@ -5192,7 +5397,7 @@ function vectorTextPaths(node) {
|
|
|
5192
5397
|
const glyphs = layout.glyphs.slice(f.from, f.to);
|
|
5193
5398
|
if (!glyphs.length) return;
|
|
5194
5399
|
if (!outlines) {
|
|
5195
|
-
if (node.frame.kind === "path") {
|
|
5400
|
+
if (node.frame.kind === "path" || layout.text.orientation === "vertical") {
|
|
5196
5401
|
for (const g of glyphs) if (!isTextSpace(g.char)) out.push(...polygon2(glyphBox(g)));
|
|
5197
5402
|
} else {
|
|
5198
5403
|
const first = glyphs[0];
|
|
@@ -5234,7 +5439,7 @@ function polygon2(pts) {
|
|
|
5234
5439
|
return [{ type: "M", x: pts[0].x, y: pts[0].y }, ...pts.slice(1).map((p) => ({ type: "L", x: p.x, y: p.y })), { type: "Z" }];
|
|
5235
5440
|
}
|
|
5236
5441
|
function vectorTextPaintSource(node, fill, subpaths = []) {
|
|
5237
|
-
const { type: _t, text: _x, font: _f, runs: _r, paragraph: _p, frame: _fr, ...rest } = node;
|
|
5442
|
+
const { type: _t, text: _x, font: _f, runs: _r, paragraph: _p, frame: _fr, orientation: _o, ...rest } = node;
|
|
5238
5443
|
const out = { ...rest, type: "path", subpaths, fillRule: "nonzero" };
|
|
5239
5444
|
if (fill === void 0) return out;
|
|
5240
5445
|
if (Array.isArray(out.fills) && out.fills.length) {
|
|
@@ -5257,7 +5462,7 @@ function vectorTextCaret(node, index) {
|
|
|
5257
5462
|
if (node.frame.kind !== "path") {
|
|
5258
5463
|
const c = caretAt(layout.text, index);
|
|
5259
5464
|
const o = layout.origin;
|
|
5260
|
-
return { top: { x: o.x + c.x, y: o.y + c.
|
|
5465
|
+
return { top: { x: o.x + c.from.x, y: o.y + c.from.y }, bottom: { x: o.x + c.to.x, y: o.y + c.to.y }, line: c.line };
|
|
5261
5466
|
}
|
|
5262
5467
|
const g = glyphs.find((x) => x.index >= index);
|
|
5263
5468
|
if (g && g.index === index) return at2(g, 0);
|
|
@@ -5298,6 +5503,10 @@ function vectorTextSelectionQuads(node, start, end) {
|
|
|
5298
5503
|
const layout = layoutVectorText(node);
|
|
5299
5504
|
const picked = layout.glyphs.filter((g) => g.index >= start && g.index < end);
|
|
5300
5505
|
if (node.frame.kind === "path") return picked.map(glyphBox);
|
|
5506
|
+
if (layout.text.orientation === "vertical") {
|
|
5507
|
+
const o = layout.origin;
|
|
5508
|
+
return textSelectionQuads(layout.text, start, end).map((q) => q.map((p) => ({ x: o.x + p.x, y: o.y + p.y })));
|
|
5509
|
+
}
|
|
5301
5510
|
const byLine = /* @__PURE__ */ new Map();
|
|
5302
5511
|
for (const g of picked) {
|
|
5303
5512
|
const line = layout.fragments[g.fragment].line;
|
|
@@ -5336,6 +5545,7 @@ function patchVectorCharacterStyle(base, patch, path = "style") {
|
|
|
5336
5545
|
case "italic":
|
|
5337
5546
|
case "underline":
|
|
5338
5547
|
case "strikethrough":
|
|
5548
|
+
case "upright":
|
|
5339
5549
|
if (typeof v2 !== "boolean" && v2 !== null) fail(`${path}.${k} must be true or false.`);
|
|
5340
5550
|
if (v2) out[k] = true;
|
|
5341
5551
|
else delete out[k];
|
|
@@ -5413,6 +5623,11 @@ function normalizeVectorTextRuns(text, runs) {
|
|
|
5413
5623
|
if (cleaned.every((r) => !Object.keys(r.style).length)) return null;
|
|
5414
5624
|
return cleaned;
|
|
5415
5625
|
}
|
|
5626
|
+
function vectorTextOrientation(value, path = "orientation") {
|
|
5627
|
+
if (value === void 0 || value === null || value === "horizontal") return void 0;
|
|
5628
|
+
if (value !== "vertical") fail(`${path} must be horizontal or vertical, got ${JSON.stringify(value)}.`);
|
|
5629
|
+
return "vertical";
|
|
5630
|
+
}
|
|
5416
5631
|
function sanitizeVectorTextFields(value) {
|
|
5417
5632
|
const text = typeof value.text === "string" ? value.text.replace(/\r\n?/g, "\n") : "";
|
|
5418
5633
|
const font = patchVectorCharacterStyle({ ...DEFAULT_VECTOR_FONT }, value.font ?? {}, "font");
|
|
@@ -5425,7 +5640,8 @@ function sanitizeVectorTextFields(value) {
|
|
|
5425
5640
|
) : null;
|
|
5426
5641
|
const paragraph = value.paragraph ? patchVectorParagraphStyle({}, value.paragraph) : null;
|
|
5427
5642
|
const frame = sanitizeVectorTextFrame(value.frame ?? { kind: "point", x: 0, y: 0 });
|
|
5428
|
-
|
|
5643
|
+
const orientation = vectorTextOrientation(value.orientation);
|
|
5644
|
+
return { text, font, ...runs ? { runs } : {}, ...paragraph && Object.keys(paragraph).length ? { paragraph } : {}, frame, ...orientation ? { orientation } : {} };
|
|
5429
5645
|
}
|
|
5430
5646
|
function vectorTextRange(text, start, end) {
|
|
5431
5647
|
const s = start === void 0 ? 0 : start;
|
|
@@ -5567,6 +5783,13 @@ function vectorTextBoxOutline(node) {
|
|
|
5567
5783
|
layout.text.lines.forEach((l, i) => {
|
|
5568
5784
|
const line = layout.lines[i];
|
|
5569
5785
|
if (!line) return;
|
|
5786
|
+
if (l.matrix) {
|
|
5787
|
+
const r = textLineRect(l);
|
|
5788
|
+
const o = layout.origin;
|
|
5789
|
+
const h = Math.max(r.height, 1);
|
|
5790
|
+
out.push(...polygon2([{ x: o.x + r.x, y: o.y + r.y }, { x: o.x + r.x + r.width, y: o.y + r.y }, { x: o.x + r.x + r.width, y: o.y + r.y + h }, { x: o.x + r.x, y: o.y + r.y + h }]));
|
|
5791
|
+
return;
|
|
5792
|
+
}
|
|
5570
5793
|
const top = line.baseline - l.ascent;
|
|
5571
5794
|
const bottom = line.baseline + l.descent;
|
|
5572
5795
|
const w = Math.max(l.width, 1);
|
|
@@ -8514,7 +8737,7 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
8514
8737
|
};
|
|
8515
8738
|
const emitTextElement = (node, depth, attrs) => {
|
|
8516
8739
|
const geometry = vectorTextGeometryNode(node);
|
|
8517
|
-
const fields = { text: node.text, font: node.font, ...node.runs?.length ? { runs: node.runs } : {}, ...node.paragraph ? { paragraph: node.paragraph } : {}, frame: node.frame, ...node.effects?.length ? { effects: node.effects } : {} };
|
|
8740
|
+
const fields = { text: node.text, font: node.font, ...node.runs?.length ? { runs: node.runs } : {}, ...node.paragraph ? { paragraph: node.paragraph } : {}, frame: node.frame, ...node.orientation ? { orientation: node.orientation } : {}, ...node.effects?.length ? { effects: node.effects } : {} };
|
|
8518
8741
|
let data = ` data-vector-text="${escapeAttr2(JSON.stringify(fields))}"`;
|
|
8519
8742
|
if (hasVectorPaintStacks(node)) data += ` data-vector-fills="${escapeAttr2(JSON.stringify(vectorFills(node)))}" data-vector-strokes="${escapeAttr2(JSON.stringify(vectorStrokes(node)))}"`;
|
|
8520
8743
|
else {
|
|
@@ -10685,13 +10908,18 @@ export {
|
|
|
10685
10908
|
paragraphRanges,
|
|
10686
10909
|
fitParagraphRuns,
|
|
10687
10910
|
fontOf,
|
|
10688
|
-
|
|
10911
|
+
transformTextRect,
|
|
10912
|
+
lineToTextSpace,
|
|
10689
10913
|
layoutText,
|
|
10690
10914
|
textIndexAt,
|
|
10691
10915
|
caretAt,
|
|
10916
|
+
textSelectionQuads,
|
|
10917
|
+
textArrowStep,
|
|
10918
|
+
textLineMove,
|
|
10692
10919
|
textDecorations,
|
|
10693
10920
|
textRectPath,
|
|
10694
10921
|
textFragmentOutline,
|
|
10922
|
+
drawTextFragment,
|
|
10695
10923
|
cssFontFamily,
|
|
10696
10924
|
cssFont,
|
|
10697
10925
|
createCanvasTextShaper,
|
|
@@ -10723,6 +10951,7 @@ export {
|
|
|
10723
10951
|
patchVectorParagraphStyle,
|
|
10724
10952
|
sanitizeVectorTextFrame,
|
|
10725
10953
|
normalizeVectorTextRuns,
|
|
10954
|
+
vectorTextOrientation,
|
|
10726
10955
|
sanitizeVectorTextFields,
|
|
10727
10956
|
vectorTextRange,
|
|
10728
10957
|
replaceVectorText,
|