@embedpdf/plugin-annotation 1.0.14 → 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.
@@ -1,7 +1,7 @@
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, isFreeText } 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 "react/jsx-runtime";
4
- import { restoreOffset, rectEquals, PdfAnnotationBorderStyle, PdfAnnotationSubtype, expandRect, rectFromPoints, textAlignmentToCss, standardFontCss, PdfVerticalAlignment, 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/react";
6
6
  import { useSelectionCapability } from "@embedpdf/plugin-selection/react";
7
7
  import { Fragment, useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from "react";
@@ -150,6 +150,7 @@ function useDragResize({
150
150
  isResizable,
151
151
  computePatch,
152
152
  computeVertices,
153
+ lockAspectRatio = false,
153
154
  currentRect,
154
155
  setCurrentRect,
155
156
  setCurrentVertices,
@@ -184,6 +185,23 @@ function useDragResize({
184
185
  oy += dy;
185
186
  h -= dy;
186
187
  }
188
+ if (lockAspectRatio && startRect.current) {
189
+ const ratio = startRect.current.size.width / startRect.current.size.height;
190
+ const anchorRight = ox + w;
191
+ const anchorBottom = oy + h;
192
+ const horizontalPrimary = dir.current.includes("left") || dir.current.includes("right");
193
+ if (horizontalPrimary) {
194
+ h = w / ratio;
195
+ } else {
196
+ w = h * ratio;
197
+ }
198
+ if (dir.current.includes("left")) {
199
+ ox = anchorRight - w;
200
+ }
201
+ if (dir.current.includes("top")) {
202
+ oy = anchorBottom - h;
203
+ }
204
+ }
187
205
  }
188
206
  if (w < 1 || h < 1) return currentRect;
189
207
  w = clamp(w, 1, pageW);
@@ -220,8 +238,14 @@ function useDragResize({
220
238
  setCurrentRect(patch.rect ?? nextRect);
221
239
  setPreviewObject(patch);
222
240
  };
223
- const onPointerUp = () => {
241
+ const onPointerUp = (e) => {
224
242
  if (drag.current === "idle") return;
243
+ if ((e == null ? void 0 : e.currentTarget) && e.pointerId !== void 0) {
244
+ try {
245
+ e.currentTarget.releasePointerCapture(e.pointerId);
246
+ } catch {
247
+ }
248
+ }
225
249
  const usedDir = dir.current || "bottom-right";
226
250
  drag.current = "idle";
227
251
  let patch = { rect: currentRect };
@@ -257,7 +281,9 @@ function useDragResize({
257
281
  rootHandlers: {
258
282
  onPointerDown,
259
283
  onPointerMove,
260
- onPointerUp
284
+ onPointerUp,
285
+ onPointerCancel: () => onPointerUp(),
286
+ onLostPointerCapture: () => onPointerUp()
261
287
  },
262
288
  startResize
263
289
  };
@@ -335,6 +361,7 @@ function AnnotationContainer({
335
361
  isSelected = false,
336
362
  isDraggable = true,
337
363
  isResizable = true,
364
+ lockAspectRatio = false,
338
365
  computeVertices,
339
366
  computePatch,
340
367
  selectionMenu,
@@ -356,6 +383,7 @@ function AnnotationContainer({
356
383
  isSelected,
357
384
  isDraggable,
358
385
  isResizable,
386
+ lockAspectRatio,
359
387
  computePatch,
360
388
  computeVertices,
361
389
  currentRect,
@@ -1269,6 +1297,86 @@ function FreeText({
1269
1297
  }
1270
1298
  );
1271
1299
  }
1300
+ function RenderAnnotation({
1301
+ pageIndex,
1302
+ annotation,
1303
+ scaleFactor = 1,
1304
+ style,
1305
+ ...props
1306
+ }) {
1307
+ const { provides: annotationProvides } = useAnnotationCapability();
1308
+ const [imageUrl, setImageUrl] = useState(null);
1309
+ const urlRef = useRef(null);
1310
+ const { width, height } = annotation.rect.size;
1311
+ useEffect(() => {
1312
+ if (annotationProvides) {
1313
+ const task = annotationProvides.renderAnnotation({
1314
+ pageIndex,
1315
+ annotation,
1316
+ scaleFactor,
1317
+ dpr: window.devicePixelRatio
1318
+ });
1319
+ task.wait((blob) => {
1320
+ const url = URL.createObjectURL(blob);
1321
+ setImageUrl(url);
1322
+ urlRef.current = url;
1323
+ }, ignore);
1324
+ return () => {
1325
+ if (urlRef.current) {
1326
+ URL.revokeObjectURL(urlRef.current);
1327
+ urlRef.current = null;
1328
+ } else {
1329
+ task.abort({
1330
+ code: PdfErrorCode.Cancelled,
1331
+ message: "canceled render task"
1332
+ });
1333
+ }
1334
+ };
1335
+ }
1336
+ }, [pageIndex, scaleFactor, annotationProvides, annotation.id, width, height]);
1337
+ const handleImageLoad = () => {
1338
+ if (urlRef.current) {
1339
+ URL.revokeObjectURL(urlRef.current);
1340
+ urlRef.current = null;
1341
+ }
1342
+ };
1343
+ return /* @__PURE__ */ jsx(Fragment, { children: imageUrl && /* @__PURE__ */ jsx(
1344
+ "img",
1345
+ {
1346
+ src: imageUrl,
1347
+ onLoad: handleImageLoad,
1348
+ ...props,
1349
+ style: {
1350
+ width: "100%",
1351
+ height: "100%",
1352
+ ...style || {}
1353
+ }
1354
+ }
1355
+ ) });
1356
+ }
1357
+ function Stamp({ isSelected, annotation, pageIndex, scale, onClick }) {
1358
+ return /* @__PURE__ */ jsx(
1359
+ "div",
1360
+ {
1361
+ style: {
1362
+ position: "absolute",
1363
+ width: "100%",
1364
+ height: "100%",
1365
+ zIndex: 2,
1366
+ pointerEvents: isSelected ? "none" : "auto"
1367
+ },
1368
+ onPointerDown: onClick,
1369
+ children: annotation.pdfId !== void 0 && /* @__PURE__ */ jsx(
1370
+ RenderAnnotation,
1371
+ {
1372
+ pageIndex,
1373
+ annotation: { ...annotation.object, id: annotation.pdfId },
1374
+ scaleFactor: scale
1375
+ }
1376
+ )
1377
+ }
1378
+ );
1379
+ }
1272
1380
  function Annotations(annotationsProps) {
1273
1381
  const { pageIndex, scale, selectionMenu } = annotationsProps;
1274
1382
  const { provides: annotationProvides } = useAnnotationCapability();
@@ -1664,6 +1772,34 @@ function Annotations(annotationsProps) {
1664
1772
  annotation.localId
1665
1773
  );
1666
1774
  }
1775
+ if (isStamp(annotation)) {
1776
+ return /* @__PURE__ */ jsx(
1777
+ AnnotationContainer,
1778
+ {
1779
+ trackedAnnotation: annotation,
1780
+ isSelected,
1781
+ isDraggable: true,
1782
+ isResizable: true,
1783
+ selectionMenu,
1784
+ lockAspectRatio: true,
1785
+ style: {
1786
+ mixBlendMode: blendModeToCss(annotation.object.blendMode ?? PdfBlendMode.Normal)
1787
+ },
1788
+ ...annotationsProps,
1789
+ children: (_object) => /* @__PURE__ */ jsx(
1790
+ Stamp,
1791
+ {
1792
+ isSelected,
1793
+ annotation,
1794
+ pageIndex,
1795
+ scale,
1796
+ onClick: (e) => handleClick(e, annotation)
1797
+ }
1798
+ )
1799
+ },
1800
+ annotation.localId
1801
+ );
1802
+ }
1667
1803
  return null;
1668
1804
  }) });
1669
1805
  }
@@ -2787,6 +2923,92 @@ const FreeTextPaint = ({
2787
2923
  }
2788
2924
  );
2789
2925
  };
2926
+ const StampPaint = ({ pageIndex, scale, pageWidth, pageHeight }) => {
2927
+ const { provides: annotationProvides } = useAnnotationCapability();
2928
+ const inputRef = useRef(null);
2929
+ const canvasRef = useRef(null);
2930
+ const [activeTool, setActiveTool] = useState({ variantKey: null, defaults: null });
2931
+ useEffect(() => {
2932
+ if (!annotationProvides) return;
2933
+ return annotationProvides.onActiveToolChange(setActiveTool);
2934
+ }, [annotationProvides]);
2935
+ if (!activeTool.defaults) return null;
2936
+ if (activeTool.defaults.subtype !== PdfAnnotationSubtype.STAMP) return null;
2937
+ const { register } = usePointerHandlers({ modeId: "stamp", pageIndex });
2938
+ const pageWidthPDF = pageWidth / scale;
2939
+ const pageHeightPDF = pageHeight / scale;
2940
+ const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
2941
+ const [start, setStart] = useState(null);
2942
+ const handlers = useMemo(
2943
+ () => ({
2944
+ onPointerDown: (pos, evt) => {
2945
+ var _a;
2946
+ const x = clamp(pos.x, 0, pageWidthPDF);
2947
+ const y = clamp(pos.y, 0, pageHeightPDF);
2948
+ setStart({ x, y });
2949
+ (_a = inputRef.current) == null ? void 0 : _a.click();
2950
+ }
2951
+ }),
2952
+ [pageWidthPDF, pageHeightPDF]
2953
+ );
2954
+ useEffect(() => register ? register(handlers) : void 0, [register, handlers]);
2955
+ const onChange = async (e) => {
2956
+ var _a;
2957
+ if (!annotationProvides || !start) return;
2958
+ const file = (_a = e.currentTarget.files) == null ? void 0 : _a[0];
2959
+ if (!file) return;
2960
+ const img = await new Promise((res, rej) => {
2961
+ const i = new Image();
2962
+ i.onload = () => res(i);
2963
+ i.onerror = rej;
2964
+ i.src = URL.createObjectURL(file);
2965
+ });
2966
+ const imgW = img.naturalWidth;
2967
+ const imgH = img.naturalHeight;
2968
+ const maxW = pageWidthPDF;
2969
+ const maxH = pageHeightPDF;
2970
+ const scaleFactor = Math.min(1, maxW / imgW, maxH / imgH);
2971
+ const pdfW = imgW * scaleFactor;
2972
+ const pdfH = imgH * scaleFactor;
2973
+ const posX = clamp(start.x, 0, maxW - pdfW);
2974
+ const posY = clamp(start.y, 0, maxH - pdfH);
2975
+ const rect = {
2976
+ origin: { x: posX, y: posY },
2977
+ size: { width: pdfW, height: pdfH }
2978
+ };
2979
+ const canvas = canvasRef.current;
2980
+ if (!canvas) return;
2981
+ canvas.width = pdfW;
2982
+ canvas.height = pdfH;
2983
+ const ctx = canvas.getContext("2d");
2984
+ ctx.drawImage(img, 0, 0, pdfW, pdfH);
2985
+ const imageData = ctx.getImageData(0, 0, pdfW, pdfH);
2986
+ const anno = {
2987
+ type: PdfAnnotationSubtype.STAMP,
2988
+ flags: ["print"],
2989
+ pageIndex,
2990
+ id: Date.now() + Math.random(),
2991
+ rect
2992
+ };
2993
+ annotationProvides.createAnnotation(pageIndex, anno, { imageData });
2994
+ annotationProvides.setActiveVariant(null);
2995
+ annotationProvides.selectAnnotation(pageIndex, anno.id);
2996
+ setStart(null);
2997
+ };
2998
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2999
+ /* @__PURE__ */ jsx("canvas", { style: { display: "none" }, ref: canvasRef }),
3000
+ /* @__PURE__ */ jsx(
3001
+ "input",
3002
+ {
3003
+ ref: inputRef,
3004
+ type: "file",
3005
+ accept: "image/png,image/jpeg",
3006
+ style: { display: "none" },
3007
+ onChange
3008
+ }
3009
+ )
3010
+ ] });
3011
+ };
2790
3012
  function AnnotationLayer({
2791
3013
  pageIndex,
2792
3014
  scale,
@@ -2871,6 +3093,15 @@ function AnnotationLayer({
2871
3093
  pageWidth,
2872
3094
  pageHeight
2873
3095
  }
3096
+ ),
3097
+ /* @__PURE__ */ jsx(
3098
+ StampPaint,
3099
+ {
3100
+ pageIndex,
3101
+ scale,
3102
+ pageWidth,
3103
+ pageHeight
3104
+ }
2874
3105
  )
2875
3106
  ]
2876
3107
  }