@embedpdf/plugin-annotation 1.0.13 → 1.0.15

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.
Files changed (32) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +103 -49
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/annotation-plugin.d.ts +1 -0
  6. package/dist/lib/helpers.d.ts +9 -2
  7. package/dist/lib/types.d.ts +48 -7
  8. package/dist/preact/adapter.d.ts +6 -0
  9. package/dist/preact/index.cjs +1 -1
  10. package/dist/preact/index.cjs.map +1 -1
  11. package/dist/preact/index.js +500 -9
  12. package/dist/preact/index.js.map +1 -1
  13. package/dist/react/adapter.d.ts +6 -1
  14. package/dist/react/index.cjs +1 -1
  15. package/dist/react/index.cjs.map +1 -1
  16. package/dist/react/index.js +500 -9
  17. package/dist/react/index.js.map +1 -1
  18. package/dist/shared-preact/components/annotation-container.d.ts +4 -2
  19. package/dist/shared-preact/components/annotations/free-text-paint.d.ts +10 -0
  20. package/dist/shared-preact/components/annotations/free-text.d.ts +13 -0
  21. package/dist/shared-preact/components/annotations/stamp-paint.d.ts +8 -0
  22. package/dist/shared-preact/components/annotations/stamp.d.ts +12 -0
  23. package/dist/shared-preact/components/render-annotation.d.ts +1 -1
  24. package/dist/shared-preact/hooks/use-drag-resize.d.ts +5 -2
  25. package/dist/shared-react/components/annotation-container.d.ts +4 -2
  26. package/dist/shared-react/components/annotations/free-text-paint.d.ts +10 -0
  27. package/dist/shared-react/components/annotations/free-text.d.ts +13 -0
  28. package/dist/shared-react/components/annotations/stamp-paint.d.ts +8 -0
  29. package/dist/shared-react/components/annotations/stamp.d.ts +12 -0
  30. package/dist/shared-react/components/render-annotation.d.ts +1 -1
  31. package/dist/shared-react/hooks/use-drag-resize.d.ts +5 -2
  32. package/package.json +9 -9
@@ -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, isStamp } 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, ignore, PdfErrorCode, 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) {
@@ -150,6 +151,7 @@ function useDragResize({
150
151
  isResizable,
151
152
  computePatch,
152
153
  computeVertices,
154
+ lockAspectRatio = false,
153
155
  currentRect,
154
156
  setCurrentRect,
155
157
  setCurrentVertices,
@@ -184,6 +186,23 @@ function useDragResize({
184
186
  oy += dy;
185
187
  h -= dy;
186
188
  }
189
+ if (lockAspectRatio && startRect.current) {
190
+ const ratio = startRect.current.size.width / startRect.current.size.height;
191
+ const anchorRight = ox + w;
192
+ const anchorBottom = oy + h;
193
+ const horizontalPrimary = dir.current.includes("left") || dir.current.includes("right");
194
+ if (horizontalPrimary) {
195
+ h = w / ratio;
196
+ } else {
197
+ w = h * ratio;
198
+ }
199
+ if (dir.current.includes("left")) {
200
+ ox = anchorRight - w;
201
+ }
202
+ if (dir.current.includes("top")) {
203
+ oy = anchorBottom - h;
204
+ }
205
+ }
187
206
  }
188
207
  if (w < 1 || h < 1) return currentRect;
189
208
  w = clamp(w, 1, pageW);
@@ -220,8 +239,14 @@ function useDragResize({
220
239
  setCurrentRect(patch.rect ?? nextRect);
221
240
  setPreviewObject(patch);
222
241
  };
223
- const onPointerUp = () => {
242
+ const onPointerUp = (e) => {
224
243
  if (drag.current === "idle") return;
244
+ if ((e == null ? void 0 : e.currentTarget) && e.pointerId !== void 0) {
245
+ try {
246
+ e.currentTarget.releasePointerCapture(e.pointerId);
247
+ } catch {
248
+ }
249
+ }
225
250
  const usedDir = dir.current || "bottom-right";
226
251
  drag.current = "idle";
227
252
  let patch = { rect: currentRect };
@@ -257,7 +282,9 @@ function useDragResize({
257
282
  rootHandlers: {
258
283
  onPointerDown,
259
284
  onPointerMove,
260
- onPointerUp
285
+ onPointerUp,
286
+ onPointerCancel: () => onPointerUp(),
287
+ onLostPointerCapture: () => onPointerUp()
261
288
  },
262
289
  startResize
263
290
  };
@@ -335,9 +362,11 @@ function AnnotationContainer({
335
362
  isSelected = false,
336
363
  isDraggable = true,
337
364
  isResizable = true,
365
+ lockAspectRatio = false,
338
366
  computeVertices,
339
367
  computePatch,
340
368
  selectionMenu,
369
+ onDoubleClick,
341
370
  ...props
342
371
  }) {
343
372
  const { provides: annotationProvides } = useAnnotationCapability();
@@ -355,6 +384,7 @@ function AnnotationContainer({
355
384
  isSelected,
356
385
  isDraggable,
357
386
  isResizable,
387
+ lockAspectRatio,
358
388
  computePatch,
359
389
  computeVertices,
360
390
  currentRect,
@@ -376,6 +406,7 @@ function AnnotationContainer({
376
406
  "div",
377
407
  {
378
408
  ...rootHandlers,
409
+ ...mapDoubleClick(onDoubleClick),
379
410
  style: {
380
411
  position: "absolute",
381
412
  outline: isSelected ? "1px solid #007ACC" : "none",
@@ -386,6 +417,9 @@ function AnnotationContainer({
386
417
  height: `${currentRect.size.height * scale}px`,
387
418
  pointerEvents: isSelected ? "auto" : "none",
388
419
  cursor: isSelected && isDraggable ? "move" : "default",
420
+ ...isSelected && {
421
+ zIndex: 3
422
+ },
389
423
  ...style
390
424
  },
391
425
  ...props,
@@ -1192,6 +1226,158 @@ const patchPolygon = (orig, ctx) => {
1192
1226
  vertices: moved
1193
1227
  };
1194
1228
  };
1229
+ function FreeText({
1230
+ isSelected,
1231
+ isEditing,
1232
+ annotation,
1233
+ pageIndex,
1234
+ scale,
1235
+ onClick
1236
+ }) {
1237
+ const editorRef = useRef(null);
1238
+ const { provides: annotationProvides } = useAnnotationCapability();
1239
+ useEffect(() => {
1240
+ if (isEditing && editorRef.current) {
1241
+ const editor = editorRef.current;
1242
+ editor.focus();
1243
+ const selection = window.getSelection();
1244
+ if (selection) {
1245
+ const range = document.createRange();
1246
+ range.selectNodeContents(editor);
1247
+ range.collapse(false);
1248
+ selection.removeAllRanges();
1249
+ selection.addRange(range);
1250
+ }
1251
+ }
1252
+ }, [isEditing]);
1253
+ const handleBlur = () => {
1254
+ if (!annotationProvides) return;
1255
+ if (!editorRef.current) return;
1256
+ annotationProvides.updateAnnotation(pageIndex, annotation.localId, {
1257
+ contents: editorRef.current.innerText
1258
+ });
1259
+ };
1260
+ return /* @__PURE__ */ jsx(
1261
+ "div",
1262
+ {
1263
+ style: {
1264
+ position: "absolute",
1265
+ width: annotation.object.rect.size.width * scale,
1266
+ height: annotation.object.rect.size.height * scale,
1267
+ cursor: isSelected && !isEditing ? "move" : "default",
1268
+ pointerEvents: isSelected && !isEditing ? "none" : "auto",
1269
+ zIndex: 2
1270
+ },
1271
+ onPointerDown: onClick,
1272
+ children: /* @__PURE__ */ jsx(
1273
+ "span",
1274
+ {
1275
+ ref: editorRef,
1276
+ onBlur: handleBlur,
1277
+ style: {
1278
+ color: annotation.object.fontColor,
1279
+ fontSize: annotation.object.fontSize * scale,
1280
+ fontFamily: standardFontCss(annotation.object.fontFamily),
1281
+ textAlign: textAlignmentToCss(annotation.object.textAlign),
1282
+ flexDirection: "column",
1283
+ justifyContent: annotation.object.verticalAlign === PdfVerticalAlignment.Top ? "flex-start" : annotation.object.verticalAlign === PdfVerticalAlignment.Middle ? "center" : "flex-end",
1284
+ display: "flex",
1285
+ backgroundColor: annotation.object.backgroundColor,
1286
+ opacity: annotation.object.opacity,
1287
+ width: "100%",
1288
+ height: "100%",
1289
+ lineHeight: "1.18",
1290
+ overflow: "hidden",
1291
+ cursor: isEditing ? "text" : "pointer"
1292
+ },
1293
+ contentEditable: isEditing,
1294
+ suppressContentEditableWarning: true,
1295
+ children: annotation.object.contents
1296
+ }
1297
+ )
1298
+ }
1299
+ );
1300
+ }
1301
+ function RenderAnnotation({
1302
+ pageIndex,
1303
+ annotation,
1304
+ scaleFactor = 1,
1305
+ style,
1306
+ ...props
1307
+ }) {
1308
+ const { provides: annotationProvides } = useAnnotationCapability();
1309
+ const [imageUrl, setImageUrl] = useState(null);
1310
+ const urlRef = useRef(null);
1311
+ const { width, height } = annotation.rect.size;
1312
+ useEffect(() => {
1313
+ if (annotationProvides) {
1314
+ const task = annotationProvides.renderAnnotation({
1315
+ pageIndex,
1316
+ annotation,
1317
+ scaleFactor,
1318
+ dpr: window.devicePixelRatio
1319
+ });
1320
+ task.wait((blob) => {
1321
+ const url = URL.createObjectURL(blob);
1322
+ setImageUrl(url);
1323
+ urlRef.current = url;
1324
+ }, ignore);
1325
+ return () => {
1326
+ if (urlRef.current) {
1327
+ URL.revokeObjectURL(urlRef.current);
1328
+ urlRef.current = null;
1329
+ } else {
1330
+ task.abort({
1331
+ code: PdfErrorCode.Cancelled,
1332
+ message: "canceled render task"
1333
+ });
1334
+ }
1335
+ };
1336
+ }
1337
+ }, [pageIndex, scaleFactor, annotationProvides, annotation.id, width, height]);
1338
+ const handleImageLoad = () => {
1339
+ if (urlRef.current) {
1340
+ URL.revokeObjectURL(urlRef.current);
1341
+ urlRef.current = null;
1342
+ }
1343
+ };
1344
+ return /* @__PURE__ */ jsx(Fragment, { children: imageUrl && /* @__PURE__ */ jsx(
1345
+ "img",
1346
+ {
1347
+ src: imageUrl,
1348
+ onLoad: handleImageLoad,
1349
+ ...props,
1350
+ style: {
1351
+ width: "100%",
1352
+ height: "100%",
1353
+ ...style || {}
1354
+ }
1355
+ }
1356
+ ) });
1357
+ }
1358
+ function Stamp({ isSelected, annotation, pageIndex, scale, onClick }) {
1359
+ return /* @__PURE__ */ jsx(
1360
+ "div",
1361
+ {
1362
+ style: {
1363
+ position: "absolute",
1364
+ width: "100%",
1365
+ height: "100%",
1366
+ zIndex: 2,
1367
+ pointerEvents: isSelected ? "none" : "auto"
1368
+ },
1369
+ onPointerDown: onClick,
1370
+ children: annotation.pdfId !== void 0 && /* @__PURE__ */ jsx(
1371
+ RenderAnnotation,
1372
+ {
1373
+ pageIndex,
1374
+ annotation: { ...annotation.object, id: annotation.pdfId },
1375
+ scaleFactor: scale
1376
+ }
1377
+ )
1378
+ }
1379
+ );
1380
+ }
1195
1381
  function Annotations(annotationsProps) {
1196
1382
  const { pageIndex, scale, selectionMenu } = annotationsProps;
1197
1383
  const { provides: annotationProvides } = useAnnotationCapability();
@@ -1199,6 +1385,7 @@ function Annotations(annotationsProps) {
1199
1385
  const [annotations, setAnnotations] = useState([]);
1200
1386
  const { register } = usePointerHandlers({ pageIndex });
1201
1387
  const [selectionState, setSelectionState] = useState(null);
1388
+ const [editingId, setEditingId] = useState(null);
1202
1389
  useEffect(() => {
1203
1390
  if (annotationProvides) {
1204
1391
  annotationProvides.onStateChange((state) => {
@@ -1212,6 +1399,7 @@ function Annotations(annotationsProps) {
1212
1399
  onPointerDown: (_, pe) => {
1213
1400
  if (pe.target === pe.currentTarget && annotationProvides) {
1214
1401
  annotationProvides.deselectAnnotation();
1402
+ setEditingId(null);
1215
1403
  }
1216
1404
  }
1217
1405
  }),
@@ -1223,6 +1411,7 @@ function Annotations(annotationsProps) {
1223
1411
  if (annotationProvides && selectionProvides) {
1224
1412
  annotationProvides.selectAnnotation(pageIndex, annotation.localId);
1225
1413
  selectionProvides.clear();
1414
+ setEditingId(null);
1226
1415
  }
1227
1416
  },
1228
1417
  [annotationProvides, selectionProvides, pageIndex]
@@ -1232,6 +1421,7 @@ function Annotations(annotationsProps) {
1232
1421
  }, [register, handlers]);
1233
1422
  return /* @__PURE__ */ jsx(Fragment$1, { children: annotations.map((annotation) => {
1234
1423
  const isSelected = (selectionState == null ? void 0 : selectionState.localId) === annotation.localId;
1424
+ const isEditing = editingId === annotation.localId;
1235
1425
  if (isInk(annotation)) {
1236
1426
  return /* @__PURE__ */ jsx(
1237
1427
  AnnotationContainer,
@@ -1547,6 +1737,70 @@ function Annotations(annotationsProps) {
1547
1737
  annotation.localId
1548
1738
  );
1549
1739
  }
1740
+ if (isFreeText(annotation)) {
1741
+ return /* @__PURE__ */ jsx(
1742
+ AnnotationContainer,
1743
+ {
1744
+ trackedAnnotation: annotation,
1745
+ isSelected,
1746
+ isDraggable: true,
1747
+ isResizable: true,
1748
+ selectionMenu,
1749
+ outlineOffset: 6,
1750
+ onDoubleClick: (e) => {
1751
+ e.stopPropagation();
1752
+ setEditingId(annotation.localId);
1753
+ },
1754
+ style: {
1755
+ mixBlendMode: blendModeToCss(annotation.object.blendMode ?? PdfBlendMode.Normal)
1756
+ },
1757
+ ...annotationsProps,
1758
+ children: (object) => /* @__PURE__ */ jsx(
1759
+ FreeText,
1760
+ {
1761
+ isSelected,
1762
+ isEditing,
1763
+ annotation: {
1764
+ ...annotation,
1765
+ object
1766
+ },
1767
+ pageIndex,
1768
+ scale,
1769
+ onClick: (e) => handleClick(e, annotation)
1770
+ }
1771
+ )
1772
+ },
1773
+ annotation.localId
1774
+ );
1775
+ }
1776
+ if (isStamp(annotation)) {
1777
+ return /* @__PURE__ */ jsx(
1778
+ AnnotationContainer,
1779
+ {
1780
+ trackedAnnotation: annotation,
1781
+ isSelected,
1782
+ isDraggable: true,
1783
+ isResizable: true,
1784
+ selectionMenu,
1785
+ lockAspectRatio: true,
1786
+ style: {
1787
+ mixBlendMode: blendModeToCss(annotation.object.blendMode ?? PdfBlendMode.Normal)
1788
+ },
1789
+ ...annotationsProps,
1790
+ children: (_object) => /* @__PURE__ */ jsx(
1791
+ Stamp,
1792
+ {
1793
+ isSelected,
1794
+ annotation,
1795
+ pageIndex,
1796
+ scale,
1797
+ onClick: (e) => handleClick(e, annotation)
1798
+ }
1799
+ )
1800
+ },
1801
+ annotation.localId
1802
+ );
1803
+ }
1550
1804
  return null;
1551
1805
  }) });
1552
1806
  }
@@ -1571,8 +1825,9 @@ function TextMarkup({ pageIndex, scale }) {
1571
1825
  return off;
1572
1826
  }, [annotationProvides]);
1573
1827
  if (!boundingRect) return null;
1574
- switch (activeTool.variantKey) {
1575
- case makeVariantKey(PdfAnnotationSubtype.UNDERLINE):
1828
+ if (!activeTool.defaults) return null;
1829
+ switch (activeTool.defaults.subtype) {
1830
+ case PdfAnnotationSubtype.UNDERLINE:
1576
1831
  return /* @__PURE__ */ jsx(
1577
1832
  "div",
1578
1833
  {
@@ -1593,7 +1848,7 @@ function TextMarkup({ pageIndex, scale }) {
1593
1848
  )
1594
1849
  }
1595
1850
  );
1596
- case makeVariantKey(PdfAnnotationSubtype.HIGHLIGHT):
1851
+ case PdfAnnotationSubtype.HIGHLIGHT:
1597
1852
  return /* @__PURE__ */ jsx(
1598
1853
  "div",
1599
1854
  {
@@ -1614,7 +1869,7 @@ function TextMarkup({ pageIndex, scale }) {
1614
1869
  )
1615
1870
  }
1616
1871
  );
1617
- case makeVariantKey(PdfAnnotationSubtype.STRIKEOUT):
1872
+ case PdfAnnotationSubtype.STRIKEOUT:
1618
1873
  return /* @__PURE__ */ jsx(
1619
1874
  "div",
1620
1875
  {
@@ -1635,7 +1890,7 @@ function TextMarkup({ pageIndex, scale }) {
1635
1890
  )
1636
1891
  }
1637
1892
  );
1638
- case makeVariantKey(PdfAnnotationSubtype.SQUIGGLY):
1893
+ case PdfAnnotationSubtype.SQUIGGLY:
1639
1894
  return /* @__PURE__ */ jsx(
1640
1895
  "div",
1641
1896
  {
@@ -2537,6 +2792,224 @@ const PolygonPaint = ({
2537
2792
  }
2538
2793
  );
2539
2794
  };
2795
+ const FreeTextPaint = ({
2796
+ pageIndex,
2797
+ scale,
2798
+ pageWidth,
2799
+ pageHeight,
2800
+ cursor = "text"
2801
+ }) => {
2802
+ const { provides: annotationProvides } = useAnnotationCapability();
2803
+ const [activeTool, setActiveTool] = useState({ variantKey: null, defaults: null });
2804
+ useEffect(() => annotationProvides == null ? void 0 : annotationProvides.onActiveToolChange(setActiveTool), [annotationProvides]);
2805
+ if (!activeTool.defaults || activeTool.defaults.subtype !== PdfAnnotationSubtype.FREETEXT)
2806
+ return null;
2807
+ const toolFontColor = activeTool.defaults.fontColor ?? "#000000";
2808
+ const toolOpacity = activeTool.defaults.opacity ?? 1;
2809
+ const toolFontSize = activeTool.defaults.fontSize ?? 12;
2810
+ const toolFontFamily = activeTool.defaults.fontFamily;
2811
+ const toolBackgroundColor = activeTool.defaults.backgroundColor ?? "transparent";
2812
+ const toolTextAlign = activeTool.defaults.textAlign;
2813
+ const toolVerticalAlign = activeTool.defaults.verticalAlign;
2814
+ const toolContent = activeTool.defaults.content ?? "Insert text here";
2815
+ const { register } = usePointerHandlers({ modeId: "freeText", pageIndex });
2816
+ const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
2817
+ const pageWidthPDF = pageWidth / scale;
2818
+ const pageHeightPDF = pageHeight / scale;
2819
+ const [start, setStart] = useState(null);
2820
+ const [current, setCurrent] = useState(null);
2821
+ const commitFreeText = (p1, p2) => {
2822
+ const minX2 = Math.min(p1.x, p2.x);
2823
+ const minY2 = Math.min(p1.y, p2.y);
2824
+ const maxX2 = Math.max(p1.x, p2.x);
2825
+ const maxY2 = Math.max(p1.y, p2.y);
2826
+ const w = maxX2 - minX2;
2827
+ const h = maxY2 - minY2;
2828
+ if (w < 1 || h < 1) return;
2829
+ const rect = {
2830
+ origin: { x: minX2, y: minY2 },
2831
+ size: { width: w, height: h }
2832
+ };
2833
+ const anno = {
2834
+ type: PdfAnnotationSubtype.FREETEXT,
2835
+ rect,
2836
+ contents: toolContent,
2837
+ fontColor: toolFontColor,
2838
+ fontSize: toolFontSize,
2839
+ fontFamily: toolFontFamily,
2840
+ opacity: toolOpacity,
2841
+ backgroundColor: toolBackgroundColor,
2842
+ textAlign: toolTextAlign,
2843
+ verticalAlign: toolVerticalAlign,
2844
+ pageIndex,
2845
+ id: Date.now() + Math.random()
2846
+ };
2847
+ annotationProvides.createAnnotation(pageIndex, anno);
2848
+ annotationProvides.setActiveVariant(null);
2849
+ annotationProvides.selectAnnotation(pageIndex, anno.id);
2850
+ };
2851
+ const handlers = useMemo(
2852
+ () => ({
2853
+ onPointerDown: (pos, evt) => {
2854
+ var _a, _b;
2855
+ const x = clamp(pos.x, 0, pageWidthPDF);
2856
+ const y = clamp(pos.y, 0, pageHeightPDF);
2857
+ setStart({ x, y });
2858
+ setCurrent({ x, y });
2859
+ (_b = (_a = evt.target) == null ? void 0 : _a.setPointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
2860
+ },
2861
+ onPointerMove: (pos) => {
2862
+ if (!start) return;
2863
+ const x = clamp(pos.x, 0, pageWidthPDF);
2864
+ const y = clamp(pos.y, 0, pageHeightPDF);
2865
+ setCurrent({ x, y });
2866
+ },
2867
+ onPointerUp: (_, evt) => {
2868
+ var _a, _b;
2869
+ if (start && current && annotationProvides) {
2870
+ commitFreeText(start, current);
2871
+ }
2872
+ (_b = (_a = evt.target) == null ? void 0 : _a.releasePointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
2873
+ setStart(null);
2874
+ setCurrent(null);
2875
+ },
2876
+ onPointerCancel: (_, evt) => {
2877
+ var _a, _b;
2878
+ (_b = (_a = evt.target) == null ? void 0 : _a.releasePointerCapture) == null ? void 0 : _b.call(_a, evt.pointerId);
2879
+ setStart(null);
2880
+ setCurrent(null);
2881
+ }
2882
+ }),
2883
+ [start, current, annotationProvides, pageWidthPDF, pageHeightPDF]
2884
+ );
2885
+ useEffect(() => register ? register(handlers) : void 0, [register, handlers]);
2886
+ if (!start || !current) return null;
2887
+ const minX = Math.min(start.x, current.x);
2888
+ const minY = Math.min(start.y, current.y);
2889
+ const maxX = Math.max(start.x, current.x);
2890
+ const maxY = Math.max(start.y, current.y);
2891
+ const dw = maxX - minX;
2892
+ const dh = maxY - minY;
2893
+ return /* @__PURE__ */ jsx(
2894
+ "svg",
2895
+ {
2896
+ style: {
2897
+ position: "absolute",
2898
+ left: minX * scale,
2899
+ top: minY * scale,
2900
+ width: dw * scale,
2901
+ height: dh * scale,
2902
+ pointerEvents: "none",
2903
+ zIndex: 2
2904
+ },
2905
+ width: dw * scale,
2906
+ height: dh * scale,
2907
+ viewBox: `0 0 ${dw} ${dh}`,
2908
+ children: /* @__PURE__ */ jsx(
2909
+ "rect",
2910
+ {
2911
+ x: 0,
2912
+ y: 0,
2913
+ width: dw,
2914
+ height: dh,
2915
+ fill: "transparent",
2916
+ style: {
2917
+ stroke: toolFontColor,
2918
+ strokeWidth: 1,
2919
+ strokeDasharray: "4,4",
2920
+ cursor
2921
+ }
2922
+ }
2923
+ )
2924
+ }
2925
+ );
2926
+ };
2927
+ const StampPaint = ({ pageIndex, scale, pageWidth, pageHeight }) => {
2928
+ const { provides: annotationProvides } = useAnnotationCapability();
2929
+ const inputRef = useRef(null);
2930
+ const canvasRef = useRef(null);
2931
+ const [activeTool, setActiveTool] = useState({ variantKey: null, defaults: null });
2932
+ useEffect(() => {
2933
+ if (!annotationProvides) return;
2934
+ return annotationProvides.onActiveToolChange(setActiveTool);
2935
+ }, [annotationProvides]);
2936
+ if (!activeTool.defaults) return null;
2937
+ if (activeTool.defaults.subtype !== PdfAnnotationSubtype.STAMP) return null;
2938
+ const { register } = usePointerHandlers({ modeId: "stamp", pageIndex });
2939
+ const pageWidthPDF = pageWidth / scale;
2940
+ const pageHeightPDF = pageHeight / scale;
2941
+ const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
2942
+ const [start, setStart] = useState(null);
2943
+ const handlers = useMemo(
2944
+ () => ({
2945
+ onPointerDown: (pos, evt) => {
2946
+ var _a;
2947
+ const x = clamp(pos.x, 0, pageWidthPDF);
2948
+ const y = clamp(pos.y, 0, pageHeightPDF);
2949
+ setStart({ x, y });
2950
+ (_a = inputRef.current) == null ? void 0 : _a.click();
2951
+ }
2952
+ }),
2953
+ [pageWidthPDF, pageHeightPDF]
2954
+ );
2955
+ useEffect(() => register ? register(handlers) : void 0, [register, handlers]);
2956
+ const onChange = async (e) => {
2957
+ var _a;
2958
+ if (!annotationProvides || !start) return;
2959
+ const file = (_a = e.currentTarget.files) == null ? void 0 : _a[0];
2960
+ if (!file) return;
2961
+ const img = await new Promise((res, rej) => {
2962
+ const i = new Image();
2963
+ i.onload = () => res(i);
2964
+ i.onerror = rej;
2965
+ i.src = URL.createObjectURL(file);
2966
+ });
2967
+ const imgW = img.naturalWidth;
2968
+ const imgH = img.naturalHeight;
2969
+ const maxW = pageWidthPDF;
2970
+ const maxH = pageHeightPDF;
2971
+ const scaleFactor = Math.min(1, maxW / imgW, maxH / imgH);
2972
+ const pdfW = imgW * scaleFactor;
2973
+ const pdfH = imgH * scaleFactor;
2974
+ const posX = clamp(start.x, 0, maxW - pdfW);
2975
+ const posY = clamp(start.y, 0, maxH - pdfH);
2976
+ const rect = {
2977
+ origin: { x: posX, y: posY },
2978
+ size: { width: pdfW, height: pdfH }
2979
+ };
2980
+ const canvas = canvasRef.current;
2981
+ if (!canvas) return;
2982
+ canvas.width = pdfW;
2983
+ canvas.height = pdfH;
2984
+ const ctx = canvas.getContext("2d");
2985
+ ctx.drawImage(img, 0, 0, pdfW, pdfH);
2986
+ const imageData = ctx.getImageData(0, 0, pdfW, pdfH);
2987
+ const anno = {
2988
+ type: PdfAnnotationSubtype.STAMP,
2989
+ flags: ["print"],
2990
+ pageIndex,
2991
+ id: Date.now() + Math.random(),
2992
+ rect
2993
+ };
2994
+ annotationProvides.createAnnotation(pageIndex, anno, { imageData });
2995
+ annotationProvides.setActiveVariant(null);
2996
+ annotationProvides.selectAnnotation(pageIndex, anno.id);
2997
+ setStart(null);
2998
+ };
2999
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3000
+ /* @__PURE__ */ jsx("canvas", { style: { display: "none" }, ref: canvasRef }),
3001
+ /* @__PURE__ */ jsx(
3002
+ "input",
3003
+ {
3004
+ ref: inputRef,
3005
+ type: "file",
3006
+ accept: "image/png,image/jpeg",
3007
+ style: { display: "none" },
3008
+ onChange
3009
+ }
3010
+ )
3011
+ ] });
3012
+ };
2540
3013
  function AnnotationLayer({
2541
3014
  pageIndex,
2542
3015
  scale,
@@ -2612,6 +3085,24 @@ function AnnotationLayer({
2612
3085
  pageWidth,
2613
3086
  pageHeight
2614
3087
  }
3088
+ ),
3089
+ /* @__PURE__ */ jsx(
3090
+ FreeTextPaint,
3091
+ {
3092
+ pageIndex,
3093
+ scale,
3094
+ pageWidth,
3095
+ pageHeight
3096
+ }
3097
+ ),
3098
+ /* @__PURE__ */ jsx(
3099
+ StampPaint,
3100
+ {
3101
+ pageIndex,
3102
+ scale,
3103
+ pageWidth,
3104
+ pageHeight
3105
+ }
2615
3106
  )
2616
3107
  ]
2617
3108
  }