@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/preact";
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 "preact/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/preact";
6
6
  import { useSelectionCapability } from "@embedpdf/plugin-selection/preact";
7
7
  import { Fragment } from "preact";
@@ -151,6 +151,7 @@ function useDragResize({
151
151
  isResizable,
152
152
  computePatch,
153
153
  computeVertices,
154
+ lockAspectRatio = false,
154
155
  currentRect,
155
156
  setCurrentRect,
156
157
  setCurrentVertices,
@@ -185,6 +186,23 @@ function useDragResize({
185
186
  oy += dy;
186
187
  h -= dy;
187
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
+ }
188
206
  }
189
207
  if (w < 1 || h < 1) return currentRect;
190
208
  w = clamp(w, 1, pageW);
@@ -221,8 +239,14 @@ function useDragResize({
221
239
  setCurrentRect(patch.rect ?? nextRect);
222
240
  setPreviewObject(patch);
223
241
  };
224
- const onPointerUp = () => {
242
+ const onPointerUp = (e) => {
225
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
+ }
226
250
  const usedDir = dir.current || "bottom-right";
227
251
  drag.current = "idle";
228
252
  let patch = { rect: currentRect };
@@ -258,7 +282,9 @@ function useDragResize({
258
282
  rootHandlers: {
259
283
  onPointerDown,
260
284
  onPointerMove,
261
- onPointerUp
285
+ onPointerUp,
286
+ onPointerCancel: () => onPointerUp(),
287
+ onLostPointerCapture: () => onPointerUp()
262
288
  },
263
289
  startResize
264
290
  };
@@ -336,6 +362,7 @@ function AnnotationContainer({
336
362
  isSelected = false,
337
363
  isDraggable = true,
338
364
  isResizable = true,
365
+ lockAspectRatio = false,
339
366
  computeVertices,
340
367
  computePatch,
341
368
  selectionMenu,
@@ -357,6 +384,7 @@ function AnnotationContainer({
357
384
  isSelected,
358
385
  isDraggable,
359
386
  isResizable,
387
+ lockAspectRatio,
360
388
  computePatch,
361
389
  computeVertices,
362
390
  currentRect,
@@ -1270,6 +1298,86 @@ function FreeText({
1270
1298
  }
1271
1299
  );
1272
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
+ }
1273
1381
  function Annotations(annotationsProps) {
1274
1382
  const { pageIndex, scale, selectionMenu } = annotationsProps;
1275
1383
  const { provides: annotationProvides } = useAnnotationCapability();
@@ -1665,6 +1773,34 @@ function Annotations(annotationsProps) {
1665
1773
  annotation.localId
1666
1774
  );
1667
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
+ }
1668
1804
  return null;
1669
1805
  }) });
1670
1806
  }
@@ -2788,6 +2924,92 @@ const FreeTextPaint = ({
2788
2924
  }
2789
2925
  );
2790
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
+ };
2791
3013
  function AnnotationLayer({
2792
3014
  pageIndex,
2793
3015
  scale,
@@ -2872,6 +3094,15 @@ function AnnotationLayer({
2872
3094
  pageWidth,
2873
3095
  pageHeight
2874
3096
  }
3097
+ ),
3098
+ /* @__PURE__ */ jsx(
3099
+ StampPaint,
3100
+ {
3101
+ pageIndex,
3102
+ scale,
3103
+ pageWidth,
3104
+ pageHeight
3105
+ }
2875
3106
  )
2876
3107
  ]
2877
3108
  }