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