@embedpdf/plugin-annotation 1.0.13 → 1.0.14

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.
@@ -1,12 +1,13 @@
1
1
  import { usePlugin, useCapability } from "@embedpdf/core/react";
2
- import { AnnotationPlugin, patching, getAnnotationsByPageIndex, getSelectedAnnotationByPageIndex, isInk, isSquare, isCircle, isUnderline, isStrikeout, isSquiggly, isHighlight, isLine, isPolyline, isPolygon, makeVariantKey } from "@embedpdf/plugin-annotation";
2
+ import { AnnotationPlugin, patching, getAnnotationsByPageIndex, getSelectedAnnotationByPageIndex, isInk, isSquare, isCircle, isUnderline, isStrikeout, isSquiggly, isHighlight, isLine, isPolyline, isPolygon, isFreeText } from "@embedpdf/plugin-annotation";
3
3
  import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
4
- import { restoreOffset, rectEquals, PdfAnnotationBorderStyle, PdfAnnotationSubtype, expandRect, rectFromPoints, blendModeToCss, PdfBlendMode } from "@embedpdf/models";
4
+ import { restoreOffset, rectEquals, PdfAnnotationBorderStyle, PdfAnnotationSubtype, expandRect, rectFromPoints, textAlignmentToCss, standardFontCss, PdfVerticalAlignment, blendModeToCss, PdfBlendMode } from "@embedpdf/models";
5
5
  import { usePointerHandlers } from "@embedpdf/plugin-interaction-manager/react";
6
6
  import { useSelectionCapability } from "@embedpdf/plugin-selection/react";
7
7
  import { Fragment, useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from "react";
8
8
  const useAnnotationPlugin = () => usePlugin(AnnotationPlugin.id);
9
9
  const useAnnotationCapability = () => useCapability(AnnotationPlugin.id);
10
+ const mapDoubleClick = (handler) => handler ? { onDoubleClick: handler } : {};
10
11
  function getCounterRotation(rect, rotation) {
11
12
  const { width: w, height: h } = rect.size;
12
13
  switch (rotation % 4) {
@@ -337,6 +338,7 @@ function AnnotationContainer({
337
338
  computeVertices,
338
339
  computePatch,
339
340
  selectionMenu,
341
+ onDoubleClick,
340
342
  ...props
341
343
  }) {
342
344
  const { provides: annotationProvides } = useAnnotationCapability();
@@ -375,6 +377,7 @@ function AnnotationContainer({
375
377
  "div",
376
378
  {
377
379
  ...rootHandlers,
380
+ ...mapDoubleClick(onDoubleClick),
378
381
  style: {
379
382
  position: "absolute",
380
383
  outline: isSelected ? "1px solid #007ACC" : "none",
@@ -385,6 +388,9 @@ function AnnotationContainer({
385
388
  height: `${currentRect.size.height * scale}px`,
386
389
  pointerEvents: isSelected ? "auto" : "none",
387
390
  cursor: isSelected && isDraggable ? "move" : "default",
391
+ ...isSelected && {
392
+ zIndex: 3
393
+ },
388
394
  ...style
389
395
  },
390
396
  ...props,
@@ -1191,6 +1197,78 @@ const patchPolygon = (orig, ctx) => {
1191
1197
  vertices: moved
1192
1198
  };
1193
1199
  };
1200
+ function FreeText({
1201
+ isSelected,
1202
+ isEditing,
1203
+ annotation,
1204
+ pageIndex,
1205
+ scale,
1206
+ onClick
1207
+ }) {
1208
+ const editorRef = useRef(null);
1209
+ const { provides: annotationProvides } = useAnnotationCapability();
1210
+ useEffect(() => {
1211
+ if (isEditing && editorRef.current) {
1212
+ const editor = editorRef.current;
1213
+ editor.focus();
1214
+ const selection = window.getSelection();
1215
+ if (selection) {
1216
+ const range = document.createRange();
1217
+ range.selectNodeContents(editor);
1218
+ range.collapse(false);
1219
+ selection.removeAllRanges();
1220
+ selection.addRange(range);
1221
+ }
1222
+ }
1223
+ }, [isEditing]);
1224
+ const handleBlur = () => {
1225
+ if (!annotationProvides) return;
1226
+ if (!editorRef.current) return;
1227
+ annotationProvides.updateAnnotation(pageIndex, annotation.localId, {
1228
+ contents: editorRef.current.innerText
1229
+ });
1230
+ };
1231
+ return /* @__PURE__ */ jsx(
1232
+ "div",
1233
+ {
1234
+ style: {
1235
+ position: "absolute",
1236
+ width: annotation.object.rect.size.width * scale,
1237
+ height: annotation.object.rect.size.height * scale,
1238
+ cursor: isSelected && !isEditing ? "move" : "default",
1239
+ pointerEvents: isSelected && !isEditing ? "none" : "auto",
1240
+ zIndex: 2
1241
+ },
1242
+ onPointerDown: onClick,
1243
+ children: /* @__PURE__ */ jsx(
1244
+ "span",
1245
+ {
1246
+ ref: editorRef,
1247
+ onBlur: handleBlur,
1248
+ style: {
1249
+ color: annotation.object.fontColor,
1250
+ fontSize: annotation.object.fontSize * scale,
1251
+ fontFamily: standardFontCss(annotation.object.fontFamily),
1252
+ textAlign: textAlignmentToCss(annotation.object.textAlign),
1253
+ flexDirection: "column",
1254
+ justifyContent: annotation.object.verticalAlign === PdfVerticalAlignment.Top ? "flex-start" : annotation.object.verticalAlign === PdfVerticalAlignment.Middle ? "center" : "flex-end",
1255
+ display: "flex",
1256
+ backgroundColor: annotation.object.backgroundColor,
1257
+ opacity: annotation.object.opacity,
1258
+ width: "100%",
1259
+ height: "100%",
1260
+ lineHeight: "1.18",
1261
+ overflow: "hidden",
1262
+ cursor: isEditing ? "text" : "pointer"
1263
+ },
1264
+ contentEditable: isEditing,
1265
+ suppressContentEditableWarning: true,
1266
+ children: annotation.object.contents
1267
+ }
1268
+ )
1269
+ }
1270
+ );
1271
+ }
1194
1272
  function Annotations(annotationsProps) {
1195
1273
  const { pageIndex, scale, selectionMenu } = annotationsProps;
1196
1274
  const { provides: annotationProvides } = useAnnotationCapability();
@@ -1198,6 +1276,7 @@ function Annotations(annotationsProps) {
1198
1276
  const [annotations, setAnnotations] = useState([]);
1199
1277
  const { register } = usePointerHandlers({ pageIndex });
1200
1278
  const [selectionState, setSelectionState] = useState(null);
1279
+ const [editingId, setEditingId] = useState(null);
1201
1280
  useEffect(() => {
1202
1281
  if (annotationProvides) {
1203
1282
  annotationProvides.onStateChange((state) => {
@@ -1211,6 +1290,7 @@ function Annotations(annotationsProps) {
1211
1290
  onPointerDown: (_, pe) => {
1212
1291
  if (pe.target === pe.currentTarget && annotationProvides) {
1213
1292
  annotationProvides.deselectAnnotation();
1293
+ setEditingId(null);
1214
1294
  }
1215
1295
  }
1216
1296
  }),
@@ -1222,6 +1302,7 @@ function Annotations(annotationsProps) {
1222
1302
  if (annotationProvides && selectionProvides) {
1223
1303
  annotationProvides.selectAnnotation(pageIndex, annotation.localId);
1224
1304
  selectionProvides.clear();
1305
+ setEditingId(null);
1225
1306
  }
1226
1307
  },
1227
1308
  [annotationProvides, selectionProvides, pageIndex]
@@ -1231,6 +1312,7 @@ function Annotations(annotationsProps) {
1231
1312
  }, [register, handlers]);
1232
1313
  return /* @__PURE__ */ jsx(Fragment$1, { children: annotations.map((annotation) => {
1233
1314
  const isSelected = (selectionState == null ? void 0 : selectionState.localId) === annotation.localId;
1315
+ const isEditing = editingId === annotation.localId;
1234
1316
  if (isInk(annotation)) {
1235
1317
  return /* @__PURE__ */ jsx(
1236
1318
  AnnotationContainer,
@@ -1546,6 +1628,42 @@ function Annotations(annotationsProps) {
1546
1628
  annotation.localId
1547
1629
  );
1548
1630
  }
1631
+ if (isFreeText(annotation)) {
1632
+ return /* @__PURE__ */ jsx(
1633
+ AnnotationContainer,
1634
+ {
1635
+ trackedAnnotation: annotation,
1636
+ isSelected,
1637
+ isDraggable: true,
1638
+ isResizable: true,
1639
+ selectionMenu,
1640
+ outlineOffset: 6,
1641
+ onDoubleClick: (e) => {
1642
+ e.stopPropagation();
1643
+ setEditingId(annotation.localId);
1644
+ },
1645
+ style: {
1646
+ mixBlendMode: blendModeToCss(annotation.object.blendMode ?? PdfBlendMode.Normal)
1647
+ },
1648
+ ...annotationsProps,
1649
+ children: (object) => /* @__PURE__ */ jsx(
1650
+ FreeText,
1651
+ {
1652
+ isSelected,
1653
+ isEditing,
1654
+ annotation: {
1655
+ ...annotation,
1656
+ object
1657
+ },
1658
+ pageIndex,
1659
+ scale,
1660
+ onClick: (e) => handleClick(e, annotation)
1661
+ }
1662
+ )
1663
+ },
1664
+ annotation.localId
1665
+ );
1666
+ }
1549
1667
  return null;
1550
1668
  }) });
1551
1669
  }
@@ -1570,8 +1688,9 @@ function TextMarkup({ pageIndex, scale }) {
1570
1688
  return off;
1571
1689
  }, [annotationProvides]);
1572
1690
  if (!boundingRect) return null;
1573
- switch (activeTool.variantKey) {
1574
- case makeVariantKey(PdfAnnotationSubtype.UNDERLINE):
1691
+ if (!activeTool.defaults) return null;
1692
+ switch (activeTool.defaults.subtype) {
1693
+ case PdfAnnotationSubtype.UNDERLINE:
1575
1694
  return /* @__PURE__ */ jsx(
1576
1695
  "div",
1577
1696
  {
@@ -1592,7 +1711,7 @@ function TextMarkup({ pageIndex, scale }) {
1592
1711
  )
1593
1712
  }
1594
1713
  );
1595
- case makeVariantKey(PdfAnnotationSubtype.HIGHLIGHT):
1714
+ case PdfAnnotationSubtype.HIGHLIGHT:
1596
1715
  return /* @__PURE__ */ jsx(
1597
1716
  "div",
1598
1717
  {
@@ -1613,7 +1732,7 @@ function TextMarkup({ pageIndex, scale }) {
1613
1732
  )
1614
1733
  }
1615
1734
  );
1616
- case makeVariantKey(PdfAnnotationSubtype.STRIKEOUT):
1735
+ case PdfAnnotationSubtype.STRIKEOUT:
1617
1736
  return /* @__PURE__ */ jsx(
1618
1737
  "div",
1619
1738
  {
@@ -1634,7 +1753,7 @@ function TextMarkup({ pageIndex, scale }) {
1634
1753
  )
1635
1754
  }
1636
1755
  );
1637
- case makeVariantKey(PdfAnnotationSubtype.SQUIGGLY):
1756
+ case PdfAnnotationSubtype.SQUIGGLY:
1638
1757
  return /* @__PURE__ */ jsx(
1639
1758
  "div",
1640
1759
  {
@@ -2536,6 +2655,138 @@ const PolygonPaint = ({
2536
2655
  }
2537
2656
  );
2538
2657
  };
2658
+ const FreeTextPaint = ({
2659
+ pageIndex,
2660
+ scale,
2661
+ pageWidth,
2662
+ pageHeight,
2663
+ cursor = "text"
2664
+ }) => {
2665
+ const { provides: annotationProvides } = useAnnotationCapability();
2666
+ const [activeTool, setActiveTool] = useState({ variantKey: null, defaults: null });
2667
+ useEffect(() => annotationProvides == null ? void 0 : annotationProvides.onActiveToolChange(setActiveTool), [annotationProvides]);
2668
+ if (!activeTool.defaults || activeTool.defaults.subtype !== PdfAnnotationSubtype.FREETEXT)
2669
+ return null;
2670
+ const toolFontColor = activeTool.defaults.fontColor ?? "#000000";
2671
+ const toolOpacity = activeTool.defaults.opacity ?? 1;
2672
+ const toolFontSize = activeTool.defaults.fontSize ?? 12;
2673
+ const toolFontFamily = activeTool.defaults.fontFamily;
2674
+ const toolBackgroundColor = activeTool.defaults.backgroundColor ?? "transparent";
2675
+ const toolTextAlign = activeTool.defaults.textAlign;
2676
+ const toolVerticalAlign = activeTool.defaults.verticalAlign;
2677
+ const toolContent = activeTool.defaults.content ?? "Insert text here";
2678
+ const { register } = usePointerHandlers({ modeId: "freeText", pageIndex });
2679
+ const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
2680
+ const pageWidthPDF = pageWidth / scale;
2681
+ const pageHeightPDF = pageHeight / scale;
2682
+ const [start, setStart] = useState(null);
2683
+ const [current, setCurrent] = useState(null);
2684
+ const commitFreeText = (p1, p2) => {
2685
+ const minX2 = Math.min(p1.x, p2.x);
2686
+ const minY2 = Math.min(p1.y, p2.y);
2687
+ const maxX2 = Math.max(p1.x, p2.x);
2688
+ const maxY2 = Math.max(p1.y, p2.y);
2689
+ const w = maxX2 - minX2;
2690
+ const h = maxY2 - minY2;
2691
+ if (w < 1 || h < 1) return;
2692
+ const rect = {
2693
+ origin: { x: minX2, y: minY2 },
2694
+ size: { width: w, height: h }
2695
+ };
2696
+ const anno = {
2697
+ type: PdfAnnotationSubtype.FREETEXT,
2698
+ rect,
2699
+ contents: toolContent,
2700
+ fontColor: toolFontColor,
2701
+ fontSize: toolFontSize,
2702
+ fontFamily: toolFontFamily,
2703
+ opacity: toolOpacity,
2704
+ backgroundColor: toolBackgroundColor,
2705
+ textAlign: toolTextAlign,
2706
+ verticalAlign: toolVerticalAlign,
2707
+ pageIndex,
2708
+ id: Date.now() + Math.random()
2709
+ };
2710
+ annotationProvides.createAnnotation(pageIndex, anno);
2711
+ annotationProvides.setActiveVariant(null);
2712
+ annotationProvides.selectAnnotation(pageIndex, anno.id);
2713
+ };
2714
+ const handlers = useMemo(
2715
+ () => ({
2716
+ onPointerDown: (pos, evt) => {
2717
+ var _a, _b;
2718
+ const x = clamp(pos.x, 0, pageWidthPDF);
2719
+ const y = clamp(pos.y, 0, pageHeightPDF);
2720
+ setStart({ x, y });
2721
+ setCurrent({ x, y });
2722
+ (_b = (_a = evt.target) == null ? void 0 : _a.setPointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
2723
+ },
2724
+ onPointerMove: (pos) => {
2725
+ if (!start) return;
2726
+ const x = clamp(pos.x, 0, pageWidthPDF);
2727
+ const y = clamp(pos.y, 0, pageHeightPDF);
2728
+ setCurrent({ x, y });
2729
+ },
2730
+ onPointerUp: (_, evt) => {
2731
+ var _a, _b;
2732
+ if (start && current && annotationProvides) {
2733
+ commitFreeText(start, current);
2734
+ }
2735
+ (_b = (_a = evt.target) == null ? void 0 : _a.releasePointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
2736
+ setStart(null);
2737
+ setCurrent(null);
2738
+ },
2739
+ onPointerCancel: (_, evt) => {
2740
+ var _a, _b;
2741
+ (_b = (_a = evt.target) == null ? void 0 : _a.releasePointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
2742
+ setStart(null);
2743
+ setCurrent(null);
2744
+ }
2745
+ }),
2746
+ [start, current, annotationProvides, pageWidthPDF, pageHeightPDF]
2747
+ );
2748
+ useEffect(() => register ? register(handlers) : void 0, [register, handlers]);
2749
+ if (!start || !current) return null;
2750
+ const minX = Math.min(start.x, current.x);
2751
+ const minY = Math.min(start.y, current.y);
2752
+ const maxX = Math.max(start.x, current.x);
2753
+ const maxY = Math.max(start.y, current.y);
2754
+ const dw = maxX - minX;
2755
+ const dh = maxY - minY;
2756
+ return /* @__PURE__ */ jsx(
2757
+ "svg",
2758
+ {
2759
+ style: {
2760
+ position: "absolute",
2761
+ left: minX * scale,
2762
+ top: minY * scale,
2763
+ width: dw * scale,
2764
+ height: dh * scale,
2765
+ pointerEvents: "none",
2766
+ zIndex: 2
2767
+ },
2768
+ width: dw * scale,
2769
+ height: dh * scale,
2770
+ viewBox: `0 0 ${dw} ${dh}`,
2771
+ children: /* @__PURE__ */ jsx(
2772
+ "rect",
2773
+ {
2774
+ x: 0,
2775
+ y: 0,
2776
+ width: dw,
2777
+ height: dh,
2778
+ fill: "transparent",
2779
+ style: {
2780
+ stroke: toolFontColor,
2781
+ strokeWidth: 1,
2782
+ strokeDasharray: "4,4",
2783
+ cursor
2784
+ }
2785
+ }
2786
+ )
2787
+ }
2788
+ );
2789
+ };
2539
2790
  function AnnotationLayer({
2540
2791
  pageIndex,
2541
2792
  scale,
@@ -2611,6 +2862,15 @@ function AnnotationLayer({
2611
2862
  pageWidth,
2612
2863
  pageHeight
2613
2864
  }
2865
+ ),
2866
+ /* @__PURE__ */ jsx(
2867
+ FreeTextPaint,
2868
+ {
2869
+ pageIndex,
2870
+ scale,
2871
+ pageWidth,
2872
+ pageHeight
2873
+ }
2614
2874
  )
2615
2875
  ]
2616
2876
  }