@embedpdf/plugin-annotation 2.13.0 → 2.14.1
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 +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +522 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/annotation-plugin.d.ts +25 -0
- package/dist/lib/handlers/callout-free-text.handler.d.ts +3 -0
- package/dist/lib/handlers/index.d.ts +1 -0
- package/dist/lib/handlers/types.d.ts +7 -1
- package/dist/lib/helpers.d.ts +8 -0
- package/dist/lib/patching/patch-utils.d.ts +38 -1
- package/dist/lib/patching/patches/callout-freetext.patch.d.ts +3 -0
- package/dist/lib/patching/patches/index.d.ts +1 -0
- package/dist/lib/tools/default-tools.d.ts +35 -0
- package/dist/lib/types.d.ts +21 -2
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +422 -32
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +422 -32
- package/dist/react/index.js.map +1 -1
- package/dist/shared/components/annotation-container.d.ts +13 -1
- package/dist/shared/components/annotations/callout-free-text-preview.d.ts +15 -0
- package/dist/shared/components/annotations/callout-free-text.d.ts +15 -0
- package/dist/shared/components/types.d.ts +12 -0
- package/dist/shared-preact/components/annotation-container.d.ts +13 -1
- package/dist/shared-preact/components/annotations/callout-free-text-preview.d.ts +15 -0
- package/dist/shared-preact/components/annotations/callout-free-text.d.ts +15 -0
- package/dist/shared-preact/components/types.d.ts +12 -0
- package/dist/shared-react/components/annotation-container.d.ts +13 -1
- package/dist/shared-react/components/annotations/callout-free-text-preview.d.ts +15 -0
- package/dist/shared-react/components/annotations/callout-free-text.d.ts +15 -0
- package/dist/shared-react/components/types.d.ts +12 -0
- package/dist/svelte/components/annotations/CalloutFreeText.svelte.d.ts +15 -0
- package/dist/svelte/components/annotations/CalloutFreeTextPreview.svelte.d.ts +10 -0
- package/dist/svelte/components/renderers/CalloutFreeTextRenderer.svelte.d.ts +5 -0
- package/dist/svelte/components/types.d.ts +12 -0
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +528 -106
- package/dist/svelte/index.js.map +1 -1
- package/dist/svelte/types.d.ts +12 -0
- package/dist/vue/components/annotation-container.vue.d.ts +12 -0
- package/dist/vue/components/annotations/callout-free-text-preview.vue.d.ts +10 -0
- package/dist/vue/components/annotations/callout-free-text.vue.d.ts +25 -0
- package/dist/vue/components/renderers/callout-free-text-renderer.vue.d.ts +6 -0
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +601 -211
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/types.d.ts +12 -0
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -207,6 +207,22 @@ function hasLockedFlag(annotation) {
|
|
|
207
207
|
var _a;
|
|
208
208
|
return ((_a = annotation.flags) == null ? void 0 : _a.includes("locked")) ?? false;
|
|
209
209
|
}
|
|
210
|
+
function hasNoViewFlag(annotation) {
|
|
211
|
+
var _a;
|
|
212
|
+
return ((_a = annotation.flags) == null ? void 0 : _a.includes("noView")) ?? false;
|
|
213
|
+
}
|
|
214
|
+
function hasHiddenFlag(annotation) {
|
|
215
|
+
var _a;
|
|
216
|
+
return ((_a = annotation.flags) == null ? void 0 : _a.includes("hidden")) ?? false;
|
|
217
|
+
}
|
|
218
|
+
function hasReadOnlyFlag(annotation) {
|
|
219
|
+
var _a;
|
|
220
|
+
return ((_a = annotation.flags) == null ? void 0 : _a.includes("readOnly")) ?? false;
|
|
221
|
+
}
|
|
222
|
+
function hasLockedContentsFlag(annotation) {
|
|
223
|
+
var _a;
|
|
224
|
+
return ((_a = annotation.flags) == null ? void 0 : _a.includes("lockedContents")) ?? false;
|
|
225
|
+
}
|
|
210
226
|
const getAnnotationsByPageIndex = (s, page) => (s.pages[page] ?? []).map((uid) => s.byUid[uid]);
|
|
211
227
|
const getAnnotations = (s) => {
|
|
212
228
|
const out = {};
|
|
@@ -737,6 +753,109 @@ function compensateRotatedVertexEdit(original, vertices, tightRect) {
|
|
|
737
753
|
if (Math.abs(qx) < 1e-8 && Math.abs(qy) < 1e-8) return vertices;
|
|
738
754
|
return vertices.map((v) => ({ x: v.x + qx, y: v.y + qy }));
|
|
739
755
|
}
|
|
756
|
+
function computeTextBoxFromRD(rect, rd) {
|
|
757
|
+
if (!rd) return rect;
|
|
758
|
+
return {
|
|
759
|
+
origin: { x: rect.origin.x + rd.left, y: rect.origin.y + rd.top },
|
|
760
|
+
size: {
|
|
761
|
+
width: Math.max(0, rect.size.width - rd.left - rd.right),
|
|
762
|
+
height: Math.max(0, rect.size.height - rd.top - rd.bottom)
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function computeRDFromTextBox(overallRect, textBox) {
|
|
767
|
+
return {
|
|
768
|
+
left: textBox.origin.x - overallRect.origin.x,
|
|
769
|
+
top: textBox.origin.y - overallRect.origin.y,
|
|
770
|
+
right: overallRect.origin.x + overallRect.size.width - (textBox.origin.x + textBox.size.width),
|
|
771
|
+
bottom: overallRect.origin.y + overallRect.size.height - (textBox.origin.y + textBox.size.height)
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
function computeCalloutConnectionPoint(knee, textBox) {
|
|
775
|
+
const cx = textBox.origin.x + textBox.size.width / 2;
|
|
776
|
+
const cy = textBox.origin.y + textBox.size.height / 2;
|
|
777
|
+
const dx = knee.x - cx;
|
|
778
|
+
const dy = knee.y - cy;
|
|
779
|
+
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
780
|
+
return dx > 0 ? { x: textBox.origin.x + textBox.size.width, y: cy } : { x: textBox.origin.x, y: cy };
|
|
781
|
+
}
|
|
782
|
+
return dy > 0 ? { x: cx, y: textBox.origin.y + textBox.size.height } : { x: cx, y: textBox.origin.y };
|
|
783
|
+
}
|
|
784
|
+
function computeCalloutOverallRect(textBox, calloutLine, lineEnding, strokeWidth) {
|
|
785
|
+
const linePoints = [...calloutLine];
|
|
786
|
+
if (lineEnding && calloutLine.length >= 2) {
|
|
787
|
+
const handler = LINE_ENDING_HANDLERS[lineEnding];
|
|
788
|
+
if (handler) {
|
|
789
|
+
const angle = Math.atan2(
|
|
790
|
+
calloutLine[1].y - calloutLine[0].y,
|
|
791
|
+
calloutLine[1].x - calloutLine[0].x
|
|
792
|
+
);
|
|
793
|
+
const localPts = handler.getLocalPoints(strokeWidth);
|
|
794
|
+
const rotationAngle = handler.getRotation(angle + Math.PI);
|
|
795
|
+
const transformed = localPts.map(
|
|
796
|
+
(p) => rotateAndTranslatePoint(p, rotationAngle, calloutLine[0])
|
|
797
|
+
);
|
|
798
|
+
linePoints.push(...transformed);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
const lineBbox = expandRect(rectFromPoints(linePoints), strokeWidth);
|
|
802
|
+
const tbRight = textBox.origin.x + textBox.size.width;
|
|
803
|
+
const tbBottom = textBox.origin.y + textBox.size.height;
|
|
804
|
+
const lnRight = lineBbox.origin.x + lineBbox.size.width;
|
|
805
|
+
const lnBottom = lineBbox.origin.y + lineBbox.size.height;
|
|
806
|
+
const minX = Math.min(textBox.origin.x, lineBbox.origin.x);
|
|
807
|
+
const minY = Math.min(textBox.origin.y, lineBbox.origin.y);
|
|
808
|
+
const maxX = Math.max(tbRight, lnRight);
|
|
809
|
+
const maxY = Math.max(tbBottom, lnBottom);
|
|
810
|
+
return {
|
|
811
|
+
origin: { x: minX, y: minY },
|
|
812
|
+
size: { width: maxX - minX, height: maxY - minY }
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
const calloutVertexConfig = {
|
|
816
|
+
extractVertices: (a) => {
|
|
817
|
+
const textBox = computeTextBoxFromRD(a.rect, a.rectangleDifferences);
|
|
818
|
+
const cl = a.calloutLine;
|
|
819
|
+
if (!cl || cl.length < 3) {
|
|
820
|
+
return [
|
|
821
|
+
{ x: a.rect.origin.x, y: a.rect.origin.y },
|
|
822
|
+
{ x: a.rect.origin.x, y: a.rect.origin.y },
|
|
823
|
+
{ x: textBox.origin.x, y: textBox.origin.y },
|
|
824
|
+
{ x: textBox.origin.x + textBox.size.width, y: textBox.origin.y + textBox.size.height }
|
|
825
|
+
];
|
|
826
|
+
}
|
|
827
|
+
return [
|
|
828
|
+
cl[0],
|
|
829
|
+
cl[1],
|
|
830
|
+
{ x: textBox.origin.x, y: textBox.origin.y },
|
|
831
|
+
{ x: textBox.origin.x + textBox.size.width, y: textBox.origin.y + textBox.size.height }
|
|
832
|
+
];
|
|
833
|
+
},
|
|
834
|
+
transformAnnotation: (a, vertices) => {
|
|
835
|
+
if (vertices.length < 4) return {};
|
|
836
|
+
const [arrowTip, knee, tbTL, tbBR] = vertices;
|
|
837
|
+
const textBox = {
|
|
838
|
+
origin: { x: Math.min(tbTL.x, tbBR.x), y: Math.min(tbTL.y, tbBR.y) },
|
|
839
|
+
size: {
|
|
840
|
+
width: Math.abs(tbBR.x - tbTL.x),
|
|
841
|
+
height: Math.abs(tbBR.y - tbTL.y)
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
const connectionPoint = computeCalloutConnectionPoint(knee, textBox);
|
|
845
|
+
const calloutLine = [arrowTip, knee, connectionPoint];
|
|
846
|
+
const overallRect = computeCalloutOverallRect(
|
|
847
|
+
textBox,
|
|
848
|
+
calloutLine,
|
|
849
|
+
a.lineEnding,
|
|
850
|
+
a.strokeWidth ?? 1
|
|
851
|
+
);
|
|
852
|
+
return {
|
|
853
|
+
calloutLine,
|
|
854
|
+
rect: overallRect,
|
|
855
|
+
rectangleDifferences: computeRDFromTextBox(overallRect, textBox)
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
};
|
|
740
859
|
function createEnding(ending, strokeWidth, rad, px, py) {
|
|
741
860
|
if (!ending) return null;
|
|
742
861
|
const handler = LINE_ENDING_HANDLERS[ending];
|
|
@@ -1874,6 +1993,80 @@ const patchFreeText = (orig, ctx) => {
|
|
|
1874
1993
|
return ctx.changes;
|
|
1875
1994
|
}
|
|
1876
1995
|
};
|
|
1996
|
+
function rebuildFromVertices(orig, arrowTip, knee, tbTL, tbBR) {
|
|
1997
|
+
const textBox = {
|
|
1998
|
+
origin: { x: Math.min(tbTL.x, tbBR.x), y: Math.min(tbTL.y, tbBR.y) },
|
|
1999
|
+
size: {
|
|
2000
|
+
width: Math.abs(tbBR.x - tbTL.x),
|
|
2001
|
+
height: Math.abs(tbBR.y - tbTL.y)
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
const connectionPoint = computeCalloutConnectionPoint(knee, textBox);
|
|
2005
|
+
const calloutLine = [arrowTip, knee, connectionPoint];
|
|
2006
|
+
const overallRect = computeCalloutOverallRect(
|
|
2007
|
+
textBox,
|
|
2008
|
+
calloutLine,
|
|
2009
|
+
orig.lineEnding,
|
|
2010
|
+
orig.strokeWidth ?? 1
|
|
2011
|
+
);
|
|
2012
|
+
return {
|
|
2013
|
+
calloutLine,
|
|
2014
|
+
rect: overallRect,
|
|
2015
|
+
rectangleDifferences: computeRDFromTextBox(overallRect, textBox)
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
const patchCalloutFreeText = (orig, ctx) => {
|
|
2019
|
+
var _a, _b;
|
|
2020
|
+
switch (ctx.type) {
|
|
2021
|
+
case "vertex-edit": {
|
|
2022
|
+
if (!ctx.changes.calloutLine) return ctx.changes;
|
|
2023
|
+
const verts = ctx.changes.calloutLine;
|
|
2024
|
+
if (verts.length < 4) return ctx.changes;
|
|
2025
|
+
return rebuildFromVertices(orig, verts[0], verts[1], verts[2], verts[3]);
|
|
2026
|
+
}
|
|
2027
|
+
case "move": {
|
|
2028
|
+
if (!ctx.changes.rect) return ctx.changes;
|
|
2029
|
+
const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
|
|
2030
|
+
const movedLine = (_a = orig.calloutLine) == null ? void 0 : _a.map((p) => ({ x: p.x + dx, y: p.y + dy }));
|
|
2031
|
+
return {
|
|
2032
|
+
...rects,
|
|
2033
|
+
...movedLine && { calloutLine: movedLine }
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
case "rotate": {
|
|
2037
|
+
const result = baseRotateChanges(orig, ctx);
|
|
2038
|
+
if (!result) return ctx.changes;
|
|
2039
|
+
const { dx, dy } = rotateOrbitDelta(orig, result);
|
|
2040
|
+
const movedLine = (_b = orig.calloutLine) == null ? void 0 : _b.map((p) => ({ x: p.x + dx, y: p.y + dy }));
|
|
2041
|
+
return {
|
|
2042
|
+
...result,
|
|
2043
|
+
...movedLine && { calloutLine: movedLine }
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
case "property-update": {
|
|
2047
|
+
if (ctx.changes.lineEnding !== void 0 || ctx.changes.strokeWidth !== void 0) {
|
|
2048
|
+
const merged = { ...orig, ...ctx.changes };
|
|
2049
|
+
if (merged.calloutLine && merged.calloutLine.length >= 3) {
|
|
2050
|
+
const textBox = computeTextBoxFromRD(orig.rect, orig.rectangleDifferences);
|
|
2051
|
+
const overallRect = computeCalloutOverallRect(
|
|
2052
|
+
textBox,
|
|
2053
|
+
merged.calloutLine,
|
|
2054
|
+
merged.lineEnding,
|
|
2055
|
+
merged.strokeWidth ?? 1
|
|
2056
|
+
);
|
|
2057
|
+
return {
|
|
2058
|
+
...ctx.changes,
|
|
2059
|
+
rect: overallRect,
|
|
2060
|
+
rectangleDifferences: computeRDFromTextBox(overallRect, textBox)
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
return ctx.changes;
|
|
2065
|
+
}
|
|
2066
|
+
default:
|
|
2067
|
+
return ctx.changes;
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
1877
2070
|
const patchStamp = (orig, ctx) => {
|
|
1878
2071
|
switch (ctx.type) {
|
|
1879
2072
|
case "move":
|
|
@@ -1901,11 +2094,17 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
1901
2094
|
calculateAABBFromVertices,
|
|
1902
2095
|
calculateRotatedRectAABB,
|
|
1903
2096
|
calculateRotatedRectAABBAroundPoint,
|
|
2097
|
+
calloutVertexConfig,
|
|
1904
2098
|
clampAnnotationToPage,
|
|
1905
2099
|
compensateRotatedVertexEdit,
|
|
2100
|
+
computeCalloutConnectionPoint,
|
|
2101
|
+
computeCalloutOverallRect,
|
|
2102
|
+
computeRDFromTextBox,
|
|
2103
|
+
computeTextBoxFromRD,
|
|
1906
2104
|
createEnding,
|
|
1907
2105
|
getRectCenter,
|
|
1908
2106
|
lineRectWithEndings,
|
|
2107
|
+
patchCalloutFreeText,
|
|
1909
2108
|
patchCircle,
|
|
1910
2109
|
patchFreeText,
|
|
1911
2110
|
patchInk,
|
|
@@ -2138,6 +2337,232 @@ const freeTextHandlerFactory = {
|
|
|
2138
2337
|
};
|
|
2139
2338
|
}
|
|
2140
2339
|
};
|
|
2340
|
+
const CLICK_THRESHOLD = 5;
|
|
2341
|
+
const DEFAULT_TB_WIDTH = 150;
|
|
2342
|
+
const DEFAULT_TB_HEIGHT = 40;
|
|
2343
|
+
const calloutFreeTextHandlerFactory = {
|
|
2344
|
+
annotationType: PdfAnnotationSubtype.FREETEXT,
|
|
2345
|
+
create(context) {
|
|
2346
|
+
const { onCommit, onPreview, getTool, pageSize, pageIndex } = context;
|
|
2347
|
+
const [getPhase, setPhase] = useState("arrow");
|
|
2348
|
+
const [getArrowTip, setArrowTip] = useState(null);
|
|
2349
|
+
const [getKnee, setKnee] = useState(null);
|
|
2350
|
+
const [getDownPos, setDownPos] = useState(null);
|
|
2351
|
+
const [getTextBoxStart, setTextBoxStart] = useState(null);
|
|
2352
|
+
const [getDragging, setDragging] = useState(false);
|
|
2353
|
+
const clampToPage = (pos) => ({
|
|
2354
|
+
x: clamp(pos.x, 0, pageSize.width),
|
|
2355
|
+
y: clamp(pos.y, 0, pageSize.height)
|
|
2356
|
+
});
|
|
2357
|
+
const clampTextBox = (tb) => ({
|
|
2358
|
+
origin: {
|
|
2359
|
+
x: clamp(tb.origin.x, 0, pageSize.width - tb.size.width),
|
|
2360
|
+
y: clamp(tb.origin.y, 0, pageSize.height - tb.size.height)
|
|
2361
|
+
},
|
|
2362
|
+
size: tb.size
|
|
2363
|
+
});
|
|
2364
|
+
const getDefaults = () => {
|
|
2365
|
+
const tool = getTool();
|
|
2366
|
+
if (!tool) return null;
|
|
2367
|
+
return {
|
|
2368
|
+
...tool.defaults,
|
|
2369
|
+
fontColor: tool.defaults.fontColor ?? "#000000",
|
|
2370
|
+
opacity: tool.defaults.opacity ?? 1,
|
|
2371
|
+
fontSize: tool.defaults.fontSize ?? 12,
|
|
2372
|
+
fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
|
|
2373
|
+
color: tool.defaults.color ?? tool.defaults.backgroundColor ?? "transparent",
|
|
2374
|
+
textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
|
|
2375
|
+
verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
|
|
2376
|
+
contents: tool.defaults.contents ?? "Insert text",
|
|
2377
|
+
flags: tool.defaults.flags ?? ["print"],
|
|
2378
|
+
lineEnding: tool.defaults.lineEnding ?? PdfAnnotationLineEnding.OpenArrow,
|
|
2379
|
+
strokeColor: tool.defaults.strokeColor ?? "#000000",
|
|
2380
|
+
strokeWidth: tool.defaults.strokeWidth ?? 1
|
|
2381
|
+
};
|
|
2382
|
+
};
|
|
2383
|
+
const isClick = (a, b) => Math.abs(a.x - b.x) < CLICK_THRESHOLD && Math.abs(a.y - b.y) < CLICK_THRESHOLD;
|
|
2384
|
+
const buildPreview = (cursor) => {
|
|
2385
|
+
const defaults = getDefaults();
|
|
2386
|
+
if (!defaults) return null;
|
|
2387
|
+
const arrowTip = getArrowTip();
|
|
2388
|
+
const knee = getKnee();
|
|
2389
|
+
const phase = getPhase();
|
|
2390
|
+
if (phase === "knee" && arrowTip) {
|
|
2391
|
+
const calloutLine = [arrowTip, cursor];
|
|
2392
|
+
const minX = Math.min(arrowTip.x, cursor.x);
|
|
2393
|
+
const minY = Math.min(arrowTip.y, cursor.y);
|
|
2394
|
+
const w = Math.abs(arrowTip.x - cursor.x);
|
|
2395
|
+
const h = Math.abs(arrowTip.y - cursor.y);
|
|
2396
|
+
const bounds = {
|
|
2397
|
+
origin: { x: minX, y: minY },
|
|
2398
|
+
size: { width: Math.max(w, 1), height: Math.max(h, 1) }
|
|
2399
|
+
};
|
|
2400
|
+
return {
|
|
2401
|
+
type: PdfAnnotationSubtype.FREETEXT,
|
|
2402
|
+
bounds,
|
|
2403
|
+
data: {
|
|
2404
|
+
...defaults,
|
|
2405
|
+
rect: bounds,
|
|
2406
|
+
calloutLine
|
|
2407
|
+
}
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
if (phase === "textbox" && arrowTip && knee) {
|
|
2411
|
+
const tbStart = getTextBoxStart();
|
|
2412
|
+
let textBox;
|
|
2413
|
+
if (getDragging() && tbStart) {
|
|
2414
|
+
textBox = {
|
|
2415
|
+
origin: { x: Math.min(tbStart.x, cursor.x), y: Math.min(tbStart.y, cursor.y) },
|
|
2416
|
+
size: {
|
|
2417
|
+
width: Math.max(Math.abs(cursor.x - tbStart.x), 20),
|
|
2418
|
+
height: Math.max(Math.abs(cursor.y - tbStart.y), 14)
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
} else {
|
|
2422
|
+
textBox = {
|
|
2423
|
+
origin: { x: cursor.x - DEFAULT_TB_WIDTH / 2, y: cursor.y - DEFAULT_TB_HEIGHT / 2 },
|
|
2424
|
+
size: { width: DEFAULT_TB_WIDTH, height: DEFAULT_TB_HEIGHT }
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
textBox = clampTextBox(textBox);
|
|
2428
|
+
const connectionPoint = computeCalloutConnectionPoint(knee, textBox);
|
|
2429
|
+
const calloutLine = [arrowTip, knee, connectionPoint];
|
|
2430
|
+
const overallRect = computeCalloutOverallRect(
|
|
2431
|
+
textBox,
|
|
2432
|
+
calloutLine,
|
|
2433
|
+
defaults.lineEnding,
|
|
2434
|
+
defaults.strokeWidth
|
|
2435
|
+
);
|
|
2436
|
+
return {
|
|
2437
|
+
type: PdfAnnotationSubtype.FREETEXT,
|
|
2438
|
+
bounds: overallRect,
|
|
2439
|
+
data: {
|
|
2440
|
+
...defaults,
|
|
2441
|
+
rect: overallRect,
|
|
2442
|
+
calloutLine,
|
|
2443
|
+
textBox
|
|
2444
|
+
}
|
|
2445
|
+
};
|
|
2446
|
+
}
|
|
2447
|
+
return null;
|
|
2448
|
+
};
|
|
2449
|
+
const commitCallout = (tb) => {
|
|
2450
|
+
const defaults = getDefaults();
|
|
2451
|
+
const arrowTip = getArrowTip();
|
|
2452
|
+
const knee = getKnee();
|
|
2453
|
+
if (!defaults || !arrowTip || !knee) return;
|
|
2454
|
+
const textBox = clampTextBox(tb);
|
|
2455
|
+
const connectionPoint = computeCalloutConnectionPoint(knee, textBox);
|
|
2456
|
+
const calloutLine = [arrowTip, knee, connectionPoint];
|
|
2457
|
+
const overallRect = computeCalloutOverallRect(
|
|
2458
|
+
textBox,
|
|
2459
|
+
calloutLine,
|
|
2460
|
+
defaults.lineEnding,
|
|
2461
|
+
defaults.strokeWidth
|
|
2462
|
+
);
|
|
2463
|
+
const rd = computeRDFromTextBox(overallRect, textBox);
|
|
2464
|
+
const anno = {
|
|
2465
|
+
...defaults,
|
|
2466
|
+
type: PdfAnnotationSubtype.FREETEXT,
|
|
2467
|
+
intent: "FreeTextCallout",
|
|
2468
|
+
rect: overallRect,
|
|
2469
|
+
rectangleDifferences: rd,
|
|
2470
|
+
calloutLine,
|
|
2471
|
+
pageIndex,
|
|
2472
|
+
id: uuidV4(),
|
|
2473
|
+
created: /* @__PURE__ */ new Date()
|
|
2474
|
+
};
|
|
2475
|
+
onCommit(anno);
|
|
2476
|
+
resetState();
|
|
2477
|
+
};
|
|
2478
|
+
const resetState = () => {
|
|
2479
|
+
setPhase("arrow");
|
|
2480
|
+
setArrowTip(null);
|
|
2481
|
+
setKnee(null);
|
|
2482
|
+
setDownPos(null);
|
|
2483
|
+
setTextBoxStart(null);
|
|
2484
|
+
setDragging(false);
|
|
2485
|
+
onPreview(null);
|
|
2486
|
+
};
|
|
2487
|
+
return {
|
|
2488
|
+
onPointerDown: (pos, evt) => {
|
|
2489
|
+
var _a, _b;
|
|
2490
|
+
const clampedPos = clampToPage(pos);
|
|
2491
|
+
const phase = getPhase();
|
|
2492
|
+
if (phase === "arrow" || phase === "knee") {
|
|
2493
|
+
setDownPos(clampedPos);
|
|
2494
|
+
(_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
|
|
2495
|
+
} else if (phase === "textbox") {
|
|
2496
|
+
setTextBoxStart(clampedPos);
|
|
2497
|
+
setDragging(true);
|
|
2498
|
+
(_b = evt.setPointerCapture) == null ? void 0 : _b.call(evt);
|
|
2499
|
+
}
|
|
2500
|
+
},
|
|
2501
|
+
onPointerMove: (pos) => {
|
|
2502
|
+
const clampedPos = clampToPage(pos);
|
|
2503
|
+
const phase = getPhase();
|
|
2504
|
+
if (phase === "textbox" && getDragging()) {
|
|
2505
|
+
onPreview(buildPreview(clampedPos));
|
|
2506
|
+
} else if (phase === "knee" || phase === "textbox") {
|
|
2507
|
+
onPreview(buildPreview(clampedPos));
|
|
2508
|
+
}
|
|
2509
|
+
},
|
|
2510
|
+
onPointerUp: (pos, evt) => {
|
|
2511
|
+
var _a, _b, _c, _d;
|
|
2512
|
+
const clampedPos = clampToPage(pos);
|
|
2513
|
+
const phase = getPhase();
|
|
2514
|
+
const downPos = getDownPos();
|
|
2515
|
+
if (phase === "arrow" && downPos && isClick(downPos, clampedPos)) {
|
|
2516
|
+
setArrowTip(clampedPos);
|
|
2517
|
+
setPhase("knee");
|
|
2518
|
+
setDownPos(null);
|
|
2519
|
+
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
if (phase === "knee" && downPos && isClick(downPos, clampedPos)) {
|
|
2523
|
+
setKnee(clampedPos);
|
|
2524
|
+
setPhase("textbox");
|
|
2525
|
+
setDownPos(null);
|
|
2526
|
+
(_b = evt.releasePointerCapture) == null ? void 0 : _b.call(evt);
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2529
|
+
if (phase === "textbox" && getDragging()) {
|
|
2530
|
+
const tbStart = getTextBoxStart();
|
|
2531
|
+
if (tbStart) {
|
|
2532
|
+
const minX = Math.min(tbStart.x, clampedPos.x);
|
|
2533
|
+
const minY = Math.min(tbStart.y, clampedPos.y);
|
|
2534
|
+
const w = Math.abs(clampedPos.x - tbStart.x);
|
|
2535
|
+
const h = Math.abs(clampedPos.y - tbStart.y);
|
|
2536
|
+
if (w > 5 || h > 5) {
|
|
2537
|
+
const textBox = {
|
|
2538
|
+
origin: { x: minX, y: minY },
|
|
2539
|
+
size: { width: Math.max(w, 20), height: Math.max(h, 14) }
|
|
2540
|
+
};
|
|
2541
|
+
commitCallout(textBox);
|
|
2542
|
+
} else {
|
|
2543
|
+
commitCallout({
|
|
2544
|
+
origin: {
|
|
2545
|
+
x: tbStart.x - DEFAULT_TB_WIDTH / 2,
|
|
2546
|
+
y: tbStart.y - DEFAULT_TB_HEIGHT / 2
|
|
2547
|
+
},
|
|
2548
|
+
size: { width: DEFAULT_TB_WIDTH, height: DEFAULT_TB_HEIGHT }
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
(_c = evt.releasePointerCapture) == null ? void 0 : _c.call(evt);
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
setDownPos(null);
|
|
2556
|
+
(_d = evt.releasePointerCapture) == null ? void 0 : _d.call(evt);
|
|
2557
|
+
},
|
|
2558
|
+
onPointerCancel: (_, evt) => {
|
|
2559
|
+
var _a;
|
|
2560
|
+
resetState();
|
|
2561
|
+
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
2562
|
+
}
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
};
|
|
2141
2566
|
const lineHandlerFactory = {
|
|
2142
2567
|
annotationType: PdfAnnotationSubtype.LINE,
|
|
2143
2568
|
create(context) {
|
|
@@ -3665,7 +4090,7 @@ const freeTextTools = [
|
|
|
3665
4090
|
name: "Free Text",
|
|
3666
4091
|
labelKey: "annotation.freeText",
|
|
3667
4092
|
categories: ["annotation", "markup"],
|
|
3668
|
-
matchScore: (a) => a.type === PdfAnnotationSubtype.FREETEXT ? 1 : 0,
|
|
4093
|
+
matchScore: (a) => a.type === PdfAnnotationSubtype.FREETEXT && a.intent !== "FreeTextCallout" ? 1 : 0,
|
|
3669
4094
|
interaction: {
|
|
3670
4095
|
exclusive: false,
|
|
3671
4096
|
cursor: "crosshair",
|
|
@@ -3705,6 +4130,44 @@ const freeTextTools = [
|
|
|
3705
4130
|
pointerHandler: freeTextHandlerFactory
|
|
3706
4131
|
}
|
|
3707
4132
|
];
|
|
4133
|
+
const calloutFreeTextTools = [
|
|
4134
|
+
{
|
|
4135
|
+
id: "freeTextCallout",
|
|
4136
|
+
name: "Callout",
|
|
4137
|
+
labelKey: "annotation.callout",
|
|
4138
|
+
categories: ["annotation", "markup"],
|
|
4139
|
+
matchScore: (a) => a.type === PdfAnnotationSubtype.FREETEXT && a.intent === "FreeTextCallout" ? 10 : 0,
|
|
4140
|
+
interaction: {
|
|
4141
|
+
exclusive: false,
|
|
4142
|
+
cursor: "crosshair",
|
|
4143
|
+
isDraggable: true,
|
|
4144
|
+
isResizable: false,
|
|
4145
|
+
isRotatable: false
|
|
4146
|
+
},
|
|
4147
|
+
defaults: {
|
|
4148
|
+
type: PdfAnnotationSubtype.FREETEXT,
|
|
4149
|
+
intent: "FreeTextCallout",
|
|
4150
|
+
contents: "Insert text",
|
|
4151
|
+
fontSize: 14,
|
|
4152
|
+
fontColor: "#E44234",
|
|
4153
|
+
fontFamily: PdfStandardFont.Helvetica,
|
|
4154
|
+
textAlign: PdfTextAlignment.Left,
|
|
4155
|
+
verticalAlign: PdfVerticalAlignment.Top,
|
|
4156
|
+
color: "transparent",
|
|
4157
|
+
opacity: 1,
|
|
4158
|
+
lineEnding: PdfAnnotationLineEnding.OpenArrow,
|
|
4159
|
+
strokeColor: "#E44234",
|
|
4160
|
+
strokeWidth: 1
|
|
4161
|
+
},
|
|
4162
|
+
behavior: {
|
|
4163
|
+
insertUpright: true,
|
|
4164
|
+
editAfterCreate: true,
|
|
4165
|
+
selectAfterCreate: true
|
|
4166
|
+
},
|
|
4167
|
+
transform: patchCalloutFreeText,
|
|
4168
|
+
pointerHandler: calloutFreeTextHandlerFactory
|
|
4169
|
+
}
|
|
4170
|
+
];
|
|
3708
4171
|
const stampTools = [
|
|
3709
4172
|
{
|
|
3710
4173
|
id: "stamp",
|
|
@@ -3771,6 +4234,7 @@ const defaultTools = [
|
|
|
3771
4234
|
...polygonTools,
|
|
3772
4235
|
...textCommentTools,
|
|
3773
4236
|
...freeTextTools,
|
|
4237
|
+
...calloutFreeTextTools,
|
|
3774
4238
|
...stampTools,
|
|
3775
4239
|
...linkTools
|
|
3776
4240
|
];
|
|
@@ -4301,7 +4765,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
4301
4765
|
const docState = this.state.documents[documentId];
|
|
4302
4766
|
if (!docState) return;
|
|
4303
4767
|
const pageAnnotations = (docState.pages[pageIndex] ?? []).map((uid) => docState.byUid[uid]).filter((ta) => ta !== void 0).filter((ta) => isSelectableAnnotation(ta));
|
|
4304
|
-
const selectedIds = pageAnnotations.filter((ta) => rectsIntersect(rect, ta.object.rect)).filter((ta) =>
|
|
4768
|
+
const selectedIds = pageAnnotations.filter((ta) => rectsIntersect(rect, ta.object.rect)).filter((ta) => this.isAnnotationSelectable(ta.object, documentId)).map((ta) => ta.object.id);
|
|
4305
4769
|
if (selectedIds.length > 0) {
|
|
4306
4770
|
const expandedIds = /* @__PURE__ */ new Set();
|
|
4307
4771
|
for (const id of selectedIds) {
|
|
@@ -4400,6 +4864,9 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
4400
4864
|
setLocked: (mode, documentId) => this.setLocked(mode, documentId),
|
|
4401
4865
|
getLocked: (documentId) => this.getLocked(documentId),
|
|
4402
4866
|
isAnnotationLocked: (annotation, documentId) => this.isAnnotationLocked(annotation, documentId),
|
|
4867
|
+
isAnnotationInteractive: (annotation, documentId) => this.isAnnotationInteractive(annotation, documentId),
|
|
4868
|
+
isAnnotationStructurallyLocked: (annotation, documentId) => this.isAnnotationStructurallyLocked(annotation, documentId),
|
|
4869
|
+
isAnnotationContentLocked: (annotation, documentId) => this.isAnnotationContentLocked(annotation, documentId),
|
|
4403
4870
|
isCategoryLocked: (category, documentId) => this.isCategoryLockedMethod(category, documentId),
|
|
4404
4871
|
isToolLocked: (toolId, documentId) => this.isToolLockedMethod(toolId, documentId),
|
|
4405
4872
|
// Sync (lightweight state update without commit)
|
|
@@ -4474,6 +4941,9 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
4474
4941
|
setLocked: (mode) => this.setLocked(mode, documentId),
|
|
4475
4942
|
getLocked: () => this.getLocked(documentId),
|
|
4476
4943
|
isAnnotationLocked: (annotation) => this.isAnnotationLocked(annotation, documentId),
|
|
4944
|
+
isAnnotationInteractive: (annotation) => this.isAnnotationInteractive(annotation, documentId),
|
|
4945
|
+
isAnnotationStructurallyLocked: (annotation) => this.isAnnotationStructurallyLocked(annotation, documentId),
|
|
4946
|
+
isAnnotationContentLocked: (annotation) => this.isAnnotationContentLocked(annotation, documentId),
|
|
4477
4947
|
isCategoryLocked: (category) => this.isCategoryLockedMethod(category, documentId),
|
|
4478
4948
|
isToolLocked: (toolId) => this.isToolLockedMethod(toolId, documentId),
|
|
4479
4949
|
syncAnnotationObject: (id, patch) => this.syncAnnotationObject(id, patch, documentId),
|
|
@@ -5016,7 +5486,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
5016
5486
|
selectAnnotation(pageIndex, id, documentId) {
|
|
5017
5487
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
5018
5488
|
const ta = this.getAnnotationById(id, docId);
|
|
5019
|
-
if (ta && this.
|
|
5489
|
+
if (ta && !this.isAnnotationSelectable(ta.object, docId)) return;
|
|
5020
5490
|
if (this.isInGroupMethod(id, docId)) {
|
|
5021
5491
|
const members = this.getGroupMembersMethod(id, docId);
|
|
5022
5492
|
const memberIds = members.map((m) => m.object.id);
|
|
@@ -5062,7 +5532,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
5062
5532
|
toggleSelectionMethod(pageIndex, id, documentId) {
|
|
5063
5533
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
5064
5534
|
const ta = this.getAnnotationById(id, docId);
|
|
5065
|
-
if (ta && this.
|
|
5535
|
+
if (ta && !this.isAnnotationSelectable(ta.object, docId)) return;
|
|
5066
5536
|
const docState = this.getDocumentState(docId);
|
|
5067
5537
|
if (docState.selectedUids.includes(id)) {
|
|
5068
5538
|
this.dispatch(removeFromSelection(docId, id));
|
|
@@ -5082,7 +5552,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
5082
5552
|
addToSelectionMethod(pageIndex, id, documentId) {
|
|
5083
5553
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
5084
5554
|
const ta = this.getAnnotationById(id, docId);
|
|
5085
|
-
if (ta && this.
|
|
5555
|
+
if (ta && !this.isAnnotationInteractive(ta.object, docId)) return;
|
|
5086
5556
|
this.dispatch(addToSelection(docId, pageIndex, id));
|
|
5087
5557
|
}
|
|
5088
5558
|
removeFromSelectionMethod(id, documentId) {
|
|
@@ -5094,7 +5564,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
5094
5564
|
const docState = this.getDocumentState(docId);
|
|
5095
5565
|
const filtered = ids.filter((id) => {
|
|
5096
5566
|
const ta = docState.byUid[id];
|
|
5097
|
-
return !ta ||
|
|
5567
|
+
return !ta || this.isAnnotationInteractive(ta.object, docId);
|
|
5098
5568
|
});
|
|
5099
5569
|
this.dispatch(setSelection(docId, filtered));
|
|
5100
5570
|
}
|
|
@@ -6082,7 +6552,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
6082
6552
|
const lockedSelected = updatedDocState.selectedUids.filter((uid) => {
|
|
6083
6553
|
const ta = updatedDocState.byUid[uid];
|
|
6084
6554
|
if (!ta) return false;
|
|
6085
|
-
return this.
|
|
6555
|
+
return !this.isAnnotationInteractive(ta.object, docId);
|
|
6086
6556
|
});
|
|
6087
6557
|
if (lockedSelected.length > 0) {
|
|
6088
6558
|
const remaining = updatedDocState.selectedUids.filter((uid) => !lockedSelected.includes(uid));
|
|
@@ -6098,6 +6568,47 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
6098
6568
|
const categories = getAnnotationCategories(tool);
|
|
6099
6569
|
return isCategoryLocked(categories, this.getLocked(documentId));
|
|
6100
6570
|
}
|
|
6571
|
+
/**
|
|
6572
|
+
* Whether the annotation can be interacted with at all (selected, edited, menu shown).
|
|
6573
|
+
* Returns false for `noView`, `hidden`, `readOnly`, or category-locked annotations.
|
|
6574
|
+
* This is the spec-compliant "can the user do anything with this?" predicate.
|
|
6575
|
+
*/
|
|
6576
|
+
isAnnotationInteractive(annotation, documentId) {
|
|
6577
|
+
if (hasNoViewFlag(annotation)) return false;
|
|
6578
|
+
if (hasHiddenFlag(annotation)) return false;
|
|
6579
|
+
if (hasReadOnlyFlag(annotation)) return false;
|
|
6580
|
+
const tool = this.findToolForAnnotation(annotation);
|
|
6581
|
+
const categories = getAnnotationCategories(tool);
|
|
6582
|
+
if (isCategoryLocked(categories, this.getLocked(documentId))) return false;
|
|
6583
|
+
return true;
|
|
6584
|
+
}
|
|
6585
|
+
/**
|
|
6586
|
+
* Whether the annotation's structure (position, size, rotation, vertices) is locked.
|
|
6587
|
+
* Non-interactive annotations are treated as structurally locked. PDF `locked` flag
|
|
6588
|
+
* makes an otherwise-interactive annotation structurally locked while still selectable.
|
|
6589
|
+
*/
|
|
6590
|
+
isAnnotationStructurallyLocked(annotation, documentId) {
|
|
6591
|
+
if (!this.isAnnotationInteractive(annotation, documentId)) return true;
|
|
6592
|
+
return hasLockedFlag(annotation);
|
|
6593
|
+
}
|
|
6594
|
+
/**
|
|
6595
|
+
* Whether the annotation's content (e.g. FreeText text) is locked.
|
|
6596
|
+
* Non-interactive annotations are treated as content-locked. PDF `lockedContents`
|
|
6597
|
+
* flag blocks content edits while still allowing move/resize.
|
|
6598
|
+
*/
|
|
6599
|
+
isAnnotationContentLocked(annotation, documentId) {
|
|
6600
|
+
if (!this.isAnnotationInteractive(annotation, documentId)) return true;
|
|
6601
|
+
return hasLockedContentsFlag(annotation);
|
|
6602
|
+
}
|
|
6603
|
+
/**
|
|
6604
|
+
* Whether a specific annotation is eligible to be added to the selection store.
|
|
6605
|
+
* Differs from the subtype-based `isSelectableAnnotation` helper: that answers
|
|
6606
|
+
* "is this subtype ever selectable?", while this answers "is this specific
|
|
6607
|
+
* annotation selectable right now given its flags and document lock state?".
|
|
6608
|
+
*/
|
|
6609
|
+
isAnnotationSelectable(annotation, documentId) {
|
|
6610
|
+
return this.isAnnotationInteractive(annotation, documentId);
|
|
6611
|
+
}
|
|
6101
6612
|
isCategoryLockedMethod(category, documentId) {
|
|
6102
6613
|
return isCategoryLocked([category], this.getLocked(documentId));
|
|
6103
6614
|
}
|
|
@@ -6430,9 +6941,13 @@ export {
|
|
|
6430
6941
|
getSidebarAnnotationsWithRepliesGroupedByPage,
|
|
6431
6942
|
getToolDefaultsById,
|
|
6432
6943
|
hasAttachedLinks,
|
|
6944
|
+
hasHiddenFlag,
|
|
6433
6945
|
hasIRTChildren,
|
|
6946
|
+
hasLockedContentsFlag,
|
|
6434
6947
|
hasLockedFlag,
|
|
6435
6948
|
hasMultipleSelected,
|
|
6949
|
+
hasNoViewFlag,
|
|
6950
|
+
hasReadOnlyFlag,
|
|
6436
6951
|
inferRotationCenterFromRects2 as inferRotationCenterFromRects,
|
|
6437
6952
|
initialDocumentState,
|
|
6438
6953
|
initialState,
|