@embedpdf/plugin-annotation 2.8.0 → 2.9.0

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 (52) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +814 -139
  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 +38 -87
  9. package/dist/lib/tools/types.d.ts +32 -1
  10. package/dist/preact/index.cjs +1 -1
  11. package/dist/preact/index.cjs.map +1 -1
  12. package/dist/preact/index.js +352 -239
  13. package/dist/preact/index.js.map +1 -1
  14. package/dist/react/index.cjs +1 -1
  15. package/dist/react/index.cjs.map +1 -1
  16. package/dist/react/index.js +352 -239
  17. package/dist/react/index.js.map +1 -1
  18. package/dist/shared/components/annotation-container.d.ts +4 -2
  19. package/dist/shared/components/annotations/circle.d.ts +6 -2
  20. package/dist/shared/components/annotations/polygon.d.ts +3 -1
  21. package/dist/shared/components/annotations/square.d.ts +6 -2
  22. package/dist/shared/components/types.d.ts +6 -2
  23. package/dist/shared-preact/components/annotation-container.d.ts +4 -2
  24. package/dist/shared-preact/components/annotations/circle.d.ts +6 -2
  25. package/dist/shared-preact/components/annotations/polygon.d.ts +3 -1
  26. package/dist/shared-preact/components/annotations/square.d.ts +6 -2
  27. package/dist/shared-preact/components/types.d.ts +6 -2
  28. package/dist/shared-react/components/annotation-container.d.ts +4 -2
  29. package/dist/shared-react/components/annotations/circle.d.ts +6 -2
  30. package/dist/shared-react/components/annotations/polygon.d.ts +3 -1
  31. package/dist/shared-react/components/annotations/square.d.ts +6 -2
  32. package/dist/shared-react/components/types.d.ts +6 -2
  33. package/dist/svelte/components/annotations/Circle.svelte.d.ts +3 -1
  34. package/dist/svelte/components/annotations/Polygon.svelte.d.ts +1 -0
  35. package/dist/svelte/components/annotations/Square.svelte.d.ts +3 -1
  36. package/dist/svelte/components/types.d.ts +2 -1
  37. package/dist/svelte/context/types.d.ts +6 -2
  38. package/dist/svelte/index.cjs +1 -1
  39. package/dist/svelte/index.cjs.map +1 -1
  40. package/dist/svelte/index.js +505 -293
  41. package/dist/svelte/index.js.map +1 -1
  42. package/dist/vue/components/annotation-container.vue.d.ts +7 -6
  43. package/dist/vue/components/annotations/circle.vue.d.ts +5 -1
  44. package/dist/vue/components/annotations/polygon.vue.d.ts +2 -0
  45. package/dist/vue/components/annotations/square.vue.d.ts +5 -1
  46. package/dist/vue/components/annotations.vue.d.ts +8 -9
  47. package/dist/vue/context/types.d.ts +6 -2
  48. package/dist/vue/index.cjs +1 -1
  49. package/dist/vue/index.cjs.map +1 -1
  50. package/dist/vue/index.js +259 -121
  51. package/dist/vue/index.js.map +1 -1
  52. 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: {
@@ -1180,6 +1191,18 @@ function useState(initialValue) {
1180
1191
  };
1181
1192
  return [getValue, setValue];
1182
1193
  }
1194
+ function isLineLike(points, threshold) {
1195
+ if (points.length < 3) return true;
1196
+ const A = points[0];
1197
+ const B = points[points.length - 1];
1198
+ const len = Math.hypot(B.x - A.x, B.y - A.y);
1199
+ if (len < 5) return false;
1200
+ const maxDev = points.reduce((max, P) => {
1201
+ const d = Math.abs((B.x - A.x) * (A.y - P.y) - (A.x - P.x) * (B.y - A.y)) / len;
1202
+ return Math.max(max, d);
1203
+ }, 0);
1204
+ return maxDev / len < threshold;
1205
+ }
1183
1206
  const inkHandlerFactory = {
1184
1207
  annotationType: PdfAnnotationSubtype.INK,
1185
1208
  create(context) {
@@ -1215,7 +1238,8 @@ const inkHandlerFactory = {
1215
1238
  data: {
1216
1239
  ...defaults,
1217
1240
  rect: bounds,
1218
- inkList: strokes
1241
+ inkList: strokes,
1242
+ blendMode: defaults.blendMode
1219
1243
  }
1220
1244
  };
1221
1245
  };
@@ -1243,6 +1267,37 @@ const inkHandlerFactory = {
1243
1267
  var _a;
1244
1268
  setIsDrawing(false);
1245
1269
  (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1270
+ const tool = getTool();
1271
+ const behavior = tool == null ? void 0 : tool.behavior;
1272
+ if (behavior == null ? void 0 : behavior.smartLineRecognition) {
1273
+ const threshold = behavior.smartLineThreshold ?? 0.15;
1274
+ const strokes = getStrokes();
1275
+ const last = strokes[strokes.length - 1];
1276
+ if (last && last.points.length > 1 && isLineLike(last.points, threshold)) {
1277
+ const first = last.points[0];
1278
+ const end = last.points[last.points.length - 1];
1279
+ const dx = end.x - first.x;
1280
+ const dy = end.y - first.y;
1281
+ const angleDeg = Math.atan2(Math.abs(dy), Math.abs(dx)) * (180 / Math.PI);
1282
+ const snapAngleDeg = behavior.snapAngleDeg ?? 15;
1283
+ if (angleDeg <= snapAngleDeg) {
1284
+ const avgY = last.points.reduce((sum, p) => sum + p.y, 0) / last.points.length;
1285
+ last.points = [
1286
+ { x: first.x, y: avgY },
1287
+ { x: end.x, y: avgY }
1288
+ ];
1289
+ } else if (angleDeg >= 90 - snapAngleDeg) {
1290
+ const avgX = last.points.reduce((sum, p) => sum + p.x, 0) / last.points.length;
1291
+ last.points = [
1292
+ { x: avgX, y: first.y },
1293
+ { x: avgX, y: end.y }
1294
+ ];
1295
+ }
1296
+ setStrokes([...strokes]);
1297
+ onPreview(getPreview());
1298
+ }
1299
+ }
1300
+ const commitDelay = (behavior == null ? void 0 : behavior.commitDelay) ?? 800;
1246
1301
  if (timerRef.current) clearTimeout(timerRef.current);
1247
1302
  timerRef.current = setTimeout(() => {
1248
1303
  const strokes = getStrokes();
@@ -1263,7 +1318,7 @@ const inkHandlerFactory = {
1263
1318
  }
1264
1319
  setStrokes([]);
1265
1320
  onPreview(null);
1266
- }, 800);
1321
+ }, commitDelay);
1267
1322
  },
1268
1323
  onPointerCancel: (_, evt) => {
1269
1324
  var _a;
@@ -1335,14 +1390,14 @@ const LINE_ENDING_HANDLERS = {
1335
1390
  },
1336
1391
  [PdfAnnotationLineEnding.Circle]: {
1337
1392
  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`;
1393
+ const r2 = sw * 5 / 2;
1394
+ return `M ${r2} 0 A ${r2} ${r2} 0 1 1 ${-r2} 0 A ${r2} ${r2} 0 1 1 ${r2} 0`;
1340
1395
  },
1341
1396
  getLocalPoints: (sw) => {
1342
- const r = sw * 5 / 2;
1397
+ const r2 = sw * 5 / 2;
1343
1398
  return [
1344
- { x: -r, y: -r },
1345
- { x: r, y: r }
1399
+ { x: -r2, y: -r2 },
1400
+ { x: r2, y: r2 }
1346
1401
  ];
1347
1402
  },
1348
1403
  getRotation: () => 0,
@@ -1484,7 +1539,7 @@ function createEnding(ending, strokeWidth, rad, px, py) {
1484
1539
  if (!ending) return null;
1485
1540
  const handler = LINE_ENDING_HANDLERS[ending];
1486
1541
  if (!handler) return null;
1487
- const toDeg = (r) => r * 180 / Math.PI;
1542
+ const toDeg = (r2) => r2 * 180 / Math.PI;
1488
1543
  const rotationAngle = handler.getRotation(rad);
1489
1544
  return {
1490
1545
  d: handler.getSvgPath(strokeWidth),
@@ -1701,8 +1756,8 @@ function useClickDetector({
1701
1756
  onMove: (pos) => {
1702
1757
  const start = getStartPos();
1703
1758
  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) {
1759
+ const distance2 = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
1760
+ if (distance2 > threshold) {
1706
1761
  setHasMoved(true);
1707
1762
  }
1708
1763
  },
@@ -2127,6 +2182,554 @@ const polylineHandlerFactory = {
2127
2182
  };
2128
2183
  }
2129
2184
  };
2185
+ function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
2186
+ const theta = rotationDegrees * Math.PI / 180;
2187
+ const A = Math.abs(Math.cos(theta));
2188
+ const B = Math.abs(Math.sin(theta));
2189
+ const det = A * A - B * B;
2190
+ const newAABBw = newAABBRect.size.width;
2191
+ const newAABBh = newAABBRect.size.height;
2192
+ let newWidth;
2193
+ let newHeight;
2194
+ if (Math.abs(det) > 1e-6) {
2195
+ newWidth = (A * newAABBw - B * newAABBh) / det;
2196
+ newHeight = (A * newAABBh - B * newAABBw) / det;
2197
+ newWidth = Math.max(newWidth, 1);
2198
+ newHeight = Math.max(newHeight, 1);
2199
+ } else {
2200
+ const origArea = originalAABBRect.size.width * originalAABBRect.size.height;
2201
+ const newArea = newAABBw * newAABBh;
2202
+ const uniformScale = origArea > 0 ? Math.sqrt(newArea / origArea) : 1;
2203
+ newWidth = originalUnrotatedRect.size.width * uniformScale;
2204
+ newHeight = originalUnrotatedRect.size.height * uniformScale;
2205
+ }
2206
+ const newCenterX = newAABBRect.origin.x + newAABBw / 2;
2207
+ const newCenterY = newAABBRect.origin.y + newAABBh / 2;
2208
+ return {
2209
+ origin: { x: newCenterX - newWidth / 2, y: newCenterY - newHeight / 2 },
2210
+ size: { width: newWidth, height: newHeight }
2211
+ };
2212
+ }
2213
+ const ANGLE_180 = Math.PI;
2214
+ const ANGLE_90 = Math.PI / 2;
2215
+ const ANGLE_34 = 34 * Math.PI / 180;
2216
+ const ANGLE_30 = 30 * Math.PI / 180;
2217
+ const ANGLE_12 = 12 * Math.PI / 180;
2218
+ class PathBuilder {
2219
+ constructor() {
2220
+ this.parts = [];
2221
+ this.bbox = {
2222
+ minX: Infinity,
2223
+ minY: Infinity,
2224
+ maxX: -Infinity,
2225
+ maxY: -Infinity
2226
+ };
2227
+ this.started = false;
2228
+ }
2229
+ moveTo(x, y) {
2230
+ const sy = -y;
2231
+ this.updateBBox(x, sy);
2232
+ this.parts.push(`M ${r(x)} ${r(sy)}`);
2233
+ this.started = true;
2234
+ }
2235
+ curveTo(x1, y1, x2, y2, x3, y3) {
2236
+ const sy1 = -y1;
2237
+ const sy2 = -y2;
2238
+ const sy3 = -y3;
2239
+ this.updateBBox(x1, sy1);
2240
+ this.updateBBox(x2, sy2);
2241
+ this.updateBBox(x3, sy3);
2242
+ this.parts.push(`C ${r(x1)} ${r(sy1)}, ${r(x2)} ${r(sy2)}, ${r(x3)} ${r(sy3)}`);
2243
+ }
2244
+ close() {
2245
+ if (this.started) {
2246
+ this.parts.push("Z");
2247
+ }
2248
+ }
2249
+ build(lineWidth) {
2250
+ const d = lineWidth > 0 ? lineWidth / 2 : 0;
2251
+ return {
2252
+ path: this.parts.join(" "),
2253
+ bbox: {
2254
+ minX: this.bbox.minX - d,
2255
+ minY: this.bbox.minY - d,
2256
+ maxX: this.bbox.maxX + d,
2257
+ maxY: this.bbox.maxY + d
2258
+ }
2259
+ };
2260
+ }
2261
+ updateBBox(x, y) {
2262
+ if (x < this.bbox.minX) this.bbox.minX = x;
2263
+ if (y < this.bbox.minY) this.bbox.minY = y;
2264
+ if (x > this.bbox.maxX) this.bbox.maxX = x;
2265
+ if (y > this.bbox.maxY) this.bbox.maxY = y;
2266
+ }
2267
+ }
2268
+ function r(n) {
2269
+ return Number(n.toFixed(4)).toString();
2270
+ }
2271
+ function distance(a, b) {
2272
+ const dx = b.x - a.x;
2273
+ const dy = b.y - a.y;
2274
+ return Math.sqrt(dx * dx + dy * dy);
2275
+ }
2276
+ function cosine(dx, hypot) {
2277
+ return hypot === 0 ? 0 : dx / hypot;
2278
+ }
2279
+ function sine(dy, hypot) {
2280
+ return hypot === 0 ? 0 : dy / hypot;
2281
+ }
2282
+ function polygonDirection(pts) {
2283
+ let a = 0;
2284
+ const len = pts.length;
2285
+ for (let i = 0; i < len; i++) {
2286
+ const j = (i + 1) % len;
2287
+ a += pts[i].x * pts[j].y - pts[i].y * pts[j].x;
2288
+ }
2289
+ return a;
2290
+ }
2291
+ function ensurePositiveWinding(pts) {
2292
+ if (polygonDirection(pts) < 0) {
2293
+ pts.reverse();
2294
+ }
2295
+ }
2296
+ function removeZeroLengthSegments(polygon) {
2297
+ if (polygon.length <= 2) return polygon;
2298
+ const tolerance = 0.5;
2299
+ const result = [polygon[0]];
2300
+ for (let i = 1; i < polygon.length; i++) {
2301
+ const prev = result[result.length - 1];
2302
+ const cur = polygon[i];
2303
+ if (Math.abs(cur.x - prev.x) >= tolerance || Math.abs(cur.y - prev.y) >= tolerance) {
2304
+ result.push(cur);
2305
+ }
2306
+ }
2307
+ return result;
2308
+ }
2309
+ function arcSegment(startAng, endAng, cx, cy, rx, ry, out, addMoveTo) {
2310
+ const cosA = Math.cos(startAng);
2311
+ const sinA = Math.sin(startAng);
2312
+ const cosB = Math.cos(endAng);
2313
+ const sinB = Math.sin(endAng);
2314
+ const denom = Math.sin((endAng - startAng) / 2);
2315
+ if (denom === 0) {
2316
+ if (addMoveTo) {
2317
+ out.moveTo(cx + rx * cosA, cy + ry * sinA);
2318
+ }
2319
+ return;
2320
+ }
2321
+ const bcp = 4 / 3 * (1 - Math.cos((endAng - startAng) / 2)) / denom;
2322
+ const p1x = cx + rx * (cosA - bcp * sinA);
2323
+ const p1y = cy + ry * (sinA + bcp * cosA);
2324
+ const p2x = cx + rx * (cosB + bcp * sinB);
2325
+ const p2y = cy + ry * (sinB - bcp * cosB);
2326
+ const p3x = cx + rx * cosB;
2327
+ const p3y = cy + ry * sinB;
2328
+ if (addMoveTo) {
2329
+ out.moveTo(cx + rx * cosA, cy + ry * sinA);
2330
+ }
2331
+ out.curveTo(p1x, p1y, p2x, p2y, p3x, p3y);
2332
+ }
2333
+ function arcSegmentToArray(startAng, endAng, cx, cy, rx, ry) {
2334
+ const cosA = Math.cos(startAng);
2335
+ const sinA = Math.sin(startAng);
2336
+ const cosB = Math.cos(endAng);
2337
+ const sinB = Math.sin(endAng);
2338
+ const denom = Math.sin((endAng - startAng) / 2);
2339
+ if (denom === 0) return [];
2340
+ const bcp = 4 / 3 * (1 - Math.cos((endAng - startAng) / 2)) / denom;
2341
+ return [
2342
+ { x: cx + rx * (cosA - bcp * sinA), y: cy + ry * (sinA + bcp * cosA) },
2343
+ { x: cx + rx * (cosB + bcp * sinB), y: cy + ry * (sinB - bcp * cosB) },
2344
+ { x: cx + rx * cosB, y: cy + ry * sinB }
2345
+ ];
2346
+ }
2347
+ function getArc(startAng, endAng, rx, ry, cx, cy, out, addMoveTo) {
2348
+ const angleIncr = ANGLE_90;
2349
+ let angleTodo = endAng - startAng;
2350
+ while (angleTodo < 0) angleTodo += 2 * Math.PI;
2351
+ const sweep = angleTodo;
2352
+ let angleDone = 0;
2353
+ if (addMoveTo) {
2354
+ out.moveTo(cx + rx * Math.cos(startAng), cy + ry * Math.sin(startAng));
2355
+ }
2356
+ while (angleTodo > angleIncr) {
2357
+ arcSegment(startAng + angleDone, startAng + angleDone + angleIncr, cx, cy, rx, ry, out, false);
2358
+ angleDone += angleIncr;
2359
+ angleTodo -= angleIncr;
2360
+ }
2361
+ if (angleTodo > 0) {
2362
+ arcSegment(startAng + angleDone, startAng + sweep, cx, cy, rx, ry, out, false);
2363
+ }
2364
+ }
2365
+ function addCornerCurl(anglePrev, angleCur, radius, cx, cy, alpha, alphaPrev, out, addMoveTo) {
2366
+ let a = anglePrev + ANGLE_180 + alphaPrev;
2367
+ const b = anglePrev + ANGLE_180 + alphaPrev - 22 * Math.PI / 180;
2368
+ arcSegment(a, b, cx, cy, radius, radius, out, addMoveTo);
2369
+ a = b;
2370
+ const bEnd = angleCur - alpha;
2371
+ getArc(a, bEnd, radius, radius, cx, cy, out, false);
2372
+ }
2373
+ function addFirstIntermediateCurl(angleCur, r2, alpha, cx, cy, out) {
2374
+ const a = angleCur + ANGLE_180;
2375
+ arcSegment(a + alpha, a + alpha - ANGLE_30, cx, cy, r2, r2, out, false);
2376
+ arcSegment(a + alpha - ANGLE_30, a + ANGLE_90, cx, cy, r2, r2, out, false);
2377
+ arcSegment(a + ANGLE_90, a + ANGLE_180 - ANGLE_34, cx, cy, r2, r2, out, false);
2378
+ }
2379
+ function getIntermediateCurlTemplate(angleCur, r2) {
2380
+ const pts = [];
2381
+ const a = angleCur + ANGLE_180;
2382
+ pts.push(...arcSegmentToArray(a + ANGLE_34, a + ANGLE_12, 0, 0, r2, r2));
2383
+ pts.push(...arcSegmentToArray(a + ANGLE_12, a + ANGLE_90, 0, 0, r2, r2));
2384
+ pts.push(...arcSegmentToArray(a + ANGLE_90, a + ANGLE_180 - ANGLE_34, 0, 0, r2, r2));
2385
+ return pts;
2386
+ }
2387
+ function outputCurlTemplate(template, x, y, out) {
2388
+ for (let i = 0; i + 2 < template.length; i += 3) {
2389
+ const a = template[i];
2390
+ const b = template[i + 1];
2391
+ const c = template[i + 2];
2392
+ out.curveTo(a.x + x, a.y + y, b.x + x, b.y + y, c.x + x, c.y + y);
2393
+ }
2394
+ }
2395
+ function computeParamsPolygon(idealRadius, k, length) {
2396
+ if (length === 0) return { n: -1, adjustedRadius: idealRadius };
2397
+ const cornerSpace = 2 * k * idealRadius;
2398
+ const remaining = length - cornerSpace;
2399
+ if (remaining <= 0) {
2400
+ return { n: 0, adjustedRadius: idealRadius };
2401
+ }
2402
+ const idealAdvance = 2 * k * idealRadius;
2403
+ const n = Math.max(1, Math.ceil(remaining / idealAdvance));
2404
+ const adjustedRadius = remaining / (n * 2 * k);
2405
+ return { n, adjustedRadius };
2406
+ }
2407
+ function cloudyPolygonImpl(vertices, isEllipse, intensity, lineWidth, out) {
2408
+ let polygon = removeZeroLengthSegments(vertices);
2409
+ ensurePositiveWinding(polygon);
2410
+ const numPoints = polygon.length;
2411
+ if (numPoints < 2) return;
2412
+ if (intensity <= 0) {
2413
+ out.moveTo(polygon[0].x, polygon[0].y);
2414
+ for (let i = 1; i < numPoints; i++) {
2415
+ out.curveTo(
2416
+ polygon[i].x,
2417
+ polygon[i].y,
2418
+ polygon[i].x,
2419
+ polygon[i].y,
2420
+ polygon[i].x,
2421
+ polygon[i].y
2422
+ );
2423
+ }
2424
+ return;
2425
+ }
2426
+ let idealRadius = isEllipse ? getEllipseCloudRadius(intensity, lineWidth) : getPolygonCloudRadius(intensity, lineWidth);
2427
+ if (idealRadius < 0.5) idealRadius = 0.5;
2428
+ const k = Math.cos(ANGLE_34);
2429
+ const edgeAlphas = [];
2430
+ for (let j = 0; j + 1 < numPoints; j++) {
2431
+ const len = distance(polygon[j], polygon[j + 1]);
2432
+ if (len <= 0 || len >= 2 * k * idealRadius) {
2433
+ edgeAlphas.push(ANGLE_34);
2434
+ } else {
2435
+ edgeAlphas.push(Math.acos(Math.min(1, len / (2 * idealRadius))));
2436
+ }
2437
+ }
2438
+ let anglePrev = 0;
2439
+ let outputStarted = false;
2440
+ for (let j = 0; j + 1 < numPoints; j++) {
2441
+ const pt = polygon[j];
2442
+ const ptNext = polygon[j + 1];
2443
+ const len = distance(pt, ptNext);
2444
+ if (len === 0) continue;
2445
+ const params = computeParamsPolygon(idealRadius, k, len);
2446
+ if (params.n < 0) {
2447
+ if (!outputStarted) {
2448
+ out.moveTo(pt.x, pt.y);
2449
+ outputStarted = true;
2450
+ }
2451
+ continue;
2452
+ }
2453
+ const edgeRadius = Math.max(0.5, params.adjustedRadius);
2454
+ const intermAdvance = 2 * k * edgeRadius;
2455
+ const firstAdvance = k * idealRadius + k * edgeRadius;
2456
+ let angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
2457
+ if (j === 0) {
2458
+ const ptPrev = polygon[numPoints - 2];
2459
+ anglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
2460
+ }
2461
+ const cos = cosine(ptNext.x - pt.x, len);
2462
+ const sin = sine(ptNext.y - pt.y, len);
2463
+ let x = pt.x;
2464
+ let y = pt.y;
2465
+ const alpha = edgeAlphas[j];
2466
+ const prevEdgeIdx = j === 0 ? numPoints - 2 : j - 1;
2467
+ const alphaPrevEdge = edgeAlphas[prevEdgeIdx] ?? ANGLE_34;
2468
+ addCornerCurl(
2469
+ anglePrev,
2470
+ angleCur,
2471
+ idealRadius,
2472
+ pt.x,
2473
+ pt.y,
2474
+ alpha,
2475
+ alphaPrevEdge,
2476
+ out,
2477
+ !outputStarted
2478
+ );
2479
+ outputStarted = true;
2480
+ if (params.n === 0) {
2481
+ x += len * cos;
2482
+ y += len * sin;
2483
+ } else {
2484
+ x += firstAdvance * cos;
2485
+ y += firstAdvance * sin;
2486
+ let numInterm = params.n;
2487
+ if (params.n >= 1) {
2488
+ addFirstIntermediateCurl(angleCur, edgeRadius, ANGLE_34, x, y, out);
2489
+ x += intermAdvance * cos;
2490
+ y += intermAdvance * sin;
2491
+ numInterm = params.n - 1;
2492
+ }
2493
+ const template = getIntermediateCurlTemplate(angleCur, edgeRadius);
2494
+ for (let i = 0; i < numInterm; i++) {
2495
+ outputCurlTemplate(template, x, y, out);
2496
+ x += intermAdvance * cos;
2497
+ y += intermAdvance * sin;
2498
+ }
2499
+ }
2500
+ anglePrev = angleCur;
2501
+ }
2502
+ }
2503
+ function flattenEllipse(left, bottom, right, top) {
2504
+ const cx = (left + right) / 2;
2505
+ const cy = (bottom + top) / 2;
2506
+ const rx = (right - left) / 2;
2507
+ const ry = (top - bottom) / 2;
2508
+ if (rx <= 0 || ry <= 0) return [];
2509
+ const numSegments = Math.max(32, Math.ceil(Math.max(rx, ry) * 2));
2510
+ const points = [];
2511
+ for (let i = 0; i <= numSegments; i++) {
2512
+ const angle = 2 * Math.PI * i / numSegments;
2513
+ points.push({
2514
+ x: cx + rx * Math.cos(angle),
2515
+ y: cy + ry * Math.sin(angle)
2516
+ });
2517
+ }
2518
+ return points;
2519
+ }
2520
+ function getEllipseCloudRadius(intensity, lineWidth) {
2521
+ return 4.75 * intensity + 0.5 * lineWidth;
2522
+ }
2523
+ function getPolygonCloudRadius(intensity, lineWidth) {
2524
+ return 4 * intensity + 0.5 * lineWidth;
2525
+ }
2526
+ function cloudyEllipseImpl(left, bottom, right, top, intensity, lineWidth, out) {
2527
+ if (intensity <= 0) {
2528
+ const rx = Math.abs(right - left) / 2;
2529
+ const ry = Math.abs(top - bottom) / 2;
2530
+ const cx = (left + right) / 2;
2531
+ const cy = (bottom + top) / 2;
2532
+ getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
2533
+ return;
2534
+ }
2535
+ const width = right - left;
2536
+ const height = top - bottom;
2537
+ let cloudRadius = getEllipseCloudRadius(intensity, lineWidth);
2538
+ const threshold1 = 0.5 * cloudRadius;
2539
+ if (width < threshold1 && height < threshold1) {
2540
+ const rx = Math.abs(right - left) / 2;
2541
+ const ry = Math.abs(top - bottom) / 2;
2542
+ const cx = (left + right) / 2;
2543
+ const cy = (bottom + top) / 2;
2544
+ getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
2545
+ return;
2546
+ }
2547
+ const threshold2 = 5;
2548
+ if (width < threshold2 && height > 20 || width > 20 && height < threshold2) {
2549
+ cloudyPolygonImpl(
2550
+ [
2551
+ { x: left, y: bottom },
2552
+ { x: right, y: bottom },
2553
+ { x: right, y: top },
2554
+ { x: left, y: top },
2555
+ { x: left, y: bottom }
2556
+ ],
2557
+ true,
2558
+ intensity,
2559
+ lineWidth,
2560
+ out
2561
+ );
2562
+ return;
2563
+ }
2564
+ const radiusAdj = Math.sin(ANGLE_12) * cloudRadius - 1.5;
2565
+ let adjLeft = left;
2566
+ let adjRight = right;
2567
+ let adjBottom = bottom;
2568
+ let adjTop = top;
2569
+ if (width > 2 * radiusAdj) {
2570
+ adjLeft += radiusAdj;
2571
+ adjRight -= radiusAdj;
2572
+ } else {
2573
+ const mid = (left + right) / 2;
2574
+ adjLeft = mid - 0.1;
2575
+ adjRight = mid + 0.1;
2576
+ }
2577
+ if (height > 2 * radiusAdj) {
2578
+ adjBottom += radiusAdj;
2579
+ adjTop -= radiusAdj;
2580
+ } else {
2581
+ const mid = (top + bottom) / 2;
2582
+ adjTop = mid + 0.1;
2583
+ adjBottom = mid - 0.1;
2584
+ }
2585
+ const flatPolygon = flattenEllipse(adjLeft, adjBottom, adjRight, adjTop);
2586
+ const numFlatPts = flatPolygon.length;
2587
+ if (numFlatPts < 2) return;
2588
+ let totLen = 0;
2589
+ for (let i = 1; i < numFlatPts; i++) {
2590
+ totLen += distance(flatPolygon[i - 1], flatPolygon[i]);
2591
+ }
2592
+ const k = Math.cos(ANGLE_34);
2593
+ let curlAdvance = 2 * k * cloudRadius;
2594
+ let n = Math.ceil(totLen / curlAdvance);
2595
+ if (n < 2) {
2596
+ const rx = Math.abs(right - left) / 2;
2597
+ const ry = Math.abs(top - bottom) / 2;
2598
+ const cx = (left + right) / 2;
2599
+ const cy = (bottom + top) / 2;
2600
+ getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
2601
+ return;
2602
+ }
2603
+ curlAdvance = totLen / n;
2604
+ cloudRadius = curlAdvance / (2 * k);
2605
+ if (cloudRadius < 0.5) {
2606
+ cloudRadius = 0.5;
2607
+ curlAdvance = 2 * k * cloudRadius;
2608
+ } else if (cloudRadius < 3) {
2609
+ const rx = Math.abs(right - left) / 2;
2610
+ const ry = Math.abs(top - bottom) / 2;
2611
+ const cx = (left + right) / 2;
2612
+ const cy = (bottom + top) / 2;
2613
+ getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
2614
+ return;
2615
+ }
2616
+ const centerPoints = [];
2617
+ let lengthRemain = 0;
2618
+ const comparisonToler = lineWidth * 0.1;
2619
+ for (let i = 0; i + 1 < numFlatPts; i++) {
2620
+ const p1 = flatPolygon[i];
2621
+ const p2 = flatPolygon[i + 1];
2622
+ const segDx = p2.x - p1.x;
2623
+ const segDy = p2.y - p1.y;
2624
+ const segLen = distance(p1, p2);
2625
+ if (segLen === 0) continue;
2626
+ let lengthTodo = segLen + lengthRemain;
2627
+ if (lengthTodo >= curlAdvance - comparisonToler || i === numFlatPts - 2) {
2628
+ const cos = cosine(segDx, segLen);
2629
+ const sin = sine(segDy, segLen);
2630
+ let d = curlAdvance - lengthRemain;
2631
+ while (lengthTodo >= curlAdvance - comparisonToler) {
2632
+ centerPoints.push({ x: p1.x + d * cos, y: p1.y + d * sin });
2633
+ lengthTodo -= curlAdvance;
2634
+ d += curlAdvance;
2635
+ }
2636
+ lengthRemain = Math.max(0, lengthTodo);
2637
+ } else {
2638
+ lengthRemain += segLen;
2639
+ }
2640
+ }
2641
+ const cpLen = centerPoints.length;
2642
+ let epAnglePrev = 0;
2643
+ let epAlphaPrev = 0;
2644
+ for (let i = 0; i < cpLen; i++) {
2645
+ const idxNext = (i + 1) % cpLen;
2646
+ const pt = centerPoints[i];
2647
+ const ptNext = centerPoints[idxNext];
2648
+ if (i === 0) {
2649
+ const ptPrev = centerPoints[cpLen - 1];
2650
+ epAnglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
2651
+ epAlphaPrev = computeParamsEllipse(ptPrev, pt, cloudRadius, curlAdvance);
2652
+ }
2653
+ const angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
2654
+ const alpha = computeParamsEllipse(pt, ptNext, cloudRadius, curlAdvance);
2655
+ addCornerCurl(epAnglePrev, angleCur, cloudRadius, pt.x, pt.y, alpha, epAlphaPrev, out, i === 0);
2656
+ epAnglePrev = angleCur;
2657
+ epAlphaPrev = alpha;
2658
+ }
2659
+ }
2660
+ function computeParamsEllipse(pt, ptNext, r2, curlAdv) {
2661
+ const len = distance(pt, ptNext);
2662
+ if (len === 0) return ANGLE_34;
2663
+ const e = len - curlAdv;
2664
+ const arg = (curlAdv / 2 + e / 2) / r2;
2665
+ return arg < -1 || arg > 1 ? 0 : Math.acos(arg);
2666
+ }
2667
+ function getCloudyBorderExtent(intensity, lineWidth, isEllipse) {
2668
+ const cloudRadius = isEllipse ? getEllipseCloudRadius(intensity, lineWidth) : getPolygonCloudRadius(intensity, lineWidth);
2669
+ return cloudRadius + lineWidth / 2;
2670
+ }
2671
+ function generateCloudyRectanglePath(rect, rd, intensity, lineWidth) {
2672
+ const out = new PathBuilder();
2673
+ let left = 0;
2674
+ let top = 0;
2675
+ let right = rect.width;
2676
+ let bottom = rect.height;
2677
+ if (rd) {
2678
+ left += rd.left;
2679
+ top += rd.top;
2680
+ right -= rd.right;
2681
+ bottom -= rd.bottom;
2682
+ } else {
2683
+ left += lineWidth / 2;
2684
+ top += lineWidth / 2;
2685
+ right -= lineWidth / 2;
2686
+ bottom -= lineWidth / 2;
2687
+ }
2688
+ const polygon = [
2689
+ { x: left, y: -top },
2690
+ { x: right, y: -top },
2691
+ { x: right, y: -bottom },
2692
+ { x: left, y: -bottom },
2693
+ { x: left, y: -top }
2694
+ ];
2695
+ cloudyPolygonImpl(polygon, false, intensity, lineWidth, out);
2696
+ out.close();
2697
+ return out.build(lineWidth);
2698
+ }
2699
+ function generateCloudyEllipsePath(rect, rd, intensity, lineWidth) {
2700
+ const out = new PathBuilder();
2701
+ let left = 0;
2702
+ let top = 0;
2703
+ let right = rect.width;
2704
+ let bottom = rect.height;
2705
+ if (rd) {
2706
+ left += rd.left;
2707
+ top += rd.top;
2708
+ right -= rd.right;
2709
+ bottom -= rd.bottom;
2710
+ }
2711
+ cloudyEllipseImpl(left, -bottom, right, -top, intensity, lineWidth, out);
2712
+ out.close();
2713
+ return out.build(lineWidth);
2714
+ }
2715
+ function generateCloudyPolygonPath(vertices, rectOrigin, intensity, lineWidth) {
2716
+ const out = new PathBuilder();
2717
+ if (vertices.length < 3) {
2718
+ return out.build(lineWidth);
2719
+ }
2720
+ const localPts = vertices.map((v) => ({
2721
+ x: v.x - rectOrigin.x,
2722
+ y: -(v.y - rectOrigin.y)
2723
+ }));
2724
+ const first = localPts[0];
2725
+ const last = localPts[localPts.length - 1];
2726
+ if (first.x !== last.x || first.y !== last.y) {
2727
+ localPts.push({ x: first.x, y: first.y });
2728
+ }
2729
+ cloudyPolygonImpl(localPts, false, intensity, lineWidth, out);
2730
+ out.close();
2731
+ return out.build(lineWidth);
2732
+ }
2130
2733
  const HANDLE_SIZE_PX = 14;
2131
2734
  const polygonHandlerFactory = {
2132
2735
  annotationType: PdfAnnotationSubtype.POLYGON,
@@ -2165,7 +2768,9 @@ const polygonHandlerFactory = {
2165
2768
  if (vertices.length < 3) return;
2166
2769
  const defaults = getDefaults();
2167
2770
  if (!defaults) return;
2168
- const rect = expandRect(rectFromPoints(vertices), defaults.strokeWidth / 2);
2771
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2772
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
2773
+ const rect = expandRect(rectFromPoints(vertices), pad);
2169
2774
  const anno = {
2170
2775
  ...defaults,
2171
2776
  vertices,
@@ -2173,7 +2778,10 @@ const polygonHandlerFactory = {
2173
2778
  type: PdfAnnotationSubtype.POLYGON,
2174
2779
  pageIndex: context.pageIndex,
2175
2780
  id: uuidV4(),
2176
- created: /* @__PURE__ */ new Date()
2781
+ created: /* @__PURE__ */ new Date(),
2782
+ ...intensity > 0 && {
2783
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2784
+ }
2177
2785
  };
2178
2786
  onCommit(anno);
2179
2787
  setVertices([]);
@@ -2186,8 +2794,10 @@ const polygonHandlerFactory = {
2186
2794
  if (vertices.length === 0 || !currentPos) return null;
2187
2795
  const defaults = getDefaults();
2188
2796
  if (!defaults) return null;
2797
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2798
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
2189
2799
  const allPoints = [...vertices, currentPos];
2190
- const bounds = expandRect(rectFromPoints(allPoints), defaults.strokeWidth / 2);
2800
+ const bounds = expandRect(rectFromPoints(allPoints), pad);
2191
2801
  return {
2192
2802
  type: PdfAnnotationSubtype.POLYGON,
2193
2803
  bounds,
@@ -2273,10 +2883,11 @@ const squareHandlerFactory = {
2273
2883
  const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2274
2884
  const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2275
2885
  const strokeWidth = defaults.strokeWidth;
2276
- const halfStroke = strokeWidth / 2;
2886
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2887
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
2277
2888
  const rect = {
2278
- origin: { x: x - halfStroke, y: y - halfStroke },
2279
- size: { width: width + strokeWidth, height: height + strokeWidth }
2889
+ origin: { x: x - pad, y: y - pad },
2890
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2280
2891
  };
2281
2892
  const anno = {
2282
2893
  ...defaults,
@@ -2284,7 +2895,10 @@ const squareHandlerFactory = {
2284
2895
  created: /* @__PURE__ */ new Date(),
2285
2896
  id: uuidV4(),
2286
2897
  pageIndex,
2287
- rect
2898
+ rect,
2899
+ ...intensity > 0 && {
2900
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2901
+ }
2288
2902
  };
2289
2903
  onCommit(anno);
2290
2904
  }
@@ -2299,17 +2913,21 @@ const squareHandlerFactory = {
2299
2913
  const defaults = getDefaults();
2300
2914
  if (!defaults) return null;
2301
2915
  const strokeWidth = defaults.strokeWidth;
2302
- const halfStroke = strokeWidth / 2;
2916
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2917
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
2303
2918
  const rect = {
2304
- origin: { x: minX - halfStroke, y: minY - halfStroke },
2305
- size: { width: width + strokeWidth, height: height + strokeWidth }
2919
+ origin: { x: minX - pad, y: minY - pad },
2920
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2306
2921
  };
2307
2922
  return {
2308
2923
  type: PdfAnnotationSubtype.SQUARE,
2309
2924
  bounds: rect,
2310
2925
  data: {
2311
2926
  rect,
2312
- ...defaults
2927
+ ...defaults,
2928
+ ...intensity > 0 && {
2929
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2930
+ }
2313
2931
  }
2314
2932
  };
2315
2933
  };
@@ -2343,13 +2961,18 @@ const squareHandlerFactory = {
2343
2961
  if (!defaults2) return;
2344
2962
  const preview = getPreview(clampedPos);
2345
2963
  if (preview) {
2964
+ const intensity = defaults2.cloudyBorderIntensity ?? 0;
2965
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, false) : void 0;
2346
2966
  const anno = {
2347
2967
  ...defaults2,
2348
2968
  type: PdfAnnotationSubtype.SQUARE,
2349
2969
  created: /* @__PURE__ */ new Date(),
2350
2970
  id: uuidV4(),
2351
2971
  pageIndex,
2352
- rect: preview.data.rect
2972
+ rect: preview.data.rect,
2973
+ ...pad !== void 0 && {
2974
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2975
+ }
2353
2976
  };
2354
2977
  onCommit(anno);
2355
2978
  }
@@ -2473,10 +3096,11 @@ const circleHandlerFactory = {
2473
3096
  const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2474
3097
  const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2475
3098
  const strokeWidth = defaults.strokeWidth;
2476
- const halfStroke = strokeWidth / 2;
3099
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
3100
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
2477
3101
  const rect = {
2478
- origin: { x: x - halfStroke, y: y - halfStroke },
2479
- size: { width: width + strokeWidth, height: height + strokeWidth }
3102
+ origin: { x: x - pad, y: y - pad },
3103
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2480
3104
  };
2481
3105
  const anno = {
2482
3106
  ...defaults,
@@ -2484,7 +3108,10 @@ const circleHandlerFactory = {
2484
3108
  created: /* @__PURE__ */ new Date(),
2485
3109
  id: uuidV4(),
2486
3110
  pageIndex,
2487
- rect
3111
+ rect,
3112
+ ...intensity > 0 && {
3113
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
3114
+ }
2488
3115
  };
2489
3116
  onCommit(anno);
2490
3117
  }
@@ -2499,17 +3126,21 @@ const circleHandlerFactory = {
2499
3126
  const defaults = getDefaults();
2500
3127
  if (!defaults) return null;
2501
3128
  const strokeWidth = defaults.strokeWidth;
2502
- const halfStroke = strokeWidth / 2;
3129
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
3130
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
2503
3131
  const rect = {
2504
- origin: { x: minX - halfStroke, y: minY - halfStroke },
2505
- size: { width: width + strokeWidth, height: height + strokeWidth }
3132
+ origin: { x: minX - pad, y: minY - pad },
3133
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2506
3134
  };
2507
3135
  return {
2508
3136
  type: PdfAnnotationSubtype.CIRCLE,
2509
3137
  bounds: rect,
2510
3138
  data: {
2511
3139
  rect,
2512
- ...defaults
3140
+ ...defaults,
3141
+ ...intensity > 0 && {
3142
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
3143
+ }
2513
3144
  }
2514
3145
  };
2515
3146
  };
@@ -2543,6 +3174,8 @@ const circleHandlerFactory = {
2543
3174
  if (!defaults2) return;
2544
3175
  const preview = getPreview(clampedPos);
2545
3176
  if (preview) {
3177
+ const intensity = defaults2.cloudyBorderIntensity ?? 0;
3178
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, true) : void 0;
2546
3179
  const anno = {
2547
3180
  ...defaults2,
2548
3181
  type: PdfAnnotationSubtype.CIRCLE,
@@ -2550,7 +3183,10 @@ const circleHandlerFactory = {
2550
3183
  created: /* @__PURE__ */ new Date(),
2551
3184
  id: uuidV4(),
2552
3185
  pageIndex,
2553
- rect: preview.data.rect
3186
+ rect: preview.data.rect,
3187
+ ...pad !== void 0 && {
3188
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
3189
+ }
2554
3190
  };
2555
3191
  onCommit(anno);
2556
3192
  }
@@ -2722,23 +3358,29 @@ const patchInk = (original, ctx) => {
2722
3358
  }
2723
3359
  case "resize": {
2724
3360
  if (!ctx.changes.rect) return ctx.changes;
2725
- const { oldRect, resolvedRect, rects } = baseResizeScaling(
3361
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
2726
3362
  original,
2727
3363
  ctx.changes.rect,
2728
3364
  ctx.metadata
2729
3365
  );
2730
- const inset = (r, pad) => ({
2731
- origin: { x: r.origin.x + pad, y: r.origin.y + pad },
3366
+ const inset = (r2, pad) => ({
3367
+ origin: { x: r2.origin.x + pad, y: r2.origin.y + pad },
2732
3368
  size: {
2733
- width: Math.max(1, r.size.width - pad * 2),
2734
- height: Math.max(1, r.size.height - pad * 2)
3369
+ width: Math.max(1, r2.size.width - pad * 2),
3370
+ height: Math.max(1, r2.size.height - pad * 2)
2735
3371
  }
2736
3372
  });
2737
- const strokeScale = Math.min(
2738
- resolvedRect.size.width / oldRect.size.width,
2739
- resolvedRect.size.height / oldRect.size.height
3373
+ const resizeEpsilon = 1e-3;
3374
+ const widthChanged = Math.abs(scaleX - 1) > resizeEpsilon;
3375
+ const heightChanged = Math.abs(scaleY - 1) > resizeEpsilon;
3376
+ const strokeScale = widthChanged && !heightChanged ? scaleX : !widthChanged && heightChanged ? scaleY : Math.min(scaleX, scaleY);
3377
+ const rawStrokeWidth = Math.max(1, original.strokeWidth * strokeScale);
3378
+ const maxStrokeWidth = Math.max(
3379
+ 1,
3380
+ Math.min(resolvedRect.size.width, resolvedRect.size.height)
2740
3381
  );
2741
- const newStrokeWidth = Math.max(1, Math.round(original.strokeWidth * strokeScale));
3382
+ const clampedStrokeWidth = Math.min(rawStrokeWidth, maxStrokeWidth);
3383
+ const newStrokeWidth = Number(clampedStrokeWidth.toFixed(1));
2742
3384
  const innerOld = inset(oldRect, original.strokeWidth / 2);
2743
3385
  const innerNew = inset(resolvedRect, newStrokeWidth / 2);
2744
3386
  const sx = innerNew.size.width / Math.max(innerOld.size.width, 1e-6);
@@ -2948,11 +3590,17 @@ const patchPolyline = (orig, ctx) => {
2948
3590
  return ctx.changes;
2949
3591
  }
2950
3592
  };
3593
+ function getPolygonPad(intensity, strokeWidth) {
3594
+ if ((intensity ?? 0) > 0) {
3595
+ return getCloudyBorderExtent(intensity, strokeWidth, false);
3596
+ }
3597
+ return strokeWidth / 2;
3598
+ }
2951
3599
  const patchPolygon = (orig, ctx) => {
2952
3600
  switch (ctx.type) {
2953
3601
  case "vertex-edit":
2954
3602
  if (ctx.changes.vertices && ctx.changes.vertices.length) {
2955
- const pad = orig.strokeWidth / 2;
3603
+ const pad = getPolygonPad(orig.cloudyBorderIntensity, orig.strokeWidth);
2956
3604
  const rawVertices = ctx.changes.vertices;
2957
3605
  const rawRect = expandRect(rectFromPoints(rawVertices), pad);
2958
3606
  const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
@@ -2996,15 +3644,30 @@ const patchPolygon = (orig, ctx) => {
2996
3644
  };
2997
3645
  }
2998
3646
  case "property-update": {
2999
- const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0;
3647
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3648
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0 || cloudyChanged;
3000
3649
  if (!needsRectUpdate) return ctx.changes;
3001
3650
  const merged = { ...orig, ...ctx.changes };
3002
- const pad = merged.strokeWidth / 2;
3651
+ const pad = getPolygonPad(merged.cloudyBorderIntensity, merged.strokeWidth);
3003
3652
  const tightRect = expandRect(rectFromPoints(merged.vertices), pad);
3653
+ let patch = ctx.changes;
3654
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3655
+ if (cloudyChanged || ctx.changes.strokeWidth !== void 0 && hasCloudy) {
3656
+ const intensity = merged.cloudyBorderIntensity ?? 0;
3657
+ if (intensity > 0) {
3658
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
3659
+ patch = {
3660
+ ...patch,
3661
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3662
+ };
3663
+ } else {
3664
+ patch = { ...patch, rectangleDifferences: void 0 };
3665
+ }
3666
+ }
3004
3667
  const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
3005
3668
  if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
3006
3669
  return {
3007
- ...ctx.changes,
3670
+ ...patch,
3008
3671
  unrotatedRect: tightRect,
3009
3672
  rect: calculateRotatedRectAABBAroundPoint(
3010
3673
  tightRect,
@@ -3013,7 +3676,7 @@ const patchPolygon = (orig, ctx) => {
3013
3676
  )
3014
3677
  };
3015
3678
  }
3016
- return { ...ctx.changes, rect: tightRect };
3679
+ return { ...patch, rect: tightRect };
3017
3680
  }
3018
3681
  default:
3019
3682
  return ctx.changes;
@@ -3029,11 +3692,29 @@ const patchCircle = (orig, ctx) => {
3029
3692
  return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
3030
3693
  case "rotate":
3031
3694
  return baseRotateChanges(orig, ctx) ?? ctx.changes;
3032
- case "property-update":
3695
+ case "property-update": {
3696
+ let patch = ctx.changes;
3697
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3698
+ const strokeChanged = ctx.changes.strokeWidth !== void 0;
3699
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3700
+ if (cloudyChanged || strokeChanged && hasCloudy) {
3701
+ const merged = { ...orig, ...ctx.changes };
3702
+ const intensity = merged.cloudyBorderIntensity ?? 0;
3703
+ if (intensity > 0) {
3704
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, true);
3705
+ patch = {
3706
+ ...patch,
3707
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3708
+ };
3709
+ } else {
3710
+ patch = { ...patch, rectangleDifferences: void 0 };
3711
+ }
3712
+ }
3033
3713
  if (ctx.changes.rotation !== void 0) {
3034
- return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3714
+ patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3035
3715
  }
3036
- return ctx.changes;
3716
+ return patch;
3717
+ }
3037
3718
  default:
3038
3719
  return ctx.changes;
3039
3720
  }
@@ -3048,11 +3729,29 @@ const patchSquare = (orig, ctx) => {
3048
3729
  return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
3049
3730
  case "rotate":
3050
3731
  return baseRotateChanges(orig, ctx) ?? ctx.changes;
3051
- case "property-update":
3732
+ case "property-update": {
3733
+ let patch = ctx.changes;
3734
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3735
+ const strokeChanged = ctx.changes.strokeWidth !== void 0;
3736
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3737
+ if (cloudyChanged || strokeChanged && hasCloudy) {
3738
+ const merged = { ...orig, ...ctx.changes };
3739
+ const intensity = merged.cloudyBorderIntensity ?? 0;
3740
+ if (intensity > 0) {
3741
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
3742
+ patch = {
3743
+ ...patch,
3744
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3745
+ };
3746
+ } else {
3747
+ patch = { ...patch, rectangleDifferences: void 0 };
3748
+ }
3749
+ }
3052
3750
  if (ctx.changes.rotation !== void 0) {
3053
- return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3751
+ patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3054
3752
  }
3055
- return ctx.changes;
3753
+ return patch;
3754
+ }
3056
3755
  default:
3057
3756
  return ctx.changes;
3058
3757
  }
@@ -3095,34 +3794,6 @@ const patchStamp = (orig, ctx) => {
3095
3794
  return ctx.changes;
3096
3795
  }
3097
3796
  };
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
3797
  const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3127
3798
  constructor(id, registry, config) {
3128
3799
  var _a, _b, _c;
@@ -5131,10 +5802,14 @@ export {
5131
5802
  calculateRotatedRectAABBAroundPoint2 as calculateRotatedRectAABBAroundPoint,
5132
5803
  convertAABBRectToUnrotatedSpace,
5133
5804
  createToolPredicate,
5805
+ generateCloudyEllipsePath,
5806
+ generateCloudyPolygonPath,
5807
+ generateCloudyRectanglePath,
5134
5808
  getAnnotationByUid,
5135
5809
  getAnnotations,
5136
5810
  getAnnotationsByPageIndex,
5137
5811
  getAttachedLinks,
5812
+ getCloudyBorderExtent,
5138
5813
  getGroupLeaderId,
5139
5814
  getGroupMembers,
5140
5815
  getIRTChildIds,