@embedpdf/plugin-annotation 2.8.0 → 2.9.1

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 (53) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +829 -148
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/geometry/cloudy-border.d.ts +90 -0
  6. package/dist/lib/geometry/index.d.ts +1 -0
  7. package/dist/lib/handlers/types.d.ts +2 -1
  8. package/dist/lib/tools/default-tools.d.ts +43 -88
  9. package/dist/lib/tools/types.d.ts +34 -1
  10. package/dist/lib/types.d.ts +3 -0
  11. package/dist/preact/index.cjs +1 -1
  12. package/dist/preact/index.cjs.map +1 -1
  13. package/dist/preact/index.js +371 -242
  14. package/dist/preact/index.js.map +1 -1
  15. package/dist/react/index.cjs +1 -1
  16. package/dist/react/index.cjs.map +1 -1
  17. package/dist/react/index.js +371 -242
  18. package/dist/react/index.js.map +1 -1
  19. package/dist/shared/components/annotation-container.d.ts +4 -2
  20. package/dist/shared/components/annotations/circle.d.ts +6 -2
  21. package/dist/shared/components/annotations/polygon.d.ts +3 -1
  22. package/dist/shared/components/annotations/square.d.ts +6 -2
  23. package/dist/shared/components/types.d.ts +6 -2
  24. package/dist/shared-preact/components/annotation-container.d.ts +4 -2
  25. package/dist/shared-preact/components/annotations/circle.d.ts +6 -2
  26. package/dist/shared-preact/components/annotations/polygon.d.ts +3 -1
  27. package/dist/shared-preact/components/annotations/square.d.ts +6 -2
  28. package/dist/shared-preact/components/types.d.ts +6 -2
  29. package/dist/shared-react/components/annotation-container.d.ts +4 -2
  30. package/dist/shared-react/components/annotations/circle.d.ts +6 -2
  31. package/dist/shared-react/components/annotations/polygon.d.ts +3 -1
  32. package/dist/shared-react/components/annotations/square.d.ts +6 -2
  33. package/dist/shared-react/components/types.d.ts +6 -2
  34. package/dist/svelte/components/annotations/Circle.svelte.d.ts +3 -1
  35. package/dist/svelte/components/annotations/Polygon.svelte.d.ts +1 -0
  36. package/dist/svelte/components/annotations/Square.svelte.d.ts +3 -1
  37. package/dist/svelte/components/types.d.ts +2 -1
  38. package/dist/svelte/context/types.d.ts +6 -2
  39. package/dist/svelte/index.cjs +1 -1
  40. package/dist/svelte/index.cjs.map +1 -1
  41. package/dist/svelte/index.js +525 -298
  42. package/dist/svelte/index.js.map +1 -1
  43. package/dist/vue/components/annotation-container.vue.d.ts +7 -6
  44. package/dist/vue/components/annotations/circle.vue.d.ts +5 -1
  45. package/dist/vue/components/annotations/polygon.vue.d.ts +2 -0
  46. package/dist/vue/components/annotations/square.vue.d.ts +5 -1
  47. package/dist/vue/components/annotations.vue.d.ts +8 -9
  48. package/dist/vue/context/types.d.ts +6 -2
  49. package/dist/vue/index.cjs +1 -1
  50. package/dist/vue/index.cjs.map +1 -1
  51. package/dist/vue/index.js +289 -135
  52. package/dist/vue/index.js.map +1 -1
  53. package/package.json +10 -10
package/dist/index.js CHANGED
@@ -340,6 +340,62 @@ const getSelectionGroupingAction = (s) => {
340
340
  }
341
341
  return selected.length >= 2 ? "group" : "disabled";
342
342
  };
343
+ const inkTools = [
344
+ {
345
+ id: "ink",
346
+ name: "Pen",
347
+ matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent !== "InkHighlight" ? 5 : 0,
348
+ interaction: {
349
+ exclusive: false,
350
+ cursor: "crosshair",
351
+ isDraggable: true,
352
+ isResizable: true,
353
+ lockAspectRatio: false
354
+ },
355
+ defaults: {
356
+ type: PdfAnnotationSubtype.INK,
357
+ strokeColor: "#E44234",
358
+ color: "#E44234",
359
+ // deprecated alias
360
+ opacity: 1,
361
+ strokeWidth: 6
362
+ },
363
+ behavior: {
364
+ commitDelay: 800
365
+ }
366
+ },
367
+ {
368
+ id: "inkHighlighter",
369
+ name: "Ink Highlighter",
370
+ matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent === "InkHighlight" ? 10 : 0,
371
+ interaction: {
372
+ exclusive: false,
373
+ cursor: "crosshair",
374
+ isDraggable: true,
375
+ isResizable: true,
376
+ lockAspectRatio: false,
377
+ lockGroupAspectRatio: (a) => {
378
+ const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
379
+ return r2 >= 6 && r2 <= 84;
380
+ }
381
+ },
382
+ defaults: {
383
+ type: PdfAnnotationSubtype.INK,
384
+ intent: "InkHighlight",
385
+ strokeColor: "#FFCD45",
386
+ color: "#FFCD45",
387
+ // deprecated alias
388
+ opacity: 1,
389
+ strokeWidth: 14,
390
+ blendMode: PdfBlendMode.Multiply
391
+ },
392
+ behavior: {
393
+ commitDelay: 800,
394
+ smartLineRecognition: true,
395
+ smartLineThreshold: 0.15
396
+ }
397
+ }
398
+ ];
343
399
  const defaultTools = [
344
400
  // Text Markup Tools
345
401
  {
@@ -482,52 +538,7 @@ const defaultTools = [
482
538
  }
483
539
  },
484
540
  // Drawing Tools
485
- {
486
- id: "ink",
487
- name: "Pen",
488
- matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent !== "InkHighlight" ? 5 : 0,
489
- interaction: {
490
- exclusive: false,
491
- cursor: "crosshair",
492
- isDraggable: true,
493
- isResizable: true,
494
- lockAspectRatio: false
495
- },
496
- defaults: {
497
- type: PdfAnnotationSubtype.INK,
498
- strokeColor: "#E44234",
499
- color: "#E44234",
500
- // deprecated alias
501
- opacity: 1,
502
- strokeWidth: 6
503
- }
504
- },
505
- {
506
- id: "inkHighlighter",
507
- name: "Ink Highlighter",
508
- matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent === "InkHighlight" ? 10 : 0,
509
- interaction: {
510
- exclusive: false,
511
- cursor: "crosshair",
512
- isDraggable: true,
513
- isResizable: true,
514
- lockAspectRatio: false,
515
- lockGroupAspectRatio: (a) => {
516
- const r = ((a.rotation ?? 0) % 90 + 90) % 90;
517
- return r >= 6 && r <= 84;
518
- }
519
- },
520
- defaults: {
521
- type: PdfAnnotationSubtype.INK,
522
- intent: "InkHighlight",
523
- strokeColor: "#FFCD45",
524
- color: "#FFCD45",
525
- // deprecated alias
526
- opacity: 1,
527
- strokeWidth: 14,
528
- blendMode: PdfBlendMode.Multiply
529
- }
530
- },
541
+ ...inkTools,
531
542
  // Shape Tools
532
543
  {
533
544
  id: "circle",
@@ -540,8 +551,8 @@ const defaultTools = [
540
551
  isResizable: true,
541
552
  lockAspectRatio: false,
542
553
  lockGroupAspectRatio: (a) => {
543
- const r = ((a.rotation ?? 0) % 90 + 90) % 90;
544
- return r >= 6 && r <= 84;
554
+ const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
555
+ return r2 >= 6 && r2 <= 84;
545
556
  }
546
557
  },
547
558
  defaults: {
@@ -568,8 +579,8 @@ const defaultTools = [
568
579
  isResizable: true,
569
580
  lockAspectRatio: false,
570
581
  lockGroupAspectRatio: (a) => {
571
- const r = ((a.rotation ?? 0) % 90 + 90) % 90;
572
- return r >= 6 && r <= 84;
582
+ const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
583
+ return r2 >= 6 && r2 <= 84;
573
584
  }
574
585
  },
575
586
  defaults: {
@@ -599,8 +610,8 @@ const defaultTools = [
599
610
  isGroupResizable: true,
600
611
  // Scales proportionally in a group
601
612
  lockGroupAspectRatio: (a) => {
602
- const r = ((a.rotation ?? 0) % 90 + 90) % 90;
603
- return r >= 6 && r <= 84;
613
+ const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
614
+ return r2 >= 6 && r2 <= 84;
604
615
  }
605
616
  },
606
617
  defaults: {
@@ -630,8 +641,8 @@ const defaultTools = [
630
641
  isGroupResizable: true,
631
642
  // Scales proportionally in a group
632
643
  lockGroupAspectRatio: (a) => {
633
- const r = ((a.rotation ?? 0) % 90 + 90) % 90;
634
- return r >= 6 && r <= 84;
644
+ const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
645
+ return r2 >= 6 && r2 <= 84;
635
646
  }
636
647
  },
637
648
  defaults: {
@@ -666,8 +677,8 @@ const defaultTools = [
666
677
  isGroupResizable: true,
667
678
  // Scales proportionally in a group
668
679
  lockGroupAspectRatio: (a) => {
669
- const r = ((a.rotation ?? 0) % 90 + 90) % 90;
670
- return r >= 6 && r <= 84;
680
+ const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
681
+ return r2 >= 6 && r2 <= 84;
671
682
  }
672
683
  },
673
684
  defaults: {
@@ -692,8 +703,8 @@ const defaultTools = [
692
703
  isGroupResizable: true,
693
704
  // Scales proportionally in a group
694
705
  lockGroupAspectRatio: (a) => {
695
- const r = ((a.rotation ?? 0) % 90 + 90) % 90;
696
- return r >= 6 && r <= 84;
706
+ const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
707
+ return r2 >= 6 && r2 <= 84;
697
708
  }
698
709
  },
699
710
  defaults: {
@@ -736,8 +747,8 @@ const defaultTools = [
736
747
  isResizable: true,
737
748
  lockAspectRatio: false,
738
749
  lockGroupAspectRatio: (a) => {
739
- const r = ((a.rotation ?? 0) % 90 + 90) % 90;
740
- return r >= 6 && r <= 84;
750
+ const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
751
+ return r2 >= 6 && r2 <= 84;
741
752
  }
742
753
  },
743
754
  defaults: {
@@ -760,7 +771,9 @@ const defaultTools = [
760
771
  defaultContent: "Insert text"
761
772
  },
762
773
  behavior: {
763
- insertUpright: true
774
+ insertUpright: true,
775
+ editAfterCreate: true,
776
+ selectAfterCreate: true
764
777
  }
765
778
  },
766
779
  {
@@ -865,13 +878,14 @@ const initialState = (cfg) => {
865
878
  }
866
879
  });
867
880
  const tools = Array.from(toolMap.values()).map((t) => {
868
- var _a, _b;
881
+ var _a, _b, _c;
869
882
  return {
870
883
  ...t,
871
884
  behavior: {
872
885
  ...t.behavior,
873
886
  deactivateToolAfterCreate: ((_a = t.behavior) == null ? void 0 : _a.deactivateToolAfterCreate) ?? cfg.deactivateToolAfterCreate ?? false,
874
- selectAfterCreate: ((_b = t.behavior) == null ? void 0 : _b.selectAfterCreate) ?? cfg.selectAfterCreate ?? true
887
+ selectAfterCreate: ((_b = t.behavior) == null ? void 0 : _b.selectAfterCreate) ?? cfg.selectAfterCreate ?? true,
888
+ editAfterCreate: ((_c = t.behavior) == null ? void 0 : _c.editAfterCreate) ?? cfg.editAfterCreate ?? false
875
889
  }
876
890
  };
877
891
  });
@@ -1180,6 +1194,18 @@ function useState(initialValue) {
1180
1194
  };
1181
1195
  return [getValue, setValue];
1182
1196
  }
1197
+ function isLineLike(points, threshold) {
1198
+ if (points.length < 3) return true;
1199
+ const A = points[0];
1200
+ const B = points[points.length - 1];
1201
+ const len = Math.hypot(B.x - A.x, B.y - A.y);
1202
+ if (len < 5) return false;
1203
+ const maxDev = points.reduce((max, P) => {
1204
+ const d = Math.abs((B.x - A.x) * (A.y - P.y) - (A.x - P.x) * (B.y - A.y)) / len;
1205
+ return Math.max(max, d);
1206
+ }, 0);
1207
+ return maxDev / len < threshold;
1208
+ }
1183
1209
  const inkHandlerFactory = {
1184
1210
  annotationType: PdfAnnotationSubtype.INK,
1185
1211
  create(context) {
@@ -1215,7 +1241,8 @@ const inkHandlerFactory = {
1215
1241
  data: {
1216
1242
  ...defaults,
1217
1243
  rect: bounds,
1218
- inkList: strokes
1244
+ inkList: strokes,
1245
+ blendMode: defaults.blendMode
1219
1246
  }
1220
1247
  };
1221
1248
  };
@@ -1243,6 +1270,37 @@ const inkHandlerFactory = {
1243
1270
  var _a;
1244
1271
  setIsDrawing(false);
1245
1272
  (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1273
+ const tool = getTool();
1274
+ const behavior = tool == null ? void 0 : tool.behavior;
1275
+ if (behavior == null ? void 0 : behavior.smartLineRecognition) {
1276
+ const threshold = behavior.smartLineThreshold ?? 0.15;
1277
+ const strokes = getStrokes();
1278
+ const last = strokes[strokes.length - 1];
1279
+ if (last && last.points.length > 1 && isLineLike(last.points, threshold)) {
1280
+ const first = last.points[0];
1281
+ const end = last.points[last.points.length - 1];
1282
+ const dx = end.x - first.x;
1283
+ const dy = end.y - first.y;
1284
+ const angleDeg = Math.atan2(Math.abs(dy), Math.abs(dx)) * (180 / Math.PI);
1285
+ const snapAngleDeg = behavior.snapAngleDeg ?? 15;
1286
+ if (angleDeg <= snapAngleDeg) {
1287
+ const avgY = last.points.reduce((sum, p) => sum + p.y, 0) / last.points.length;
1288
+ last.points = [
1289
+ { x: first.x, y: avgY },
1290
+ { x: end.x, y: avgY }
1291
+ ];
1292
+ } else if (angleDeg >= 90 - snapAngleDeg) {
1293
+ const avgX = last.points.reduce((sum, p) => sum + p.x, 0) / last.points.length;
1294
+ last.points = [
1295
+ { x: avgX, y: first.y },
1296
+ { x: avgX, y: end.y }
1297
+ ];
1298
+ }
1299
+ setStrokes([...strokes]);
1300
+ onPreview(getPreview());
1301
+ }
1302
+ }
1303
+ const commitDelay = (behavior == null ? void 0 : behavior.commitDelay) ?? 800;
1246
1304
  if (timerRef.current) clearTimeout(timerRef.current);
1247
1305
  timerRef.current = setTimeout(() => {
1248
1306
  const strokes = getStrokes();
@@ -1263,7 +1321,7 @@ const inkHandlerFactory = {
1263
1321
  }
1264
1322
  setStrokes([]);
1265
1323
  onPreview(null);
1266
- }, 800);
1324
+ }, commitDelay);
1267
1325
  },
1268
1326
  onPointerCancel: (_, evt) => {
1269
1327
  var _a;
@@ -1335,14 +1393,14 @@ const LINE_ENDING_HANDLERS = {
1335
1393
  },
1336
1394
  [PdfAnnotationLineEnding.Circle]: {
1337
1395
  getSvgPath: (sw) => {
1338
- const r = sw * 5 / 2;
1339
- return `M ${r} 0 A ${r} ${r} 0 1 1 ${-r} 0 A ${r} ${r} 0 1 1 ${r} 0`;
1396
+ const r2 = sw * 5 / 2;
1397
+ return `M ${r2} 0 A ${r2} ${r2} 0 1 1 ${-r2} 0 A ${r2} ${r2} 0 1 1 ${r2} 0`;
1340
1398
  },
1341
1399
  getLocalPoints: (sw) => {
1342
- const r = sw * 5 / 2;
1400
+ const r2 = sw * 5 / 2;
1343
1401
  return [
1344
- { x: -r, y: -r },
1345
- { x: r, y: r }
1402
+ { x: -r2, y: -r2 },
1403
+ { x: r2, y: r2 }
1346
1404
  ];
1347
1405
  },
1348
1406
  getRotation: () => 0,
@@ -1484,7 +1542,7 @@ function createEnding(ending, strokeWidth, rad, px, py) {
1484
1542
  if (!ending) return null;
1485
1543
  const handler = LINE_ENDING_HANDLERS[ending];
1486
1544
  if (!handler) return null;
1487
- const toDeg = (r) => r * 180 / Math.PI;
1545
+ const toDeg = (r2) => r2 * 180 / Math.PI;
1488
1546
  const rotationAngle = handler.getRotation(rad);
1489
1547
  return {
1490
1548
  d: handler.getSvgPath(strokeWidth),
@@ -1701,8 +1759,8 @@ function useClickDetector({
1701
1759
  onMove: (pos) => {
1702
1760
  const start = getStartPos();
1703
1761
  if (!start || getHasMoved()) return;
1704
- const distance = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
1705
- if (distance > threshold) {
1762
+ const distance2 = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
1763
+ if (distance2 > threshold) {
1706
1764
  setHasMoved(true);
1707
1765
  }
1708
1766
  },
@@ -2127,6 +2185,554 @@ const polylineHandlerFactory = {
2127
2185
  };
2128
2186
  }
2129
2187
  };
2188
+ function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
2189
+ const theta = rotationDegrees * Math.PI / 180;
2190
+ const A = Math.abs(Math.cos(theta));
2191
+ const B = Math.abs(Math.sin(theta));
2192
+ const det = A * A - B * B;
2193
+ const newAABBw = newAABBRect.size.width;
2194
+ const newAABBh = newAABBRect.size.height;
2195
+ let newWidth;
2196
+ let newHeight;
2197
+ if (Math.abs(det) > 1e-6) {
2198
+ newWidth = (A * newAABBw - B * newAABBh) / det;
2199
+ newHeight = (A * newAABBh - B * newAABBw) / det;
2200
+ newWidth = Math.max(newWidth, 1);
2201
+ newHeight = Math.max(newHeight, 1);
2202
+ } else {
2203
+ const origArea = originalAABBRect.size.width * originalAABBRect.size.height;
2204
+ const newArea = newAABBw * newAABBh;
2205
+ const uniformScale = origArea > 0 ? Math.sqrt(newArea / origArea) : 1;
2206
+ newWidth = originalUnrotatedRect.size.width * uniformScale;
2207
+ newHeight = originalUnrotatedRect.size.height * uniformScale;
2208
+ }
2209
+ const newCenterX = newAABBRect.origin.x + newAABBw / 2;
2210
+ const newCenterY = newAABBRect.origin.y + newAABBh / 2;
2211
+ return {
2212
+ origin: { x: newCenterX - newWidth / 2, y: newCenterY - newHeight / 2 },
2213
+ size: { width: newWidth, height: newHeight }
2214
+ };
2215
+ }
2216
+ const ANGLE_180 = Math.PI;
2217
+ const ANGLE_90 = Math.PI / 2;
2218
+ const ANGLE_34 = 34 * Math.PI / 180;
2219
+ const ANGLE_30 = 30 * Math.PI / 180;
2220
+ const ANGLE_12 = 12 * Math.PI / 180;
2221
+ class PathBuilder {
2222
+ constructor() {
2223
+ this.parts = [];
2224
+ this.bbox = {
2225
+ minX: Infinity,
2226
+ minY: Infinity,
2227
+ maxX: -Infinity,
2228
+ maxY: -Infinity
2229
+ };
2230
+ this.started = false;
2231
+ }
2232
+ moveTo(x, y) {
2233
+ const sy = -y;
2234
+ this.updateBBox(x, sy);
2235
+ this.parts.push(`M ${r(x)} ${r(sy)}`);
2236
+ this.started = true;
2237
+ }
2238
+ curveTo(x1, y1, x2, y2, x3, y3) {
2239
+ const sy1 = -y1;
2240
+ const sy2 = -y2;
2241
+ const sy3 = -y3;
2242
+ this.updateBBox(x1, sy1);
2243
+ this.updateBBox(x2, sy2);
2244
+ this.updateBBox(x3, sy3);
2245
+ this.parts.push(`C ${r(x1)} ${r(sy1)}, ${r(x2)} ${r(sy2)}, ${r(x3)} ${r(sy3)}`);
2246
+ }
2247
+ close() {
2248
+ if (this.started) {
2249
+ this.parts.push("Z");
2250
+ }
2251
+ }
2252
+ build(lineWidth) {
2253
+ const d = lineWidth > 0 ? lineWidth / 2 : 0;
2254
+ return {
2255
+ path: this.parts.join(" "),
2256
+ bbox: {
2257
+ minX: this.bbox.minX - d,
2258
+ minY: this.bbox.minY - d,
2259
+ maxX: this.bbox.maxX + d,
2260
+ maxY: this.bbox.maxY + d
2261
+ }
2262
+ };
2263
+ }
2264
+ updateBBox(x, y) {
2265
+ if (x < this.bbox.minX) this.bbox.minX = x;
2266
+ if (y < this.bbox.minY) this.bbox.minY = y;
2267
+ if (x > this.bbox.maxX) this.bbox.maxX = x;
2268
+ if (y > this.bbox.maxY) this.bbox.maxY = y;
2269
+ }
2270
+ }
2271
+ function r(n) {
2272
+ return Number(n.toFixed(4)).toString();
2273
+ }
2274
+ function distance(a, b) {
2275
+ const dx = b.x - a.x;
2276
+ const dy = b.y - a.y;
2277
+ return Math.sqrt(dx * dx + dy * dy);
2278
+ }
2279
+ function cosine(dx, hypot) {
2280
+ return hypot === 0 ? 0 : dx / hypot;
2281
+ }
2282
+ function sine(dy, hypot) {
2283
+ return hypot === 0 ? 0 : dy / hypot;
2284
+ }
2285
+ function polygonDirection(pts) {
2286
+ let a = 0;
2287
+ const len = pts.length;
2288
+ for (let i = 0; i < len; i++) {
2289
+ const j = (i + 1) % len;
2290
+ a += pts[i].x * pts[j].y - pts[i].y * pts[j].x;
2291
+ }
2292
+ return a;
2293
+ }
2294
+ function ensurePositiveWinding(pts) {
2295
+ if (polygonDirection(pts) < 0) {
2296
+ pts.reverse();
2297
+ }
2298
+ }
2299
+ function removeZeroLengthSegments(polygon) {
2300
+ if (polygon.length <= 2) return polygon;
2301
+ const tolerance = 0.5;
2302
+ const result = [polygon[0]];
2303
+ for (let i = 1; i < polygon.length; i++) {
2304
+ const prev = result[result.length - 1];
2305
+ const cur = polygon[i];
2306
+ if (Math.abs(cur.x - prev.x) >= tolerance || Math.abs(cur.y - prev.y) >= tolerance) {
2307
+ result.push(cur);
2308
+ }
2309
+ }
2310
+ return result;
2311
+ }
2312
+ function arcSegment(startAng, endAng, cx, cy, rx, ry, out, addMoveTo) {
2313
+ const cosA = Math.cos(startAng);
2314
+ const sinA = Math.sin(startAng);
2315
+ const cosB = Math.cos(endAng);
2316
+ const sinB = Math.sin(endAng);
2317
+ const denom = Math.sin((endAng - startAng) / 2);
2318
+ if (denom === 0) {
2319
+ if (addMoveTo) {
2320
+ out.moveTo(cx + rx * cosA, cy + ry * sinA);
2321
+ }
2322
+ return;
2323
+ }
2324
+ const bcp = 4 / 3 * (1 - Math.cos((endAng - startAng) / 2)) / denom;
2325
+ const p1x = cx + rx * (cosA - bcp * sinA);
2326
+ const p1y = cy + ry * (sinA + bcp * cosA);
2327
+ const p2x = cx + rx * (cosB + bcp * sinB);
2328
+ const p2y = cy + ry * (sinB - bcp * cosB);
2329
+ const p3x = cx + rx * cosB;
2330
+ const p3y = cy + ry * sinB;
2331
+ if (addMoveTo) {
2332
+ out.moveTo(cx + rx * cosA, cy + ry * sinA);
2333
+ }
2334
+ out.curveTo(p1x, p1y, p2x, p2y, p3x, p3y);
2335
+ }
2336
+ function arcSegmentToArray(startAng, endAng, cx, cy, rx, ry) {
2337
+ const cosA = Math.cos(startAng);
2338
+ const sinA = Math.sin(startAng);
2339
+ const cosB = Math.cos(endAng);
2340
+ const sinB = Math.sin(endAng);
2341
+ const denom = Math.sin((endAng - startAng) / 2);
2342
+ if (denom === 0) return [];
2343
+ const bcp = 4 / 3 * (1 - Math.cos((endAng - startAng) / 2)) / denom;
2344
+ return [
2345
+ { x: cx + rx * (cosA - bcp * sinA), y: cy + ry * (sinA + bcp * cosA) },
2346
+ { x: cx + rx * (cosB + bcp * sinB), y: cy + ry * (sinB - bcp * cosB) },
2347
+ { x: cx + rx * cosB, y: cy + ry * sinB }
2348
+ ];
2349
+ }
2350
+ function getArc(startAng, endAng, rx, ry, cx, cy, out, addMoveTo) {
2351
+ const angleIncr = ANGLE_90;
2352
+ let angleTodo = endAng - startAng;
2353
+ while (angleTodo < 0) angleTodo += 2 * Math.PI;
2354
+ const sweep = angleTodo;
2355
+ let angleDone = 0;
2356
+ if (addMoveTo) {
2357
+ out.moveTo(cx + rx * Math.cos(startAng), cy + ry * Math.sin(startAng));
2358
+ }
2359
+ while (angleTodo > angleIncr) {
2360
+ arcSegment(startAng + angleDone, startAng + angleDone + angleIncr, cx, cy, rx, ry, out, false);
2361
+ angleDone += angleIncr;
2362
+ angleTodo -= angleIncr;
2363
+ }
2364
+ if (angleTodo > 0) {
2365
+ arcSegment(startAng + angleDone, startAng + sweep, cx, cy, rx, ry, out, false);
2366
+ }
2367
+ }
2368
+ function addCornerCurl(anglePrev, angleCur, radius, cx, cy, alpha, alphaPrev, out, addMoveTo) {
2369
+ let a = anglePrev + ANGLE_180 + alphaPrev;
2370
+ const b = anglePrev + ANGLE_180 + alphaPrev - 22 * Math.PI / 180;
2371
+ arcSegment(a, b, cx, cy, radius, radius, out, addMoveTo);
2372
+ a = b;
2373
+ const bEnd = angleCur - alpha;
2374
+ getArc(a, bEnd, radius, radius, cx, cy, out, false);
2375
+ }
2376
+ function addFirstIntermediateCurl(angleCur, r2, alpha, cx, cy, out) {
2377
+ const a = angleCur + ANGLE_180;
2378
+ arcSegment(a + alpha, a + alpha - ANGLE_30, cx, cy, r2, r2, out, false);
2379
+ arcSegment(a + alpha - ANGLE_30, a + ANGLE_90, cx, cy, r2, r2, out, false);
2380
+ arcSegment(a + ANGLE_90, a + ANGLE_180 - ANGLE_34, cx, cy, r2, r2, out, false);
2381
+ }
2382
+ function getIntermediateCurlTemplate(angleCur, r2) {
2383
+ const pts = [];
2384
+ const a = angleCur + ANGLE_180;
2385
+ pts.push(...arcSegmentToArray(a + ANGLE_34, a + ANGLE_12, 0, 0, r2, r2));
2386
+ pts.push(...arcSegmentToArray(a + ANGLE_12, a + ANGLE_90, 0, 0, r2, r2));
2387
+ pts.push(...arcSegmentToArray(a + ANGLE_90, a + ANGLE_180 - ANGLE_34, 0, 0, r2, r2));
2388
+ return pts;
2389
+ }
2390
+ function outputCurlTemplate(template, x, y, out) {
2391
+ for (let i = 0; i + 2 < template.length; i += 3) {
2392
+ const a = template[i];
2393
+ const b = template[i + 1];
2394
+ const c = template[i + 2];
2395
+ out.curveTo(a.x + x, a.y + y, b.x + x, b.y + y, c.x + x, c.y + y);
2396
+ }
2397
+ }
2398
+ function computeParamsPolygon(idealRadius, k, length) {
2399
+ if (length === 0) return { n: -1, adjustedRadius: idealRadius };
2400
+ const cornerSpace = 2 * k * idealRadius;
2401
+ const remaining = length - cornerSpace;
2402
+ if (remaining <= 0) {
2403
+ return { n: 0, adjustedRadius: idealRadius };
2404
+ }
2405
+ const idealAdvance = 2 * k * idealRadius;
2406
+ const n = Math.max(1, Math.ceil(remaining / idealAdvance));
2407
+ const adjustedRadius = remaining / (n * 2 * k);
2408
+ return { n, adjustedRadius };
2409
+ }
2410
+ function cloudyPolygonImpl(vertices, isEllipse, intensity, lineWidth, out) {
2411
+ let polygon = removeZeroLengthSegments(vertices);
2412
+ ensurePositiveWinding(polygon);
2413
+ const numPoints = polygon.length;
2414
+ if (numPoints < 2) return;
2415
+ if (intensity <= 0) {
2416
+ out.moveTo(polygon[0].x, polygon[0].y);
2417
+ for (let i = 1; i < numPoints; i++) {
2418
+ out.curveTo(
2419
+ polygon[i].x,
2420
+ polygon[i].y,
2421
+ polygon[i].x,
2422
+ polygon[i].y,
2423
+ polygon[i].x,
2424
+ polygon[i].y
2425
+ );
2426
+ }
2427
+ return;
2428
+ }
2429
+ let idealRadius = isEllipse ? getEllipseCloudRadius(intensity, lineWidth) : getPolygonCloudRadius(intensity, lineWidth);
2430
+ if (idealRadius < 0.5) idealRadius = 0.5;
2431
+ const k = Math.cos(ANGLE_34);
2432
+ const edgeAlphas = [];
2433
+ for (let j = 0; j + 1 < numPoints; j++) {
2434
+ const len = distance(polygon[j], polygon[j + 1]);
2435
+ if (len <= 0 || len >= 2 * k * idealRadius) {
2436
+ edgeAlphas.push(ANGLE_34);
2437
+ } else {
2438
+ edgeAlphas.push(Math.acos(Math.min(1, len / (2 * idealRadius))));
2439
+ }
2440
+ }
2441
+ let anglePrev = 0;
2442
+ let outputStarted = false;
2443
+ for (let j = 0; j + 1 < numPoints; j++) {
2444
+ const pt = polygon[j];
2445
+ const ptNext = polygon[j + 1];
2446
+ const len = distance(pt, ptNext);
2447
+ if (len === 0) continue;
2448
+ const params = computeParamsPolygon(idealRadius, k, len);
2449
+ if (params.n < 0) {
2450
+ if (!outputStarted) {
2451
+ out.moveTo(pt.x, pt.y);
2452
+ outputStarted = true;
2453
+ }
2454
+ continue;
2455
+ }
2456
+ const edgeRadius = Math.max(0.5, params.adjustedRadius);
2457
+ const intermAdvance = 2 * k * edgeRadius;
2458
+ const firstAdvance = k * idealRadius + k * edgeRadius;
2459
+ let angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
2460
+ if (j === 0) {
2461
+ const ptPrev = polygon[numPoints - 2];
2462
+ anglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
2463
+ }
2464
+ const cos = cosine(ptNext.x - pt.x, len);
2465
+ const sin = sine(ptNext.y - pt.y, len);
2466
+ let x = pt.x;
2467
+ let y = pt.y;
2468
+ const alpha = edgeAlphas[j];
2469
+ const prevEdgeIdx = j === 0 ? numPoints - 2 : j - 1;
2470
+ const alphaPrevEdge = edgeAlphas[prevEdgeIdx] ?? ANGLE_34;
2471
+ addCornerCurl(
2472
+ anglePrev,
2473
+ angleCur,
2474
+ idealRadius,
2475
+ pt.x,
2476
+ pt.y,
2477
+ alpha,
2478
+ alphaPrevEdge,
2479
+ out,
2480
+ !outputStarted
2481
+ );
2482
+ outputStarted = true;
2483
+ if (params.n === 0) {
2484
+ x += len * cos;
2485
+ y += len * sin;
2486
+ } else {
2487
+ x += firstAdvance * cos;
2488
+ y += firstAdvance * sin;
2489
+ let numInterm = params.n;
2490
+ if (params.n >= 1) {
2491
+ addFirstIntermediateCurl(angleCur, edgeRadius, ANGLE_34, x, y, out);
2492
+ x += intermAdvance * cos;
2493
+ y += intermAdvance * sin;
2494
+ numInterm = params.n - 1;
2495
+ }
2496
+ const template = getIntermediateCurlTemplate(angleCur, edgeRadius);
2497
+ for (let i = 0; i < numInterm; i++) {
2498
+ outputCurlTemplate(template, x, y, out);
2499
+ x += intermAdvance * cos;
2500
+ y += intermAdvance * sin;
2501
+ }
2502
+ }
2503
+ anglePrev = angleCur;
2504
+ }
2505
+ }
2506
+ function flattenEllipse(left, bottom, right, top) {
2507
+ const cx = (left + right) / 2;
2508
+ const cy = (bottom + top) / 2;
2509
+ const rx = (right - left) / 2;
2510
+ const ry = (top - bottom) / 2;
2511
+ if (rx <= 0 || ry <= 0) return [];
2512
+ const numSegments = Math.max(32, Math.ceil(Math.max(rx, ry) * 2));
2513
+ const points = [];
2514
+ for (let i = 0; i <= numSegments; i++) {
2515
+ const angle = 2 * Math.PI * i / numSegments;
2516
+ points.push({
2517
+ x: cx + rx * Math.cos(angle),
2518
+ y: cy + ry * Math.sin(angle)
2519
+ });
2520
+ }
2521
+ return points;
2522
+ }
2523
+ function getEllipseCloudRadius(intensity, lineWidth) {
2524
+ return 4.75 * intensity + 0.5 * lineWidth;
2525
+ }
2526
+ function getPolygonCloudRadius(intensity, lineWidth) {
2527
+ return 4 * intensity + 0.5 * lineWidth;
2528
+ }
2529
+ function cloudyEllipseImpl(left, bottom, right, top, intensity, lineWidth, out) {
2530
+ if (intensity <= 0) {
2531
+ const rx = Math.abs(right - left) / 2;
2532
+ const ry = Math.abs(top - bottom) / 2;
2533
+ const cx = (left + right) / 2;
2534
+ const cy = (bottom + top) / 2;
2535
+ getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
2536
+ return;
2537
+ }
2538
+ const width = right - left;
2539
+ const height = top - bottom;
2540
+ let cloudRadius = getEllipseCloudRadius(intensity, lineWidth);
2541
+ const threshold1 = 0.5 * cloudRadius;
2542
+ if (width < threshold1 && height < threshold1) {
2543
+ const rx = Math.abs(right - left) / 2;
2544
+ const ry = Math.abs(top - bottom) / 2;
2545
+ const cx = (left + right) / 2;
2546
+ const cy = (bottom + top) / 2;
2547
+ getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
2548
+ return;
2549
+ }
2550
+ const threshold2 = 5;
2551
+ if (width < threshold2 && height > 20 || width > 20 && height < threshold2) {
2552
+ cloudyPolygonImpl(
2553
+ [
2554
+ { x: left, y: bottom },
2555
+ { x: right, y: bottom },
2556
+ { x: right, y: top },
2557
+ { x: left, y: top },
2558
+ { x: left, y: bottom }
2559
+ ],
2560
+ true,
2561
+ intensity,
2562
+ lineWidth,
2563
+ out
2564
+ );
2565
+ return;
2566
+ }
2567
+ const radiusAdj = Math.sin(ANGLE_12) * cloudRadius - 1.5;
2568
+ let adjLeft = left;
2569
+ let adjRight = right;
2570
+ let adjBottom = bottom;
2571
+ let adjTop = top;
2572
+ if (width > 2 * radiusAdj) {
2573
+ adjLeft += radiusAdj;
2574
+ adjRight -= radiusAdj;
2575
+ } else {
2576
+ const mid = (left + right) / 2;
2577
+ adjLeft = mid - 0.1;
2578
+ adjRight = mid + 0.1;
2579
+ }
2580
+ if (height > 2 * radiusAdj) {
2581
+ adjBottom += radiusAdj;
2582
+ adjTop -= radiusAdj;
2583
+ } else {
2584
+ const mid = (top + bottom) / 2;
2585
+ adjTop = mid + 0.1;
2586
+ adjBottom = mid - 0.1;
2587
+ }
2588
+ const flatPolygon = flattenEllipse(adjLeft, adjBottom, adjRight, adjTop);
2589
+ const numFlatPts = flatPolygon.length;
2590
+ if (numFlatPts < 2) return;
2591
+ let totLen = 0;
2592
+ for (let i = 1; i < numFlatPts; i++) {
2593
+ totLen += distance(flatPolygon[i - 1], flatPolygon[i]);
2594
+ }
2595
+ const k = Math.cos(ANGLE_34);
2596
+ let curlAdvance = 2 * k * cloudRadius;
2597
+ let n = Math.ceil(totLen / curlAdvance);
2598
+ if (n < 2) {
2599
+ const rx = Math.abs(right - left) / 2;
2600
+ const ry = Math.abs(top - bottom) / 2;
2601
+ const cx = (left + right) / 2;
2602
+ const cy = (bottom + top) / 2;
2603
+ getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
2604
+ return;
2605
+ }
2606
+ curlAdvance = totLen / n;
2607
+ cloudRadius = curlAdvance / (2 * k);
2608
+ if (cloudRadius < 0.5) {
2609
+ cloudRadius = 0.5;
2610
+ curlAdvance = 2 * k * cloudRadius;
2611
+ } else if (cloudRadius < 3) {
2612
+ const rx = Math.abs(right - left) / 2;
2613
+ const ry = Math.abs(top - bottom) / 2;
2614
+ const cx = (left + right) / 2;
2615
+ const cy = (bottom + top) / 2;
2616
+ getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
2617
+ return;
2618
+ }
2619
+ const centerPoints = [];
2620
+ let lengthRemain = 0;
2621
+ const comparisonToler = lineWidth * 0.1;
2622
+ for (let i = 0; i + 1 < numFlatPts; i++) {
2623
+ const p1 = flatPolygon[i];
2624
+ const p2 = flatPolygon[i + 1];
2625
+ const segDx = p2.x - p1.x;
2626
+ const segDy = p2.y - p1.y;
2627
+ const segLen = distance(p1, p2);
2628
+ if (segLen === 0) continue;
2629
+ let lengthTodo = segLen + lengthRemain;
2630
+ if (lengthTodo >= curlAdvance - comparisonToler || i === numFlatPts - 2) {
2631
+ const cos = cosine(segDx, segLen);
2632
+ const sin = sine(segDy, segLen);
2633
+ let d = curlAdvance - lengthRemain;
2634
+ while (lengthTodo >= curlAdvance - comparisonToler) {
2635
+ centerPoints.push({ x: p1.x + d * cos, y: p1.y + d * sin });
2636
+ lengthTodo -= curlAdvance;
2637
+ d += curlAdvance;
2638
+ }
2639
+ lengthRemain = Math.max(0, lengthTodo);
2640
+ } else {
2641
+ lengthRemain += segLen;
2642
+ }
2643
+ }
2644
+ const cpLen = centerPoints.length;
2645
+ let epAnglePrev = 0;
2646
+ let epAlphaPrev = 0;
2647
+ for (let i = 0; i < cpLen; i++) {
2648
+ const idxNext = (i + 1) % cpLen;
2649
+ const pt = centerPoints[i];
2650
+ const ptNext = centerPoints[idxNext];
2651
+ if (i === 0) {
2652
+ const ptPrev = centerPoints[cpLen - 1];
2653
+ epAnglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
2654
+ epAlphaPrev = computeParamsEllipse(ptPrev, pt, cloudRadius, curlAdvance);
2655
+ }
2656
+ const angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
2657
+ const alpha = computeParamsEllipse(pt, ptNext, cloudRadius, curlAdvance);
2658
+ addCornerCurl(epAnglePrev, angleCur, cloudRadius, pt.x, pt.y, alpha, epAlphaPrev, out, i === 0);
2659
+ epAnglePrev = angleCur;
2660
+ epAlphaPrev = alpha;
2661
+ }
2662
+ }
2663
+ function computeParamsEllipse(pt, ptNext, r2, curlAdv) {
2664
+ const len = distance(pt, ptNext);
2665
+ if (len === 0) return ANGLE_34;
2666
+ const e = len - curlAdv;
2667
+ const arg = (curlAdv / 2 + e / 2) / r2;
2668
+ return arg < -1 || arg > 1 ? 0 : Math.acos(arg);
2669
+ }
2670
+ function getCloudyBorderExtent(intensity, lineWidth, isEllipse) {
2671
+ const cloudRadius = isEllipse ? getEllipseCloudRadius(intensity, lineWidth) : getPolygonCloudRadius(intensity, lineWidth);
2672
+ return cloudRadius + lineWidth / 2;
2673
+ }
2674
+ function generateCloudyRectanglePath(rect, rd, intensity, lineWidth) {
2675
+ const out = new PathBuilder();
2676
+ let left = 0;
2677
+ let top = 0;
2678
+ let right = rect.width;
2679
+ let bottom = rect.height;
2680
+ if (rd) {
2681
+ left += rd.left;
2682
+ top += rd.top;
2683
+ right -= rd.right;
2684
+ bottom -= rd.bottom;
2685
+ } else {
2686
+ left += lineWidth / 2;
2687
+ top += lineWidth / 2;
2688
+ right -= lineWidth / 2;
2689
+ bottom -= lineWidth / 2;
2690
+ }
2691
+ const polygon = [
2692
+ { x: left, y: -top },
2693
+ { x: right, y: -top },
2694
+ { x: right, y: -bottom },
2695
+ { x: left, y: -bottom },
2696
+ { x: left, y: -top }
2697
+ ];
2698
+ cloudyPolygonImpl(polygon, false, intensity, lineWidth, out);
2699
+ out.close();
2700
+ return out.build(lineWidth);
2701
+ }
2702
+ function generateCloudyEllipsePath(rect, rd, intensity, lineWidth) {
2703
+ const out = new PathBuilder();
2704
+ let left = 0;
2705
+ let top = 0;
2706
+ let right = rect.width;
2707
+ let bottom = rect.height;
2708
+ if (rd) {
2709
+ left += rd.left;
2710
+ top += rd.top;
2711
+ right -= rd.right;
2712
+ bottom -= rd.bottom;
2713
+ }
2714
+ cloudyEllipseImpl(left, -bottom, right, -top, intensity, lineWidth, out);
2715
+ out.close();
2716
+ return out.build(lineWidth);
2717
+ }
2718
+ function generateCloudyPolygonPath(vertices, rectOrigin, intensity, lineWidth) {
2719
+ const out = new PathBuilder();
2720
+ if (vertices.length < 3) {
2721
+ return out.build(lineWidth);
2722
+ }
2723
+ const localPts = vertices.map((v) => ({
2724
+ x: v.x - rectOrigin.x,
2725
+ y: -(v.y - rectOrigin.y)
2726
+ }));
2727
+ const first = localPts[0];
2728
+ const last = localPts[localPts.length - 1];
2729
+ if (first.x !== last.x || first.y !== last.y) {
2730
+ localPts.push({ x: first.x, y: first.y });
2731
+ }
2732
+ cloudyPolygonImpl(localPts, false, intensity, lineWidth, out);
2733
+ out.close();
2734
+ return out.build(lineWidth);
2735
+ }
2130
2736
  const HANDLE_SIZE_PX = 14;
2131
2737
  const polygonHandlerFactory = {
2132
2738
  annotationType: PdfAnnotationSubtype.POLYGON,
@@ -2165,7 +2771,9 @@ const polygonHandlerFactory = {
2165
2771
  if (vertices.length < 3) return;
2166
2772
  const defaults = getDefaults();
2167
2773
  if (!defaults) return;
2168
- const rect = expandRect(rectFromPoints(vertices), defaults.strokeWidth / 2);
2774
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2775
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
2776
+ const rect = expandRect(rectFromPoints(vertices), pad);
2169
2777
  const anno = {
2170
2778
  ...defaults,
2171
2779
  vertices,
@@ -2173,7 +2781,10 @@ const polygonHandlerFactory = {
2173
2781
  type: PdfAnnotationSubtype.POLYGON,
2174
2782
  pageIndex: context.pageIndex,
2175
2783
  id: uuidV4(),
2176
- created: /* @__PURE__ */ new Date()
2784
+ created: /* @__PURE__ */ new Date(),
2785
+ ...intensity > 0 && {
2786
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2787
+ }
2177
2788
  };
2178
2789
  onCommit(anno);
2179
2790
  setVertices([]);
@@ -2186,8 +2797,10 @@ const polygonHandlerFactory = {
2186
2797
  if (vertices.length === 0 || !currentPos) return null;
2187
2798
  const defaults = getDefaults();
2188
2799
  if (!defaults) return null;
2800
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2801
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
2189
2802
  const allPoints = [...vertices, currentPos];
2190
- const bounds = expandRect(rectFromPoints(allPoints), defaults.strokeWidth / 2);
2803
+ const bounds = expandRect(rectFromPoints(allPoints), pad);
2191
2804
  return {
2192
2805
  type: PdfAnnotationSubtype.POLYGON,
2193
2806
  bounds,
@@ -2273,10 +2886,11 @@ const squareHandlerFactory = {
2273
2886
  const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2274
2887
  const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2275
2888
  const strokeWidth = defaults.strokeWidth;
2276
- const halfStroke = strokeWidth / 2;
2889
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2890
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
2277
2891
  const rect = {
2278
- origin: { x: x - halfStroke, y: y - halfStroke },
2279
- size: { width: width + strokeWidth, height: height + strokeWidth }
2892
+ origin: { x: x - pad, y: y - pad },
2893
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2280
2894
  };
2281
2895
  const anno = {
2282
2896
  ...defaults,
@@ -2284,7 +2898,10 @@ const squareHandlerFactory = {
2284
2898
  created: /* @__PURE__ */ new Date(),
2285
2899
  id: uuidV4(),
2286
2900
  pageIndex,
2287
- rect
2901
+ rect,
2902
+ ...intensity > 0 && {
2903
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2904
+ }
2288
2905
  };
2289
2906
  onCommit(anno);
2290
2907
  }
@@ -2299,17 +2916,21 @@ const squareHandlerFactory = {
2299
2916
  const defaults = getDefaults();
2300
2917
  if (!defaults) return null;
2301
2918
  const strokeWidth = defaults.strokeWidth;
2302
- const halfStroke = strokeWidth / 2;
2919
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2920
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
2303
2921
  const rect = {
2304
- origin: { x: minX - halfStroke, y: minY - halfStroke },
2305
- size: { width: width + strokeWidth, height: height + strokeWidth }
2922
+ origin: { x: minX - pad, y: minY - pad },
2923
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2306
2924
  };
2307
2925
  return {
2308
2926
  type: PdfAnnotationSubtype.SQUARE,
2309
2927
  bounds: rect,
2310
2928
  data: {
2311
2929
  rect,
2312
- ...defaults
2930
+ ...defaults,
2931
+ ...intensity > 0 && {
2932
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2933
+ }
2313
2934
  }
2314
2935
  };
2315
2936
  };
@@ -2343,13 +2964,18 @@ const squareHandlerFactory = {
2343
2964
  if (!defaults2) return;
2344
2965
  const preview = getPreview(clampedPos);
2345
2966
  if (preview) {
2967
+ const intensity = defaults2.cloudyBorderIntensity ?? 0;
2968
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, false) : void 0;
2346
2969
  const anno = {
2347
2970
  ...defaults2,
2348
2971
  type: PdfAnnotationSubtype.SQUARE,
2349
2972
  created: /* @__PURE__ */ new Date(),
2350
2973
  id: uuidV4(),
2351
2974
  pageIndex,
2352
- rect: preview.data.rect
2975
+ rect: preview.data.rect,
2976
+ ...pad !== void 0 && {
2977
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2978
+ }
2353
2979
  };
2354
2980
  onCommit(anno);
2355
2981
  }
@@ -2473,10 +3099,11 @@ const circleHandlerFactory = {
2473
3099
  const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2474
3100
  const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2475
3101
  const strokeWidth = defaults.strokeWidth;
2476
- const halfStroke = strokeWidth / 2;
3102
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
3103
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
2477
3104
  const rect = {
2478
- origin: { x: x - halfStroke, y: y - halfStroke },
2479
- size: { width: width + strokeWidth, height: height + strokeWidth }
3105
+ origin: { x: x - pad, y: y - pad },
3106
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2480
3107
  };
2481
3108
  const anno = {
2482
3109
  ...defaults,
@@ -2484,7 +3111,10 @@ const circleHandlerFactory = {
2484
3111
  created: /* @__PURE__ */ new Date(),
2485
3112
  id: uuidV4(),
2486
3113
  pageIndex,
2487
- rect
3114
+ rect,
3115
+ ...intensity > 0 && {
3116
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
3117
+ }
2488
3118
  };
2489
3119
  onCommit(anno);
2490
3120
  }
@@ -2499,17 +3129,21 @@ const circleHandlerFactory = {
2499
3129
  const defaults = getDefaults();
2500
3130
  if (!defaults) return null;
2501
3131
  const strokeWidth = defaults.strokeWidth;
2502
- const halfStroke = strokeWidth / 2;
3132
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
3133
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
2503
3134
  const rect = {
2504
- origin: { x: minX - halfStroke, y: minY - halfStroke },
2505
- size: { width: width + strokeWidth, height: height + strokeWidth }
3135
+ origin: { x: minX - pad, y: minY - pad },
3136
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2506
3137
  };
2507
3138
  return {
2508
3139
  type: PdfAnnotationSubtype.CIRCLE,
2509
3140
  bounds: rect,
2510
3141
  data: {
2511
3142
  rect,
2512
- ...defaults
3143
+ ...defaults,
3144
+ ...intensity > 0 && {
3145
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
3146
+ }
2513
3147
  }
2514
3148
  };
2515
3149
  };
@@ -2543,6 +3177,8 @@ const circleHandlerFactory = {
2543
3177
  if (!defaults2) return;
2544
3178
  const preview = getPreview(clampedPos);
2545
3179
  if (preview) {
3180
+ const intensity = defaults2.cloudyBorderIntensity ?? 0;
3181
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, true) : void 0;
2546
3182
  const anno = {
2547
3183
  ...defaults2,
2548
3184
  type: PdfAnnotationSubtype.CIRCLE,
@@ -2550,7 +3186,10 @@ const circleHandlerFactory = {
2550
3186
  created: /* @__PURE__ */ new Date(),
2551
3187
  id: uuidV4(),
2552
3188
  pageIndex,
2553
- rect: preview.data.rect
3189
+ rect: preview.data.rect,
3190
+ ...pad !== void 0 && {
3191
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
3192
+ }
2554
3193
  };
2555
3194
  onCommit(anno);
2556
3195
  }
@@ -2722,23 +3361,29 @@ const patchInk = (original, ctx) => {
2722
3361
  }
2723
3362
  case "resize": {
2724
3363
  if (!ctx.changes.rect) return ctx.changes;
2725
- const { oldRect, resolvedRect, rects } = baseResizeScaling(
3364
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
2726
3365
  original,
2727
3366
  ctx.changes.rect,
2728
3367
  ctx.metadata
2729
3368
  );
2730
- const inset = (r, pad) => ({
2731
- origin: { x: r.origin.x + pad, y: r.origin.y + pad },
3369
+ const inset = (r2, pad) => ({
3370
+ origin: { x: r2.origin.x + pad, y: r2.origin.y + pad },
2732
3371
  size: {
2733
- width: Math.max(1, r.size.width - pad * 2),
2734
- height: Math.max(1, r.size.height - pad * 2)
3372
+ width: Math.max(1, r2.size.width - pad * 2),
3373
+ height: Math.max(1, r2.size.height - pad * 2)
2735
3374
  }
2736
3375
  });
2737
- const strokeScale = Math.min(
2738
- resolvedRect.size.width / oldRect.size.width,
2739
- resolvedRect.size.height / oldRect.size.height
3376
+ const resizeEpsilon = 1e-3;
3377
+ const widthChanged = Math.abs(scaleX - 1) > resizeEpsilon;
3378
+ const heightChanged = Math.abs(scaleY - 1) > resizeEpsilon;
3379
+ const strokeScale = widthChanged && !heightChanged ? scaleX : !widthChanged && heightChanged ? scaleY : Math.min(scaleX, scaleY);
3380
+ const rawStrokeWidth = Math.max(1, original.strokeWidth * strokeScale);
3381
+ const maxStrokeWidth = Math.max(
3382
+ 1,
3383
+ Math.min(resolvedRect.size.width, resolvedRect.size.height)
2740
3384
  );
2741
- const newStrokeWidth = Math.max(1, Math.round(original.strokeWidth * strokeScale));
3385
+ const clampedStrokeWidth = Math.min(rawStrokeWidth, maxStrokeWidth);
3386
+ const newStrokeWidth = Number(clampedStrokeWidth.toFixed(1));
2742
3387
  const innerOld = inset(oldRect, original.strokeWidth / 2);
2743
3388
  const innerNew = inset(resolvedRect, newStrokeWidth / 2);
2744
3389
  const sx = innerNew.size.width / Math.max(innerOld.size.width, 1e-6);
@@ -2948,11 +3593,17 @@ const patchPolyline = (orig, ctx) => {
2948
3593
  return ctx.changes;
2949
3594
  }
2950
3595
  };
3596
+ function getPolygonPad(intensity, strokeWidth) {
3597
+ if ((intensity ?? 0) > 0) {
3598
+ return getCloudyBorderExtent(intensity, strokeWidth, false);
3599
+ }
3600
+ return strokeWidth / 2;
3601
+ }
2951
3602
  const patchPolygon = (orig, ctx) => {
2952
3603
  switch (ctx.type) {
2953
3604
  case "vertex-edit":
2954
3605
  if (ctx.changes.vertices && ctx.changes.vertices.length) {
2955
- const pad = orig.strokeWidth / 2;
3606
+ const pad = getPolygonPad(orig.cloudyBorderIntensity, orig.strokeWidth);
2956
3607
  const rawVertices = ctx.changes.vertices;
2957
3608
  const rawRect = expandRect(rectFromPoints(rawVertices), pad);
2958
3609
  const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
@@ -2996,15 +3647,30 @@ const patchPolygon = (orig, ctx) => {
2996
3647
  };
2997
3648
  }
2998
3649
  case "property-update": {
2999
- const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0;
3650
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3651
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0 || cloudyChanged;
3000
3652
  if (!needsRectUpdate) return ctx.changes;
3001
3653
  const merged = { ...orig, ...ctx.changes };
3002
- const pad = merged.strokeWidth / 2;
3654
+ const pad = getPolygonPad(merged.cloudyBorderIntensity, merged.strokeWidth);
3003
3655
  const tightRect = expandRect(rectFromPoints(merged.vertices), pad);
3656
+ let patch = ctx.changes;
3657
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3658
+ if (cloudyChanged || ctx.changes.strokeWidth !== void 0 && hasCloudy) {
3659
+ const intensity = merged.cloudyBorderIntensity ?? 0;
3660
+ if (intensity > 0) {
3661
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
3662
+ patch = {
3663
+ ...patch,
3664
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3665
+ };
3666
+ } else {
3667
+ patch = { ...patch, rectangleDifferences: void 0 };
3668
+ }
3669
+ }
3004
3670
  const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
3005
3671
  if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
3006
3672
  return {
3007
- ...ctx.changes,
3673
+ ...patch,
3008
3674
  unrotatedRect: tightRect,
3009
3675
  rect: calculateRotatedRectAABBAroundPoint(
3010
3676
  tightRect,
@@ -3013,7 +3679,7 @@ const patchPolygon = (orig, ctx) => {
3013
3679
  )
3014
3680
  };
3015
3681
  }
3016
- return { ...ctx.changes, rect: tightRect };
3682
+ return { ...patch, rect: tightRect };
3017
3683
  }
3018
3684
  default:
3019
3685
  return ctx.changes;
@@ -3029,11 +3695,29 @@ const patchCircle = (orig, ctx) => {
3029
3695
  return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
3030
3696
  case "rotate":
3031
3697
  return baseRotateChanges(orig, ctx) ?? ctx.changes;
3032
- case "property-update":
3698
+ case "property-update": {
3699
+ let patch = ctx.changes;
3700
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3701
+ const strokeChanged = ctx.changes.strokeWidth !== void 0;
3702
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3703
+ if (cloudyChanged || strokeChanged && hasCloudy) {
3704
+ const merged = { ...orig, ...ctx.changes };
3705
+ const intensity = merged.cloudyBorderIntensity ?? 0;
3706
+ if (intensity > 0) {
3707
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, true);
3708
+ patch = {
3709
+ ...patch,
3710
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3711
+ };
3712
+ } else {
3713
+ patch = { ...patch, rectangleDifferences: void 0 };
3714
+ }
3715
+ }
3033
3716
  if (ctx.changes.rotation !== void 0) {
3034
- return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3717
+ patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3035
3718
  }
3036
- return ctx.changes;
3719
+ return patch;
3720
+ }
3037
3721
  default:
3038
3722
  return ctx.changes;
3039
3723
  }
@@ -3048,11 +3732,29 @@ const patchSquare = (orig, ctx) => {
3048
3732
  return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
3049
3733
  case "rotate":
3050
3734
  return baseRotateChanges(orig, ctx) ?? ctx.changes;
3051
- case "property-update":
3735
+ case "property-update": {
3736
+ let patch = ctx.changes;
3737
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3738
+ const strokeChanged = ctx.changes.strokeWidth !== void 0;
3739
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3740
+ if (cloudyChanged || strokeChanged && hasCloudy) {
3741
+ const merged = { ...orig, ...ctx.changes };
3742
+ const intensity = merged.cloudyBorderIntensity ?? 0;
3743
+ if (intensity > 0) {
3744
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
3745
+ patch = {
3746
+ ...patch,
3747
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3748
+ };
3749
+ } else {
3750
+ patch = { ...patch, rectangleDifferences: void 0 };
3751
+ }
3752
+ }
3052
3753
  if (ctx.changes.rotation !== void 0) {
3053
- return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3754
+ patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3054
3755
  }
3055
- return ctx.changes;
3756
+ return patch;
3757
+ }
3056
3758
  default:
3057
3759
  return ctx.changes;
3058
3760
  }
@@ -3095,34 +3797,6 @@ const patchStamp = (orig, ctx) => {
3095
3797
  return ctx.changes;
3096
3798
  }
3097
3799
  };
3098
- function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
3099
- const theta = rotationDegrees * Math.PI / 180;
3100
- const A = Math.abs(Math.cos(theta));
3101
- const B = Math.abs(Math.sin(theta));
3102
- const det = A * A - B * B;
3103
- const newAABBw = newAABBRect.size.width;
3104
- const newAABBh = newAABBRect.size.height;
3105
- let newWidth;
3106
- let newHeight;
3107
- if (Math.abs(det) > 1e-6) {
3108
- newWidth = (A * newAABBw - B * newAABBh) / det;
3109
- newHeight = (A * newAABBh - B * newAABBw) / det;
3110
- newWidth = Math.max(newWidth, 1);
3111
- newHeight = Math.max(newHeight, 1);
3112
- } else {
3113
- const origArea = originalAABBRect.size.width * originalAABBRect.size.height;
3114
- const newArea = newAABBw * newAABBh;
3115
- const uniformScale = origArea > 0 ? Math.sqrt(newArea / origArea) : 1;
3116
- newWidth = originalUnrotatedRect.size.width * uniformScale;
3117
- newHeight = originalUnrotatedRect.size.height * uniformScale;
3118
- }
3119
- const newCenterX = newAABBRect.origin.x + newAABBw / 2;
3120
- const newCenterY = newAABBRect.origin.y + newAABBh / 2;
3121
- return {
3122
- origin: { x: newCenterX - newWidth / 2, y: newCenterY - newHeight / 2 },
3123
- size: { width: newWidth, height: newHeight }
3124
- };
3125
- }
3126
3800
  const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3127
3801
  constructor(id, registry, config) {
3128
3802
  var _a, _b, _c;
@@ -3476,12 +4150,13 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3476
4150
  // Pass through services
3477
4151
  onPreview: (state) => callbacks.onPreview(tool.id, state),
3478
4152
  onCommit: (annotation, ctx) => {
3479
- var _a2, _b;
3480
- this.createAnnotation(pageIndex, annotation, ctx, documentId);
3481
- if ((_a2 = tool.behavior) == null ? void 0 : _a2.deactivateToolAfterCreate) {
4153
+ var _a2, _b, _c;
4154
+ const editAfterCreate = ((_a2 = tool.behavior) == null ? void 0 : _a2.editAfterCreate) ?? false;
4155
+ this.createAnnotation(pageIndex, annotation, ctx, documentId, { editAfterCreate });
4156
+ if ((_b = tool.behavior) == null ? void 0 : _b.deactivateToolAfterCreate) {
3482
4157
  this.setActiveTool(null, documentId);
3483
4158
  }
3484
- if ((_b = tool.behavior) == null ? void 0 : _b.selectAfterCreate) {
4159
+ if (((_c = tool.behavior) == null ? void 0 : _c.selectAfterCreate) || editAfterCreate) {
3485
4160
  this.selectAnnotation(pageIndex, annotation.id, documentId);
3486
4161
  }
3487
4162
  },
@@ -3651,7 +4326,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3651
4326
  }
3652
4327
  if (this.config.autoCommit !== false) this.commit(documentId);
3653
4328
  }
3654
- createAnnotation(pageIndex, annotation, ctx, documentId) {
4329
+ createAnnotation(pageIndex, annotation, ctx, documentId, options) {
3655
4330
  const docId = documentId ?? this.getActiveDocumentId();
3656
4331
  if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
3657
4332
  this.logger.debug(
@@ -3668,6 +4343,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3668
4343
  ...annotation,
3669
4344
  author: annotation.author ?? this.config.annotationAuthor
3670
4345
  };
4346
+ const editAfterCreate = options == null ? void 0 : options.editAfterCreate;
3671
4347
  const execute = () => {
3672
4348
  this.dispatch(createAnnotation(docId, pageIndex, newAnnotation));
3673
4349
  if (ctx) contexts.set(id, ctx);
@@ -3677,7 +4353,8 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3677
4353
  annotation: newAnnotation,
3678
4354
  pageIndex,
3679
4355
  ctx,
3680
- committed: false
4356
+ committed: false,
4357
+ editAfterCreate
3681
4358
  });
3682
4359
  };
3683
4360
  if (!this.history) {
@@ -5131,10 +5808,14 @@ export {
5131
5808
  calculateRotatedRectAABBAroundPoint2 as calculateRotatedRectAABBAroundPoint,
5132
5809
  convertAABBRectToUnrotatedSpace,
5133
5810
  createToolPredicate,
5811
+ generateCloudyEllipsePath,
5812
+ generateCloudyPolygonPath,
5813
+ generateCloudyRectanglePath,
5134
5814
  getAnnotationByUid,
5135
5815
  getAnnotations,
5136
5816
  getAnnotationsByPageIndex,
5137
5817
  getAttachedLinks,
5818
+ getCloudyBorderExtent,
5138
5819
  getGroupLeaderId,
5139
5820
  getGroupMembers,
5140
5821
  getIRTChildIds,