@embedpdf/core-annotation 3.0.0-next.11 → 3.0.0-next.13
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/index.cjs +365 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -2
- package/dist/index.d.ts +102 -2
- package/dist/index.js +354 -39
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -207,6 +207,18 @@ const TEXT_PROPS = [
|
|
|
207
207
|
key: "fontColor",
|
|
208
208
|
label: "Text color"
|
|
209
209
|
},
|
|
210
|
+
{
|
|
211
|
+
key: "bold",
|
|
212
|
+
label: "Bold"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
key: "italic",
|
|
216
|
+
label: "Italic"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
key: "underline",
|
|
220
|
+
label: "Underline"
|
|
221
|
+
},
|
|
210
222
|
{
|
|
211
223
|
key: "textAlign",
|
|
212
224
|
label: "Align"
|
|
@@ -2687,41 +2699,60 @@ function geomDragHandle(g, handle, to) {
|
|
|
2687
2699
|
}
|
|
2688
2700
|
return g;
|
|
2689
2701
|
}
|
|
2702
|
+
/**
|
|
2703
|
+
* The text PLATE inset of a free-text box: twice the border width. The plate
|
|
2704
|
+
* — where text lays out, clips and scrolls — is the box deflated by this on
|
|
2705
|
+
* every side: the ink band and an equal breathing band, so the text never
|
|
2706
|
+
* touches the stroke. Acrobat's rule, measured 1–12 pt on plain boxes and
|
|
2707
|
+
* 1–7 pt on callouts (plan `2026-09-15-free-text-plate-inset.md`); the
|
|
2708
|
+
* engine's `FreeTextPlate` is the same formula, so the live editor sits
|
|
2709
|
+
* exactly where the baked text lands. Acrobat's thinnest border is 1 pt; a
|
|
2710
|
+
* width of 0 is ours alone and gives no inset (the plate is the box).
|
|
2711
|
+
*/
|
|
2712
|
+
function textPlateInset(strokeWidth) {
|
|
2713
|
+
return 2 * Math.max(0, strokeWidth);
|
|
2714
|
+
}
|
|
2715
|
+
/** A callout's leader (open polyline, its connection point extended under
|
|
2716
|
+
* the box border by half the stroke — the AP generator's `adjusted_conn` —
|
|
2717
|
+
* so the leader meets the border ink without an angular gap) + the arrow at
|
|
2718
|
+
* its tip. */
|
|
2719
|
+
function calloutLeaderNodes(g, callout, strokeWidth) {
|
|
2720
|
+
const pts = [...calloutLinePoints(g)];
|
|
2721
|
+
if (strokeWidth > 0 && pts.length >= 2) {
|
|
2722
|
+
const last = pts[pts.length - 1];
|
|
2723
|
+
const prev = pts[pts.length - 2];
|
|
2724
|
+
const dx = last.x - prev.x;
|
|
2725
|
+
const dy = last.y - prev.y;
|
|
2726
|
+
const len = Math.hypot(dx, dy);
|
|
2727
|
+
if (len > 0) pts[pts.length - 1] = {
|
|
2728
|
+
x: last.x + dx / len * (strokeWidth / 2),
|
|
2729
|
+
y: last.y + dy / len * (strokeWidth / 2)
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
const nodes = [{
|
|
2733
|
+
kind: "poly",
|
|
2734
|
+
points: pts,
|
|
2735
|
+
closed: false
|
|
2736
|
+
}];
|
|
2737
|
+
const seg = calloutEndingSeg(pts, callout.ending);
|
|
2738
|
+
if (seg) nodes.push(...endingNodes(seg.tip, seg.angle, seg.ending, strokeWidth));
|
|
2739
|
+
return nodes;
|
|
2740
|
+
}
|
|
2690
2741
|
function geomScene(g, strokeWidth = 0, border) {
|
|
2691
2742
|
if (g.t === "text") {
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
const dy = last.y - prev.y;
|
|
2699
|
-
const len = Math.hypot(dx, dy);
|
|
2700
|
-
if (len > 0) pts[pts.length - 1] = {
|
|
2701
|
-
x: last.x + dx / len * (strokeWidth / 2),
|
|
2702
|
-
y: last.y + dy / len * (strokeWidth / 2)
|
|
2703
|
-
};
|
|
2704
|
-
}
|
|
2705
|
-
const nodes = [{
|
|
2743
|
+
const nodes = [];
|
|
2744
|
+
if (g.callout) nodes.push(...calloutLeaderNodes(g, g.callout, strokeWidth));
|
|
2745
|
+
const r = insetRect(g.rect, strokeWidth / 2);
|
|
2746
|
+
const rot = g.rot ?? 0;
|
|
2747
|
+
const c = rectCenter(g.rect);
|
|
2748
|
+
nodes.push(rot ? {
|
|
2706
2749
|
kind: "poly",
|
|
2707
|
-
points:
|
|
2708
|
-
closed:
|
|
2709
|
-
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
const r = insetRect(g.rect, strokeWidth / 2);
|
|
2714
|
-
const rot = g.rot ?? 0;
|
|
2715
|
-
const c = rectCenter(g.rect);
|
|
2716
|
-
nodes.push(rot ? {
|
|
2717
|
-
kind: "poly",
|
|
2718
|
-
points: rectCornerPoints(r).map((p) => rotatePoint(p, c, rot)),
|
|
2719
|
-
closed: true
|
|
2720
|
-
} : {
|
|
2721
|
-
kind: "rect",
|
|
2722
|
-
rect: r
|
|
2723
|
-
});
|
|
2724
|
-
}
|
|
2750
|
+
points: rectCornerPoints(r).map((p) => rotatePoint(p, c, rot)),
|
|
2751
|
+
closed: true
|
|
2752
|
+
} : {
|
|
2753
|
+
kind: "rect",
|
|
2754
|
+
rect: r
|
|
2755
|
+
});
|
|
2725
2756
|
return nodes;
|
|
2726
2757
|
}
|
|
2727
2758
|
if (g.t === "caret") {
|
|
@@ -3227,7 +3258,10 @@ const textStyleFromProps = (p) => ({
|
|
|
3227
3258
|
fontFamily: p.fontFamily,
|
|
3228
3259
|
fontSize: p.fontSize,
|
|
3229
3260
|
fontColor: p.fontColor,
|
|
3230
|
-
textAlign: p.textAlign
|
|
3261
|
+
textAlign: p.textAlign,
|
|
3262
|
+
...p.bold !== void 0 ? { bold: p.bold } : {},
|
|
3263
|
+
...p.italic !== void 0 ? { italic: p.italic } : {},
|
|
3264
|
+
...p.underline !== void 0 ? { underline: p.underline } : {}
|
|
3231
3265
|
});
|
|
3232
3266
|
/** Does this kind's prop table declare `link` (attachable link)? The link
|
|
3233
3267
|
* KIND itself also declares it, but stores its own `/A` instead. */
|
|
@@ -3253,6 +3287,9 @@ function readProp(a, key) {
|
|
|
3253
3287
|
case "fontSize": return a.text?.fontSize;
|
|
3254
3288
|
case "fontColor": return a.text?.fontColor;
|
|
3255
3289
|
case "textAlign": return a.text?.textAlign;
|
|
3290
|
+
case "bold": return a.text ? a.text.bold ?? false : void 0;
|
|
3291
|
+
case "italic": return a.text ? a.text.italic ?? false : void 0;
|
|
3292
|
+
case "underline": return a.text ? a.text.underline ?? false : void 0;
|
|
3256
3293
|
case "icon": return a.icon;
|
|
3257
3294
|
case "link": return a.link;
|
|
3258
3295
|
}
|
|
@@ -3340,6 +3377,17 @@ function applyProps(a, patch) {
|
|
|
3340
3377
|
text.textAlign = patch.textAlign;
|
|
3341
3378
|
textChanged = true;
|
|
3342
3379
|
}
|
|
3380
|
+
for (const key of [
|
|
3381
|
+
"bold",
|
|
3382
|
+
"italic",
|
|
3383
|
+
"underline"
|
|
3384
|
+
]) {
|
|
3385
|
+
const value = patch[key];
|
|
3386
|
+
if (value !== void 0 && takes.has(key) && (text[key] ?? false) !== value) {
|
|
3387
|
+
text[key] = value;
|
|
3388
|
+
textChanged = true;
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3343
3391
|
if (textChanged) next = {
|
|
3344
3392
|
...next,
|
|
3345
3393
|
text
|
|
@@ -3469,6 +3517,236 @@ function clickCreateGeom(subtype, placement, def) {
|
|
|
3469
3517
|
return null;
|
|
3470
3518
|
}
|
|
3471
3519
|
//#endregion
|
|
3520
|
+
//#region src/richtext.ts
|
|
3521
|
+
/** Length of the plain projection. */
|
|
3522
|
+
function richTextLength(doc) {
|
|
3523
|
+
let n = 0;
|
|
3524
|
+
doc.paragraphs.forEach((p, i) => {
|
|
3525
|
+
if (i > 0) n += 1;
|
|
3526
|
+
for (const r of p.runs) n += r.text.length;
|
|
3527
|
+
});
|
|
3528
|
+
return n;
|
|
3529
|
+
}
|
|
3530
|
+
const DELTA_KEYS = [
|
|
3531
|
+
"family",
|
|
3532
|
+
"weight",
|
|
3533
|
+
"italic",
|
|
3534
|
+
"size",
|
|
3535
|
+
"color",
|
|
3536
|
+
"decoration",
|
|
3537
|
+
"script",
|
|
3538
|
+
"letterSpacing",
|
|
3539
|
+
"horizontalScale",
|
|
3540
|
+
"unknown"
|
|
3541
|
+
];
|
|
3542
|
+
function sameDecoration(a, b) {
|
|
3543
|
+
if (a === b) return true;
|
|
3544
|
+
if (!a || !b || a.length !== b.length) return false;
|
|
3545
|
+
const sorted = (x) => [...x].sort();
|
|
3546
|
+
const sa = sorted(a);
|
|
3547
|
+
const sb = sorted(b);
|
|
3548
|
+
return sa.every((v, i) => v === sb[i]);
|
|
3549
|
+
}
|
|
3550
|
+
/** Two deltas override the same properties with the same values. */
|
|
3551
|
+
function sameStyleDelta(a, b) {
|
|
3552
|
+
const da = a ?? {};
|
|
3553
|
+
const db = b ?? {};
|
|
3554
|
+
for (const key of DELTA_KEYS) if (key === "decoration") {
|
|
3555
|
+
if (!sameDecoration(da.decoration, db.decoration)) return false;
|
|
3556
|
+
} else if (da[key] !== db[key]) return false;
|
|
3557
|
+
return true;
|
|
3558
|
+
}
|
|
3559
|
+
/** A delta with every `undefined` member removed; `undefined` when empty. */
|
|
3560
|
+
function cleanDelta(delta) {
|
|
3561
|
+
if (!delta) return void 0;
|
|
3562
|
+
const out = {};
|
|
3563
|
+
let any = false;
|
|
3564
|
+
for (const key of DELTA_KEYS) {
|
|
3565
|
+
const value = delta[key];
|
|
3566
|
+
if (value !== void 0) {
|
|
3567
|
+
out[key] = value;
|
|
3568
|
+
any = true;
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
return any ? out : void 0;
|
|
3572
|
+
}
|
|
3573
|
+
/**
|
|
3574
|
+
* Merge adjacent runs whose deltas agree, drop empty runs, strip empty
|
|
3575
|
+
* deltas. A paragraph with no text keeps a single empty run so the editor
|
|
3576
|
+
* has somewhere to put the caret.
|
|
3577
|
+
*/
|
|
3578
|
+
function normalizeRuns(doc) {
|
|
3579
|
+
const paragraphs = doc.paragraphs.map((p) => {
|
|
3580
|
+
const runs = [];
|
|
3581
|
+
for (const run of p.runs) {
|
|
3582
|
+
if (run.text.length === 0) continue;
|
|
3583
|
+
const style = cleanDelta(run.style);
|
|
3584
|
+
const last = runs[runs.length - 1];
|
|
3585
|
+
if (last && sameStyleDelta(last.style, style)) runs[runs.length - 1] = {
|
|
3586
|
+
...last,
|
|
3587
|
+
text: last.text + run.text
|
|
3588
|
+
};
|
|
3589
|
+
else runs.push(style ? {
|
|
3590
|
+
text: run.text,
|
|
3591
|
+
style
|
|
3592
|
+
} : { text: run.text });
|
|
3593
|
+
}
|
|
3594
|
+
if (runs.length === 0) runs.push({ text: "" });
|
|
3595
|
+
return {
|
|
3596
|
+
...p,
|
|
3597
|
+
runs
|
|
3598
|
+
};
|
|
3599
|
+
});
|
|
3600
|
+
return {
|
|
3601
|
+
...doc,
|
|
3602
|
+
paragraphs
|
|
3603
|
+
};
|
|
3604
|
+
}
|
|
3605
|
+
/** Where a flat offset falls: paragraph index and offset within it (the
|
|
3606
|
+
* separator between paragraphs belongs to the end of the earlier one). */
|
|
3607
|
+
function locateOffset(doc, offset) {
|
|
3608
|
+
let pos = 0;
|
|
3609
|
+
for (let i = 0; i < doc.paragraphs.length; i++) {
|
|
3610
|
+
const length = doc.paragraphs[i].runs.reduce((n, r) => n + r.text.length, 0);
|
|
3611
|
+
if (offset <= pos + length || i === doc.paragraphs.length - 1) return {
|
|
3612
|
+
paragraph: i,
|
|
3613
|
+
offset: Math.max(0, Math.min(length, offset - pos))
|
|
3614
|
+
};
|
|
3615
|
+
pos += length + 1;
|
|
3616
|
+
}
|
|
3617
|
+
return {
|
|
3618
|
+
paragraph: 0,
|
|
3619
|
+
offset: 0
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
/** The paragraph with a run boundary guaranteed at a local offset. */
|
|
3623
|
+
function splitRunsAt(paragraph, offset) {
|
|
3624
|
+
const runs = [];
|
|
3625
|
+
let pos = 0;
|
|
3626
|
+
for (const run of paragraph.runs) {
|
|
3627
|
+
const end = pos + run.text.length;
|
|
3628
|
+
if (offset > pos && offset < end) {
|
|
3629
|
+
const cut = offset - pos;
|
|
3630
|
+
runs.push({
|
|
3631
|
+
...run,
|
|
3632
|
+
text: run.text.slice(0, cut)
|
|
3633
|
+
});
|
|
3634
|
+
runs.push({
|
|
3635
|
+
...run,
|
|
3636
|
+
text: run.text.slice(cut)
|
|
3637
|
+
});
|
|
3638
|
+
} else runs.push(run);
|
|
3639
|
+
pos = end;
|
|
3640
|
+
}
|
|
3641
|
+
return {
|
|
3642
|
+
...paragraph,
|
|
3643
|
+
runs
|
|
3644
|
+
};
|
|
3645
|
+
}
|
|
3646
|
+
/**
|
|
3647
|
+
* Apply a style delta to every character of a range: the runs it touches are
|
|
3648
|
+
* split at its boundaries, the covered pieces take the delta on top of what
|
|
3649
|
+
* they already override (`clear` names properties to stop overriding, so
|
|
3650
|
+
* they follow the body again), and the result is normalised. An empty range
|
|
3651
|
+
* is a no-op.
|
|
3652
|
+
*/
|
|
3653
|
+
function applyStyleToRange(doc, range, delta, clear = []) {
|
|
3654
|
+
const start = Math.max(0, Math.min(range.start, range.end));
|
|
3655
|
+
const end = Math.max(range.start, range.end);
|
|
3656
|
+
if (end <= start) return doc;
|
|
3657
|
+
const from = locateOffset(doc, start);
|
|
3658
|
+
const to = locateOffset(doc, end);
|
|
3659
|
+
const paragraphs = doc.paragraphs.map((paragraph, index) => {
|
|
3660
|
+
if (index < from.paragraph || index > to.paragraph) return paragraph;
|
|
3661
|
+
const localStart = index === from.paragraph ? from.offset : 0;
|
|
3662
|
+
const localEnd = index === to.paragraph ? to.offset : paragraph.runs.reduce((n, r) => n + r.text.length, 0);
|
|
3663
|
+
if (localEnd <= localStart) return paragraph;
|
|
3664
|
+
const split = splitRunsAt(splitRunsAt(paragraph, localStart), localEnd);
|
|
3665
|
+
let pos = 0;
|
|
3666
|
+
const runs = split.runs.map((run) => {
|
|
3667
|
+
const runStart = pos;
|
|
3668
|
+
pos += run.text.length;
|
|
3669
|
+
if (runStart < localStart || runStart >= localEnd || run.text.length === 0) return run;
|
|
3670
|
+
const style = {
|
|
3671
|
+
...run.style ?? {},
|
|
3672
|
+
...delta
|
|
3673
|
+
};
|
|
3674
|
+
for (const key of clear) delete style[key];
|
|
3675
|
+
return {
|
|
3676
|
+
...run,
|
|
3677
|
+
style
|
|
3678
|
+
};
|
|
3679
|
+
});
|
|
3680
|
+
return {
|
|
3681
|
+
...split,
|
|
3682
|
+
runs
|
|
3683
|
+
};
|
|
3684
|
+
});
|
|
3685
|
+
return normalizeRuns({
|
|
3686
|
+
...doc,
|
|
3687
|
+
paragraphs
|
|
3688
|
+
});
|
|
3689
|
+
}
|
|
3690
|
+
/**
|
|
3691
|
+
* The delta in force at a caret: the run the offset falls in, or, at a run
|
|
3692
|
+
* boundary, the run before it (what typing there would inherit). Undefined
|
|
3693
|
+
* for an empty document.
|
|
3694
|
+
*/
|
|
3695
|
+
function styleAt(doc, offset) {
|
|
3696
|
+
const at = locateOffset(doc, offset);
|
|
3697
|
+
const paragraph = doc.paragraphs[at.paragraph];
|
|
3698
|
+
if (!paragraph) return void 0;
|
|
3699
|
+
let pos = 0;
|
|
3700
|
+
let previous;
|
|
3701
|
+
for (const run of paragraph.runs) {
|
|
3702
|
+
const end = pos + run.text.length;
|
|
3703
|
+
if (at.offset > pos && at.offset <= end) return run.style ?? {};
|
|
3704
|
+
if (at.offset === pos && pos === 0) return run.style ?? {};
|
|
3705
|
+
previous = run;
|
|
3706
|
+
pos = end;
|
|
3707
|
+
}
|
|
3708
|
+
return previous?.style ?? {};
|
|
3709
|
+
}
|
|
3710
|
+
/** Every run overlapping the range satisfies `predicate` (an empty range
|
|
3711
|
+
* tests the caret's style). */
|
|
3712
|
+
function rangeHasStyle(doc, range, predicate) {
|
|
3713
|
+
const start = Math.min(range.start, range.end);
|
|
3714
|
+
const end = Math.max(range.start, range.end);
|
|
3715
|
+
if (end <= start) return predicate(styleAt(doc, start) ?? {});
|
|
3716
|
+
const from = locateOffset(doc, start);
|
|
3717
|
+
const to = locateOffset(doc, end);
|
|
3718
|
+
let any = false;
|
|
3719
|
+
for (let index = from.paragraph; index <= to.paragraph; index++) {
|
|
3720
|
+
const paragraph = doc.paragraphs[index];
|
|
3721
|
+
const localStart = index === from.paragraph ? from.offset : 0;
|
|
3722
|
+
const localEnd = index === to.paragraph ? to.offset : paragraph.runs.reduce((n, r) => n + r.text.length, 0);
|
|
3723
|
+
let pos = 0;
|
|
3724
|
+
for (const run of paragraph.runs) {
|
|
3725
|
+
const runEnd = pos + run.text.length;
|
|
3726
|
+
if (runEnd > localStart && pos < localEnd && run.text.length > 0) {
|
|
3727
|
+
any = true;
|
|
3728
|
+
if (!predicate(run.style ?? {})) return false;
|
|
3729
|
+
}
|
|
3730
|
+
pos = runEnd;
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
return any;
|
|
3734
|
+
}
|
|
3735
|
+
/** Plain paragraphs from text: one per line break, one unstyled run each. */
|
|
3736
|
+
function paragraphsFromPlainText(text) {
|
|
3737
|
+
return text.split(/\r\n|\r|\n/).map((line) => ({ runs: [{ text: line }] }));
|
|
3738
|
+
}
|
|
3739
|
+
/** The plain projection: paragraphs joined by `\r`, runs concatenated. */
|
|
3740
|
+
function plainTextOf(doc) {
|
|
3741
|
+
return doc.paragraphs.map((p) => p.runs.map((r) => r.text).join("")).join("\r");
|
|
3742
|
+
}
|
|
3743
|
+
/** True when nothing in the document overrides the body: no run delta, no
|
|
3744
|
+
* paragraph property. Such a document commits as plain `contents`. */
|
|
3745
|
+
function isPlainRichText(doc) {
|
|
3746
|
+
if (doc.body && Object.keys(doc.body).length > 0) return false;
|
|
3747
|
+
return doc.paragraphs.every((p) => p.align === void 0 && p.dir === void 0 && p.lineHeight === void 0 && p.margins === void 0 && p.textIndent === void 0 && p.unknown === void 0 && p.runs.every((r) => cleanDelta(r.style) === void 0));
|
|
3748
|
+
}
|
|
3749
|
+
//#endregion
|
|
3472
3750
|
//#region src/snap.ts
|
|
3473
3751
|
/**
|
|
3474
3752
|
* Pure alignment snapping for the move gesture. Compares the moving selection's
|
|
@@ -3902,6 +4180,7 @@ function update(m, msg) {
|
|
|
3902
4180
|
draft: null
|
|
3903
4181
|
}, []] : [m, []];
|
|
3904
4182
|
case "setText": return setText(m, msg.id, msg.text);
|
|
4183
|
+
case "setRichText": return setRichText(m, msg.id, msg.doc);
|
|
3905
4184
|
case "endTextEdit": return m.editing ? [{
|
|
3906
4185
|
...m,
|
|
3907
4186
|
editing: null
|
|
@@ -3915,12 +4194,49 @@ function update(m, msg) {
|
|
|
3915
4194
|
function setText(m, id, text) {
|
|
3916
4195
|
const a = m.byId[id];
|
|
3917
4196
|
if (!a) return [m, []];
|
|
4197
|
+
const data = a.data && a.data.subtype === "free-text" ? {
|
|
4198
|
+
...a.data,
|
|
4199
|
+
contents: text,
|
|
4200
|
+
richText: {
|
|
4201
|
+
...a.data.richText,
|
|
4202
|
+
paragraphs: paragraphsFromPlainText(text)
|
|
4203
|
+
}
|
|
4204
|
+
} : a.data ? {
|
|
4205
|
+
...a.data,
|
|
4206
|
+
contents: text
|
|
4207
|
+
} : a.data;
|
|
4208
|
+
const next = toVector({
|
|
4209
|
+
...a,
|
|
4210
|
+
data
|
|
4211
|
+
});
|
|
4212
|
+
return [{
|
|
4213
|
+
...m,
|
|
4214
|
+
byId: {
|
|
4215
|
+
...m.byId,
|
|
4216
|
+
[id]: next
|
|
4217
|
+
}
|
|
4218
|
+
}, []];
|
|
4219
|
+
}
|
|
4220
|
+
/** Apply the editor's rich document optimistically. The DTO's body is kept
|
|
4221
|
+
* (a partial input body layers on it); `contents` is the projection. */
|
|
4222
|
+
function setRichText(m, id, doc) {
|
|
4223
|
+
const a = m.byId[id];
|
|
4224
|
+
if (!a || !a.data || a.data.subtype !== "free-text") return [m, []];
|
|
4225
|
+
const normalized = normalizeRuns(doc);
|
|
4226
|
+
const richText = {
|
|
4227
|
+
body: {
|
|
4228
|
+
...a.data.richText.body,
|
|
4229
|
+
...normalized.body ?? {}
|
|
4230
|
+
},
|
|
4231
|
+
paragraphs: normalized.paragraphs
|
|
4232
|
+
};
|
|
3918
4233
|
const next = toVector({
|
|
3919
4234
|
...a,
|
|
3920
|
-
data:
|
|
4235
|
+
data: {
|
|
3921
4236
|
...a.data,
|
|
3922
|
-
|
|
3923
|
-
|
|
4237
|
+
richText,
|
|
4238
|
+
contents: plainTextOf(richText)
|
|
4239
|
+
}
|
|
3924
4240
|
});
|
|
3925
4241
|
return [{
|
|
3926
4242
|
...m,
|
|
@@ -5522,7 +5838,7 @@ function effAp(m, id, view) {
|
|
|
5522
5838
|
function effSource(m, id) {
|
|
5523
5839
|
const a = m.byId[id];
|
|
5524
5840
|
if (capsFor(a.subtype).opaqueBody) return a.source;
|
|
5525
|
-
if (m.editing === id && a.geom.t === "text"
|
|
5841
|
+
if (m.editing === id && a.geom.t === "text") return "vector";
|
|
5526
5842
|
const d = m.draft;
|
|
5527
5843
|
if (d?.g === "handle" && d.id === id) return "vector";
|
|
5528
5844
|
if ((d?.g === "rotate" || d?.g === "group") && d.ids.includes(id)) return "vector";
|
|
@@ -5538,7 +5854,6 @@ function pageItems(m, pon, view) {
|
|
|
5538
5854
|
const items = [];
|
|
5539
5855
|
for (const id of paintOrder(m, pon)) {
|
|
5540
5856
|
const a = m.byId[id];
|
|
5541
|
-
if (a.geom.t === "text" && !a.geom.callout && textIsLive(m, id)) continue;
|
|
5542
5857
|
const geom = effGeom(m, id, view);
|
|
5543
5858
|
const style = effStyle(a, view);
|
|
5544
5859
|
const ap = effAp(m, id, view);
|
|
@@ -5933,6 +6248,7 @@ exports.annotDeletable = annotDeletable;
|
|
|
5933
6248
|
exports.annotInteractive = annotInteractive;
|
|
5934
6249
|
exports.annotTransformable = annotTransformable;
|
|
5935
6250
|
exports.applyProps = applyProps;
|
|
6251
|
+
exports.applyStyleToRange = applyStyleToRange;
|
|
5936
6252
|
exports.calloutConnection = calloutConnection;
|
|
5937
6253
|
exports.calloutLinePoints = calloutLinePoints;
|
|
5938
6254
|
exports.canMove = canMove;
|
|
@@ -5980,28 +6296,36 @@ exports.initialTextStyle = initialTextStyle;
|
|
|
5980
6296
|
exports.interactive = interactive;
|
|
5981
6297
|
exports.isAttachedLink = isAttachedLink;
|
|
5982
6298
|
exports.isConversationOnly = isConversationOnly;
|
|
6299
|
+
exports.isPlainRichText = isPlainRichText;
|
|
5983
6300
|
exports.isRotatableGeom = isRotatableGeom;
|
|
5984
6301
|
exports.isSelectable = isSelectable;
|
|
5985
6302
|
exports.isSubstrateOnly = isSubstrateOnly;
|
|
5986
6303
|
exports.linkChildrenOf = linkChildrenOf;
|
|
5987
6304
|
exports.linkOf = linkOf;
|
|
6305
|
+
exports.locateOffset = locateOffset;
|
|
5988
6306
|
exports.mergeFlags = mergeFlags;
|
|
5989
6307
|
exports.normalizeDeg = normalizeDeg;
|
|
6308
|
+
exports.normalizeRuns = normalizeRuns;
|
|
5990
6309
|
exports.obbFromGeom = obbFromGeom;
|
|
5991
6310
|
exports.pageItems = pageItems;
|
|
6311
|
+
exports.paragraphsFromPlainText = paragraphsFromPlainText;
|
|
5992
6312
|
exports.pdfToContentPoint = pdfToContentPoint;
|
|
5993
6313
|
exports.pdfToContentRect = pdfToContentRect;
|
|
5994
6314
|
exports.placeRotateKnob = placeRotateKnob;
|
|
6315
|
+
exports.plainTextOf = plainTextOf;
|
|
5995
6316
|
exports.pointInQuad = pointInQuad;
|
|
5996
6317
|
exports.propsFor = propsFor;
|
|
5997
6318
|
exports.quadIntersectsRect = quadIntersectsRect;
|
|
6319
|
+
exports.rangeHasStyle = rangeHasStyle;
|
|
5998
6320
|
exports.readProp = readProp;
|
|
5999
6321
|
exports.rectFromPoints = rectFromPoints;
|
|
6000
6322
|
exports.resolveClickPlacement = resolveClickPlacement;
|
|
6323
|
+
exports.richTextLength = richTextLength;
|
|
6001
6324
|
exports.rotateDraftDelta = rotateDraftDelta;
|
|
6002
6325
|
exports.rotateKnob = rotateKnob;
|
|
6003
6326
|
exports.rotatedAabb = rotatedAabb;
|
|
6004
6327
|
exports.rotatedHandleCursor = rotatedHandleCursor;
|
|
6328
|
+
exports.sameStyleDelta = sameStyleDelta;
|
|
6005
6329
|
exports.scene = scene;
|
|
6006
6330
|
exports.selectedItems = selectedItems;
|
|
6007
6331
|
exports.selectionAnchor = selectionAnchor;
|
|
@@ -6012,9 +6336,12 @@ exports.selectionKnob = selectionKnob;
|
|
|
6012
6336
|
exports.selectionQuad = selectionQuad;
|
|
6013
6337
|
exports.shapeRectFor = shapeRectFor;
|
|
6014
6338
|
exports.sharedProps = sharedProps;
|
|
6339
|
+
exports.splitRunsAt = splitRunsAt;
|
|
6015
6340
|
exports.straightenInkStroke = straightenInkStroke;
|
|
6341
|
+
exports.styleAt = styleAt;
|
|
6016
6342
|
exports.styleFromProps = styleFromProps;
|
|
6017
6343
|
exports.textBoxes = textBoxes;
|
|
6344
|
+
exports.textPlateInset = textPlateInset;
|
|
6018
6345
|
exports.textStyleFromProps = textStyleFromProps;
|
|
6019
6346
|
exports.transposedAboutCenter = transposedAboutCenter;
|
|
6020
6347
|
exports.unanchoredGeom = unanchoredGeom;
|