@embedpdf/plugin-annotation 2.5.0 → 2.6.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 (71) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +1061 -430
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/annotation-plugin.d.ts +24 -6
  6. package/dist/lib/geometry/index.d.ts +1 -0
  7. package/dist/lib/geometry/rotation.d.ts +32 -0
  8. package/dist/lib/handlers/types.d.ts +3 -1
  9. package/dist/lib/index.d.ts +1 -0
  10. package/dist/lib/patching/base-patch.d.ts +87 -0
  11. package/dist/lib/patching/index.d.ts +1 -0
  12. package/dist/lib/patching/insert-upright.d.ts +20 -0
  13. package/dist/lib/patching/patch-registry.d.ts +14 -1
  14. package/dist/lib/patching/patch-utils.d.ts +54 -1
  15. package/dist/lib/patching/patches/circle.patch.d.ts +3 -0
  16. package/dist/lib/patching/patches/freetext.patch.d.ts +3 -0
  17. package/dist/lib/patching/patches/index.d.ts +4 -0
  18. package/dist/lib/patching/patches/square.patch.d.ts +3 -0
  19. package/dist/lib/patching/patches/stamp.patch.d.ts +3 -0
  20. package/dist/lib/tools/default-tools.d.ts +32 -0
  21. package/dist/lib/tools/types.d.ts +20 -1
  22. package/dist/lib/types.d.ts +67 -3
  23. package/dist/preact/adapter.d.ts +3 -0
  24. package/dist/preact/index.cjs +1 -1
  25. package/dist/preact/index.cjs.map +1 -1
  26. package/dist/preact/index.js +793 -126
  27. package/dist/preact/index.js.map +1 -1
  28. package/dist/react/adapter.d.ts +2 -1
  29. package/dist/react/index.cjs +1 -1
  30. package/dist/react/index.cjs.map +1 -1
  31. package/dist/react/index.js +793 -126
  32. package/dist/react/index.js.map +1 -1
  33. package/dist/shared/components/annotation-container.d.ts +10 -2
  34. package/dist/shared/components/annotation-layer.d.ts +9 -3
  35. package/dist/shared/components/annotations.d.ts +4 -1
  36. package/dist/shared/components/group-selection-box.d.ts +12 -4
  37. package/dist/shared/components/render-annotation.d.ts +2 -1
  38. package/dist/shared/components/types.d.ts +51 -1
  39. package/dist/shared-preact/components/annotation-container.d.ts +10 -2
  40. package/dist/shared-preact/components/annotation-layer.d.ts +9 -3
  41. package/dist/shared-preact/components/annotations.d.ts +4 -1
  42. package/dist/shared-preact/components/group-selection-box.d.ts +12 -4
  43. package/dist/shared-preact/components/render-annotation.d.ts +2 -1
  44. package/dist/shared-preact/components/types.d.ts +51 -1
  45. package/dist/shared-react/components/annotation-container.d.ts +10 -2
  46. package/dist/shared-react/components/annotation-layer.d.ts +9 -3
  47. package/dist/shared-react/components/annotations.d.ts +4 -1
  48. package/dist/shared-react/components/group-selection-box.d.ts +12 -4
  49. package/dist/shared-react/components/render-annotation.d.ts +2 -1
  50. package/dist/shared-react/components/types.d.ts +51 -1
  51. package/dist/svelte/components/AnnotationLayer.svelte.d.ts +8 -2
  52. package/dist/svelte/components/Annotations.svelte.d.ts +7 -1
  53. package/dist/svelte/components/GroupSelectionBox.svelte.d.ts +11 -3
  54. package/dist/svelte/components/RenderAnnotation.svelte.d.ts +1 -0
  55. package/dist/svelte/components/types.d.ts +14 -1
  56. package/dist/svelte/index.cjs +1 -1
  57. package/dist/svelte/index.cjs.map +1 -1
  58. package/dist/svelte/index.js +1166 -330
  59. package/dist/svelte/index.js.map +1 -1
  60. package/dist/svelte/types.d.ts +53 -0
  61. package/dist/vue/components/annotation-container.vue.d.ts +35 -9
  62. package/dist/vue/components/annotation-layer.vue.d.ts +29 -5
  63. package/dist/vue/components/annotations.vue.d.ts +278 -134
  64. package/dist/vue/components/group-selection-box.vue.d.ts +35 -10
  65. package/dist/vue/components/render-annotation.vue.d.ts +2 -0
  66. package/dist/vue/index.cjs +1 -1
  67. package/dist/vue/index.cjs.map +1 -1
  68. package/dist/vue/index.js +945 -161
  69. package/dist/vue/index.js.map +1 -1
  70. package/dist/vue/types.d.ts +52 -0
  71. package/package.json +11 -10
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { clamp, BasePlugin, createBehaviorEmitter } from "@embedpdf/core";
2
- import { PdfAnnotationSubtype, PdfAnnotationReplyType, PdfBlendMode, PdfAnnotationBorderStyle, PdfAnnotationLineEnding, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, expandRect, rectFromPoints, uuidV4, rotateAndTranslatePoint, PdfAnnotationIcon, PdfPermissionFlag, ignore, PdfTaskHelper, PdfErrorCode, Task, TaskStage } from "@embedpdf/models";
2
+ import { PdfAnnotationSubtype, PdfAnnotationReplyType, PdfBlendMode, PdfAnnotationBorderStyle, PdfAnnotationLineEnding, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, expandRect, rectFromPoints, uuidV4, getRectCenter, inferRotationCenterFromRects, calculateRotatedRectAABBAroundPoint, rotateAndTranslatePoint, calculateRotatedRectAABB, rotatePointAround, rotateVertices, PdfAnnotationIcon, PdfPermissionFlag, ignore, PdfTaskHelper, PdfErrorCode, Task, TaskStage } from "@embedpdf/models";
3
+ import { calculateRotatedRectAABB as calculateRotatedRectAABB2, calculateRotatedRectAABBAroundPoint as calculateRotatedRectAABBAroundPoint2, getRectCenter as getRectCenter2, inferRotationCenterFromRects as inferRotationCenterFromRects2, rotatePointAround as rotatePointAround2, rotateVertices as rotateVertices2 } from "@embedpdf/models";
3
4
  const ANNOTATION_PLUGIN_ID = "annotation";
4
5
  const manifest = {
5
6
  id: ANNOTATION_PLUGIN_ID,
@@ -431,7 +432,11 @@ const defaultTools = [
431
432
  cursor: "crosshair",
432
433
  isDraggable: true,
433
434
  isResizable: true,
434
- lockAspectRatio: false
435
+ lockAspectRatio: false,
436
+ lockGroupAspectRatio: (a) => {
437
+ const r = ((a.rotation ?? 0) % 90 + 90) % 90;
438
+ return r >= 6 && r <= 84;
439
+ }
435
440
  },
436
441
  defaults: {
437
442
  type: PdfAnnotationSubtype.INK,
@@ -454,7 +459,11 @@ const defaultTools = [
454
459
  cursor: "crosshair",
455
460
  isDraggable: true,
456
461
  isResizable: true,
457
- lockAspectRatio: false
462
+ lockAspectRatio: false,
463
+ lockGroupAspectRatio: (a) => {
464
+ const r = ((a.rotation ?? 0) % 90 + 90) % 90;
465
+ return r >= 6 && r <= 84;
466
+ }
458
467
  },
459
468
  defaults: {
460
469
  type: PdfAnnotationSubtype.CIRCLE,
@@ -478,7 +487,11 @@ const defaultTools = [
478
487
  cursor: "crosshair",
479
488
  isDraggable: true,
480
489
  isResizable: true,
481
- lockAspectRatio: false
490
+ lockAspectRatio: false,
491
+ lockGroupAspectRatio: (a) => {
492
+ const r = ((a.rotation ?? 0) % 90 + 90) % 90;
493
+ return r >= 6 && r <= 84;
494
+ }
482
495
  },
483
496
  defaults: {
484
497
  type: PdfAnnotationSubtype.SQUARE,
@@ -504,8 +517,12 @@ const defaultTools = [
504
517
  isResizable: false,
505
518
  // Uses vertex editing when selected individually
506
519
  lockAspectRatio: false,
507
- isGroupResizable: true
520
+ isGroupResizable: true,
508
521
  // Scales proportionally in a group
522
+ lockGroupAspectRatio: (a) => {
523
+ const r = ((a.rotation ?? 0) % 90 + 90) % 90;
524
+ return r >= 6 && r <= 84;
525
+ }
509
526
  },
510
527
  defaults: {
511
528
  type: PdfAnnotationSubtype.LINE,
@@ -531,8 +548,12 @@ const defaultTools = [
531
548
  isResizable: false,
532
549
  // Uses vertex editing when selected individually
533
550
  lockAspectRatio: false,
534
- isGroupResizable: true
551
+ isGroupResizable: true,
535
552
  // Scales proportionally in a group
553
+ lockGroupAspectRatio: (a) => {
554
+ const r = ((a.rotation ?? 0) % 90 + 90) % 90;
555
+ return r >= 6 && r <= 84;
556
+ }
536
557
  },
537
558
  defaults: {
538
559
  type: PdfAnnotationSubtype.LINE,
@@ -563,8 +584,12 @@ const defaultTools = [
563
584
  isResizable: false,
564
585
  // Uses vertex editing when selected individually
565
586
  lockAspectRatio: false,
566
- isGroupResizable: true
587
+ isGroupResizable: true,
567
588
  // Scales proportionally in a group
589
+ lockGroupAspectRatio: (a) => {
590
+ const r = ((a.rotation ?? 0) % 90 + 90) % 90;
591
+ return r >= 6 && r <= 84;
592
+ }
568
593
  },
569
594
  defaults: {
570
595
  type: PdfAnnotationSubtype.POLYLINE,
@@ -585,8 +610,12 @@ const defaultTools = [
585
610
  isResizable: false,
586
611
  // Uses vertex editing when selected individually
587
612
  lockAspectRatio: false,
588
- isGroupResizable: true
613
+ isGroupResizable: true,
589
614
  // Scales proportionally in a group
615
+ lockGroupAspectRatio: (a) => {
616
+ const r = ((a.rotation ?? 0) % 90 + 90) % 90;
617
+ return r >= 6 && r <= 84;
618
+ }
590
619
  },
591
620
  defaults: {
592
621
  type: PdfAnnotationSubtype.POLYGON,
@@ -606,7 +635,11 @@ const defaultTools = [
606
635
  cursor: "crosshair",
607
636
  isDraggable: true,
608
637
  isResizable: true,
609
- lockAspectRatio: false
638
+ lockAspectRatio: false,
639
+ lockGroupAspectRatio: (a) => {
640
+ const r = ((a.rotation ?? 0) % 90 + 90) % 90;
641
+ return r >= 6 && r <= 84;
642
+ }
610
643
  },
611
644
  defaults: {
612
645
  type: PdfAnnotationSubtype.FREETEXT,
@@ -626,6 +659,9 @@ const defaultTools = [
626
659
  enabled: true,
627
660
  defaultSize: { width: 100, height: 20 },
628
661
  defaultContent: "Insert text"
662
+ },
663
+ behavior: {
664
+ insertUpright: true
629
665
  }
630
666
  },
631
667
  {
@@ -637,11 +673,15 @@ const defaultTools = [
637
673
  cursor: "copy",
638
674
  isDraggable: true,
639
675
  isResizable: true,
640
- lockAspectRatio: true
676
+ lockAspectRatio: true,
677
+ lockGroupAspectRatio: true
641
678
  },
642
679
  defaults: {
643
680
  type: PdfAnnotationSubtype.STAMP
644
681
  // No imageSrc by default, which tells the UI to open a file picker
682
+ },
683
+ behavior: {
684
+ insertUpright: true
645
685
  }
646
686
  }
647
687
  ];
@@ -683,14 +723,44 @@ const patchAnno = (docState, uid, patch) => {
683
723
  };
684
724
  };
685
725
  const initialState = (cfg) => {
686
- const toolMap = /* @__PURE__ */ new Map();
687
- defaultTools.forEach((t) => toolMap.set(t.id, t));
688
- (cfg.tools || []).forEach((t) => toolMap.set(t.id, t));
726
+ const defaultMap = /* @__PURE__ */ new Map();
727
+ defaultTools.forEach((t) => defaultMap.set(t.id, t));
728
+ const toolMap = new Map(defaultMap);
729
+ (cfg.tools || []).forEach((userTool) => {
730
+ const base = defaultMap.get(userTool.id);
731
+ if (base) {
732
+ toolMap.set(userTool.id, {
733
+ ...base,
734
+ ...userTool,
735
+ defaults: { ...base.defaults, ...userTool.defaults },
736
+ interaction: { ...base.interaction, ...userTool.interaction },
737
+ behavior: { ...base.behavior, ...userTool.behavior },
738
+ ...base.clickBehavior || userTool.clickBehavior ? {
739
+ clickBehavior: {
740
+ ...base.clickBehavior,
741
+ ...userTool.clickBehavior
742
+ }
743
+ } : {}
744
+ });
745
+ } else {
746
+ toolMap.set(userTool.id, userTool);
747
+ }
748
+ });
749
+ const tools = Array.from(toolMap.values()).map((t) => {
750
+ var _a, _b;
751
+ return {
752
+ ...t,
753
+ behavior: {
754
+ ...t.behavior,
755
+ deactivateToolAfterCreate: ((_a = t.behavior) == null ? void 0 : _a.deactivateToolAfterCreate) ?? cfg.deactivateToolAfterCreate ?? false,
756
+ selectAfterCreate: ((_b = t.behavior) == null ? void 0 : _b.selectAfterCreate) ?? cfg.selectAfterCreate ?? true
757
+ }
758
+ };
759
+ });
689
760
  return {
690
761
  documents: {},
691
762
  activeDocumentId: null,
692
- // `Array.from(toolMap.values())` now correctly returns `AnnotationTool[]`, which matches the state's type.
693
- tools: Array.from(toolMap.values()),
763
+ tools,
694
764
  colorPresets: cfg.colorPresets ?? DEFAULT_COLORS
695
765
  };
696
766
  };
@@ -1115,149 +1185,6 @@ function useClickDetector({
1115
1185
  }
1116
1186
  };
1117
1187
  }
1118
- const freeTextHandlerFactory = {
1119
- annotationType: PdfAnnotationSubtype.FREETEXT,
1120
- create(context) {
1121
- const { onCommit, onPreview, getTool, pageSize, pageIndex } = context;
1122
- const [getStart, setStart] = useState(null);
1123
- const clampToPage = (pos) => ({
1124
- x: clamp(pos.x, 0, pageSize.width),
1125
- y: clamp(pos.y, 0, pageSize.height)
1126
- });
1127
- const getDefaults = () => {
1128
- const tool = getTool();
1129
- if (!tool) return null;
1130
- return {
1131
- ...tool.defaults,
1132
- fontColor: tool.defaults.fontColor ?? "#000000",
1133
- opacity: tool.defaults.opacity ?? 1,
1134
- fontSize: tool.defaults.fontSize ?? 12,
1135
- fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
1136
- color: tool.defaults.color ?? tool.defaults.backgroundColor ?? "transparent",
1137
- textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
1138
- verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
1139
- contents: tool.defaults.contents ?? "Insert text here",
1140
- flags: tool.defaults.flags ?? ["print"]
1141
- };
1142
- };
1143
- const clickDetector = useClickDetector({
1144
- threshold: 5,
1145
- getTool,
1146
- onClickDetected: (pos, tool) => {
1147
- const defaults = getDefaults();
1148
- if (!defaults) return;
1149
- const clickConfig = tool.clickBehavior;
1150
- if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
1151
- const { width, height } = clickConfig.defaultSize;
1152
- const halfWidth = width / 2;
1153
- const halfHeight = height / 2;
1154
- const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
1155
- const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
1156
- const rect = {
1157
- origin: { x, y },
1158
- size: { width, height }
1159
- };
1160
- const contents = clickConfig.defaultContent ?? defaults.contents;
1161
- const anno = {
1162
- ...defaults,
1163
- contents,
1164
- type: PdfAnnotationSubtype.FREETEXT,
1165
- rect,
1166
- pageIndex,
1167
- id: uuidV4(),
1168
- created: /* @__PURE__ */ new Date()
1169
- };
1170
- onCommit(anno);
1171
- }
1172
- });
1173
- const getPreview = (current) => {
1174
- const start = getStart();
1175
- if (!start) return null;
1176
- const defaults = getDefaults();
1177
- if (!defaults) return null;
1178
- const minX = Math.min(start.x, current.x);
1179
- const minY = Math.min(start.y, current.y);
1180
- const width = Math.abs(start.x - current.x);
1181
- const height = Math.abs(start.y - current.y);
1182
- const rect = {
1183
- origin: { x: minX, y: minY },
1184
- size: { width, height }
1185
- };
1186
- return {
1187
- type: PdfAnnotationSubtype.FREETEXT,
1188
- bounds: rect,
1189
- data: {
1190
- ...defaults,
1191
- rect
1192
- }
1193
- };
1194
- };
1195
- return {
1196
- onPointerDown: (pos, evt) => {
1197
- var _a;
1198
- const clampedPos = clampToPage(pos);
1199
- setStart(clampedPos);
1200
- clickDetector.onStart(clampedPos);
1201
- onPreview(getPreview(clampedPos));
1202
- (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
1203
- },
1204
- onPointerMove: (pos) => {
1205
- const clampedPos = clampToPage(pos);
1206
- clickDetector.onMove(clampedPos);
1207
- if (getStart() && clickDetector.hasMoved()) {
1208
- onPreview(getPreview(clampedPos));
1209
- }
1210
- },
1211
- onPointerUp: (pos, evt) => {
1212
- var _a;
1213
- const start = getStart();
1214
- if (!start) return;
1215
- const defaults = getDefaults();
1216
- if (!defaults) return;
1217
- const clampedPos = clampToPage(pos);
1218
- if (!clickDetector.hasMoved()) {
1219
- clickDetector.onEnd(clampedPos);
1220
- } else {
1221
- const minX = Math.min(start.x, clampedPos.x);
1222
- const minY = Math.min(start.y, clampedPos.y);
1223
- const width = Math.abs(start.x - clampedPos.x);
1224
- const height = Math.abs(start.y - clampedPos.y);
1225
- const rect = {
1226
- origin: { x: minX, y: minY },
1227
- size: { width, height }
1228
- };
1229
- const anno = {
1230
- ...defaults,
1231
- type: PdfAnnotationSubtype.FREETEXT,
1232
- rect,
1233
- pageIndex: context.pageIndex,
1234
- id: uuidV4(),
1235
- created: /* @__PURE__ */ new Date()
1236
- };
1237
- onCommit(anno);
1238
- }
1239
- setStart(null);
1240
- onPreview(null);
1241
- clickDetector.reset();
1242
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1243
- },
1244
- onPointerLeave: (_, evt) => {
1245
- var _a;
1246
- setStart(null);
1247
- onPreview(null);
1248
- clickDetector.reset();
1249
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1250
- },
1251
- onPointerCancel: (_, evt) => {
1252
- var _a;
1253
- setStart(null);
1254
- onPreview(null);
1255
- clickDetector.reset();
1256
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1257
- }
1258
- };
1259
- }
1260
- };
1261
1188
  function createArrowHandler(isClosed) {
1262
1189
  const calculateGeometry = (sw) => {
1263
1190
  const len = sw * 9;
@@ -1373,6 +1300,13 @@ const LINE_ENDING_HANDLERS = {
1373
1300
  [PdfAnnotationLineEnding.Slash]: createLineHandler(18, (angle) => angle + Math.PI / 1.5)
1374
1301
  };
1375
1302
  const EXTRA_PADDING = 1.2;
1303
+ function calculateAABBFromVertices(vertices, padding = 0) {
1304
+ if (vertices.length === 0) {
1305
+ return { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } };
1306
+ }
1307
+ const baseRect = rectFromPoints(vertices);
1308
+ return padding > 0 ? expandRect(baseRect, padding) : baseRect;
1309
+ }
1376
1310
  function lineRectWithEndings(vertices, strokeWidth, endings) {
1377
1311
  if (!vertices || vertices.length === 0) {
1378
1312
  return { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } };
@@ -1407,6 +1341,54 @@ function lineRectWithEndings(vertices, strokeWidth, endings) {
1407
1341
  const pad = strokeWidth / 2 + EXTRA_PADDING * strokeWidth;
1408
1342
  return expandRect(baseRect, pad);
1409
1343
  }
1344
+ function resolveVertexEditRects(original, tightRect) {
1345
+ if (!original.unrotatedRect) return { rect: tightRect };
1346
+ const center = getRectCenter(tightRect);
1347
+ return {
1348
+ rect: calculateRotatedRectAABBAroundPoint(tightRect, original.rotation ?? 0, center),
1349
+ unrotatedRect: tightRect
1350
+ };
1351
+ }
1352
+ function resolveAnnotationRotationCenter(original) {
1353
+ if (!original.unrotatedRect) return getRectCenter(original.rect);
1354
+ return inferRotationCenterFromRects(
1355
+ original.unrotatedRect,
1356
+ original.rect,
1357
+ original.rotation ?? 0
1358
+ );
1359
+ }
1360
+ function resolveRotateRects(original, nextUnrotatedRect, angleDegrees) {
1361
+ const baseCenter = resolveAnnotationRotationCenter(original);
1362
+ const baseRect = original.unrotatedRect ?? original.rect;
1363
+ const translation = {
1364
+ x: nextUnrotatedRect.origin.x - baseRect.origin.x,
1365
+ y: nextUnrotatedRect.origin.y - baseRect.origin.y
1366
+ };
1367
+ const nextCenter = {
1368
+ x: baseCenter.x + translation.x,
1369
+ y: baseCenter.y + translation.y
1370
+ };
1371
+ return {
1372
+ rect: calculateRotatedRectAABBAroundPoint(nextUnrotatedRect, angleDegrees, nextCenter),
1373
+ unrotatedRect: nextUnrotatedRect
1374
+ };
1375
+ }
1376
+ function compensateRotatedVertexEdit(original, vertices, tightRect) {
1377
+ if (!original.unrotatedRect) return vertices;
1378
+ const angle = original.rotation ?? 0;
1379
+ if (Math.abs(angle % 360) < 1e-8) return vertices;
1380
+ const baseCenter = resolveAnnotationRotationCenter(original);
1381
+ const nextCenter = getRectCenter(tightRect);
1382
+ const rad = angle * Math.PI / 180;
1383
+ const cos = Math.cos(rad);
1384
+ const sin = Math.sin(rad);
1385
+ const dx = baseCenter.x - nextCenter.x;
1386
+ const dy = baseCenter.y - nextCenter.y;
1387
+ const qx = (1 - cos) * dx + sin * dy;
1388
+ const qy = -sin * dx + (1 - cos) * dy;
1389
+ if (Math.abs(qx) < 1e-8 && Math.abs(qy) < 1e-8) return vertices;
1390
+ return vertices.map((v) => ({ x: v.x + qx, y: v.y + qy }));
1391
+ }
1410
1392
  function createEnding(ending, strokeWidth, rad, px, py) {
1411
1393
  if (!ending) return null;
1412
1394
  const handler = LINE_ENDING_HANDLERS[ending];
@@ -1439,14 +1421,298 @@ class PatchRegistry {
1439
1421
  }
1440
1422
  }
1441
1423
  const patchRegistry = new PatchRegistry();
1424
+ function baseRotateChanges(orig, ctx) {
1425
+ var _a;
1426
+ if (((_a = ctx.metadata) == null ? void 0 : _a.rotationAngle) === void 0) return null;
1427
+ const angleDegrees = ctx.metadata.rotationAngle;
1428
+ const baseUnrotatedRect = ctx.changes.unrotatedRect ?? orig.unrotatedRect ?? orig.rect;
1429
+ const normalizedUnrotatedRect = {
1430
+ origin: { ...baseUnrotatedRect.origin },
1431
+ size: { ...baseUnrotatedRect.size }
1432
+ };
1433
+ return {
1434
+ ...resolveRotateRects(orig, normalizedUnrotatedRect, angleDegrees),
1435
+ rotation: angleDegrees
1436
+ };
1437
+ }
1438
+ function basePropertyRotationChanges(orig, newRotation) {
1439
+ const unrotatedRect = orig.unrotatedRect ?? orig.rect;
1440
+ return {
1441
+ rotation: newRotation,
1442
+ ...resolveRotateRects(orig, unrotatedRect, newRotation)
1443
+ };
1444
+ }
1445
+ function baseMoveChanges(orig, newRect) {
1446
+ const dx = newRect.origin.x - orig.rect.origin.x;
1447
+ const dy = newRect.origin.y - orig.rect.origin.y;
1448
+ const rects = { rect: newRect };
1449
+ if (orig.unrotatedRect) {
1450
+ rects.unrotatedRect = {
1451
+ origin: { x: orig.unrotatedRect.origin.x + dx, y: orig.unrotatedRect.origin.y + dy },
1452
+ size: { ...orig.unrotatedRect.size }
1453
+ };
1454
+ }
1455
+ return { dx, dy, rects };
1456
+ }
1457
+ function baseResizeScaling(orig, newRect, metadata) {
1458
+ const oldRect = orig.unrotatedRect ?? orig.rect;
1459
+ let scaleX = newRect.size.width / oldRect.size.width;
1460
+ let scaleY = newRect.size.height / oldRect.size.height;
1461
+ const minSize = 10;
1462
+ if (newRect.size.width < minSize || newRect.size.height < minSize) {
1463
+ scaleX = Math.max(scaleX, minSize / oldRect.size.width);
1464
+ scaleY = Math.max(scaleY, minSize / oldRect.size.height);
1465
+ newRect = {
1466
+ origin: newRect.origin,
1467
+ size: {
1468
+ width: oldRect.size.width * scaleX,
1469
+ height: oldRect.size.height * scaleY
1470
+ }
1471
+ };
1472
+ }
1473
+ if (metadata == null ? void 0 : metadata.maintainAspectRatio) {
1474
+ const minScale = Math.min(scaleX, scaleY);
1475
+ scaleX = minScale;
1476
+ scaleY = minScale;
1477
+ newRect = {
1478
+ origin: newRect.origin,
1479
+ size: {
1480
+ width: oldRect.size.width * minScale,
1481
+ height: oldRect.size.height * minScale
1482
+ }
1483
+ };
1484
+ }
1485
+ const rects = orig.unrotatedRect ? {
1486
+ unrotatedRect: newRect,
1487
+ rect: calculateRotatedRectAABB(newRect, orig.rotation ?? 0)
1488
+ } : { rect: newRect };
1489
+ return { scaleX, scaleY, oldRect, resolvedRect: newRect, rects };
1490
+ }
1491
+ function rotateOrbitDelta(orig, rotateResult) {
1492
+ const baseRect = orig.unrotatedRect ?? orig.rect;
1493
+ const newRect = rotateResult.unrotatedRect ?? baseRect;
1494
+ return {
1495
+ dx: newRect.origin.x - baseRect.origin.x,
1496
+ dy: newRect.origin.y - baseRect.origin.y
1497
+ };
1498
+ }
1499
+ function applyInsertUpright(annotation, pageRotation, rectWasDrawn) {
1500
+ if (pageRotation === 0 || annotation.rotation !== void 0) return annotation;
1501
+ const counterDeg = (4 - pageRotation) % 4 * 90;
1502
+ let baseAnnotation = annotation;
1503
+ if (rectWasDrawn && (pageRotation === 1 || pageRotation === 3)) {
1504
+ const originalRect = annotation.rect;
1505
+ const centerX = originalRect.origin.x + originalRect.size.width / 2;
1506
+ const centerY = originalRect.origin.y + originalRect.size.height / 2;
1507
+ baseAnnotation = {
1508
+ ...annotation,
1509
+ rect: {
1510
+ origin: {
1511
+ x: centerX - originalRect.size.height / 2,
1512
+ y: centerY - originalRect.size.width / 2
1513
+ },
1514
+ size: {
1515
+ width: originalRect.size.height,
1516
+ height: originalRect.size.width
1517
+ }
1518
+ }
1519
+ };
1520
+ }
1521
+ const { rotation, rect, unrotatedRect } = basePropertyRotationChanges(baseAnnotation, counterDeg);
1522
+ return { ...baseAnnotation, rotation, rect, unrotatedRect };
1523
+ }
1524
+ function clampAnnotationToPage(annotation, pageSize) {
1525
+ const clampedX = clamp(annotation.rect.origin.x, 0, pageSize.width - annotation.rect.size.width);
1526
+ const clampedY = clamp(
1527
+ annotation.rect.origin.y,
1528
+ 0,
1529
+ pageSize.height - annotation.rect.size.height
1530
+ );
1531
+ const shiftX = clampedX - annotation.rect.origin.x;
1532
+ const shiftY = clampedY - annotation.rect.origin.y;
1533
+ if (shiftX === 0 && shiftY === 0) return annotation;
1534
+ return {
1535
+ ...annotation,
1536
+ rect: { origin: { x: clampedX, y: clampedY }, size: annotation.rect.size },
1537
+ ...annotation.unrotatedRect ? {
1538
+ unrotatedRect: {
1539
+ origin: {
1540
+ x: annotation.unrotatedRect.origin.x + shiftX,
1541
+ y: annotation.unrotatedRect.origin.y + shiftY
1542
+ },
1543
+ size: annotation.unrotatedRect.size
1544
+ }
1545
+ } : {}
1546
+ };
1547
+ }
1442
1548
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1443
1549
  __proto__: null,
1444
1550
  LINE_ENDING_HANDLERS,
1445
1551
  PatchRegistry,
1552
+ applyInsertUpright,
1553
+ calculateAABBFromVertices,
1554
+ calculateRotatedRectAABB,
1555
+ calculateRotatedRectAABBAroundPoint,
1556
+ clampAnnotationToPage,
1557
+ compensateRotatedVertexEdit,
1446
1558
  createEnding,
1559
+ getRectCenter,
1447
1560
  lineRectWithEndings,
1448
- patchRegistry
1561
+ patchRegistry,
1562
+ resolveAnnotationRotationCenter,
1563
+ resolveRotateRects,
1564
+ resolveVertexEditRects,
1565
+ rotatePointAroundCenter: rotatePointAround,
1566
+ rotateVertices
1449
1567
  }, Symbol.toStringTag, { value: "Module" }));
1568
+ const freeTextHandlerFactory = {
1569
+ annotationType: PdfAnnotationSubtype.FREETEXT,
1570
+ create(context) {
1571
+ const { onCommit, onPreview, getTool, pageSize, pageIndex, pageRotation } = context;
1572
+ const [getStart, setStart] = useState(null);
1573
+ const clampToPage = (pos) => ({
1574
+ x: clamp(pos.x, 0, pageSize.width),
1575
+ y: clamp(pos.y, 0, pageSize.height)
1576
+ });
1577
+ const getDefaults = () => {
1578
+ const tool = getTool();
1579
+ if (!tool) return null;
1580
+ return {
1581
+ ...tool.defaults,
1582
+ fontColor: tool.defaults.fontColor ?? "#000000",
1583
+ opacity: tool.defaults.opacity ?? 1,
1584
+ fontSize: tool.defaults.fontSize ?? 12,
1585
+ fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
1586
+ color: tool.defaults.color ?? tool.defaults.backgroundColor ?? "transparent",
1587
+ textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
1588
+ verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
1589
+ contents: tool.defaults.contents ?? "Insert text here",
1590
+ flags: tool.defaults.flags ?? ["print"]
1591
+ };
1592
+ };
1593
+ const clickDetector = useClickDetector({
1594
+ threshold: 5,
1595
+ getTool,
1596
+ onClickDetected: (pos, tool) => {
1597
+ var _a;
1598
+ const defaults = getDefaults();
1599
+ if (!defaults) return;
1600
+ const clickConfig = tool.clickBehavior;
1601
+ if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
1602
+ const { width, height } = clickConfig.defaultSize;
1603
+ const rect = {
1604
+ origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
1605
+ size: { width, height }
1606
+ };
1607
+ const contents = clickConfig.defaultContent ?? defaults.contents;
1608
+ let anno = {
1609
+ ...defaults,
1610
+ contents,
1611
+ type: PdfAnnotationSubtype.FREETEXT,
1612
+ rect,
1613
+ pageIndex,
1614
+ id: uuidV4(),
1615
+ created: /* @__PURE__ */ new Date()
1616
+ };
1617
+ if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
1618
+ anno = applyInsertUpright(anno, pageRotation, false);
1619
+ }
1620
+ anno = clampAnnotationToPage(anno, pageSize);
1621
+ onCommit(anno);
1622
+ }
1623
+ });
1624
+ const getPreview = (current) => {
1625
+ const start = getStart();
1626
+ if (!start) return null;
1627
+ const defaults = getDefaults();
1628
+ if (!defaults) return null;
1629
+ const minX = Math.min(start.x, current.x);
1630
+ const minY = Math.min(start.y, current.y);
1631
+ const width = Math.abs(start.x - current.x);
1632
+ const height = Math.abs(start.y - current.y);
1633
+ const rect = {
1634
+ origin: { x: minX, y: minY },
1635
+ size: { width, height }
1636
+ };
1637
+ return {
1638
+ type: PdfAnnotationSubtype.FREETEXT,
1639
+ bounds: rect,
1640
+ data: {
1641
+ ...defaults,
1642
+ rect
1643
+ }
1644
+ };
1645
+ };
1646
+ return {
1647
+ onPointerDown: (pos, evt) => {
1648
+ var _a;
1649
+ const clampedPos = clampToPage(pos);
1650
+ setStart(clampedPos);
1651
+ clickDetector.onStart(clampedPos);
1652
+ onPreview(getPreview(clampedPos));
1653
+ (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
1654
+ },
1655
+ onPointerMove: (pos) => {
1656
+ const clampedPos = clampToPage(pos);
1657
+ clickDetector.onMove(clampedPos);
1658
+ if (getStart() && clickDetector.hasMoved()) {
1659
+ onPreview(getPreview(clampedPos));
1660
+ }
1661
+ },
1662
+ onPointerUp: (pos, evt) => {
1663
+ var _a, _b;
1664
+ const start = getStart();
1665
+ if (!start) return;
1666
+ const defaults = getDefaults();
1667
+ if (!defaults) return;
1668
+ const clampedPos = clampToPage(pos);
1669
+ if (!clickDetector.hasMoved()) {
1670
+ clickDetector.onEnd(clampedPos);
1671
+ } else {
1672
+ const minX = Math.min(start.x, clampedPos.x);
1673
+ const minY = Math.min(start.y, clampedPos.y);
1674
+ const width = Math.abs(start.x - clampedPos.x);
1675
+ const height = Math.abs(start.y - clampedPos.y);
1676
+ const rect = {
1677
+ origin: { x: minX, y: minY },
1678
+ size: { width, height }
1679
+ };
1680
+ const tool = getTool();
1681
+ let anno = {
1682
+ ...defaults,
1683
+ type: PdfAnnotationSubtype.FREETEXT,
1684
+ rect,
1685
+ pageIndex: context.pageIndex,
1686
+ id: uuidV4(),
1687
+ created: /* @__PURE__ */ new Date()
1688
+ };
1689
+ if ((_a = tool == null ? void 0 : tool.behavior) == null ? void 0 : _a.insertUpright) {
1690
+ anno = applyInsertUpright(anno, pageRotation, true);
1691
+ }
1692
+ onCommit(anno);
1693
+ }
1694
+ setStart(null);
1695
+ onPreview(null);
1696
+ clickDetector.reset();
1697
+ (_b = evt.releasePointerCapture) == null ? void 0 : _b.call(evt);
1698
+ },
1699
+ onPointerLeave: (_, evt) => {
1700
+ var _a;
1701
+ setStart(null);
1702
+ onPreview(null);
1703
+ clickDetector.reset();
1704
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1705
+ },
1706
+ onPointerCancel: (_, evt) => {
1707
+ var _a;
1708
+ setStart(null);
1709
+ onPreview(null);
1710
+ clickDetector.reset();
1711
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1712
+ }
1713
+ };
1714
+ }
1715
+ };
1450
1716
  const lineHandlerFactory = {
1451
1717
  annotationType: PdfAnnotationSubtype.LINE,
1452
1718
  create(context) {
@@ -1953,22 +2219,19 @@ const squareHandlerFactory = {
1953
2219
  const stampHandlerFactory = {
1954
2220
  annotationType: PdfAnnotationSubtype.STAMP,
1955
2221
  create(context) {
1956
- const { services, onCommit, getTool, pageSize } = context;
2222
+ const { services, onCommit, getTool, pageSize, pageRotation } = context;
1957
2223
  return {
1958
2224
  onPointerDown: (pos) => {
1959
2225
  const tool = getTool();
1960
2226
  if (!tool) return;
1961
2227
  const { imageSrc, imageSize } = tool.defaults;
1962
2228
  const placeStamp = (imageData, width, height) => {
1963
- const originX = pos.x - width / 2;
1964
- const originY = pos.y - height / 2;
1965
- const finalX = clamp(originX, 0, pageSize.width - width);
1966
- const finalY = clamp(originY, 0, pageSize.height - height);
2229
+ var _a;
1967
2230
  const rect = {
1968
- origin: { x: finalX, y: finalY },
2231
+ origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
1969
2232
  size: { width, height }
1970
2233
  };
1971
- const anno = {
2234
+ let anno = {
1972
2235
  ...tool.defaults,
1973
2236
  rect,
1974
2237
  type: PdfAnnotationSubtype.STAMP,
@@ -1979,6 +2242,10 @@ const stampHandlerFactory = {
1979
2242
  id: uuidV4(),
1980
2243
  created: /* @__PURE__ */ new Date()
1981
2244
  };
2245
+ if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
2246
+ anno = applyInsertUpright(anno, pageRotation, false);
2247
+ }
2248
+ anno = clampAnnotationToPage(anno, pageSize);
1982
2249
  onCommit(anno, { imageData });
1983
2250
  };
1984
2251
  if (imageSrc) {
@@ -2149,322 +2416,424 @@ const circleHandlerFactory = {
2149
2416
  }
2150
2417
  };
2151
2418
  }
2152
- };
2153
- const patchInk = (original, ctx) => {
2154
- var _a;
2155
- switch (ctx.type) {
2156
- case "vertex-edit":
2157
- return ctx.changes;
2158
- case "move":
2159
- if (ctx.changes.rect) {
2160
- const dx = ctx.changes.rect.origin.x - original.rect.origin.x;
2161
- const dy = ctx.changes.rect.origin.y - original.rect.origin.y;
2162
- const movedInkList = original.inkList.map((stroke) => ({
2163
- points: stroke.points.map((p) => ({
2164
- x: p.x + dx,
2165
- y: p.y + dy
2166
- }))
2167
- }));
2168
- return {
2169
- rect: ctx.changes.rect,
2170
- inkList: movedInkList
2171
- };
2172
- }
2419
+ };
2420
+ const patchInk = (original, ctx) => {
2421
+ switch (ctx.type) {
2422
+ case "vertex-edit":
2173
2423
  return ctx.changes;
2174
- case "resize":
2175
- if (ctx.changes.rect) {
2176
- const oldRect = original.rect;
2177
- const newRect = ctx.changes.rect;
2178
- let scaleX = newRect.size.width / oldRect.size.width;
2179
- let scaleY = newRect.size.height / oldRect.size.height;
2180
- const minSize = 10;
2181
- if (newRect.size.width < minSize || newRect.size.height < minSize) {
2182
- scaleX = Math.max(scaleX, minSize / oldRect.size.width);
2183
- scaleY = Math.max(scaleY, minSize / oldRect.size.height);
2184
- ctx.changes.rect = {
2185
- origin: newRect.origin,
2186
- size: {
2187
- width: oldRect.size.width * scaleX,
2188
- height: oldRect.size.height * scaleY
2189
- }
2190
- };
2191
- }
2192
- if ((_a = ctx.metadata) == null ? void 0 : _a.maintainAspectRatio) {
2193
- const minScale = Math.min(scaleX, scaleY);
2194
- scaleX = minScale;
2195
- scaleY = minScale;
2196
- ctx.changes.rect.size = {
2197
- width: oldRect.size.width * minScale,
2198
- height: oldRect.size.height * minScale
2199
- };
2424
+ case "move": {
2425
+ if (!ctx.changes.rect) return ctx.changes;
2426
+ const { dx, dy, rects } = baseMoveChanges(original, ctx.changes.rect);
2427
+ return {
2428
+ ...rects,
2429
+ inkList: original.inkList.map((stroke) => ({
2430
+ points: stroke.points.map((p) => ({ x: p.x + dx, y: p.y + dy }))
2431
+ }))
2432
+ };
2433
+ }
2434
+ case "resize": {
2435
+ if (!ctx.changes.rect) return ctx.changes;
2436
+ const { oldRect, resolvedRect, rects } = baseResizeScaling(
2437
+ original,
2438
+ ctx.changes.rect,
2439
+ ctx.metadata
2440
+ );
2441
+ const inset = (r, pad) => ({
2442
+ origin: { x: r.origin.x + pad, y: r.origin.y + pad },
2443
+ size: {
2444
+ width: Math.max(1, r.size.width - pad * 2),
2445
+ height: Math.max(1, r.size.height - pad * 2)
2200
2446
  }
2201
- const inset = (r, pad) => ({
2202
- origin: { x: r.origin.x + pad, y: r.origin.y + pad },
2203
- size: {
2204
- width: Math.max(1, r.size.width - pad * 2),
2205
- height: Math.max(1, r.size.height - pad * 2)
2206
- }
2207
- });
2208
- const strokeScale = Math.min(
2209
- ctx.changes.rect.size.width / oldRect.size.width,
2210
- ctx.changes.rect.size.height / oldRect.size.height
2211
- );
2212
- const newStrokeWidth = Math.max(1, Math.round(original.strokeWidth * strokeScale));
2213
- const innerOld = inset(oldRect, original.strokeWidth / 2);
2214
- const innerNew = inset(ctx.changes.rect, newStrokeWidth / 2);
2215
- const sx = innerNew.size.width / Math.max(innerOld.size.width, 1e-6);
2216
- const sy = innerNew.size.height / Math.max(innerOld.size.height, 1e-6);
2217
- const newInkList = original.inkList.map((stroke) => ({
2447
+ });
2448
+ const strokeScale = Math.min(
2449
+ resolvedRect.size.width / oldRect.size.width,
2450
+ resolvedRect.size.height / oldRect.size.height
2451
+ );
2452
+ const newStrokeWidth = Math.max(1, Math.round(original.strokeWidth * strokeScale));
2453
+ const innerOld = inset(oldRect, original.strokeWidth / 2);
2454
+ const innerNew = inset(resolvedRect, newStrokeWidth / 2);
2455
+ const sx = innerNew.size.width / Math.max(innerOld.size.width, 1e-6);
2456
+ const sy = innerNew.size.height / Math.max(innerOld.size.height, 1e-6);
2457
+ return {
2458
+ ...rects,
2459
+ inkList: original.inkList.map((stroke) => ({
2218
2460
  points: stroke.points.map((p) => ({
2219
2461
  x: innerNew.origin.x + (p.x - innerOld.origin.x) * sx,
2220
2462
  y: innerNew.origin.y + (p.y - innerOld.origin.y) * sy
2221
2463
  }))
2222
- }));
2464
+ })),
2465
+ strokeWidth: newStrokeWidth
2466
+ };
2467
+ }
2468
+ case "rotate": {
2469
+ const result = baseRotateChanges(original, ctx);
2470
+ if (!result) return ctx.changes;
2471
+ const { dx, dy } = rotateOrbitDelta(original, result);
2472
+ return {
2473
+ ...result,
2474
+ inkList: original.inkList.map((stroke) => ({
2475
+ points: stroke.points.map((p) => ({ x: p.x + dx, y: p.y + dy }))
2476
+ }))
2477
+ };
2478
+ }
2479
+ case "property-update": {
2480
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0;
2481
+ if (!needsRectUpdate) return ctx.changes;
2482
+ const merged = { ...original, ...ctx.changes };
2483
+ const pts = merged.inkList.flatMap((s) => s.points);
2484
+ const tightRect = expandRect(rectFromPoints(pts), merged.strokeWidth / 2);
2485
+ const effectiveRotation = ctx.changes.rotation ?? original.rotation ?? 0;
2486
+ if (original.unrotatedRect || ctx.changes.rotation !== void 0) {
2223
2487
  return {
2224
- rect: ctx.changes.rect,
2225
- inkList: newInkList,
2226
- strokeWidth: newStrokeWidth
2488
+ ...ctx.changes,
2489
+ unrotatedRect: tightRect,
2490
+ rect: calculateRotatedRectAABBAroundPoint(
2491
+ tightRect,
2492
+ effectiveRotation,
2493
+ resolveAnnotationRotationCenter(original)
2494
+ )
2227
2495
  };
2228
2496
  }
2229
- return ctx.changes;
2230
- case "property-update":
2231
- if (ctx.changes.strokeWidth !== void 0) {
2232
- const merged = { ...original, ...ctx.changes };
2233
- const pts = merged.inkList.flatMap((s) => s.points);
2234
- const rect = expandRect(rectFromPoints(pts), merged.strokeWidth / 2);
2235
- return { ...ctx.changes, rect };
2236
- }
2237
- return ctx.changes;
2497
+ return { ...ctx.changes, rect: tightRect };
2498
+ }
2238
2499
  default:
2239
2500
  return ctx.changes;
2240
2501
  }
2241
2502
  };
2242
2503
  const patchLine = (orig, ctx) => {
2243
- var _a;
2244
2504
  switch (ctx.type) {
2245
2505
  case "vertex-edit":
2246
2506
  if (ctx.changes.linePoints) {
2247
2507
  const { start, end } = ctx.changes.linePoints;
2248
- const rect = lineRectWithEndings([start, end], orig.strokeWidth, orig.lineEndings);
2249
- return {
2250
- rect,
2251
- linePoints: { start, end }
2252
- };
2253
- }
2254
- return ctx.changes;
2255
- case "move":
2256
- if (ctx.changes.rect) {
2257
- const dx = ctx.changes.rect.origin.x - orig.rect.origin.x;
2258
- const dy = ctx.changes.rect.origin.y - orig.rect.origin.y;
2508
+ const rawPoints = [start, end];
2509
+ const rawRect = lineRectWithEndings(rawPoints, orig.strokeWidth, orig.lineEndings);
2510
+ const compensated = compensateRotatedVertexEdit(orig, rawPoints, rawRect);
2511
+ const rect = lineRectWithEndings(compensated, orig.strokeWidth, orig.lineEndings);
2259
2512
  return {
2260
- rect: ctx.changes.rect,
2261
- linePoints: {
2262
- start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
2263
- end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
2264
- }
2513
+ ...resolveVertexEditRects(orig, rect),
2514
+ linePoints: { start: compensated[0], end: compensated[1] }
2265
2515
  };
2266
2516
  }
2267
2517
  return ctx.changes;
2268
- case "resize":
2269
- if (ctx.changes.rect) {
2270
- const oldRect = orig.rect;
2271
- const newRect = ctx.changes.rect;
2272
- let scaleX = newRect.size.width / oldRect.size.width;
2273
- let scaleY = newRect.size.height / oldRect.size.height;
2274
- const minSize = 10;
2275
- if (newRect.size.width < minSize || newRect.size.height < minSize) {
2276
- scaleX = Math.max(scaleX, minSize / oldRect.size.width);
2277
- scaleY = Math.max(scaleY, minSize / oldRect.size.height);
2278
- ctx.changes.rect = {
2279
- origin: newRect.origin,
2280
- size: {
2281
- width: oldRect.size.width * scaleX,
2282
- height: oldRect.size.height * scaleY
2283
- }
2284
- };
2285
- }
2286
- if ((_a = ctx.metadata) == null ? void 0 : _a.maintainAspectRatio) {
2287
- const minScale = Math.min(scaleX, scaleY);
2288
- scaleX = minScale;
2289
- scaleY = minScale;
2290
- ctx.changes.rect.size = {
2291
- width: oldRect.size.width * minScale,
2292
- height: oldRect.size.height * minScale
2293
- };
2518
+ case "move": {
2519
+ if (!ctx.changes.rect) return ctx.changes;
2520
+ const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
2521
+ return {
2522
+ ...rects,
2523
+ linePoints: {
2524
+ start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
2525
+ end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
2294
2526
  }
2295
- const newLinePoints = {
2527
+ };
2528
+ }
2529
+ case "resize": {
2530
+ if (!ctx.changes.rect) return ctx.changes;
2531
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
2532
+ orig,
2533
+ ctx.changes.rect,
2534
+ ctx.metadata
2535
+ );
2536
+ return {
2537
+ ...rects,
2538
+ linePoints: {
2296
2539
  start: {
2297
- x: ctx.changes.rect.origin.x + (orig.linePoints.start.x - oldRect.origin.x) * scaleX,
2298
- y: ctx.changes.rect.origin.y + (orig.linePoints.start.y - oldRect.origin.y) * scaleY
2540
+ x: resolvedRect.origin.x + (orig.linePoints.start.x - oldRect.origin.x) * scaleX,
2541
+ y: resolvedRect.origin.y + (orig.linePoints.start.y - oldRect.origin.y) * scaleY
2299
2542
  },
2300
2543
  end: {
2301
- x: ctx.changes.rect.origin.x + (orig.linePoints.end.x - oldRect.origin.x) * scaleX,
2302
- y: ctx.changes.rect.origin.y + (orig.linePoints.end.y - oldRect.origin.y) * scaleY
2544
+ x: resolvedRect.origin.x + (orig.linePoints.end.x - oldRect.origin.x) * scaleX,
2545
+ y: resolvedRect.origin.y + (orig.linePoints.end.y - oldRect.origin.y) * scaleY
2303
2546
  }
2304
- };
2547
+ }
2548
+ };
2549
+ }
2550
+ case "rotate": {
2551
+ const result = baseRotateChanges(orig, ctx);
2552
+ if (!result) return ctx.changes;
2553
+ const { dx, dy } = rotateOrbitDelta(orig, result);
2554
+ return {
2555
+ ...result,
2556
+ linePoints: {
2557
+ start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
2558
+ end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
2559
+ }
2560
+ };
2561
+ }
2562
+ case "property-update": {
2563
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0 || ctx.changes.rotation !== void 0;
2564
+ if (!needsRectUpdate) return ctx.changes;
2565
+ const merged = { ...orig, ...ctx.changes };
2566
+ const tightRect = lineRectWithEndings(
2567
+ [merged.linePoints.start, merged.linePoints.end],
2568
+ merged.strokeWidth,
2569
+ merged.lineEndings
2570
+ );
2571
+ const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
2572
+ if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
2305
2573
  return {
2306
- rect: ctx.changes.rect,
2307
- linePoints: newLinePoints
2574
+ ...ctx.changes,
2575
+ unrotatedRect: tightRect,
2576
+ rect: calculateRotatedRectAABBAroundPoint(
2577
+ tightRect,
2578
+ effectiveRotation,
2579
+ resolveAnnotationRotationCenter(orig)
2580
+ )
2308
2581
  };
2309
2582
  }
2310
- return ctx.changes;
2311
- case "property-update":
2312
- if (ctx.changes.strokeWidth || ctx.changes.lineEndings) {
2313
- const merged = { ...orig, ...ctx.changes };
2314
- const rect = lineRectWithEndings(
2315
- [merged.linePoints.start, merged.linePoints.end],
2316
- merged.strokeWidth,
2317
- merged.lineEndings
2318
- );
2319
- return { ...ctx.changes, rect };
2320
- }
2321
- return ctx.changes;
2583
+ return { ...ctx.changes, rect: tightRect };
2584
+ }
2322
2585
  default:
2323
2586
  return ctx.changes;
2324
2587
  }
2325
2588
  };
2326
2589
  const patchPolyline = (orig, ctx) => {
2327
- var _a;
2328
2590
  switch (ctx.type) {
2329
2591
  case "vertex-edit":
2330
2592
  if (ctx.changes.vertices && ctx.changes.vertices.length) {
2593
+ const rawVertices = ctx.changes.vertices;
2594
+ const rawRect = lineRectWithEndings(rawVertices, orig.strokeWidth, orig.lineEndings);
2595
+ const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
2596
+ const rect = lineRectWithEndings(compensated, orig.strokeWidth, orig.lineEndings);
2331
2597
  return {
2332
- rect: lineRectWithEndings(ctx.changes.vertices, orig.strokeWidth, orig.lineEndings),
2333
- vertices: ctx.changes.vertices
2334
- };
2335
- }
2336
- return ctx.changes;
2337
- case "move":
2338
- if (ctx.changes.rect) {
2339
- const dx = ctx.changes.rect.origin.x - orig.rect.origin.x;
2340
- const dy = ctx.changes.rect.origin.y - orig.rect.origin.y;
2341
- const moved = orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }));
2342
- return {
2343
- rect: ctx.changes.rect,
2344
- vertices: moved
2598
+ ...resolveVertexEditRects(orig, rect),
2599
+ vertices: compensated
2345
2600
  };
2346
2601
  }
2347
2602
  return ctx.changes;
2348
- case "resize":
2349
- if (ctx.changes.rect) {
2350
- const oldRect = orig.rect;
2351
- const newRect = ctx.changes.rect;
2352
- let scaleX = newRect.size.width / oldRect.size.width;
2353
- let scaleY = newRect.size.height / oldRect.size.height;
2354
- const minSize = 10;
2355
- if (newRect.size.width < minSize || newRect.size.height < minSize) {
2356
- scaleX = Math.max(scaleX, minSize / oldRect.size.width);
2357
- scaleY = Math.max(scaleY, minSize / oldRect.size.height);
2358
- ctx.changes.rect = {
2359
- origin: newRect.origin,
2360
- size: {
2361
- width: oldRect.size.width * scaleX,
2362
- height: oldRect.size.height * scaleY
2363
- }
2364
- };
2365
- }
2366
- if ((_a = ctx.metadata) == null ? void 0 : _a.maintainAspectRatio) {
2367
- const minScale = Math.min(scaleX, scaleY);
2368
- scaleX = minScale;
2369
- scaleY = minScale;
2370
- ctx.changes.rect.size = {
2371
- width: oldRect.size.width * minScale,
2372
- height: oldRect.size.height * minScale
2373
- };
2374
- }
2375
- const scaledVertices = orig.vertices.map((vertex) => ({
2376
- x: ctx.changes.rect.origin.x + (vertex.x - oldRect.origin.x) * scaleX,
2377
- y: ctx.changes.rect.origin.y + (vertex.y - oldRect.origin.y) * scaleY
2378
- }));
2603
+ case "move": {
2604
+ if (!ctx.changes.rect) return ctx.changes;
2605
+ const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
2606
+ return {
2607
+ ...rects,
2608
+ vertices: orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }))
2609
+ };
2610
+ }
2611
+ case "resize": {
2612
+ if (!ctx.changes.rect) return ctx.changes;
2613
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
2614
+ orig,
2615
+ ctx.changes.rect,
2616
+ ctx.metadata
2617
+ );
2618
+ return {
2619
+ ...rects,
2620
+ vertices: orig.vertices.map((v) => ({
2621
+ x: resolvedRect.origin.x + (v.x - oldRect.origin.x) * scaleX,
2622
+ y: resolvedRect.origin.y + (v.y - oldRect.origin.y) * scaleY
2623
+ }))
2624
+ };
2625
+ }
2626
+ case "rotate": {
2627
+ const result = baseRotateChanges(orig, ctx);
2628
+ if (!result) return ctx.changes;
2629
+ const { dx, dy } = rotateOrbitDelta(orig, result);
2630
+ return {
2631
+ ...result,
2632
+ vertices: orig.vertices.map((v) => ({ x: v.x + dx, y: v.y + dy }))
2633
+ };
2634
+ }
2635
+ case "property-update": {
2636
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0 || ctx.changes.rotation !== void 0;
2637
+ if (!needsRectUpdate) return ctx.changes;
2638
+ const merged = { ...orig, ...ctx.changes };
2639
+ const tightRect = lineRectWithEndings(
2640
+ merged.vertices,
2641
+ merged.strokeWidth,
2642
+ merged.lineEndings
2643
+ );
2644
+ const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
2645
+ if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
2379
2646
  return {
2380
- rect: ctx.changes.rect,
2381
- vertices: scaledVertices
2647
+ ...ctx.changes,
2648
+ unrotatedRect: tightRect,
2649
+ rect: calculateRotatedRectAABBAroundPoint(
2650
+ tightRect,
2651
+ effectiveRotation,
2652
+ resolveAnnotationRotationCenter(orig)
2653
+ )
2382
2654
  };
2383
2655
  }
2384
- return ctx.changes;
2385
- case "property-update":
2386
- if (ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0) {
2387
- const merged = { ...orig, ...ctx.changes };
2388
- const rect = lineRectWithEndings(merged.vertices, merged.strokeWidth, merged.lineEndings);
2389
- return { ...ctx.changes, rect };
2390
- }
2391
- return ctx.changes;
2656
+ return { ...ctx.changes, rect: tightRect };
2657
+ }
2392
2658
  default:
2393
2659
  return ctx.changes;
2394
2660
  }
2395
2661
  };
2396
2662
  const patchPolygon = (orig, ctx) => {
2397
- var _a;
2398
2663
  switch (ctx.type) {
2399
2664
  case "vertex-edit":
2400
2665
  if (ctx.changes.vertices && ctx.changes.vertices.length) {
2401
2666
  const pad = orig.strokeWidth / 2;
2667
+ const rawVertices = ctx.changes.vertices;
2668
+ const rawRect = expandRect(rectFromPoints(rawVertices), pad);
2669
+ const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
2670
+ const rect = expandRect(rectFromPoints(compensated), pad);
2402
2671
  return {
2403
- rect: expandRect(rectFromPoints(ctx.changes.vertices), pad),
2404
- vertices: ctx.changes.vertices
2672
+ ...resolveVertexEditRects(orig, rect),
2673
+ vertices: compensated
2405
2674
  };
2406
2675
  }
2407
2676
  return ctx.changes;
2408
- case "move":
2409
- if (ctx.changes.rect) {
2410
- const dx = ctx.changes.rect.origin.x - orig.rect.origin.x;
2411
- const dy = ctx.changes.rect.origin.y - orig.rect.origin.y;
2412
- const moved = orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }));
2677
+ case "move": {
2678
+ if (!ctx.changes.rect) return ctx.changes;
2679
+ const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
2680
+ return {
2681
+ ...rects,
2682
+ vertices: orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }))
2683
+ };
2684
+ }
2685
+ case "resize": {
2686
+ if (!ctx.changes.rect) return ctx.changes;
2687
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
2688
+ orig,
2689
+ ctx.changes.rect,
2690
+ ctx.metadata
2691
+ );
2692
+ return {
2693
+ ...rects,
2694
+ vertices: orig.vertices.map((v) => ({
2695
+ x: resolvedRect.origin.x + (v.x - oldRect.origin.x) * scaleX,
2696
+ y: resolvedRect.origin.y + (v.y - oldRect.origin.y) * scaleY
2697
+ }))
2698
+ };
2699
+ }
2700
+ case "rotate": {
2701
+ const result = baseRotateChanges(orig, ctx);
2702
+ if (!result) return ctx.changes;
2703
+ const { dx, dy } = rotateOrbitDelta(orig, result);
2704
+ return {
2705
+ ...result,
2706
+ vertices: orig.vertices.map((v) => ({ x: v.x + dx, y: v.y + dy }))
2707
+ };
2708
+ }
2709
+ case "property-update": {
2710
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0;
2711
+ if (!needsRectUpdate) return ctx.changes;
2712
+ const merged = { ...orig, ...ctx.changes };
2713
+ const pad = merged.strokeWidth / 2;
2714
+ const tightRect = expandRect(rectFromPoints(merged.vertices), pad);
2715
+ const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
2716
+ if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
2413
2717
  return {
2414
- rect: ctx.changes.rect,
2415
- vertices: moved
2718
+ ...ctx.changes,
2719
+ unrotatedRect: tightRect,
2720
+ rect: calculateRotatedRectAABBAroundPoint(
2721
+ tightRect,
2722
+ effectiveRotation,
2723
+ resolveAnnotationRotationCenter(orig)
2724
+ )
2416
2725
  };
2417
2726
  }
2727
+ return { ...ctx.changes, rect: tightRect };
2728
+ }
2729
+ default:
2418
2730
  return ctx.changes;
2731
+ }
2732
+ };
2733
+ const patchCircle = (orig, ctx) => {
2734
+ switch (ctx.type) {
2735
+ case "move":
2736
+ if (!ctx.changes.rect) return ctx.changes;
2737
+ return baseMoveChanges(orig, ctx.changes.rect).rects;
2419
2738
  case "resize":
2420
- if (ctx.changes.rect) {
2421
- const oldRect = orig.rect;
2422
- const newRect = ctx.changes.rect;
2423
- let scaleX = newRect.size.width / oldRect.size.width;
2424
- let scaleY = newRect.size.height / oldRect.size.height;
2425
- const minSize = 10;
2426
- if (newRect.size.width < minSize || newRect.size.height < minSize) {
2427
- scaleX = Math.max(scaleX, minSize / oldRect.size.width);
2428
- scaleY = Math.max(scaleY, minSize / oldRect.size.height);
2429
- ctx.changes.rect = {
2430
- origin: newRect.origin,
2431
- size: {
2432
- width: oldRect.size.width * scaleX,
2433
- height: oldRect.size.height * scaleY
2434
- }
2435
- };
2436
- }
2437
- if ((_a = ctx.metadata) == null ? void 0 : _a.maintainAspectRatio) {
2438
- const minScale = Math.min(scaleX, scaleY);
2439
- scaleX = minScale;
2440
- scaleY = minScale;
2441
- ctx.changes.rect.size = {
2442
- width: oldRect.size.width * minScale,
2443
- height: oldRect.size.height * minScale
2444
- };
2445
- }
2446
- const scaledVertices = orig.vertices.map((vertex) => ({
2447
- x: ctx.changes.rect.origin.x + (vertex.x - oldRect.origin.x) * scaleX,
2448
- y: ctx.changes.rect.origin.y + (vertex.y - oldRect.origin.y) * scaleY
2449
- }));
2450
- return {
2451
- rect: ctx.changes.rect,
2452
- vertices: scaledVertices
2453
- };
2739
+ if (!ctx.changes.rect) return ctx.changes;
2740
+ return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
2741
+ case "rotate":
2742
+ return baseRotateChanges(orig, ctx) ?? ctx.changes;
2743
+ case "property-update":
2744
+ if (ctx.changes.rotation !== void 0) {
2745
+ return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
2746
+ }
2747
+ return ctx.changes;
2748
+ default:
2749
+ return ctx.changes;
2750
+ }
2751
+ };
2752
+ const patchSquare = (orig, ctx) => {
2753
+ switch (ctx.type) {
2754
+ case "move":
2755
+ if (!ctx.changes.rect) return ctx.changes;
2756
+ return baseMoveChanges(orig, ctx.changes.rect).rects;
2757
+ case "resize":
2758
+ if (!ctx.changes.rect) return ctx.changes;
2759
+ return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
2760
+ case "rotate":
2761
+ return baseRotateChanges(orig, ctx) ?? ctx.changes;
2762
+ case "property-update":
2763
+ if (ctx.changes.rotation !== void 0) {
2764
+ return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
2454
2765
  }
2455
2766
  return ctx.changes;
2767
+ default:
2768
+ return ctx.changes;
2769
+ }
2770
+ };
2771
+ const patchFreeText = (orig, ctx) => {
2772
+ switch (ctx.type) {
2773
+ case "move":
2774
+ if (!ctx.changes.rect) return ctx.changes;
2775
+ return baseMoveChanges(orig, ctx.changes.rect).rects;
2776
+ case "resize":
2777
+ if (!ctx.changes.rect) return ctx.changes;
2778
+ return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
2779
+ case "rotate":
2780
+ return baseRotateChanges(orig, ctx) ?? ctx.changes;
2781
+ case "property-update":
2782
+ if (ctx.changes.rotation !== void 0) {
2783
+ return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
2784
+ }
2785
+ return ctx.changes;
2786
+ default:
2787
+ return ctx.changes;
2788
+ }
2789
+ };
2790
+ const patchStamp = (orig, ctx) => {
2791
+ switch (ctx.type) {
2792
+ case "move":
2793
+ if (!ctx.changes.rect) return ctx.changes;
2794
+ return baseMoveChanges(orig, ctx.changes.rect).rects;
2795
+ case "resize":
2796
+ if (!ctx.changes.rect) return ctx.changes;
2797
+ return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
2798
+ case "rotate":
2799
+ return baseRotateChanges(orig, ctx) ?? ctx.changes;
2456
2800
  case "property-update":
2457
- if (ctx.changes.strokeWidth !== void 0) {
2458
- const merged = { ...orig, ...ctx.changes };
2459
- const pad = merged.strokeWidth / 2;
2460
- const rect = expandRect(rectFromPoints(merged.vertices), pad);
2461
- return { ...ctx.changes, rect };
2801
+ if (ctx.changes.rotation !== void 0) {
2802
+ return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
2462
2803
  }
2463
2804
  return ctx.changes;
2464
2805
  default:
2465
2806
  return ctx.changes;
2466
2807
  }
2467
2808
  };
2809
+ function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
2810
+ const theta = rotationDegrees * Math.PI / 180;
2811
+ const A = Math.abs(Math.cos(theta));
2812
+ const B = Math.abs(Math.sin(theta));
2813
+ const det = A * A - B * B;
2814
+ const newAABBw = newAABBRect.size.width;
2815
+ const newAABBh = newAABBRect.size.height;
2816
+ let newWidth;
2817
+ let newHeight;
2818
+ if (Math.abs(det) > 1e-6) {
2819
+ newWidth = (A * newAABBw - B * newAABBh) / det;
2820
+ newHeight = (A * newAABBh - B * newAABBw) / det;
2821
+ newWidth = Math.max(newWidth, 1);
2822
+ newHeight = Math.max(newHeight, 1);
2823
+ } else {
2824
+ const origArea = originalAABBRect.size.width * originalAABBRect.size.height;
2825
+ const newArea = newAABBw * newAABBh;
2826
+ const uniformScale = origArea > 0 ? Math.sqrt(newArea / origArea) : 1;
2827
+ newWidth = originalUnrotatedRect.size.width * uniformScale;
2828
+ newHeight = originalUnrotatedRect.size.height * uniformScale;
2829
+ }
2830
+ const newCenterX = newAABBRect.origin.x + newAABBw / 2;
2831
+ const newCenterY = newAABBRect.origin.y + newAABBh / 2;
2832
+ return {
2833
+ origin: { x: newCenterX - newWidth / 2, y: newCenterY - newHeight / 2 },
2834
+ size: { width: newWidth, height: newHeight }
2835
+ };
2836
+ }
2468
2837
  const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2469
2838
  constructor(id, registry, config) {
2470
2839
  var _a, _b, _c;
@@ -2484,6 +2853,8 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2484
2853
  this.unifiedDrag$ = createBehaviorEmitter();
2485
2854
  this.unifiedResizeStates = /* @__PURE__ */ new Map();
2486
2855
  this.unifiedResize$ = createBehaviorEmitter();
2856
+ this.unifiedRotateStates = /* @__PURE__ */ new Map();
2857
+ this.unifiedRotate$ = createBehaviorEmitter();
2487
2858
  this.config = config;
2488
2859
  this.selection = ((_a = registry.getPlugin("selection")) == null ? void 0 : _a.provides()) ?? null;
2489
2860
  this.history = ((_b = registry.getPlugin("history")) == null ? void 0 : _b.provides()) ?? null;
@@ -2513,7 +2884,11 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2513
2884
  if (this.selection) {
2514
2885
  for (const tool of this.state.tools) {
2515
2886
  if (tool.interaction.textSelection) {
2516
- this.selection.enableForMode(tool.interaction.mode ?? tool.id, { showRects: false });
2887
+ this.selection.enableForMode(tool.interaction.mode ?? tool.id, {
2888
+ showSelectionRects: false,
2889
+ enableSelection: true,
2890
+ enableMarquee: false
2891
+ });
2517
2892
  }
2518
2893
  }
2519
2894
  }
@@ -2544,6 +2919,10 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2544
2919
  this.patchRegistry.register(PdfAnnotationSubtype.LINE, patchLine);
2545
2920
  this.patchRegistry.register(PdfAnnotationSubtype.POLYLINE, patchPolyline);
2546
2921
  this.patchRegistry.register(PdfAnnotationSubtype.POLYGON, patchPolygon);
2922
+ this.patchRegistry.register(PdfAnnotationSubtype.CIRCLE, patchCircle);
2923
+ this.patchRegistry.register(PdfAnnotationSubtype.SQUARE, patchSquare);
2924
+ this.patchRegistry.register(PdfAnnotationSubtype.FREETEXT, patchFreeText);
2925
+ this.patchRegistry.register(PdfAnnotationSubtype.STAMP, patchStamp);
2547
2926
  }
2548
2927
  async initialize() {
2549
2928
  var _a, _b, _c;
@@ -2563,7 +2942,8 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2563
2942
  this.dispatch(setActiveToolId(s.documentId, newToolId));
2564
2943
  }
2565
2944
  });
2566
- (_b = this.selection) == null ? void 0 : _b.onMarqueeEnd(({ documentId, pageIndex, rect }) => {
2945
+ (_b = this.selection) == null ? void 0 : _b.onMarqueeEnd(({ documentId, pageIndex, rect, modeId }) => {
2946
+ if (modeId !== "pointerMode") return;
2567
2947
  const docState = this.state.documents[documentId];
2568
2948
  if (!docState) return;
2569
2949
  const pageAnnotations = (docState.pages[pageIndex] ?? []).map((uid) => docState.byUid[uid]).filter((ta) => ta !== void 0).filter((ta) => !isLink(ta));
@@ -2595,6 +2975,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2595
2975
  if (!formattedSelection || !selectionText) return;
2596
2976
  for (const selection of formattedSelection) {
2597
2977
  selectionText.wait((text) => {
2978
+ var _a3, _b3;
2598
2979
  const annotationId = uuidV4();
2599
2980
  this.createAnnotation(
2600
2981
  selection.pageIndex,
@@ -2612,10 +2993,10 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2612
2993
  void 0,
2613
2994
  documentId
2614
2995
  );
2615
- if (this.getToolBehavior(activeTool, "deactivateToolAfterCreate")) {
2996
+ if ((_a3 = activeTool.behavior) == null ? void 0 : _a3.deactivateToolAfterCreate) {
2616
2997
  this.setActiveTool(null, documentId);
2617
2998
  }
2618
- if (this.getToolBehavior(activeTool, "selectAfterCreate")) {
2999
+ if ((_b3 = activeTool.behavior) == null ? void 0 : _b3.selectAfterCreate) {
2619
3000
  this.selectAnnotation(selection.pageIndex, annotationId, documentId);
2620
3001
  }
2621
3002
  }, ignore);
@@ -2752,6 +3133,9 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2752
3133
  tool: this.getActiveTool(documentId)
2753
3134
  });
2754
3135
  }
3136
+ if ((prevDoc == null ? void 0 : prevDoc.selectedUids) !== nextDoc.selectedUids) {
3137
+ this.updateAnnotationSelectionActivity(documentId, nextDoc);
3138
+ }
2755
3139
  }
2756
3140
  }
2757
3141
  if (prev.tools !== next.tools) {
@@ -2786,6 +3170,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2786
3170
  if (!this.interactionManager) return () => {
2787
3171
  };
2788
3172
  const unregisterFns = [];
3173
+ const effectivePageRotation = ((page.rotation ?? 0) + ((docState == null ? void 0 : docState.rotation) ?? 0)) % 4;
2789
3174
  for (const tool of this.state.tools) {
2790
3175
  if (!tool.defaults.type) continue;
2791
3176
  const factory = this.handlerFactories.get(tool.defaults.type);
@@ -2793,16 +3178,18 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2793
3178
  const context = {
2794
3179
  pageIndex,
2795
3180
  pageSize: page.size,
3181
+ pageRotation: effectivePageRotation,
2796
3182
  scale,
2797
3183
  services: callbacks.services,
2798
3184
  // Pass through services
2799
3185
  onPreview: (state) => callbacks.onPreview(tool.id, state),
2800
3186
  onCommit: (annotation, ctx) => {
3187
+ var _a2, _b;
2801
3188
  this.createAnnotation(pageIndex, annotation, ctx, documentId);
2802
- if (this.getToolBehavior(tool, "deactivateToolAfterCreate")) {
3189
+ if ((_a2 = tool.behavior) == null ? void 0 : _a2.deactivateToolAfterCreate) {
2803
3190
  this.setActiveTool(null, documentId);
2804
3191
  }
2805
- if (this.getToolBehavior(tool, "selectAfterCreate")) {
3192
+ if ((_b = tool.behavior) == null ? void 0 : _b.selectAfterCreate) {
2806
3193
  this.selectAnnotation(pageIndex, annotation.id, documentId);
2807
3194
  }
2808
3195
  },
@@ -3125,6 +3512,27 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3125
3512
  const docId = documentId ?? this.getActiveDocumentId();
3126
3513
  this.dispatch(deselectAnnotation(docId));
3127
3514
  }
3515
+ /**
3516
+ * Derive page activity from the current annotation selection.
3517
+ * Called from onStoreUpdated whenever selectedUids changes,
3518
+ * so ALL selection code paths are covered automatically.
3519
+ */
3520
+ updateAnnotationSelectionActivity(docId, docState) {
3521
+ var _a, _b;
3522
+ if (docState.selectedUids.length === 0) {
3523
+ (_a = this.interactionManager) == null ? void 0 : _a.releasePageActivity(docId, "annotation-selection");
3524
+ return;
3525
+ }
3526
+ const firstUid = docState.selectedUids[0];
3527
+ const ta = docState.byUid[firstUid];
3528
+ if (ta) {
3529
+ (_b = this.interactionManager) == null ? void 0 : _b.claimPageActivity(
3530
+ docId,
3531
+ "annotation-selection",
3532
+ ta.object.pageIndex
3533
+ );
3534
+ }
3535
+ }
3128
3536
  // ─────────────────────────────────────────────────────────
3129
3537
  // Multi-Select Methods
3130
3538
  // ─────────────────────────────────────────────────────────
@@ -3505,6 +3913,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3505
3913
  return annotations.map((anno) => ({
3506
3914
  id: anno.id,
3507
3915
  originalRect: anno.rect,
3916
+ originalUnrotatedRect: anno.unrotatedRect,
3508
3917
  pageIndex: anno.pageIndex,
3509
3918
  isAttachedLink: anno.isAttachedLink,
3510
3919
  parentId: anno.parentId,
@@ -3541,12 +3950,24 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3541
3950
  */
3542
3951
  computeResizePreviewPatches(computedRects, documentId) {
3543
3952
  const previewPatches = {};
3953
+ const state = this.unifiedResizeStates.get(documentId);
3954
+ const participantMap = state ? new Map(state.participatingAnnotations.map((p) => [p.id, p])) : void 0;
3544
3955
  for (const [id, newRect] of computedRects) {
3545
3956
  const ta = this.getAnnotationById(id, documentId);
3546
3957
  if (!ta) continue;
3958
+ let targetRect = newRect;
3959
+ const info = participantMap == null ? void 0 : participantMap.get(id);
3960
+ if ((state == null ? void 0 : state.isGroupResize) && (info == null ? void 0 : info.originalUnrotatedRect)) {
3961
+ targetRect = convertAABBRectToUnrotatedSpace(
3962
+ newRect,
3963
+ info.originalRect,
3964
+ info.originalUnrotatedRect,
3965
+ ta.object.rotation ?? 0
3966
+ );
3967
+ }
3547
3968
  previewPatches[id] = this.transformAnnotation(ta.object, {
3548
3969
  type: "resize",
3549
- changes: { rect: newRect }
3970
+ changes: { rect: targetRect }
3550
3971
  });
3551
3972
  }
3552
3973
  return previewPatches;
@@ -3568,6 +3989,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3568
3989
  annotationsWithLinks.push({
3569
3990
  id,
3570
3991
  rect: ta.object.rect,
3992
+ unrotatedRect: ta.object.unrotatedRect ? this.cloneRect(ta.object.unrotatedRect) : void 0,
3571
3993
  pageIndex: ta.object.pageIndex,
3572
3994
  isAttachedLink: false
3573
3995
  });
@@ -3578,6 +4000,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3578
4000
  annotationsWithLinks.push({
3579
4001
  id: link.object.id,
3580
4002
  rect: link.object.rect,
4003
+ unrotatedRect: link.object.unrotatedRect ? this.cloneRect(link.object.unrotatedRect) : void 0,
3581
4004
  pageIndex: link.object.pageIndex,
3582
4005
  isAttachedLink: true,
3583
4006
  parentId: id
@@ -3597,6 +4020,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3597
4020
  const state = {
3598
4021
  documentId,
3599
4022
  isResizing: true,
4023
+ isGroupResize: annotationIds.length > 1,
3600
4024
  primaryIds: annotationIds,
3601
4025
  attachedLinkIds,
3602
4026
  allParticipantIds,
@@ -3663,12 +4087,23 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3663
4087
  state.currentGroupBox
3664
4088
  );
3665
4089
  const patches = [];
4090
+ const participantMap = new Map(state.participatingAnnotations.map((p) => [p.id, p]));
3666
4091
  for (const [id, newRect] of computedRects) {
3667
4092
  const ta = this.getAnnotationById(id, documentId);
3668
4093
  if (!ta) continue;
4094
+ let targetRect = newRect;
4095
+ const info = participantMap.get(id);
4096
+ if (state.isGroupResize && (info == null ? void 0 : info.originalUnrotatedRect)) {
4097
+ targetRect = convertAABBRectToUnrotatedSpace(
4098
+ newRect,
4099
+ info.originalRect,
4100
+ info.originalUnrotatedRect,
4101
+ ta.object.rotation ?? 0
4102
+ );
4103
+ }
3669
4104
  const patch = this.transformAnnotation(ta.object, {
3670
4105
  type: "resize",
3671
- changes: { rect: newRect }
4106
+ changes: { rect: targetRect }
3672
4107
  });
3673
4108
  patches.push({ pageIndex: ta.object.pageIndex, id, patch });
3674
4109
  }
@@ -3719,6 +4154,206 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
3719
4154
  get onResizeChange() {
3720
4155
  return this.unifiedResize$.on;
3721
4156
  }
4157
+ // ─────────────────────────────────────────────────────────
4158
+ // Unified Rotation API (Plugin owns all rotation logic)
4159
+ // ─────────────────────────────────────────────────────────
4160
+ cloneRect(rect) {
4161
+ return {
4162
+ origin: { x: rect.origin.x, y: rect.origin.y },
4163
+ size: { width: rect.size.width, height: rect.size.height }
4164
+ };
4165
+ }
4166
+ translateRect(rect, delta) {
4167
+ return {
4168
+ origin: {
4169
+ x: rect.origin.x + delta.x,
4170
+ y: rect.origin.y + delta.y
4171
+ },
4172
+ size: { ...rect.size }
4173
+ };
4174
+ }
4175
+ normalizeAngle(angle) {
4176
+ const normalized = angle % 360;
4177
+ return normalized < 0 ? normalized + 360 : normalized;
4178
+ }
4179
+ normalizeDelta(delta) {
4180
+ const normalized = (delta + 540) % 360 - 180;
4181
+ return normalized;
4182
+ }
4183
+ buildRotationParticipants(annotationIds, documentId) {
4184
+ const participants = [];
4185
+ const attachedLinkIds = [];
4186
+ for (const id of annotationIds) {
4187
+ const ta = this.getAnnotationById(id, documentId);
4188
+ if (!ta) continue;
4189
+ participants.push({
4190
+ id,
4191
+ rect: this.cloneRect(ta.object.rect),
4192
+ pageIndex: ta.object.pageIndex,
4193
+ rotation: ta.object.rotation ?? 0,
4194
+ unrotatedRect: ta.object.unrotatedRect ? this.cloneRect(ta.object.unrotatedRect) : void 0,
4195
+ isAttachedLink: false
4196
+ });
4197
+ const links = this.getAttachedLinksMethod(id, documentId);
4198
+ for (const link of links) {
4199
+ if (attachedLinkIds.includes(link.object.id)) continue;
4200
+ attachedLinkIds.push(link.object.id);
4201
+ participants.push({
4202
+ id: link.object.id,
4203
+ rect: this.cloneRect(link.object.rect),
4204
+ pageIndex: link.object.pageIndex,
4205
+ rotation: link.object.rotation ?? 0,
4206
+ unrotatedRect: link.object.unrotatedRect ? this.cloneRect(link.object.unrotatedRect) : void 0,
4207
+ isAttachedLink: true,
4208
+ parentId: id
4209
+ });
4210
+ }
4211
+ }
4212
+ return { participants, attachedLinkIds };
4213
+ }
4214
+ computeRotatePreviewPatches(state, documentId) {
4215
+ const preview = {};
4216
+ for (const participant of state.participants) {
4217
+ const ta = this.getAnnotationById(participant.id, documentId);
4218
+ if (!ta) continue;
4219
+ const originalCenter = resolveAnnotationRotationCenter({
4220
+ rect: participant.rect,
4221
+ unrotatedRect: participant.unrotatedRect,
4222
+ rotation: participant.rotation
4223
+ });
4224
+ const rotatedCenter = rotatePointAround(
4225
+ originalCenter,
4226
+ state.rotationCenter,
4227
+ state.delta
4228
+ );
4229
+ const translation = {
4230
+ x: rotatedCenter.x - originalCenter.x,
4231
+ y: rotatedCenter.y - originalCenter.y
4232
+ };
4233
+ const nextRotation = this.normalizeAngle(participant.rotation + state.delta);
4234
+ const patch = this.transformAnnotation(ta.object, {
4235
+ type: "rotate",
4236
+ changes: {
4237
+ rotation: nextRotation,
4238
+ unrotatedRect: this.translateRect(
4239
+ participant.unrotatedRect ?? participant.rect,
4240
+ translation
4241
+ )
4242
+ },
4243
+ metadata: {
4244
+ rotationAngle: nextRotation,
4245
+ rotationDelta: state.delta,
4246
+ rotationCenter: state.rotationCenter
4247
+ }
4248
+ });
4249
+ if (!patch.rect && (translation.x !== 0 || translation.y !== 0)) {
4250
+ patch.rect = {
4251
+ origin: {
4252
+ x: ta.object.rect.origin.x + translation.x,
4253
+ y: ta.object.rect.origin.y + translation.y
4254
+ },
4255
+ size: { ...ta.object.rect.size }
4256
+ };
4257
+ }
4258
+ preview[participant.id] = patch;
4259
+ }
4260
+ return preview;
4261
+ }
4262
+ startRotation(documentId, options) {
4263
+ const { annotationIds, cursorAngle, rotationCenter } = options;
4264
+ const { participants, attachedLinkIds } = this.buildRotationParticipants(
4265
+ annotationIds,
4266
+ documentId
4267
+ );
4268
+ if (participants.length === 0) return;
4269
+ const rects = participants.map((p) => p.rect);
4270
+ const groupBox = this.computeUnifiedGroupBoundingBox(rects);
4271
+ const center = rotationCenter ?? {
4272
+ x: groupBox.origin.x + groupBox.size.width / 2,
4273
+ y: groupBox.origin.y + groupBox.size.height / 2
4274
+ };
4275
+ const state = {
4276
+ documentId,
4277
+ isRotating: true,
4278
+ primaryIds: annotationIds,
4279
+ attachedLinkIds,
4280
+ allParticipantIds: participants.map((p) => p.id),
4281
+ rotationCenter: center,
4282
+ cursorStartAngle: cursorAngle,
4283
+ currentAngle: cursorAngle,
4284
+ delta: 0,
4285
+ participants
4286
+ };
4287
+ this.unifiedRotateStates.set(documentId, state);
4288
+ const previewPatches = this.computeRotatePreviewPatches(state, documentId);
4289
+ this.unifiedRotate$.emit({
4290
+ documentId,
4291
+ type: "start",
4292
+ state,
4293
+ previewPatches
4294
+ });
4295
+ }
4296
+ updateRotation(documentId, cursorAngle, rotationDelta) {
4297
+ const state = this.unifiedRotateStates.get(documentId);
4298
+ if (!(state == null ? void 0 : state.isRotating)) {
4299
+ return;
4300
+ }
4301
+ const delta = rotationDelta !== void 0 ? rotationDelta : this.normalizeDelta(cursorAngle - state.cursorStartAngle);
4302
+ const newState = {
4303
+ ...state,
4304
+ currentAngle: cursorAngle,
4305
+ delta
4306
+ };
4307
+ this.unifiedRotateStates.set(documentId, newState);
4308
+ const previewPatches = this.computeRotatePreviewPatches(newState, documentId);
4309
+ this.unifiedRotate$.emit({
4310
+ documentId,
4311
+ type: "update",
4312
+ state: newState,
4313
+ previewPatches
4314
+ });
4315
+ }
4316
+ commitRotation(documentId) {
4317
+ const state = this.unifiedRotateStates.get(documentId);
4318
+ if (!state) return;
4319
+ const previewPatches = this.computeRotatePreviewPatches(state, documentId);
4320
+ const patches = [];
4321
+ for (const [id, patch] of Object.entries(previewPatches)) {
4322
+ const ta = this.getAnnotationById(id, documentId);
4323
+ if (!ta) continue;
4324
+ patches.push({ pageIndex: ta.object.pageIndex, id, patch });
4325
+ }
4326
+ if (patches.length > 0) {
4327
+ this.updateAnnotationsMethod(patches, documentId);
4328
+ }
4329
+ this.unifiedRotate$.emit({
4330
+ documentId,
4331
+ type: "end",
4332
+ state: { ...state, isRotating: false },
4333
+ previewPatches
4334
+ });
4335
+ this.unifiedRotateStates.delete(documentId);
4336
+ }
4337
+ cancelRotation(documentId) {
4338
+ const state = this.unifiedRotateStates.get(documentId);
4339
+ if (!state) return;
4340
+ this.unifiedRotate$.emit({
4341
+ documentId,
4342
+ type: "cancel",
4343
+ state: { ...state, isRotating: false, delta: 0, currentAngle: state.cursorStartAngle },
4344
+ previewPatches: {}
4345
+ });
4346
+ this.unifiedRotateStates.delete(documentId);
4347
+ }
4348
+ getRotateState(documentId) {
4349
+ return this.unifiedRotateStates.get(documentId) ?? null;
4350
+ }
4351
+ /**
4352
+ * Subscribe to unified rotation state changes.
4353
+ */
4354
+ get onRotateChange() {
4355
+ return this.unifiedRotate$.on;
4356
+ }
3722
4357
  updateAnnotationsMethod(patches, documentId) {
3723
4358
  const docId = documentId ?? this.getActiveDocumentId();
3724
4359
  if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
@@ -4011,17 +4646,6 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
4011
4646
  );
4012
4647
  return task;
4013
4648
  }
4014
- /**
4015
- * Gets the effective behavior setting for a tool, checking tool-specific config first,
4016
- * then falling back to plugin config.
4017
- */
4018
- getToolBehavior(tool, setting) {
4019
- var _a;
4020
- if (((_a = tool.behavior) == null ? void 0 : _a[setting]) !== void 0) {
4021
- return tool.behavior[setting];
4022
- }
4023
- return this.config[setting] !== false;
4024
- }
4025
4649
  };
4026
4650
  _AnnotationPlugin.id = "annotation";
4027
4651
  let AnnotationPlugin = _AnnotationPlugin;
@@ -4058,6 +4682,9 @@ export {
4058
4682
  ANNOTATION_PLUGIN_ID,
4059
4683
  AnnotationPlugin,
4060
4684
  AnnotationPluginPackage,
4685
+ calculateRotatedRectAABB2 as calculateRotatedRectAABB,
4686
+ calculateRotatedRectAABBAroundPoint2 as calculateRotatedRectAABBAroundPoint,
4687
+ convertAABBRectToUnrotatedSpace,
4061
4688
  createToolPredicate,
4062
4689
  getAnnotationByUid,
4063
4690
  getAnnotations,
@@ -4067,6 +4694,7 @@ export {
4067
4694
  getGroupMembers,
4068
4695
  getIRTChildIds,
4069
4696
  getIRTChildrenByType,
4697
+ getRectCenter2 as getRectCenter,
4070
4698
  getSelectedAnnotation,
4071
4699
  getSelectedAnnotationByPageIndex,
4072
4700
  getSelectedAnnotationIds,
@@ -4079,6 +4707,7 @@ export {
4079
4707
  hasAttachedLinks,
4080
4708
  hasIRTChildren,
4081
4709
  hasMultipleSelected,
4710
+ inferRotationCenterFromRects2 as inferRotationCenterFromRects,
4082
4711
  initialDocumentState,
4083
4712
  initialState,
4084
4713
  isAnnotationSelected,
@@ -4116,6 +4745,8 @@ export {
4116
4745
  manifest,
4117
4746
  index as patching,
4118
4747
  rectsIntersect,
4119
- resolveInteractionProp
4748
+ resolveInteractionProp,
4749
+ rotatePointAround2 as rotatePointAroundCenter,
4750
+ rotateVertices2 as rotateVertices
4120
4751
  };
4121
4752
  //# sourceMappingURL=index.js.map