@embedpdf/plugin-annotation 2.12.0 → 2.13.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.
package/dist/index.js CHANGED
@@ -893,495 +893,252 @@ function clampAnnotationToPage(annotation, pageSize) {
893
893
  } : {}
894
894
  };
895
895
  }
896
- const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
897
- __proto__: null,
898
- LINE_ENDING_HANDLERS,
899
- PatchRegistry,
900
- applyInsertUpright,
901
- calculateAABBFromVertices,
902
- calculateRotatedRectAABB,
903
- calculateRotatedRectAABBAroundPoint,
904
- clampAnnotationToPage,
905
- compensateRotatedVertexEdit,
906
- createEnding,
907
- getRectCenter,
908
- lineRectWithEndings,
909
- patchRegistry,
910
- resolveAnnotationRotationCenter,
911
- resolveRotateRects,
912
- resolveVertexEditRects,
913
- rotatePointAroundCenter: rotatePointAround,
914
- rotateVertices
915
- }, Symbol.toStringTag, { value: "Module" }));
916
- const COMMENT_SIZE = 24;
917
- const textHandlerFactory = {
918
- annotationType: PdfAnnotationSubtype.TEXT,
919
- create(context) {
920
- const { onCommit, getTool, pageSize } = context;
921
- return {
922
- onPointerDown: (pos) => {
923
- const tool = getTool();
924
- if (!tool) return;
925
- const rect = {
926
- origin: { x: pos.x - COMMENT_SIZE / 2, y: pos.y - COMMENT_SIZE / 2 },
927
- size: { width: COMMENT_SIZE, height: COMMENT_SIZE }
928
- };
929
- let anno = {
930
- ...tool.defaults,
931
- rect,
932
- type: PdfAnnotationSubtype.TEXT,
933
- name: tool.defaults.name ?? PdfAnnotationName.Comment,
934
- contents: tool.defaults.contents ?? "",
935
- flags: tool.defaults.flags ?? ["print", "noRotate", "noZoom"],
936
- pageIndex: context.pageIndex,
937
- id: uuidV4(),
938
- created: /* @__PURE__ */ new Date()
896
+ const patchInk = (original, ctx) => {
897
+ switch (ctx.type) {
898
+ case "vertex-edit":
899
+ return ctx.changes;
900
+ case "move": {
901
+ if (!ctx.changes.rect) return ctx.changes;
902
+ const { dx, dy, rects } = baseMoveChanges(original, ctx.changes.rect);
903
+ return {
904
+ ...rects,
905
+ inkList: original.inkList.map((stroke) => ({
906
+ points: stroke.points.map((p) => ({ x: p.x + dx, y: p.y + dy }))
907
+ }))
908
+ };
909
+ }
910
+ case "resize": {
911
+ if (!ctx.changes.rect) return ctx.changes;
912
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
913
+ original,
914
+ ctx.changes.rect,
915
+ ctx.metadata
916
+ );
917
+ const inset = (r2, pad) => ({
918
+ origin: { x: r2.origin.x + pad, y: r2.origin.y + pad },
919
+ size: {
920
+ width: Math.max(1, r2.size.width - pad * 2),
921
+ height: Math.max(1, r2.size.height - pad * 2)
922
+ }
923
+ });
924
+ const resizeEpsilon = 1e-3;
925
+ const widthChanged = Math.abs(scaleX - 1) > resizeEpsilon;
926
+ const heightChanged = Math.abs(scaleY - 1) > resizeEpsilon;
927
+ const strokeScale = widthChanged && !heightChanged ? scaleX : !widthChanged && heightChanged ? scaleY : Math.min(scaleX, scaleY);
928
+ const rawStrokeWidth = Math.max(1, original.strokeWidth * strokeScale);
929
+ const maxStrokeWidth = Math.max(
930
+ 1,
931
+ Math.min(resolvedRect.size.width, resolvedRect.size.height)
932
+ );
933
+ const clampedStrokeWidth = Math.min(rawStrokeWidth, maxStrokeWidth);
934
+ const newStrokeWidth = Number(clampedStrokeWidth.toFixed(1));
935
+ const innerOld = inset(oldRect, original.strokeWidth / 2);
936
+ const innerNew = inset(resolvedRect, newStrokeWidth / 2);
937
+ const sx = innerNew.size.width / Math.max(innerOld.size.width, 1e-6);
938
+ const sy = innerNew.size.height / Math.max(innerOld.size.height, 1e-6);
939
+ return {
940
+ ...rects,
941
+ inkList: original.inkList.map((stroke) => ({
942
+ points: stroke.points.map((p) => ({
943
+ x: innerNew.origin.x + (p.x - innerOld.origin.x) * sx,
944
+ y: innerNew.origin.y + (p.y - innerOld.origin.y) * sy
945
+ }))
946
+ })),
947
+ strokeWidth: newStrokeWidth
948
+ };
949
+ }
950
+ case "rotate": {
951
+ const result = baseRotateChanges(original, ctx);
952
+ if (!result) return ctx.changes;
953
+ const { dx, dy } = rotateOrbitDelta(original, result);
954
+ return {
955
+ ...result,
956
+ inkList: original.inkList.map((stroke) => ({
957
+ points: stroke.points.map((p) => ({ x: p.x + dx, y: p.y + dy }))
958
+ }))
959
+ };
960
+ }
961
+ case "property-update": {
962
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0;
963
+ if (!needsRectUpdate) return ctx.changes;
964
+ const merged = { ...original, ...ctx.changes };
965
+ const pts = merged.inkList.flatMap((s) => s.points);
966
+ const tightRect = expandRect(rectFromPoints(pts), merged.strokeWidth / 2);
967
+ const effectiveRotation = ctx.changes.rotation ?? original.rotation ?? 0;
968
+ if (original.unrotatedRect || ctx.changes.rotation !== void 0) {
969
+ return {
970
+ ...ctx.changes,
971
+ unrotatedRect: tightRect,
972
+ rect: calculateRotatedRectAABBAroundPoint(
973
+ tightRect,
974
+ effectiveRotation,
975
+ resolveAnnotationRotationCenter(original)
976
+ )
939
977
  };
940
- anno = clampAnnotationToPage(anno, pageSize);
941
- onCommit(anno);
942
978
  }
943
- };
979
+ return { ...ctx.changes, rect: tightRect };
980
+ }
981
+ default:
982
+ return ctx.changes;
944
983
  }
945
984
  };
946
- function useClickDetector({
947
- threshold = 5,
948
- getTool,
949
- onClickDetected
950
- }) {
951
- const [getStartPos, setStartPos] = useState(null);
952
- const [getHasMoved, setHasMoved] = useState(false);
953
- return {
954
- onStart: (pos) => {
955
- setStartPos(pos);
956
- setHasMoved(false);
957
- },
958
- onMove: (pos) => {
959
- const start = getStartPos();
960
- if (!start || getHasMoved()) return;
961
- const distance2 = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
962
- if (distance2 > threshold) {
963
- setHasMoved(true);
985
+ const patchLine = (orig, ctx) => {
986
+ switch (ctx.type) {
987
+ case "vertex-edit":
988
+ if (ctx.changes.linePoints) {
989
+ const { start, end } = ctx.changes.linePoints;
990
+ const rawPoints = [start, end];
991
+ const rawRect = lineRectWithEndings(rawPoints, orig.strokeWidth, orig.lineEndings);
992
+ const compensated = compensateRotatedVertexEdit(orig, rawPoints, rawRect);
993
+ const rect = lineRectWithEndings(compensated, orig.strokeWidth, orig.lineEndings);
994
+ return {
995
+ ...resolveVertexEditRects(orig, rect),
996
+ linePoints: { start: compensated[0], end: compensated[1] }
997
+ };
964
998
  }
965
- },
966
- onEnd: (pos) => {
967
- var _a;
968
- const start = getStartPos();
969
- if (start && !getHasMoved()) {
970
- const tool = getTool();
971
- if (tool && "clickBehavior" in tool && ((_a = tool.clickBehavior) == null ? void 0 : _a.enabled)) {
972
- onClickDetected(pos, tool);
999
+ return ctx.changes;
1000
+ case "move": {
1001
+ if (!ctx.changes.rect) return ctx.changes;
1002
+ const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
1003
+ return {
1004
+ ...rects,
1005
+ linePoints: {
1006
+ start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
1007
+ end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
973
1008
  }
974
- }
975
- setStartPos(null);
976
- setHasMoved(false);
977
- },
978
- hasMoved: getHasMoved,
979
- reset: () => {
980
- setStartPos(null);
981
- setHasMoved(false);
1009
+ };
982
1010
  }
983
- };
984
- }
985
- const freeTextHandlerFactory = {
986
- annotationType: PdfAnnotationSubtype.FREETEXT,
987
- create(context) {
988
- const { onCommit, onPreview, getTool, pageSize, pageIndex, pageRotation } = context;
989
- const [getStart, setStart] = useState(null);
990
- const clampToPage = (pos) => ({
991
- x: clamp(pos.x, 0, pageSize.width),
992
- y: clamp(pos.y, 0, pageSize.height)
993
- });
994
- const getDefaults = () => {
995
- const tool = getTool();
996
- if (!tool) return null;
1011
+ case "resize": {
1012
+ if (!ctx.changes.rect) return ctx.changes;
1013
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
1014
+ orig,
1015
+ ctx.changes.rect,
1016
+ ctx.metadata
1017
+ );
997
1018
  return {
998
- ...tool.defaults,
999
- fontColor: tool.defaults.fontColor ?? "#000000",
1000
- opacity: tool.defaults.opacity ?? 1,
1001
- fontSize: tool.defaults.fontSize ?? 12,
1002
- fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
1003
- color: tool.defaults.color ?? tool.defaults.backgroundColor ?? "transparent",
1004
- textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
1005
- verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
1006
- contents: tool.defaults.contents ?? "Insert text here",
1007
- flags: tool.defaults.flags ?? ["print"]
1008
- };
1009
- };
1010
- const clickDetector = useClickDetector({
1011
- threshold: 5,
1012
- getTool,
1013
- onClickDetected: (pos, tool) => {
1014
- var _a;
1015
- const defaults = getDefaults();
1016
- if (!defaults) return;
1017
- const clickConfig = tool.clickBehavior;
1018
- if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
1019
- const { width, height } = clickConfig.defaultSize;
1020
- const rect = {
1021
- origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
1022
- size: { width, height }
1023
- };
1024
- const contents = clickConfig.defaultContent ?? defaults.contents;
1025
- let anno = {
1026
- ...defaults,
1027
- contents,
1028
- type: PdfAnnotationSubtype.FREETEXT,
1029
- rect,
1030
- pageIndex,
1031
- id: uuidV4(),
1032
- created: /* @__PURE__ */ new Date()
1033
- };
1034
- if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
1035
- anno = applyInsertUpright(anno, pageRotation, false);
1036
- }
1037
- anno = clampAnnotationToPage(anno, pageSize);
1038
- onCommit(anno);
1039
- }
1040
- });
1041
- const getPreview = (current) => {
1042
- const start = getStart();
1043
- if (!start) return null;
1044
- const defaults = getDefaults();
1045
- if (!defaults) return null;
1046
- const minX = Math.min(start.x, current.x);
1047
- const minY = Math.min(start.y, current.y);
1048
- const width = Math.abs(start.x - current.x);
1049
- const height = Math.abs(start.y - current.y);
1050
- const rect = {
1051
- origin: { x: minX, y: minY },
1052
- size: { width, height }
1019
+ ...rects,
1020
+ linePoints: {
1021
+ start: {
1022
+ x: resolvedRect.origin.x + (orig.linePoints.start.x - oldRect.origin.x) * scaleX,
1023
+ y: resolvedRect.origin.y + (orig.linePoints.start.y - oldRect.origin.y) * scaleY
1024
+ },
1025
+ end: {
1026
+ x: resolvedRect.origin.x + (orig.linePoints.end.x - oldRect.origin.x) * scaleX,
1027
+ y: resolvedRect.origin.y + (orig.linePoints.end.y - oldRect.origin.y) * scaleY
1028
+ }
1029
+ }
1053
1030
  };
1031
+ }
1032
+ case "rotate": {
1033
+ const result = baseRotateChanges(orig, ctx);
1034
+ if (!result) return ctx.changes;
1035
+ const { dx, dy } = rotateOrbitDelta(orig, result);
1054
1036
  return {
1055
- type: PdfAnnotationSubtype.FREETEXT,
1056
- bounds: rect,
1057
- data: {
1058
- ...defaults,
1059
- rect
1037
+ ...result,
1038
+ linePoints: {
1039
+ start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
1040
+ end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
1060
1041
  }
1061
1042
  };
1062
- };
1063
- return {
1064
- onPointerDown: (pos, evt) => {
1065
- var _a;
1066
- const clampedPos = clampToPage(pos);
1067
- setStart(clampedPos);
1068
- clickDetector.onStart(clampedPos);
1069
- onPreview(getPreview(clampedPos));
1070
- (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
1071
- },
1072
- onPointerMove: (pos) => {
1073
- const clampedPos = clampToPage(pos);
1074
- clickDetector.onMove(clampedPos);
1075
- if (getStart() && clickDetector.hasMoved()) {
1076
- onPreview(getPreview(clampedPos));
1077
- }
1078
- },
1079
- onPointerUp: (pos, evt) => {
1080
- var _a, _b;
1081
- const start = getStart();
1082
- if (!start) return;
1083
- const defaults = getDefaults();
1084
- if (!defaults) return;
1085
- const clampedPos = clampToPage(pos);
1086
- if (!clickDetector.hasMoved()) {
1087
- clickDetector.onEnd(clampedPos);
1088
- } else {
1089
- const minX = Math.min(start.x, clampedPos.x);
1090
- const minY = Math.min(start.y, clampedPos.y);
1091
- const width = Math.abs(start.x - clampedPos.x);
1092
- const height = Math.abs(start.y - clampedPos.y);
1093
- const rect = {
1094
- origin: { x: minX, y: minY },
1095
- size: { width, height }
1096
- };
1097
- const tool = getTool();
1098
- let anno = {
1099
- ...defaults,
1100
- type: PdfAnnotationSubtype.FREETEXT,
1101
- rect,
1102
- pageIndex: context.pageIndex,
1103
- id: uuidV4(),
1104
- created: /* @__PURE__ */ new Date()
1105
- };
1106
- if ((_a = tool == null ? void 0 : tool.behavior) == null ? void 0 : _a.insertUpright) {
1107
- anno = applyInsertUpright(anno, pageRotation, true);
1108
- }
1109
- onCommit(anno);
1110
- }
1111
- setStart(null);
1112
- onPreview(null);
1113
- clickDetector.reset();
1114
- (_b = evt.releasePointerCapture) == null ? void 0 : _b.call(evt);
1115
- },
1116
- onPointerLeave: (_, evt) => {
1117
- var _a;
1118
- setStart(null);
1119
- onPreview(null);
1120
- clickDetector.reset();
1121
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1122
- },
1123
- onPointerCancel: (_, evt) => {
1124
- var _a;
1125
- setStart(null);
1126
- onPreview(null);
1127
- clickDetector.reset();
1128
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1043
+ }
1044
+ case "property-update": {
1045
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0 || ctx.changes.rotation !== void 0;
1046
+ if (!needsRectUpdate) return ctx.changes;
1047
+ const merged = { ...orig, ...ctx.changes };
1048
+ const tightRect = lineRectWithEndings(
1049
+ [merged.linePoints.start, merged.linePoints.end],
1050
+ merged.strokeWidth,
1051
+ merged.lineEndings
1052
+ );
1053
+ const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
1054
+ if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
1055
+ return {
1056
+ ...ctx.changes,
1057
+ unrotatedRect: tightRect,
1058
+ rect: calculateRotatedRectAABBAroundPoint(
1059
+ tightRect,
1060
+ effectiveRotation,
1061
+ resolveAnnotationRotationCenter(orig)
1062
+ )
1063
+ };
1129
1064
  }
1130
- };
1065
+ return { ...ctx.changes, rect: tightRect };
1066
+ }
1067
+ default:
1068
+ return ctx.changes;
1131
1069
  }
1132
1070
  };
1133
- const lineHandlerFactory = {
1134
- annotationType: PdfAnnotationSubtype.LINE,
1135
- create(context) {
1136
- const { pageIndex, onCommit, onPreview, getTool, pageSize } = context;
1137
- const [getStart, setStart] = useState(null);
1138
- const clampToPage = (pos) => ({
1139
- x: clamp(pos.x, 0, pageSize.width),
1140
- y: clamp(pos.y, 0, pageSize.height)
1141
- });
1142
- const getDefaults = () => {
1143
- const tool = getTool();
1144
- if (!tool) return null;
1071
+ const patchPolyline = (orig, ctx) => {
1072
+ switch (ctx.type) {
1073
+ case "vertex-edit":
1074
+ if (ctx.changes.vertices && ctx.changes.vertices.length) {
1075
+ const rawVertices = ctx.changes.vertices;
1076
+ const rawRect = lineRectWithEndings(rawVertices, orig.strokeWidth, orig.lineEndings);
1077
+ const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
1078
+ const rect = lineRectWithEndings(compensated, orig.strokeWidth, orig.lineEndings);
1079
+ return {
1080
+ ...resolveVertexEditRects(orig, rect),
1081
+ vertices: compensated
1082
+ };
1083
+ }
1084
+ return ctx.changes;
1085
+ case "move": {
1086
+ if (!ctx.changes.rect) return ctx.changes;
1087
+ const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
1145
1088
  return {
1146
- ...tool.defaults,
1147
- strokeWidth: tool.defaults.strokeWidth ?? 1,
1148
- lineEndings: tool.defaults.lineEndings ?? {
1149
- start: PdfAnnotationLineEnding.None,
1150
- end: PdfAnnotationLineEnding.None
1151
- },
1152
- color: tool.defaults.color ?? "#000000",
1153
- opacity: tool.defaults.opacity ?? 1,
1154
- strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
1155
- strokeDashArray: tool.defaults.strokeDashArray ?? [],
1156
- strokeColor: tool.defaults.strokeColor ?? "#000000",
1157
- flags: tool.defaults.flags ?? ["print"]
1089
+ ...rects,
1090
+ vertices: orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }))
1158
1091
  };
1159
- };
1160
- const clickDetector = useClickDetector({
1161
- threshold: 5,
1162
- getTool,
1163
- onClickDetected: (pos, tool) => {
1164
- const defaults = getDefaults();
1165
- if (!defaults) return;
1166
- const clickConfig = tool.clickBehavior;
1167
- if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
1168
- const angle = clickConfig.defaultAngle ?? 0;
1169
- const length = clickConfig.defaultLength;
1170
- const halfLength = length / 2;
1171
- const startX = pos.x - halfLength * Math.cos(angle);
1172
- const startY = pos.y - halfLength * Math.sin(angle);
1173
- const endX = pos.x + halfLength * Math.cos(angle);
1174
- const endY = pos.y + halfLength * Math.sin(angle);
1175
- const start = clampToPage({ x: startX, y: startY });
1176
- const end = clampToPage({ x: endX, y: endY });
1177
- const rect = lineRectWithEndings(
1178
- [start, end],
1179
- defaults.strokeWidth,
1180
- defaults.lineEndings
1181
- );
1182
- onCommit({
1183
- ...defaults,
1184
- rect,
1185
- linePoints: { start, end },
1186
- pageIndex,
1187
- id: uuidV4(),
1188
- created: /* @__PURE__ */ new Date(),
1189
- type: PdfAnnotationSubtype.LINE
1190
- });
1191
- }
1192
- });
1193
- const getPreview = (current) => {
1194
- const start = getStart();
1195
- if (!start) return null;
1196
- const defaults = getDefaults();
1197
- if (!defaults) return null;
1198
- const bounds = lineRectWithEndings(
1199
- [start, current],
1200
- defaults.strokeWidth,
1201
- defaults.lineEndings
1092
+ }
1093
+ case "resize": {
1094
+ if (!ctx.changes.rect) return ctx.changes;
1095
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
1096
+ orig,
1097
+ ctx.changes.rect,
1098
+ ctx.metadata
1202
1099
  );
1203
1100
  return {
1204
- type: PdfAnnotationSubtype.LINE,
1205
- bounds,
1206
- data: {
1207
- ...defaults,
1208
- rect: bounds,
1209
- linePoints: { start, end: current }
1210
- }
1101
+ ...rects,
1102
+ vertices: orig.vertices.map((v) => ({
1103
+ x: resolvedRect.origin.x + (v.x - oldRect.origin.x) * scaleX,
1104
+ y: resolvedRect.origin.y + (v.y - oldRect.origin.y) * scaleY
1105
+ }))
1211
1106
  };
1212
- };
1213
- return {
1214
- onPointerDown: (pos, evt) => {
1215
- var _a;
1216
- const clampedPos = clampToPage(pos);
1217
- setStart(clampedPos);
1218
- clickDetector.onStart(clampedPos);
1219
- onPreview(getPreview(clampedPos));
1220
- (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
1221
- },
1222
- onPointerMove: (pos) => {
1223
- const clampedPos = clampToPage(pos);
1224
- clickDetector.onMove(clampedPos);
1225
- if (getStart() && clickDetector.hasMoved()) {
1226
- onPreview(getPreview(clampedPos));
1227
- }
1228
- },
1229
- onPointerUp: (pos, evt) => {
1230
- var _a;
1231
- const start = getStart();
1232
- if (!start) return;
1233
- const clampedPos = clampToPage(pos);
1234
- if (!clickDetector.hasMoved()) {
1235
- clickDetector.onEnd(clampedPos);
1236
- } else {
1237
- const defaults = getDefaults();
1238
- if (!defaults) return;
1239
- if (Math.abs(clampedPos.x - start.x) > 2 || Math.abs(clampedPos.y - start.y) > 2) {
1240
- const rect = lineRectWithEndings(
1241
- [start, clampedPos],
1242
- defaults.strokeWidth,
1243
- defaults.lineEndings
1244
- );
1245
- onCommit({
1246
- ...defaults,
1247
- rect,
1248
- linePoints: { start, end: clampedPos },
1249
- pageIndex,
1250
- id: uuidV4(),
1251
- flags: ["print"],
1252
- created: /* @__PURE__ */ new Date(),
1253
- type: PdfAnnotationSubtype.LINE
1254
- });
1255
- }
1256
- }
1257
- setStart(null);
1258
- onPreview(null);
1259
- clickDetector.reset();
1260
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1261
- },
1262
- onPointerLeave: (_, evt) => {
1263
- var _a;
1264
- setStart(null);
1265
- onPreview(null);
1266
- clickDetector.reset();
1267
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1268
- },
1269
- onPointerCancel: (_, evt) => {
1270
- var _a;
1271
- setStart(null);
1272
- onPreview(null);
1273
- clickDetector.reset();
1274
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1275
- }
1276
- };
1277
- }
1278
- };
1279
- const polylineHandlerFactory = {
1280
- annotationType: PdfAnnotationSubtype.POLYLINE,
1281
- create(context) {
1282
- const { onCommit, onPreview, getTool, pageSize } = context;
1283
- const [getVertices, setVertices] = useState([]);
1284
- const [getCurrent, setCurrent] = useState(null);
1285
- const clampToPage = (pos) => ({
1286
- x: clamp(pos.x, 0, pageSize.width),
1287
- y: clamp(pos.y, 0, pageSize.height)
1288
- });
1289
- const getDefaults = () => {
1290
- const tool = getTool();
1291
- if (!tool) return null;
1107
+ }
1108
+ case "rotate": {
1109
+ const result = baseRotateChanges(orig, ctx);
1110
+ if (!result) return ctx.changes;
1111
+ const { dx, dy } = rotateOrbitDelta(orig, result);
1292
1112
  return {
1293
- ...tool.defaults,
1294
- strokeWidth: tool.defaults.strokeWidth ?? 1,
1295
- lineEndings: tool.defaults.lineEndings ?? {
1296
- start: PdfAnnotationLineEnding.None,
1297
- end: PdfAnnotationLineEnding.None
1298
- },
1299
- color: tool.defaults.color ?? "#000000",
1300
- opacity: tool.defaults.opacity ?? 1,
1301
- strokeColor: tool.defaults.strokeColor ?? "#000000",
1302
- strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
1303
- strokeDashArray: tool.defaults.strokeDashArray ?? [],
1304
- flags: tool.defaults.flags ?? ["print"]
1305
- };
1306
- };
1307
- const commitPolyline = () => {
1308
- const vertices = getVertices();
1309
- if (vertices.length < 2) return;
1310
- const defaults = getDefaults();
1311
- if (!defaults) return;
1312
- const rect = lineRectWithEndings(
1313
- vertices,
1314
- defaults.strokeWidth,
1315
- defaults.lineEndings
1316
- );
1317
- const anno = {
1318
- ...defaults,
1319
- vertices,
1320
- rect,
1321
- type: PdfAnnotationSubtype.POLYLINE,
1322
- pageIndex: context.pageIndex,
1323
- id: uuidV4(),
1324
- created: /* @__PURE__ */ new Date()
1113
+ ...result,
1114
+ vertices: orig.vertices.map((v) => ({ x: v.x + dx, y: v.y + dy }))
1325
1115
  };
1326
- onCommit(anno);
1327
- setVertices([]);
1328
- setCurrent(null);
1329
- onPreview(null);
1330
- };
1331
- const getPreview = () => {
1332
- const vertices = getVertices();
1333
- const currentPos = getCurrent();
1334
- if (vertices.length === 0 || !currentPos) return null;
1335
- const defaults = getDefaults();
1336
- if (!defaults) return null;
1337
- const allPoints = [...vertices, currentPos];
1338
- const bounds = lineRectWithEndings(
1339
- allPoints,
1340
- defaults.strokeWidth,
1341
- defaults.lineEndings
1116
+ }
1117
+ case "property-update": {
1118
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0 || ctx.changes.rotation !== void 0;
1119
+ if (!needsRectUpdate) return ctx.changes;
1120
+ const merged = { ...orig, ...ctx.changes };
1121
+ const tightRect = lineRectWithEndings(
1122
+ merged.vertices,
1123
+ merged.strokeWidth,
1124
+ merged.lineEndings
1342
1125
  );
1343
- return {
1344
- type: PdfAnnotationSubtype.POLYLINE,
1345
- bounds,
1346
- data: {
1347
- ...defaults,
1348
- rect: bounds,
1349
- vertices: allPoints,
1350
- currentVertex: currentPos
1351
- }
1352
- };
1353
- };
1354
- return {
1355
- onClick: (pos, evt) => {
1356
- if (evt.metaKey || evt.ctrlKey) {
1357
- return;
1358
- }
1359
- const clampedPos = clampToPage(pos);
1360
- const vertices = getVertices();
1361
- const lastVertex = vertices[vertices.length - 1];
1362
- if (lastVertex && Math.abs(lastVertex.x - clampedPos.x) < 1 && Math.abs(lastVertex.y - clampedPos.y) < 1) {
1363
- return;
1364
- }
1365
- setVertices([...vertices, clampedPos]);
1366
- setCurrent(clampedPos);
1367
- onPreview(getPreview());
1368
- },
1369
- onDoubleClick: () => {
1370
- commitPolyline();
1371
- },
1372
- onPointerMove: (pos) => {
1373
- if (getVertices().length > 0) {
1374
- const clampedPos = clampToPage(pos);
1375
- setCurrent(clampedPos);
1376
- onPreview(getPreview());
1377
- }
1378
- },
1379
- onPointerCancel: () => {
1380
- setVertices([]);
1381
- setCurrent(null);
1382
- onPreview(null);
1126
+ const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
1127
+ if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
1128
+ return {
1129
+ ...ctx.changes,
1130
+ unrotatedRect: tightRect,
1131
+ rect: calculateRotatedRectAABBAroundPoint(
1132
+ tightRect,
1133
+ effectiveRotation,
1134
+ resolveAnnotationRotationCenter(orig)
1135
+ )
1136
+ };
1383
1137
  }
1384
- };
1138
+ return { ...ctx.changes, rect: tightRect };
1139
+ }
1140
+ default:
1141
+ return ctx.changes;
1385
1142
  }
1386
1143
  };
1387
1144
  function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
@@ -1932,204 +1689,382 @@ function generateCloudyPolygonPath(vertices, rectOrigin, intensity, lineWidth) {
1932
1689
  out.close();
1933
1690
  return out.build(lineWidth);
1934
1691
  }
1935
- const HANDLE_SIZE_PX = 14;
1936
- const polygonHandlerFactory = {
1937
- annotationType: PdfAnnotationSubtype.POLYGON,
1938
- create(context) {
1939
- const { onCommit, onPreview, getTool, scale, pageSize } = context;
1940
- const [getVertices, setVertices] = useState([]);
1941
- const [getCurrent, setCurrent] = useState(null);
1942
- const clampToPage = (pos) => ({
1943
- x: clamp(pos.x, 0, pageSize.width),
1944
- y: clamp(pos.y, 0, pageSize.height)
1945
- });
1946
- const isInsideStartHandle = (pos) => {
1947
- const vertices = getVertices();
1948
- if (vertices.length < 2) return false;
1949
- const sizePDF = HANDLE_SIZE_PX / scale;
1950
- const half = sizePDF / 2;
1951
- const v0 = vertices[0];
1952
- return pos.x >= v0.x - half && pos.x <= v0.x + half && pos.y >= v0.y - half && pos.y <= v0.y + half;
1953
- };
1954
- const getDefaults = () => {
1955
- const tool = getTool();
1956
- if (!tool) return null;
1692
+ function getPolygonPad(intensity, strokeWidth) {
1693
+ if ((intensity ?? 0) > 0) {
1694
+ return getCloudyBorderExtent(intensity, strokeWidth, false);
1695
+ }
1696
+ return strokeWidth / 2;
1697
+ }
1698
+ const patchPolygon = (orig, ctx) => {
1699
+ switch (ctx.type) {
1700
+ case "vertex-edit":
1701
+ if (ctx.changes.vertices && ctx.changes.vertices.length) {
1702
+ const pad = getPolygonPad(orig.cloudyBorderIntensity, orig.strokeWidth);
1703
+ const rawVertices = ctx.changes.vertices;
1704
+ const rawRect = expandRect(rectFromPoints(rawVertices), pad);
1705
+ const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
1706
+ const rect = expandRect(rectFromPoints(compensated), pad);
1707
+ return {
1708
+ ...resolveVertexEditRects(orig, rect),
1709
+ vertices: compensated
1710
+ };
1711
+ }
1712
+ return ctx.changes;
1713
+ case "move": {
1714
+ if (!ctx.changes.rect) return ctx.changes;
1715
+ const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
1957
1716
  return {
1958
- ...tool.defaults,
1959
- color: tool.defaults.color ?? "#000000",
1960
- opacity: tool.defaults.opacity ?? 1,
1961
- strokeWidth: tool.defaults.strokeWidth ?? 1,
1962
- strokeColor: tool.defaults.strokeColor ?? "#000000",
1963
- strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
1964
- strokeDashArray: tool.defaults.strokeDashArray ?? [],
1965
- flags: tool.defaults.flags ?? ["print"]
1966
- };
1967
- };
1968
- const commitPolygon = () => {
1969
- const vertices = getVertices();
1970
- if (vertices.length < 3) return;
1971
- const defaults = getDefaults();
1972
- if (!defaults) return;
1973
- const intensity = defaults.cloudyBorderIntensity ?? 0;
1974
- const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
1975
- const rect = expandRect(rectFromPoints(vertices), pad);
1976
- const anno = {
1977
- ...defaults,
1978
- vertices,
1979
- rect,
1980
- type: PdfAnnotationSubtype.POLYGON,
1981
- pageIndex: context.pageIndex,
1982
- id: uuidV4(),
1983
- created: /* @__PURE__ */ new Date(),
1984
- ...intensity > 0 && {
1985
- rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
1986
- }
1717
+ ...rects,
1718
+ vertices: orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }))
1987
1719
  };
1988
- onCommit(anno);
1989
- setVertices([]);
1990
- setCurrent(null);
1991
- onPreview(null);
1992
- };
1993
- const getPreview = () => {
1994
- const vertices = getVertices();
1995
- const currentPos = getCurrent();
1996
- if (vertices.length === 0 || !currentPos) return null;
1997
- const defaults = getDefaults();
1998
- if (!defaults) return null;
1999
- const intensity = defaults.cloudyBorderIntensity ?? 0;
2000
- const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
2001
- const allPoints = [...vertices, currentPos];
2002
- const bounds = expandRect(rectFromPoints(allPoints), pad);
1720
+ }
1721
+ case "resize": {
1722
+ if (!ctx.changes.rect) return ctx.changes;
1723
+ const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
1724
+ orig,
1725
+ ctx.changes.rect,
1726
+ ctx.metadata
1727
+ );
2003
1728
  return {
2004
- type: PdfAnnotationSubtype.POLYGON,
2005
- bounds,
2006
- data: {
2007
- ...defaults,
2008
- rect: bounds,
2009
- vertices,
2010
- currentVertex: currentPos
2011
- }
1729
+ ...rects,
1730
+ vertices: orig.vertices.map((v) => ({
1731
+ x: resolvedRect.origin.x + (v.x - oldRect.origin.x) * scaleX,
1732
+ y: resolvedRect.origin.y + (v.y - oldRect.origin.y) * scaleY
1733
+ }))
2012
1734
  };
2013
- };
2014
- return {
2015
- onClick: (pos, evt) => {
2016
- if (evt.metaKey || evt.ctrlKey) {
2017
- return;
2018
- }
2019
- const clampedPos = clampToPage(pos);
2020
- if (isInsideStartHandle(clampedPos) && getVertices().length >= 3) {
2021
- commitPolygon();
2022
- return;
1735
+ }
1736
+ case "rotate": {
1737
+ const result = baseRotateChanges(orig, ctx);
1738
+ if (!result) return ctx.changes;
1739
+ const { dx, dy } = rotateOrbitDelta(orig, result);
1740
+ return {
1741
+ ...result,
1742
+ vertices: orig.vertices.map((v) => ({ x: v.x + dx, y: v.y + dy }))
1743
+ };
1744
+ }
1745
+ case "property-update": {
1746
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
1747
+ const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0 || cloudyChanged;
1748
+ if (!needsRectUpdate) return ctx.changes;
1749
+ const merged = { ...orig, ...ctx.changes };
1750
+ const pad = getPolygonPad(merged.cloudyBorderIntensity, merged.strokeWidth);
1751
+ const tightRect = expandRect(rectFromPoints(merged.vertices), pad);
1752
+ let patch = ctx.changes;
1753
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
1754
+ if (cloudyChanged || ctx.changes.strokeWidth !== void 0 && hasCloudy) {
1755
+ const intensity = merged.cloudyBorderIntensity ?? 0;
1756
+ if (intensity > 0) {
1757
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
1758
+ patch = {
1759
+ ...patch,
1760
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
1761
+ };
1762
+ } else {
1763
+ patch = { ...patch, rectangleDifferences: void 0 };
2023
1764
  }
2024
- const vertices = getVertices();
2025
- const lastVertex = vertices[vertices.length - 1];
2026
- if (lastVertex && Math.abs(lastVertex.x - clampedPos.x) < 1 && Math.abs(lastVertex.y - clampedPos.y) < 1) {
2027
- return;
1765
+ }
1766
+ const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
1767
+ if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
1768
+ return {
1769
+ ...patch,
1770
+ unrotatedRect: tightRect,
1771
+ rect: calculateRotatedRectAABBAroundPoint(
1772
+ tightRect,
1773
+ effectiveRotation,
1774
+ resolveAnnotationRotationCenter(orig)
1775
+ )
1776
+ };
1777
+ }
1778
+ return { ...patch, rect: tightRect };
1779
+ }
1780
+ default:
1781
+ return ctx.changes;
1782
+ }
1783
+ };
1784
+ const patchCircle = (orig, ctx) => {
1785
+ switch (ctx.type) {
1786
+ case "move":
1787
+ if (!ctx.changes.rect) return ctx.changes;
1788
+ return baseMoveChanges(orig, ctx.changes.rect).rects;
1789
+ case "resize":
1790
+ if (!ctx.changes.rect) return ctx.changes;
1791
+ return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
1792
+ case "rotate":
1793
+ return baseRotateChanges(orig, ctx) ?? ctx.changes;
1794
+ case "property-update": {
1795
+ let patch = ctx.changes;
1796
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
1797
+ const strokeChanged = ctx.changes.strokeWidth !== void 0;
1798
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
1799
+ if (cloudyChanged || strokeChanged && hasCloudy) {
1800
+ const merged = { ...orig, ...ctx.changes };
1801
+ const intensity = merged.cloudyBorderIntensity ?? 0;
1802
+ if (intensity > 0) {
1803
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, true);
1804
+ patch = {
1805
+ ...patch,
1806
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
1807
+ };
1808
+ } else {
1809
+ patch = { ...patch, rectangleDifferences: void 0 };
2028
1810
  }
2029
- setVertices([...vertices, clampedPos]);
2030
- setCurrent(clampedPos);
2031
- onPreview(getPreview());
2032
- },
2033
- onDoubleClick: (_) => {
2034
- commitPolygon();
2035
- },
2036
- onPointerMove: (pos) => {
2037
- if (getVertices().length > 0) {
2038
- const clampedPos = clampToPage(pos);
2039
- setCurrent(clampedPos);
2040
- onPreview(getPreview());
1811
+ }
1812
+ if (ctx.changes.rotation !== void 0) {
1813
+ patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
1814
+ }
1815
+ return patch;
1816
+ }
1817
+ default:
1818
+ return ctx.changes;
1819
+ }
1820
+ };
1821
+ const patchSquare = (orig, ctx) => {
1822
+ switch (ctx.type) {
1823
+ case "move":
1824
+ if (!ctx.changes.rect) return ctx.changes;
1825
+ return baseMoveChanges(orig, ctx.changes.rect).rects;
1826
+ case "resize":
1827
+ if (!ctx.changes.rect) return ctx.changes;
1828
+ return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
1829
+ case "rotate":
1830
+ return baseRotateChanges(orig, ctx) ?? ctx.changes;
1831
+ case "property-update": {
1832
+ let patch = ctx.changes;
1833
+ const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
1834
+ const strokeChanged = ctx.changes.strokeWidth !== void 0;
1835
+ const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
1836
+ if (cloudyChanged || strokeChanged && hasCloudy) {
1837
+ const merged = { ...orig, ...ctx.changes };
1838
+ const intensity = merged.cloudyBorderIntensity ?? 0;
1839
+ if (intensity > 0) {
1840
+ const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
1841
+ patch = {
1842
+ ...patch,
1843
+ rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
1844
+ };
1845
+ } else {
1846
+ patch = { ...patch, rectangleDifferences: void 0 };
2041
1847
  }
2042
- },
2043
- onPointerCancel: (_) => {
2044
- setVertices([]);
2045
- setCurrent(null);
2046
- onPreview(null);
2047
1848
  }
2048
- };
1849
+ if (ctx.changes.rotation !== void 0) {
1850
+ patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
1851
+ }
1852
+ return patch;
1853
+ }
1854
+ default:
1855
+ return ctx.changes;
2049
1856
  }
2050
1857
  };
2051
- const squareHandlerFactory = {
2052
- annotationType: PdfAnnotationSubtype.SQUARE,
2053
- create(context) {
2054
- const { pageIndex, onCommit, onPreview, getTool, pageSize } = context;
2055
- const [getStart, setStart] = useState(null);
2056
- const clampToPage = (pos) => ({
2057
- x: clamp(pos.x, 0, pageSize.width),
2058
- y: clamp(pos.y, 0, pageSize.height)
2059
- });
2060
- const getDefaults = () => {
2061
- const tool = getTool();
2062
- if (!tool) return null;
2063
- return {
2064
- ...tool.defaults,
2065
- flags: tool.defaults.flags ?? ["print"],
2066
- strokeWidth: tool.defaults.strokeWidth ?? 2,
2067
- strokeColor: tool.defaults.strokeColor ?? "#000000",
2068
- strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
2069
- strokeDashArray: tool.defaults.strokeDashArray ?? [],
2070
- color: tool.defaults.color ?? "#000000",
2071
- opacity: tool.defaults.opacity ?? 1
2072
- };
2073
- };
2074
- const clickDetector = useClickDetector({
2075
- threshold: 5,
2076
- getTool,
2077
- onClickDetected: (pos, tool) => {
2078
- const defaults = getDefaults();
2079
- if (!defaults) return;
2080
- const clickConfig = tool.clickBehavior;
2081
- if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
2082
- const { width, height } = clickConfig.defaultSize;
2083
- const halfWidth = width / 2;
2084
- const halfHeight = height / 2;
2085
- const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2086
- const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2087
- const strokeWidth = defaults.strokeWidth;
2088
- const intensity = defaults.cloudyBorderIntensity ?? 0;
2089
- const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
2090
- const rect = {
2091
- origin: { x: x - pad, y: y - pad },
2092
- size: { width: width + 2 * pad, height: height + 2 * pad }
2093
- };
2094
- const anno = {
2095
- ...defaults,
2096
- type: PdfAnnotationSubtype.SQUARE,
2097
- created: /* @__PURE__ */ new Date(),
2098
- id: uuidV4(),
2099
- pageIndex,
2100
- rect,
2101
- ...intensity > 0 && {
2102
- rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2103
- }
2104
- };
2105
- onCommit(anno);
1858
+ const patchFreeText = (orig, ctx) => {
1859
+ switch (ctx.type) {
1860
+ case "move":
1861
+ if (!ctx.changes.rect) return ctx.changes;
1862
+ return baseMoveChanges(orig, ctx.changes.rect).rects;
1863
+ case "resize":
1864
+ if (!ctx.changes.rect) return ctx.changes;
1865
+ return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
1866
+ case "rotate":
1867
+ return baseRotateChanges(orig, ctx) ?? ctx.changes;
1868
+ case "property-update":
1869
+ if (ctx.changes.rotation !== void 0) {
1870
+ return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
2106
1871
  }
2107
- });
2108
- const getPreview = (current) => {
2109
- const p1 = getStart();
2110
- if (!p1) return null;
2111
- const minX = Math.min(p1.x, current.x);
2112
- const minY = Math.min(p1.y, current.y);
2113
- const width = Math.abs(p1.x - current.x);
2114
- const height = Math.abs(p1.y - current.y);
1872
+ return ctx.changes;
1873
+ default:
1874
+ return ctx.changes;
1875
+ }
1876
+ };
1877
+ const patchStamp = (orig, ctx) => {
1878
+ switch (ctx.type) {
1879
+ case "move":
1880
+ if (!ctx.changes.rect) return ctx.changes;
1881
+ return baseMoveChanges(orig, ctx.changes.rect).rects;
1882
+ case "resize":
1883
+ if (!ctx.changes.rect) return ctx.changes;
1884
+ return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
1885
+ case "rotate":
1886
+ return baseRotateChanges(orig, ctx) ?? ctx.changes;
1887
+ case "property-update":
1888
+ if (ctx.changes.rotation !== void 0) {
1889
+ return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
1890
+ }
1891
+ return ctx.changes;
1892
+ default:
1893
+ return ctx.changes;
1894
+ }
1895
+ };
1896
+ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1897
+ __proto__: null,
1898
+ LINE_ENDING_HANDLERS,
1899
+ PatchRegistry,
1900
+ applyInsertUpright,
1901
+ calculateAABBFromVertices,
1902
+ calculateRotatedRectAABB,
1903
+ calculateRotatedRectAABBAroundPoint,
1904
+ clampAnnotationToPage,
1905
+ compensateRotatedVertexEdit,
1906
+ createEnding,
1907
+ getRectCenter,
1908
+ lineRectWithEndings,
1909
+ patchCircle,
1910
+ patchFreeText,
1911
+ patchInk,
1912
+ patchLine,
1913
+ patchPolygon,
1914
+ patchPolyline,
1915
+ patchRegistry,
1916
+ patchSquare,
1917
+ patchStamp,
1918
+ resolveAnnotationRotationCenter,
1919
+ resolveRotateRects,
1920
+ resolveVertexEditRects,
1921
+ rotatePointAroundCenter: rotatePointAround,
1922
+ rotateVertices
1923
+ }, Symbol.toStringTag, { value: "Module" }));
1924
+ const COMMENT_SIZE = 24;
1925
+ const textHandlerFactory = {
1926
+ annotationType: PdfAnnotationSubtype.TEXT,
1927
+ create(context) {
1928
+ const { onCommit, getTool, pageSize } = context;
1929
+ return {
1930
+ onPointerDown: (pos) => {
1931
+ const tool = getTool();
1932
+ if (!tool) return;
1933
+ const rect = {
1934
+ origin: { x: pos.x - COMMENT_SIZE / 2, y: pos.y - COMMENT_SIZE / 2 },
1935
+ size: { width: COMMENT_SIZE, height: COMMENT_SIZE }
1936
+ };
1937
+ let anno = {
1938
+ ...tool.defaults,
1939
+ rect,
1940
+ type: PdfAnnotationSubtype.TEXT,
1941
+ name: tool.defaults.name ?? PdfAnnotationName.Comment,
1942
+ contents: tool.defaults.contents ?? "",
1943
+ flags: tool.defaults.flags ?? ["print", "noRotate", "noZoom"],
1944
+ pageIndex: context.pageIndex,
1945
+ id: uuidV4(),
1946
+ created: /* @__PURE__ */ new Date()
1947
+ };
1948
+ anno = clampAnnotationToPage(anno, pageSize);
1949
+ onCommit(anno);
1950
+ }
1951
+ };
1952
+ }
1953
+ };
1954
+ function useClickDetector({
1955
+ threshold = 5,
1956
+ getTool,
1957
+ onClickDetected
1958
+ }) {
1959
+ const [getStartPos, setStartPos] = useState(null);
1960
+ const [getHasMoved, setHasMoved] = useState(false);
1961
+ return {
1962
+ onStart: (pos) => {
1963
+ setStartPos(pos);
1964
+ setHasMoved(false);
1965
+ },
1966
+ onMove: (pos) => {
1967
+ const start = getStartPos();
1968
+ if (!start || getHasMoved()) return;
1969
+ const distance2 = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
1970
+ if (distance2 > threshold) {
1971
+ setHasMoved(true);
1972
+ }
1973
+ },
1974
+ onEnd: (pos) => {
1975
+ var _a;
1976
+ const start = getStartPos();
1977
+ if (start && !getHasMoved()) {
1978
+ const tool = getTool();
1979
+ if (tool && "clickBehavior" in tool && ((_a = tool.clickBehavior) == null ? void 0 : _a.enabled)) {
1980
+ onClickDetected(pos, tool);
1981
+ }
1982
+ }
1983
+ setStartPos(null);
1984
+ setHasMoved(false);
1985
+ },
1986
+ hasMoved: getHasMoved,
1987
+ reset: () => {
1988
+ setStartPos(null);
1989
+ setHasMoved(false);
1990
+ }
1991
+ };
1992
+ }
1993
+ const freeTextHandlerFactory = {
1994
+ annotationType: PdfAnnotationSubtype.FREETEXT,
1995
+ create(context) {
1996
+ const { onCommit, onPreview, getTool, pageSize, pageIndex, pageRotation } = context;
1997
+ const [getStart, setStart] = useState(null);
1998
+ const clampToPage = (pos) => ({
1999
+ x: clamp(pos.x, 0, pageSize.width),
2000
+ y: clamp(pos.y, 0, pageSize.height)
2001
+ });
2002
+ const getDefaults = () => {
2003
+ const tool = getTool();
2004
+ if (!tool) return null;
2005
+ return {
2006
+ ...tool.defaults,
2007
+ fontColor: tool.defaults.fontColor ?? "#000000",
2008
+ opacity: tool.defaults.opacity ?? 1,
2009
+ fontSize: tool.defaults.fontSize ?? 12,
2010
+ fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
2011
+ color: tool.defaults.color ?? tool.defaults.backgroundColor ?? "transparent",
2012
+ textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
2013
+ verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
2014
+ contents: tool.defaults.contents ?? "Insert text here",
2015
+ flags: tool.defaults.flags ?? ["print"]
2016
+ };
2017
+ };
2018
+ const clickDetector = useClickDetector({
2019
+ threshold: 5,
2020
+ getTool,
2021
+ onClickDetected: (pos, tool) => {
2022
+ var _a;
2023
+ const defaults = getDefaults();
2024
+ if (!defaults) return;
2025
+ const clickConfig = tool.clickBehavior;
2026
+ if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
2027
+ const { width, height } = clickConfig.defaultSize;
2028
+ const rect = {
2029
+ origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
2030
+ size: { width, height }
2031
+ };
2032
+ const contents = clickConfig.defaultContent ?? defaults.contents;
2033
+ let anno = {
2034
+ ...defaults,
2035
+ contents,
2036
+ type: PdfAnnotationSubtype.FREETEXT,
2037
+ rect,
2038
+ pageIndex,
2039
+ id: uuidV4(),
2040
+ created: /* @__PURE__ */ new Date()
2041
+ };
2042
+ if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
2043
+ anno = applyInsertUpright(anno, pageRotation, false);
2044
+ }
2045
+ anno = clampAnnotationToPage(anno, pageSize);
2046
+ onCommit(anno);
2047
+ }
2048
+ });
2049
+ const getPreview = (current) => {
2050
+ const start = getStart();
2051
+ if (!start) return null;
2115
2052
  const defaults = getDefaults();
2116
2053
  if (!defaults) return null;
2117
- const strokeWidth = defaults.strokeWidth;
2118
- const intensity = defaults.cloudyBorderIntensity ?? 0;
2119
- const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
2054
+ const minX = Math.min(start.x, current.x);
2055
+ const minY = Math.min(start.y, current.y);
2056
+ const width = Math.abs(start.x - current.x);
2057
+ const height = Math.abs(start.y - current.y);
2120
2058
  const rect = {
2121
- origin: { x: minX - pad, y: minY - pad },
2122
- size: { width: width + 2 * pad, height: height + 2 * pad }
2059
+ origin: { x: minX, y: minY },
2060
+ size: { width, height }
2123
2061
  };
2124
2062
  return {
2125
- type: PdfAnnotationSubtype.SQUARE,
2063
+ type: PdfAnnotationSubtype.FREETEXT,
2126
2064
  bounds: rect,
2127
2065
  data: {
2128
- rect,
2129
2066
  ...defaults,
2130
- ...intensity > 0 && {
2131
- rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2132
- }
2067
+ rect
2133
2068
  }
2134
2069
  };
2135
2070
  };
@@ -2150,39 +2085,41 @@ const squareHandlerFactory = {
2150
2085
  }
2151
2086
  },
2152
2087
  onPointerUp: (pos, evt) => {
2153
- var _a;
2154
- const p1 = getStart();
2155
- if (!p1) return;
2088
+ var _a, _b;
2089
+ const start = getStart();
2090
+ if (!start) return;
2156
2091
  const defaults = getDefaults();
2157
2092
  if (!defaults) return;
2158
2093
  const clampedPos = clampToPage(pos);
2159
2094
  if (!clickDetector.hasMoved()) {
2160
2095
  clickDetector.onEnd(clampedPos);
2161
2096
  } else {
2162
- const defaults2 = getDefaults();
2163
- if (!defaults2) return;
2164
- const preview = getPreview(clampedPos);
2165
- if (preview) {
2166
- const intensity = defaults2.cloudyBorderIntensity ?? 0;
2167
- const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, false) : void 0;
2168
- const anno = {
2169
- ...defaults2,
2170
- type: PdfAnnotationSubtype.SQUARE,
2171
- created: /* @__PURE__ */ new Date(),
2172
- id: uuidV4(),
2173
- pageIndex,
2174
- rect: preview.data.rect,
2175
- ...pad !== void 0 && {
2176
- rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2177
- }
2178
- };
2179
- onCommit(anno);
2097
+ const minX = Math.min(start.x, clampedPos.x);
2098
+ const minY = Math.min(start.y, clampedPos.y);
2099
+ const width = Math.abs(start.x - clampedPos.x);
2100
+ const height = Math.abs(start.y - clampedPos.y);
2101
+ const rect = {
2102
+ origin: { x: minX, y: minY },
2103
+ size: { width, height }
2104
+ };
2105
+ const tool = getTool();
2106
+ let anno = {
2107
+ ...defaults,
2108
+ type: PdfAnnotationSubtype.FREETEXT,
2109
+ rect,
2110
+ pageIndex: context.pageIndex,
2111
+ id: uuidV4(),
2112
+ created: /* @__PURE__ */ new Date()
2113
+ };
2114
+ if ((_a = tool == null ? void 0 : tool.behavior) == null ? void 0 : _a.insertUpright) {
2115
+ anno = applyInsertUpright(anno, pageRotation, true);
2180
2116
  }
2117
+ onCommit(anno);
2181
2118
  }
2182
2119
  setStart(null);
2183
2120
  onPreview(null);
2184
2121
  clickDetector.reset();
2185
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
2122
+ (_b = evt.releasePointerCapture) == null ? void 0 : _b.call(evt);
2186
2123
  },
2187
2124
  onPointerLeave: (_, evt) => {
2188
2125
  var _a;
@@ -2201,149 +2138,30 @@ const squareHandlerFactory = {
2201
2138
  };
2202
2139
  }
2203
2140
  };
2204
- const imageFetchCache = /* @__PURE__ */ new Map();
2205
- const stampHandlerFactory = {
2206
- annotationType: PdfAnnotationSubtype.STAMP,
2141
+ const lineHandlerFactory = {
2142
+ annotationType: PdfAnnotationSubtype.LINE,
2207
2143
  create(context) {
2208
- const { services, onCommit, onPreview, getTool, pageSize, pageRotation } = context;
2209
- let cachedBuffer = null;
2210
- let cachedSize = null;
2211
- const commitStamp = (pos, width, height, ctx) => {
2212
- var _a;
2213
- const tool = getTool();
2214
- if (!tool) return;
2215
- const rect = {
2216
- origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
2217
- size: { width, height }
2218
- };
2219
- let anno = {
2220
- ...tool.defaults,
2221
- rect,
2222
- type: PdfAnnotationSubtype.STAMP,
2223
- name: tool.defaults.name ?? PdfAnnotationName.Image,
2224
- subject: tool.defaults.subject ?? "Stamp",
2225
- flags: tool.defaults.flags ?? ["print"],
2226
- pageIndex: context.pageIndex,
2227
- id: uuidV4(),
2228
- created: /* @__PURE__ */ new Date()
2229
- };
2230
- if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
2231
- anno = applyInsertUpright(anno, pageRotation, false);
2232
- }
2233
- anno = clampAnnotationToPage(anno, pageSize);
2234
- onCommit(anno, ctx);
2235
- };
2236
- const commitFromBuffer = (pos, buffer, imageSize) => {
2237
- const meta = getImageMetadata(buffer);
2238
- if (!meta || meta.mimeType === "application/pdf") return false;
2239
- const fitted = fitSizeWithin(meta, pageSize);
2240
- const width = (imageSize == null ? void 0 : imageSize.width) ?? fitted.width;
2241
- const height = (imageSize == null ? void 0 : imageSize.height) ?? fitted.height;
2242
- commitStamp(pos, width, height, { data: buffer });
2243
- return true;
2244
- };
2245
- return {
2246
- onHandlerActiveStart: () => {
2247
- const tool = getTool();
2248
- const imageSrc = tool == null ? void 0 : tool.defaults.imageSrc;
2249
- if (!imageSrc) return;
2250
- let entry = imageFetchCache.get(imageSrc);
2251
- if (!entry) {
2252
- const promise = fetch(imageSrc).then((res) => res.arrayBuffer()).then((buffer) => {
2253
- const meta = getImageMetadata(buffer);
2254
- if (!meta || meta.mimeType === "application/pdf") return null;
2255
- const fitted = fitSizeWithin(meta, pageSize);
2256
- const imageSize = tool.defaults.imageSize;
2257
- return {
2258
- buffer,
2259
- width: (imageSize == null ? void 0 : imageSize.width) ?? fitted.width,
2260
- height: (imageSize == null ? void 0 : imageSize.height) ?? fitted.height
2261
- };
2262
- }).catch(() => null);
2263
- entry = { promise, refs: 1 };
2264
- imageFetchCache.set(imageSrc, entry);
2265
- } else {
2266
- entry.refs++;
2267
- }
2268
- entry.promise.then((result) => {
2269
- if (!result) return;
2270
- cachedBuffer = result.buffer;
2271
- cachedSize = { width: result.width, height: result.height };
2272
- });
2273
- },
2274
- onHandlerActiveEnd: () => {
2275
- const tool = getTool();
2276
- const imageSrc = tool == null ? void 0 : tool.defaults.imageSrc;
2277
- if (imageSrc) {
2278
- const entry = imageFetchCache.get(imageSrc);
2279
- if (entry && --entry.refs <= 0) {
2280
- imageFetchCache.delete(imageSrc);
2281
- }
2282
- }
2283
- cachedBuffer = null;
2284
- cachedSize = null;
2285
- onPreview(null);
2286
- },
2287
- onPointerMove: (pos) => {
2288
- var _a;
2289
- const tool = getTool();
2290
- if (!((_a = tool == null ? void 0 : tool.behavior) == null ? void 0 : _a.showGhost) || !cachedSize || !tool.defaults.imageSrc) return;
2291
- const rect = {
2292
- origin: { x: pos.x - cachedSize.width / 2, y: pos.y - cachedSize.height / 2 },
2293
- size: cachedSize
2294
- };
2295
- onPreview({
2296
- type: PdfAnnotationSubtype.STAMP,
2297
- bounds: rect,
2298
- data: { rect, ghostUrl: tool.defaults.imageSrc, pageRotation }
2299
- });
2300
- },
2301
- onPointerDown: (pos) => {
2302
- const tool = getTool();
2303
- if (!tool) return;
2304
- const { imageSrc, imageSize } = tool.defaults;
2305
- if (imageSrc) {
2306
- onPreview(null);
2307
- if (cachedBuffer) {
2308
- commitFromBuffer(pos, cachedBuffer, imageSize);
2309
- } else {
2310
- fetch(imageSrc).then((res) => res.arrayBuffer()).then((buffer) => commitFromBuffer(pos, buffer, imageSize));
2311
- }
2312
- } else {
2313
- services.requestFile({
2314
- accept: "image/png,image/jpeg",
2315
- onFile: (file) => {
2316
- file.arrayBuffer().then((buffer) => commitFromBuffer(pos, buffer));
2317
- }
2318
- });
2319
- }
2320
- },
2321
- onPointerLeave: () => {
2322
- onPreview(null);
2323
- }
2324
- };
2325
- }
2326
- };
2327
- const circleHandlerFactory = {
2328
- annotationType: PdfAnnotationSubtype.CIRCLE,
2329
- create(context) {
2330
- const { pageIndex, onCommit, onPreview, getTool, pageSize } = context;
2331
- const [getStart, setStart] = useState(null);
2332
- const clampToPage = (pos) => ({
2333
- x: clamp(pos.x, 0, pageSize.width),
2334
- y: clamp(pos.y, 0, pageSize.height)
2335
- });
2336
- const getDefaults = () => {
2144
+ const { pageIndex, onCommit, onPreview, getTool, pageSize } = context;
2145
+ const [getStart, setStart] = useState(null);
2146
+ const clampToPage = (pos) => ({
2147
+ x: clamp(pos.x, 0, pageSize.width),
2148
+ y: clamp(pos.y, 0, pageSize.height)
2149
+ });
2150
+ const getDefaults = () => {
2337
2151
  const tool = getTool();
2338
2152
  if (!tool) return null;
2339
2153
  return {
2340
2154
  ...tool.defaults,
2341
- strokeWidth: tool.defaults.strokeWidth ?? 2,
2342
- strokeColor: tool.defaults.strokeColor ?? "#000000",
2343
- strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
2344
- strokeDashArray: tool.defaults.strokeDashArray ?? [],
2155
+ strokeWidth: tool.defaults.strokeWidth ?? 1,
2156
+ lineEndings: tool.defaults.lineEndings ?? {
2157
+ start: PdfAnnotationLineEnding.None,
2158
+ end: PdfAnnotationLineEnding.None
2159
+ },
2345
2160
  color: tool.defaults.color ?? "#000000",
2346
2161
  opacity: tool.defaults.opacity ?? 1,
2162
+ strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
2163
+ strokeDashArray: tool.defaults.strokeDashArray ?? [],
2164
+ strokeColor: tool.defaults.strokeColor ?? "#000000",
2347
2165
  flags: tool.defaults.flags ?? ["print"]
2348
2166
  };
2349
2167
  };
@@ -2355,57 +2173,48 @@ const circleHandlerFactory = {
2355
2173
  if (!defaults) return;
2356
2174
  const clickConfig = tool.clickBehavior;
2357
2175
  if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
2358
- const { width, height } = clickConfig.defaultSize;
2359
- const halfWidth = width / 2;
2360
- const halfHeight = height / 2;
2361
- const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2362
- const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2363
- const strokeWidth = defaults.strokeWidth;
2364
- const intensity = defaults.cloudyBorderIntensity ?? 0;
2365
- const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
2366
- const rect = {
2367
- origin: { x: x - pad, y: y - pad },
2368
- size: { width: width + 2 * pad, height: height + 2 * pad }
2369
- };
2370
- const anno = {
2176
+ const angle = clickConfig.defaultAngle ?? 0;
2177
+ const length = clickConfig.defaultLength;
2178
+ const halfLength = length / 2;
2179
+ const startX = pos.x - halfLength * Math.cos(angle);
2180
+ const startY = pos.y - halfLength * Math.sin(angle);
2181
+ const endX = pos.x + halfLength * Math.cos(angle);
2182
+ const endY = pos.y + halfLength * Math.sin(angle);
2183
+ const start = clampToPage({ x: startX, y: startY });
2184
+ const end = clampToPage({ x: endX, y: endY });
2185
+ const rect = lineRectWithEndings(
2186
+ [start, end],
2187
+ defaults.strokeWidth,
2188
+ defaults.lineEndings
2189
+ );
2190
+ onCommit({
2371
2191
  ...defaults,
2372
- type: PdfAnnotationSubtype.CIRCLE,
2373
- created: /* @__PURE__ */ new Date(),
2374
- id: uuidV4(),
2375
- pageIndex,
2376
2192
  rect,
2377
- ...intensity > 0 && {
2378
- rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2379
- }
2380
- };
2381
- onCommit(anno);
2193
+ linePoints: { start, end },
2194
+ pageIndex,
2195
+ id: uuidV4(),
2196
+ created: /* @__PURE__ */ new Date(),
2197
+ type: PdfAnnotationSubtype.LINE
2198
+ });
2382
2199
  }
2383
2200
  });
2384
2201
  const getPreview = (current) => {
2385
- const p1 = getStart();
2386
- if (!p1) return null;
2387
- const minX = Math.min(p1.x, current.x);
2388
- const minY = Math.min(p1.y, current.y);
2389
- const width = Math.abs(p1.x - current.x);
2390
- const height = Math.abs(p1.y - current.y);
2202
+ const start = getStart();
2203
+ if (!start) return null;
2391
2204
  const defaults = getDefaults();
2392
2205
  if (!defaults) return null;
2393
- const strokeWidth = defaults.strokeWidth;
2394
- const intensity = defaults.cloudyBorderIntensity ?? 0;
2395
- const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
2396
- const rect = {
2397
- origin: { x: minX - pad, y: minY - pad },
2398
- size: { width: width + 2 * pad, height: height + 2 * pad }
2399
- };
2206
+ const bounds = lineRectWithEndings(
2207
+ [start, current],
2208
+ defaults.strokeWidth,
2209
+ defaults.lineEndings
2210
+ );
2400
2211
  return {
2401
- type: PdfAnnotationSubtype.CIRCLE,
2402
- bounds: rect,
2212
+ type: PdfAnnotationSubtype.LINE,
2213
+ bounds,
2403
2214
  data: {
2404
- rect,
2405
2215
  ...defaults,
2406
- ...intensity > 0 && {
2407
- rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2408
- }
2216
+ rect: bounds,
2217
+ linePoints: { start, end: current }
2409
2218
  }
2410
2219
  };
2411
2220
  };
@@ -2427,33 +2236,30 @@ const circleHandlerFactory = {
2427
2236
  },
2428
2237
  onPointerUp: (pos, evt) => {
2429
2238
  var _a;
2430
- const p1 = getStart();
2431
- if (!p1) return;
2432
- const defaults = getDefaults();
2433
- if (!defaults) return;
2239
+ const start = getStart();
2240
+ if (!start) return;
2434
2241
  const clampedPos = clampToPage(pos);
2435
2242
  if (!clickDetector.hasMoved()) {
2436
2243
  clickDetector.onEnd(clampedPos);
2437
2244
  } else {
2438
- const defaults2 = getDefaults();
2439
- if (!defaults2) return;
2440
- const preview = getPreview(clampedPos);
2441
- if (preview) {
2442
- const intensity = defaults2.cloudyBorderIntensity ?? 0;
2443
- const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, true) : void 0;
2444
- const anno = {
2445
- ...defaults2,
2446
- type: PdfAnnotationSubtype.CIRCLE,
2245
+ const defaults = getDefaults();
2246
+ if (!defaults) return;
2247
+ if (Math.abs(clampedPos.x - start.x) > 2 || Math.abs(clampedPos.y - start.y) > 2) {
2248
+ const rect = lineRectWithEndings(
2249
+ [start, clampedPos],
2250
+ defaults.strokeWidth,
2251
+ defaults.lineEndings
2252
+ );
2253
+ onCommit({
2254
+ ...defaults,
2255
+ rect,
2256
+ linePoints: { start, end: clampedPos },
2257
+ pageIndex,
2258
+ id: uuidV4(),
2447
2259
  flags: ["print"],
2448
2260
  created: /* @__PURE__ */ new Date(),
2449
- id: uuidV4(),
2450
- pageIndex,
2451
- rect: preview.data.rect,
2452
- ...pad !== void 0 && {
2453
- rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2454
- }
2455
- };
2456
- onCommit(anno);
2261
+ type: PdfAnnotationSubtype.LINE
2262
+ });
2457
2263
  }
2458
2264
  }
2459
2265
  setStart(null);
@@ -2478,11 +2284,12 @@ const circleHandlerFactory = {
2478
2284
  };
2479
2285
  }
2480
2286
  };
2481
- const linkHandlerFactory = {
2482
- annotationType: PdfAnnotationSubtype.LINK,
2287
+ const polylineHandlerFactory = {
2288
+ annotationType: PdfAnnotationSubtype.POLYLINE,
2483
2289
  create(context) {
2484
- const { pageIndex, onCommit, onPreview, getTool, pageSize } = context;
2485
- const [getStart, setStart] = useState(null);
2290
+ const { onCommit, onPreview, getTool, pageSize } = context;
2291
+ const [getVertices, setVertices] = useState([]);
2292
+ const [getCurrent, setCurrent] = useState(null);
2486
2293
  const clampToPage = (pos) => ({
2487
2294
  x: clamp(pos.x, 0, pageSize.width),
2488
2295
  y: clamp(pos.y, 0, pageSize.height)
@@ -2492,11 +2299,236 @@ const linkHandlerFactory = {
2492
2299
  if (!tool) return null;
2493
2300
  return {
2494
2301
  ...tool.defaults,
2495
- flags: tool.defaults.flags ?? ["print"],
2302
+ strokeWidth: tool.defaults.strokeWidth ?? 1,
2303
+ lineEndings: tool.defaults.lineEndings ?? {
2304
+ start: PdfAnnotationLineEnding.None,
2305
+ end: PdfAnnotationLineEnding.None
2306
+ },
2307
+ color: tool.defaults.color ?? "#000000",
2308
+ opacity: tool.defaults.opacity ?? 1,
2309
+ strokeColor: tool.defaults.strokeColor ?? "#000000",
2310
+ strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
2311
+ strokeDashArray: tool.defaults.strokeDashArray ?? [],
2312
+ flags: tool.defaults.flags ?? ["print"]
2313
+ };
2314
+ };
2315
+ const commitPolyline = () => {
2316
+ const vertices = getVertices();
2317
+ if (vertices.length < 2) return;
2318
+ const defaults = getDefaults();
2319
+ if (!defaults) return;
2320
+ const rect = lineRectWithEndings(
2321
+ vertices,
2322
+ defaults.strokeWidth,
2323
+ defaults.lineEndings
2324
+ );
2325
+ const anno = {
2326
+ ...defaults,
2327
+ vertices,
2328
+ rect,
2329
+ type: PdfAnnotationSubtype.POLYLINE,
2330
+ pageIndex: context.pageIndex,
2331
+ id: uuidV4(),
2332
+ created: /* @__PURE__ */ new Date()
2333
+ };
2334
+ onCommit(anno);
2335
+ setVertices([]);
2336
+ setCurrent(null);
2337
+ onPreview(null);
2338
+ };
2339
+ const getPreview = () => {
2340
+ const vertices = getVertices();
2341
+ const currentPos = getCurrent();
2342
+ if (vertices.length === 0 || !currentPos) return null;
2343
+ const defaults = getDefaults();
2344
+ if (!defaults) return null;
2345
+ const allPoints = [...vertices, currentPos];
2346
+ const bounds = lineRectWithEndings(
2347
+ allPoints,
2348
+ defaults.strokeWidth,
2349
+ defaults.lineEndings
2350
+ );
2351
+ return {
2352
+ type: PdfAnnotationSubtype.POLYLINE,
2353
+ bounds,
2354
+ data: {
2355
+ ...defaults,
2356
+ rect: bounds,
2357
+ vertices: allPoints,
2358
+ currentVertex: currentPos
2359
+ }
2360
+ };
2361
+ };
2362
+ return {
2363
+ onClick: (pos, evt) => {
2364
+ if (evt.metaKey || evt.ctrlKey) {
2365
+ return;
2366
+ }
2367
+ const clampedPos = clampToPage(pos);
2368
+ const vertices = getVertices();
2369
+ const lastVertex = vertices[vertices.length - 1];
2370
+ if (lastVertex && Math.abs(lastVertex.x - clampedPos.x) < 1 && Math.abs(lastVertex.y - clampedPos.y) < 1) {
2371
+ return;
2372
+ }
2373
+ setVertices([...vertices, clampedPos]);
2374
+ setCurrent(clampedPos);
2375
+ onPreview(getPreview());
2376
+ },
2377
+ onDoubleClick: () => {
2378
+ commitPolyline();
2379
+ },
2380
+ onPointerMove: (pos) => {
2381
+ if (getVertices().length > 0) {
2382
+ const clampedPos = clampToPage(pos);
2383
+ setCurrent(clampedPos);
2384
+ onPreview(getPreview());
2385
+ }
2386
+ },
2387
+ onPointerCancel: () => {
2388
+ setVertices([]);
2389
+ setCurrent(null);
2390
+ onPreview(null);
2391
+ }
2392
+ };
2393
+ }
2394
+ };
2395
+ const HANDLE_SIZE_PX = 14;
2396
+ const polygonHandlerFactory = {
2397
+ annotationType: PdfAnnotationSubtype.POLYGON,
2398
+ create(context) {
2399
+ const { onCommit, onPreview, getTool, scale, pageSize } = context;
2400
+ const [getVertices, setVertices] = useState([]);
2401
+ const [getCurrent, setCurrent] = useState(null);
2402
+ const clampToPage = (pos) => ({
2403
+ x: clamp(pos.x, 0, pageSize.width),
2404
+ y: clamp(pos.y, 0, pageSize.height)
2405
+ });
2406
+ const isInsideStartHandle = (pos) => {
2407
+ const vertices = getVertices();
2408
+ if (vertices.length < 2) return false;
2409
+ const sizePDF = HANDLE_SIZE_PX / scale;
2410
+ const half = sizePDF / 2;
2411
+ const v0 = vertices[0];
2412
+ return pos.x >= v0.x - half && pos.x <= v0.x + half && pos.y >= v0.y - half && pos.y <= v0.y + half;
2413
+ };
2414
+ const getDefaults = () => {
2415
+ const tool = getTool();
2416
+ if (!tool) return null;
2417
+ return {
2418
+ ...tool.defaults,
2419
+ color: tool.defaults.color ?? "#000000",
2420
+ opacity: tool.defaults.opacity ?? 1,
2421
+ strokeWidth: tool.defaults.strokeWidth ?? 1,
2422
+ strokeColor: tool.defaults.strokeColor ?? "#000000",
2423
+ strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
2424
+ strokeDashArray: tool.defaults.strokeDashArray ?? [],
2425
+ flags: tool.defaults.flags ?? ["print"]
2426
+ };
2427
+ };
2428
+ const commitPolygon = () => {
2429
+ const vertices = getVertices();
2430
+ if (vertices.length < 3) return;
2431
+ const defaults = getDefaults();
2432
+ if (!defaults) return;
2433
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2434
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
2435
+ const rect = expandRect(rectFromPoints(vertices), pad);
2436
+ const anno = {
2437
+ ...defaults,
2438
+ vertices,
2439
+ rect,
2440
+ type: PdfAnnotationSubtype.POLYGON,
2441
+ pageIndex: context.pageIndex,
2442
+ id: uuidV4(),
2443
+ created: /* @__PURE__ */ new Date(),
2444
+ ...intensity > 0 && {
2445
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2446
+ }
2447
+ };
2448
+ onCommit(anno);
2449
+ setVertices([]);
2450
+ setCurrent(null);
2451
+ onPreview(null);
2452
+ };
2453
+ const getPreview = () => {
2454
+ const vertices = getVertices();
2455
+ const currentPos = getCurrent();
2456
+ if (vertices.length === 0 || !currentPos) return null;
2457
+ const defaults = getDefaults();
2458
+ if (!defaults) return null;
2459
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2460
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
2461
+ const allPoints = [...vertices, currentPos];
2462
+ const bounds = expandRect(rectFromPoints(allPoints), pad);
2463
+ return {
2464
+ type: PdfAnnotationSubtype.POLYGON,
2465
+ bounds,
2466
+ data: {
2467
+ ...defaults,
2468
+ rect: bounds,
2469
+ vertices,
2470
+ currentVertex: currentPos
2471
+ }
2472
+ };
2473
+ };
2474
+ return {
2475
+ onClick: (pos, evt) => {
2476
+ if (evt.metaKey || evt.ctrlKey) {
2477
+ return;
2478
+ }
2479
+ const clampedPos = clampToPage(pos);
2480
+ if (isInsideStartHandle(clampedPos) && getVertices().length >= 3) {
2481
+ commitPolygon();
2482
+ return;
2483
+ }
2484
+ const vertices = getVertices();
2485
+ const lastVertex = vertices[vertices.length - 1];
2486
+ if (lastVertex && Math.abs(lastVertex.x - clampedPos.x) < 1 && Math.abs(lastVertex.y - clampedPos.y) < 1) {
2487
+ return;
2488
+ }
2489
+ setVertices([...vertices, clampedPos]);
2490
+ setCurrent(clampedPos);
2491
+ onPreview(getPreview());
2492
+ },
2493
+ onDoubleClick: (_) => {
2494
+ commitPolygon();
2495
+ },
2496
+ onPointerMove: (pos) => {
2497
+ if (getVertices().length > 0) {
2498
+ const clampedPos = clampToPage(pos);
2499
+ setCurrent(clampedPos);
2500
+ onPreview(getPreview());
2501
+ }
2502
+ },
2503
+ onPointerCancel: (_) => {
2504
+ setVertices([]);
2505
+ setCurrent(null);
2506
+ onPreview(null);
2507
+ }
2508
+ };
2509
+ }
2510
+ };
2511
+ const squareHandlerFactory = {
2512
+ annotationType: PdfAnnotationSubtype.SQUARE,
2513
+ create(context) {
2514
+ const { pageIndex, onCommit, onPreview, getTool, pageSize } = context;
2515
+ const [getStart, setStart] = useState(null);
2516
+ const clampToPage = (pos) => ({
2517
+ x: clamp(pos.x, 0, pageSize.width),
2518
+ y: clamp(pos.y, 0, pageSize.height)
2519
+ });
2520
+ const getDefaults = () => {
2521
+ const tool = getTool();
2522
+ if (!tool) return null;
2523
+ return {
2524
+ ...tool.defaults,
2525
+ flags: tool.defaults.flags ?? ["print"],
2496
2526
  strokeWidth: tool.defaults.strokeWidth ?? 2,
2497
- strokeColor: tool.defaults.strokeColor ?? "#0000FF",
2498
- strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.UNDERLINE,
2499
- strokeDashArray: tool.defaults.strokeDashArray ?? []
2527
+ strokeColor: tool.defaults.strokeColor ?? "#000000",
2528
+ strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
2529
+ strokeDashArray: tool.defaults.strokeDashArray ?? [],
2530
+ color: tool.defaults.color ?? "#000000",
2531
+ opacity: tool.defaults.opacity ?? 1
2500
2532
  };
2501
2533
  };
2502
2534
  const clickDetector = useClickDetector({
@@ -2512,18 +2544,23 @@ const linkHandlerFactory = {
2512
2544
  const halfHeight = height / 2;
2513
2545
  const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2514
2546
  const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2547
+ const strokeWidth = defaults.strokeWidth;
2548
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2549
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
2515
2550
  const rect = {
2516
- origin: { x, y },
2517
- size: { width, height }
2551
+ origin: { x: x - pad, y: y - pad },
2552
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2518
2553
  };
2519
2554
  const anno = {
2520
2555
  ...defaults,
2521
- type: PdfAnnotationSubtype.LINK,
2522
- target: void 0,
2556
+ type: PdfAnnotationSubtype.SQUARE,
2523
2557
  created: /* @__PURE__ */ new Date(),
2524
2558
  id: uuidV4(),
2525
2559
  pageIndex,
2526
- rect
2560
+ rect,
2561
+ ...intensity > 0 && {
2562
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2563
+ }
2527
2564
  };
2528
2565
  onCommit(anno);
2529
2566
  }
@@ -2537,19 +2574,22 @@ const linkHandlerFactory = {
2537
2574
  const height = Math.abs(p1.y - current.y);
2538
2575
  const defaults = getDefaults();
2539
2576
  if (!defaults) return null;
2577
+ const strokeWidth = defaults.strokeWidth;
2578
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2579
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
2540
2580
  const rect = {
2541
- origin: { x: minX, y: minY },
2542
- size: { width, height }
2581
+ origin: { x: minX - pad, y: minY - pad },
2582
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2543
2583
  };
2544
2584
  return {
2545
- type: PdfAnnotationSubtype.LINK,
2585
+ type: PdfAnnotationSubtype.SQUARE,
2546
2586
  bounds: rect,
2547
2587
  data: {
2548
2588
  rect,
2549
- strokeColor: defaults.strokeColor,
2550
- strokeWidth: defaults.strokeWidth,
2551
- strokeStyle: defaults.strokeStyle,
2552
- strokeDashArray: defaults.strokeDashArray
2589
+ ...defaults,
2590
+ ...intensity > 0 && {
2591
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2592
+ }
2553
2593
  }
2554
2594
  };
2555
2595
  };
@@ -2579,16 +2619,22 @@ const linkHandlerFactory = {
2579
2619
  if (!clickDetector.hasMoved()) {
2580
2620
  clickDetector.onEnd(clampedPos);
2581
2621
  } else {
2622
+ const defaults2 = getDefaults();
2623
+ if (!defaults2) return;
2582
2624
  const preview = getPreview(clampedPos);
2583
2625
  if (preview) {
2626
+ const intensity = defaults2.cloudyBorderIntensity ?? 0;
2627
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, false) : void 0;
2584
2628
  const anno = {
2585
- ...defaults,
2586
- type: PdfAnnotationSubtype.LINK,
2587
- target: void 0,
2629
+ ...defaults2,
2630
+ type: PdfAnnotationSubtype.SQUARE,
2588
2631
  created: /* @__PURE__ */ new Date(),
2589
2632
  id: uuidV4(),
2590
2633
  pageIndex,
2591
- rect: preview.data.rect
2634
+ rect: preview.data.rect,
2635
+ ...pad !== void 0 && {
2636
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2637
+ }
2592
2638
  };
2593
2639
  onCommit(anno);
2594
2640
  }
@@ -2615,585 +2661,547 @@ const linkHandlerFactory = {
2615
2661
  };
2616
2662
  }
2617
2663
  };
2618
- const textMarkupSelectionHandler = {
2619
- toolId: "__textMarkup__",
2620
- handle(context, selections, getText) {
2621
- const tool = context.getTool();
2622
- if (!tool) return;
2623
- for (const selection of selections) {
2624
- const id = uuidV4();
2625
- getText().then((text) => {
2626
- var _a;
2627
- context.createAnnotation(selection.pageIndex, {
2628
- ...tool.defaults,
2629
- rect: selection.rect,
2630
- segmentRects: selection.segmentRects,
2631
- pageIndex: selection.pageIndex,
2632
- created: /* @__PURE__ */ new Date(),
2633
- id,
2634
- ...text != null && { custom: { text } }
2635
- });
2636
- if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
2637
- context.selectAnnotation(selection.pageIndex, id);
2638
- }
2639
- });
2640
- }
2641
- }
2642
- };
2643
- function computeCaretRect(lastSegRect) {
2644
- const lineHeight = lastSegRect.size.height;
2645
- const height = lineHeight / 2;
2646
- const width = height;
2647
- const lineEndX = lastSegRect.origin.x + lastSegRect.size.width;
2648
- return {
2649
- origin: {
2650
- x: lineEndX - width / 2,
2651
- y: lastSegRect.origin.y + lineHeight / 2
2652
- },
2653
- size: { width, height }
2654
- };
2655
- }
2656
- const insertTextSelectionHandler = {
2657
- toolId: "insertText",
2658
- handle(context, selections, getText) {
2659
- const tool = context.getTool();
2660
- if (!tool) return;
2661
- const getDefaults = () => ({
2662
- strokeColor: tool.defaults.strokeColor ?? "#E44234",
2663
- opacity: tool.defaults.opacity ?? 1,
2664
- flags: tool.defaults.flags ?? ["print"]
2665
- });
2666
- for (const selection of selections) {
2667
- const lastSegRect = selection.segmentRects[selection.segmentRects.length - 1];
2668
- if (!lastSegRect) continue;
2669
- const caretRect = computeCaretRect(lastSegRect);
2670
- const caretId = uuidV4();
2671
- const defaults = getDefaults();
2672
- getText().then((text) => {
2673
- var _a;
2674
- context.createAnnotation(selection.pageIndex, {
2675
- type: PdfAnnotationSubtype.CARET,
2676
- id: caretId,
2677
- pageIndex: selection.pageIndex,
2678
- rect: caretRect,
2679
- strokeColor: defaults.strokeColor,
2680
- opacity: defaults.opacity,
2681
- intent: "Insert",
2682
- rectangleDifferences: { left: 0.5, top: 0.5, right: 0.5, bottom: 0.5 },
2683
- created: /* @__PURE__ */ new Date(),
2684
- flags: defaults.flags,
2685
- ...text != null && { custom: { text } }
2686
- });
2687
- if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
2688
- context.selectAnnotation(selection.pageIndex, caretId);
2689
- }
2690
- });
2691
- }
2692
- }
2693
- };
2694
- const replaceTextSelectionHandler = {
2695
- toolId: "replaceText",
2696
- handle(context, selections, getText) {
2697
- const tool = context.getTool();
2698
- if (!tool) return;
2699
- const getDefaults = () => ({
2700
- strokeColor: tool.defaults.strokeColor ?? "#E44234",
2701
- opacity: tool.defaults.opacity ?? 1,
2702
- flags: tool.defaults.flags ?? ["print"]
2703
- });
2704
- for (const selection of selections) {
2705
- const lastSegRect = selection.segmentRects[selection.segmentRects.length - 1];
2706
- if (!lastSegRect) continue;
2707
- const caretRect = computeCaretRect(lastSegRect);
2708
- const caretId = uuidV4();
2709
- const strikeoutId = uuidV4();
2710
- const defaults = getDefaults();
2711
- getText().then((text) => {
2712
- var _a;
2713
- context.createAnnotation(selection.pageIndex, {
2714
- type: PdfAnnotationSubtype.CARET,
2715
- id: caretId,
2716
- pageIndex: selection.pageIndex,
2717
- rect: caretRect,
2718
- strokeColor: defaults.strokeColor,
2719
- opacity: defaults.opacity,
2720
- intent: "Replace",
2721
- rectangleDifferences: { left: 0.5, top: 0.5, right: 0.5, bottom: 0.5 },
2722
- created: /* @__PURE__ */ new Date(),
2723
- flags: defaults.flags
2724
- });
2725
- context.createAnnotation(selection.pageIndex, {
2726
- type: PdfAnnotationSubtype.STRIKEOUT,
2727
- id: strikeoutId,
2728
- pageIndex: selection.pageIndex,
2729
- rect: selection.rect,
2730
- segmentRects: selection.segmentRects,
2731
- strokeColor: defaults.strokeColor,
2732
- opacity: defaults.opacity,
2733
- intent: "StrikeOutTextEdit",
2734
- inReplyToId: caretId,
2735
- replyType: PdfAnnotationReplyType.Group,
2736
- created: /* @__PURE__ */ new Date(),
2737
- flags: defaults.flags,
2738
- ...text != null && { custom: { text } }
2739
- });
2740
- if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
2741
- context.selectAnnotation(selection.pageIndex, caretId);
2742
- }
2743
- });
2744
- }
2745
- }
2746
- };
2747
- const patchInk = (original, ctx) => {
2748
- switch (ctx.type) {
2749
- case "vertex-edit":
2750
- return ctx.changes;
2751
- case "move": {
2752
- if (!ctx.changes.rect) return ctx.changes;
2753
- const { dx, dy, rects } = baseMoveChanges(original, ctx.changes.rect);
2754
- return {
2755
- ...rects,
2756
- inkList: original.inkList.map((stroke) => ({
2757
- points: stroke.points.map((p) => ({ x: p.x + dx, y: p.y + dy }))
2758
- }))
2759
- };
2760
- }
2761
- case "resize": {
2762
- if (!ctx.changes.rect) return ctx.changes;
2763
- const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
2764
- original,
2765
- ctx.changes.rect,
2766
- ctx.metadata
2767
- );
2768
- const inset = (r2, pad) => ({
2769
- origin: { x: r2.origin.x + pad, y: r2.origin.y + pad },
2770
- size: {
2771
- width: Math.max(1, r2.size.width - pad * 2),
2772
- height: Math.max(1, r2.size.height - pad * 2)
2773
- }
2774
- });
2775
- const resizeEpsilon = 1e-3;
2776
- const widthChanged = Math.abs(scaleX - 1) > resizeEpsilon;
2777
- const heightChanged = Math.abs(scaleY - 1) > resizeEpsilon;
2778
- const strokeScale = widthChanged && !heightChanged ? scaleX : !widthChanged && heightChanged ? scaleY : Math.min(scaleX, scaleY);
2779
- const rawStrokeWidth = Math.max(1, original.strokeWidth * strokeScale);
2780
- const maxStrokeWidth = Math.max(
2781
- 1,
2782
- Math.min(resolvedRect.size.width, resolvedRect.size.height)
2783
- );
2784
- const clampedStrokeWidth = Math.min(rawStrokeWidth, maxStrokeWidth);
2785
- const newStrokeWidth = Number(clampedStrokeWidth.toFixed(1));
2786
- const innerOld = inset(oldRect, original.strokeWidth / 2);
2787
- const innerNew = inset(resolvedRect, newStrokeWidth / 2);
2788
- const sx = innerNew.size.width / Math.max(innerOld.size.width, 1e-6);
2789
- const sy = innerNew.size.height / Math.max(innerOld.size.height, 1e-6);
2790
- return {
2791
- ...rects,
2792
- inkList: original.inkList.map((stroke) => ({
2793
- points: stroke.points.map((p) => ({
2794
- x: innerNew.origin.x + (p.x - innerOld.origin.x) * sx,
2795
- y: innerNew.origin.y + (p.y - innerOld.origin.y) * sy
2796
- }))
2797
- })),
2798
- strokeWidth: newStrokeWidth
2664
+ const imageFetchCache = /* @__PURE__ */ new Map();
2665
+ const stampHandlerFactory = {
2666
+ annotationType: PdfAnnotationSubtype.STAMP,
2667
+ create(context) {
2668
+ const { services, onCommit, onPreview, getTool, pageSize, pageRotation } = context;
2669
+ let cachedBuffer = null;
2670
+ let cachedSize = null;
2671
+ const commitStamp = (pos, width, height, ctx) => {
2672
+ var _a;
2673
+ const tool = getTool();
2674
+ if (!tool) return;
2675
+ const rect = {
2676
+ origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
2677
+ size: { width, height }
2799
2678
  };
2800
- }
2801
- case "rotate": {
2802
- const result = baseRotateChanges(original, ctx);
2803
- if (!result) return ctx.changes;
2804
- const { dx, dy } = rotateOrbitDelta(original, result);
2805
- return {
2806
- ...result,
2807
- inkList: original.inkList.map((stroke) => ({
2808
- points: stroke.points.map((p) => ({ x: p.x + dx, y: p.y + dy }))
2809
- }))
2679
+ let anno = {
2680
+ ...tool.defaults,
2681
+ rect,
2682
+ type: PdfAnnotationSubtype.STAMP,
2683
+ name: tool.defaults.name ?? PdfAnnotationName.Image,
2684
+ subject: tool.defaults.subject ?? "Stamp",
2685
+ flags: tool.defaults.flags ?? ["print"],
2686
+ pageIndex: context.pageIndex,
2687
+ id: uuidV4(),
2688
+ created: /* @__PURE__ */ new Date()
2810
2689
  };
2811
- }
2812
- case "property-update": {
2813
- const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0;
2814
- if (!needsRectUpdate) return ctx.changes;
2815
- const merged = { ...original, ...ctx.changes };
2816
- const pts = merged.inkList.flatMap((s) => s.points);
2817
- const tightRect = expandRect(rectFromPoints(pts), merged.strokeWidth / 2);
2818
- const effectiveRotation = ctx.changes.rotation ?? original.rotation ?? 0;
2819
- if (original.unrotatedRect || ctx.changes.rotation !== void 0) {
2820
- return {
2821
- ...ctx.changes,
2822
- unrotatedRect: tightRect,
2823
- rect: calculateRotatedRectAABBAroundPoint(
2824
- tightRect,
2825
- effectiveRotation,
2826
- resolveAnnotationRotationCenter(original)
2827
- )
2690
+ if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
2691
+ anno = applyInsertUpright(anno, pageRotation, false);
2692
+ }
2693
+ anno = clampAnnotationToPage(anno, pageSize);
2694
+ onCommit(anno, ctx);
2695
+ };
2696
+ const commitFromBuffer = (pos, buffer, imageSize) => {
2697
+ const meta = getImageMetadata(buffer);
2698
+ if (!meta || meta.mimeType === "application/pdf") return false;
2699
+ const fitted = fitSizeWithin(meta, pageSize);
2700
+ const width = (imageSize == null ? void 0 : imageSize.width) ?? fitted.width;
2701
+ const height = (imageSize == null ? void 0 : imageSize.height) ?? fitted.height;
2702
+ commitStamp(pos, width, height, { data: buffer });
2703
+ return true;
2704
+ };
2705
+ return {
2706
+ onHandlerActiveStart: () => {
2707
+ const tool = getTool();
2708
+ const imageSrc = tool == null ? void 0 : tool.defaults.imageSrc;
2709
+ if (!imageSrc) return;
2710
+ let entry = imageFetchCache.get(imageSrc);
2711
+ if (!entry) {
2712
+ const promise = fetch(imageSrc).then((res) => res.arrayBuffer()).then((buffer) => {
2713
+ const meta = getImageMetadata(buffer);
2714
+ if (!meta || meta.mimeType === "application/pdf") return null;
2715
+ const fitted = fitSizeWithin(meta, pageSize);
2716
+ const imageSize = tool.defaults.imageSize;
2717
+ return {
2718
+ buffer,
2719
+ width: (imageSize == null ? void 0 : imageSize.width) ?? fitted.width,
2720
+ height: (imageSize == null ? void 0 : imageSize.height) ?? fitted.height
2721
+ };
2722
+ }).catch(() => null);
2723
+ entry = { promise, refs: 1 };
2724
+ imageFetchCache.set(imageSrc, entry);
2725
+ } else {
2726
+ entry.refs++;
2727
+ }
2728
+ entry.promise.then((result) => {
2729
+ if (!result) return;
2730
+ cachedBuffer = result.buffer;
2731
+ cachedSize = { width: result.width, height: result.height };
2732
+ });
2733
+ },
2734
+ onHandlerActiveEnd: () => {
2735
+ const tool = getTool();
2736
+ const imageSrc = tool == null ? void 0 : tool.defaults.imageSrc;
2737
+ if (imageSrc) {
2738
+ const entry = imageFetchCache.get(imageSrc);
2739
+ if (entry && --entry.refs <= 0) {
2740
+ imageFetchCache.delete(imageSrc);
2741
+ }
2742
+ }
2743
+ cachedBuffer = null;
2744
+ cachedSize = null;
2745
+ onPreview(null);
2746
+ },
2747
+ onPointerMove: (pos) => {
2748
+ var _a;
2749
+ const tool = getTool();
2750
+ if (!((_a = tool == null ? void 0 : tool.behavior) == null ? void 0 : _a.showGhost) || !cachedSize || !tool.defaults.imageSrc) return;
2751
+ const rect = {
2752
+ origin: { x: pos.x - cachedSize.width / 2, y: pos.y - cachedSize.height / 2 },
2753
+ size: cachedSize
2828
2754
  };
2755
+ onPreview({
2756
+ type: PdfAnnotationSubtype.STAMP,
2757
+ bounds: rect,
2758
+ data: { rect, ghostUrl: tool.defaults.imageSrc, pageRotation }
2759
+ });
2760
+ },
2761
+ onPointerDown: (pos) => {
2762
+ const tool = getTool();
2763
+ if (!tool) return;
2764
+ const { imageSrc, imageSize } = tool.defaults;
2765
+ if (imageSrc) {
2766
+ onPreview(null);
2767
+ if (cachedBuffer) {
2768
+ commitFromBuffer(pos, cachedBuffer, imageSize);
2769
+ } else {
2770
+ fetch(imageSrc).then((res) => res.arrayBuffer()).then((buffer) => commitFromBuffer(pos, buffer, imageSize));
2771
+ }
2772
+ } else {
2773
+ services.requestFile({
2774
+ accept: "image/png,image/jpeg",
2775
+ onFile: (file) => {
2776
+ file.arrayBuffer().then((buffer) => commitFromBuffer(pos, buffer));
2777
+ }
2778
+ });
2779
+ }
2780
+ },
2781
+ onPointerLeave: () => {
2782
+ onPreview(null);
2829
2783
  }
2830
- return { ...ctx.changes, rect: tightRect };
2831
- }
2832
- default:
2833
- return ctx.changes;
2784
+ };
2834
2785
  }
2835
2786
  };
2836
- const patchLine = (orig, ctx) => {
2837
- switch (ctx.type) {
2838
- case "vertex-edit":
2839
- if (ctx.changes.linePoints) {
2840
- const { start, end } = ctx.changes.linePoints;
2841
- const rawPoints = [start, end];
2842
- const rawRect = lineRectWithEndings(rawPoints, orig.strokeWidth, orig.lineEndings);
2843
- const compensated = compensateRotatedVertexEdit(orig, rawPoints, rawRect);
2844
- const rect = lineRectWithEndings(compensated, orig.strokeWidth, orig.lineEndings);
2845
- return {
2846
- ...resolveVertexEditRects(orig, rect),
2847
- linePoints: { start: compensated[0], end: compensated[1] }
2787
+ const circleHandlerFactory = {
2788
+ annotationType: PdfAnnotationSubtype.CIRCLE,
2789
+ create(context) {
2790
+ const { pageIndex, onCommit, onPreview, getTool, pageSize } = context;
2791
+ const [getStart, setStart] = useState(null);
2792
+ const clampToPage = (pos) => ({
2793
+ x: clamp(pos.x, 0, pageSize.width),
2794
+ y: clamp(pos.y, 0, pageSize.height)
2795
+ });
2796
+ const getDefaults = () => {
2797
+ const tool = getTool();
2798
+ if (!tool) return null;
2799
+ return {
2800
+ ...tool.defaults,
2801
+ strokeWidth: tool.defaults.strokeWidth ?? 2,
2802
+ strokeColor: tool.defaults.strokeColor ?? "#000000",
2803
+ strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.SOLID,
2804
+ strokeDashArray: tool.defaults.strokeDashArray ?? [],
2805
+ color: tool.defaults.color ?? "#000000",
2806
+ opacity: tool.defaults.opacity ?? 1,
2807
+ flags: tool.defaults.flags ?? ["print"]
2808
+ };
2809
+ };
2810
+ const clickDetector = useClickDetector({
2811
+ threshold: 5,
2812
+ getTool,
2813
+ onClickDetected: (pos, tool) => {
2814
+ const defaults = getDefaults();
2815
+ if (!defaults) return;
2816
+ const clickConfig = tool.clickBehavior;
2817
+ if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
2818
+ const { width, height } = clickConfig.defaultSize;
2819
+ const halfWidth = width / 2;
2820
+ const halfHeight = height / 2;
2821
+ const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2822
+ const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2823
+ const strokeWidth = defaults.strokeWidth;
2824
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2825
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
2826
+ const rect = {
2827
+ origin: { x: x - pad, y: y - pad },
2828
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2829
+ };
2830
+ const anno = {
2831
+ ...defaults,
2832
+ type: PdfAnnotationSubtype.CIRCLE,
2833
+ created: /* @__PURE__ */ new Date(),
2834
+ id: uuidV4(),
2835
+ pageIndex,
2836
+ rect,
2837
+ ...intensity > 0 && {
2838
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2839
+ }
2848
2840
  };
2841
+ onCommit(anno);
2849
2842
  }
2850
- return ctx.changes;
2851
- case "move": {
2852
- if (!ctx.changes.rect) return ctx.changes;
2853
- const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
2854
- return {
2855
- ...rects,
2856
- linePoints: {
2857
- start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
2858
- end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
2859
- }
2843
+ });
2844
+ const getPreview = (current) => {
2845
+ const p1 = getStart();
2846
+ if (!p1) return null;
2847
+ const minX = Math.min(p1.x, current.x);
2848
+ const minY = Math.min(p1.y, current.y);
2849
+ const width = Math.abs(p1.x - current.x);
2850
+ const height = Math.abs(p1.y - current.y);
2851
+ const defaults = getDefaults();
2852
+ if (!defaults) return null;
2853
+ const strokeWidth = defaults.strokeWidth;
2854
+ const intensity = defaults.cloudyBorderIntensity ?? 0;
2855
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
2856
+ const rect = {
2857
+ origin: { x: minX - pad, y: minY - pad },
2858
+ size: { width: width + 2 * pad, height: height + 2 * pad }
2860
2859
  };
2861
- }
2862
- case "resize": {
2863
- if (!ctx.changes.rect) return ctx.changes;
2864
- const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
2865
- orig,
2866
- ctx.changes.rect,
2867
- ctx.metadata
2868
- );
2869
2860
  return {
2870
- ...rects,
2871
- linePoints: {
2872
- start: {
2873
- x: resolvedRect.origin.x + (orig.linePoints.start.x - oldRect.origin.x) * scaleX,
2874
- y: resolvedRect.origin.y + (orig.linePoints.start.y - oldRect.origin.y) * scaleY
2875
- },
2876
- end: {
2877
- x: resolvedRect.origin.x + (orig.linePoints.end.x - oldRect.origin.x) * scaleX,
2878
- y: resolvedRect.origin.y + (orig.linePoints.end.y - oldRect.origin.y) * scaleY
2861
+ type: PdfAnnotationSubtype.CIRCLE,
2862
+ bounds: rect,
2863
+ data: {
2864
+ rect,
2865
+ ...defaults,
2866
+ ...intensity > 0 && {
2867
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2879
2868
  }
2880
2869
  }
2881
2870
  };
2882
- }
2883
- case "rotate": {
2884
- const result = baseRotateChanges(orig, ctx);
2885
- if (!result) return ctx.changes;
2886
- const { dx, dy } = rotateOrbitDelta(orig, result);
2887
- return {
2888
- ...result,
2889
- linePoints: {
2890
- start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
2891
- end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
2871
+ };
2872
+ return {
2873
+ onPointerDown: (pos, evt) => {
2874
+ var _a;
2875
+ const clampedPos = clampToPage(pos);
2876
+ setStart(clampedPos);
2877
+ clickDetector.onStart(clampedPos);
2878
+ onPreview(getPreview(clampedPos));
2879
+ (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
2880
+ },
2881
+ onPointerMove: (pos) => {
2882
+ const clampedPos = clampToPage(pos);
2883
+ clickDetector.onMove(clampedPos);
2884
+ if (getStart() && clickDetector.hasMoved()) {
2885
+ onPreview(getPreview(clampedPos));
2892
2886
  }
2893
- };
2894
- }
2895
- case "property-update": {
2896
- const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0 || ctx.changes.rotation !== void 0;
2897
- if (!needsRectUpdate) return ctx.changes;
2898
- const merged = { ...orig, ...ctx.changes };
2899
- const tightRect = lineRectWithEndings(
2900
- [merged.linePoints.start, merged.linePoints.end],
2901
- merged.strokeWidth,
2902
- merged.lineEndings
2903
- );
2904
- const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
2905
- if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
2906
- return {
2907
- ...ctx.changes,
2908
- unrotatedRect: tightRect,
2909
- rect: calculateRotatedRectAABBAroundPoint(
2910
- tightRect,
2911
- effectiveRotation,
2912
- resolveAnnotationRotationCenter(orig)
2913
- )
2914
- };
2887
+ },
2888
+ onPointerUp: (pos, evt) => {
2889
+ var _a;
2890
+ const p1 = getStart();
2891
+ if (!p1) return;
2892
+ const defaults = getDefaults();
2893
+ if (!defaults) return;
2894
+ const clampedPos = clampToPage(pos);
2895
+ if (!clickDetector.hasMoved()) {
2896
+ clickDetector.onEnd(clampedPos);
2897
+ } else {
2898
+ const defaults2 = getDefaults();
2899
+ if (!defaults2) return;
2900
+ const preview = getPreview(clampedPos);
2901
+ if (preview) {
2902
+ const intensity = defaults2.cloudyBorderIntensity ?? 0;
2903
+ const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, true) : void 0;
2904
+ const anno = {
2905
+ ...defaults2,
2906
+ type: PdfAnnotationSubtype.CIRCLE,
2907
+ flags: ["print"],
2908
+ created: /* @__PURE__ */ new Date(),
2909
+ id: uuidV4(),
2910
+ pageIndex,
2911
+ rect: preview.data.rect,
2912
+ ...pad !== void 0 && {
2913
+ rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
2914
+ }
2915
+ };
2916
+ onCommit(anno);
2917
+ }
2918
+ }
2919
+ setStart(null);
2920
+ onPreview(null);
2921
+ clickDetector.reset();
2922
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
2923
+ },
2924
+ onPointerLeave: (_, evt) => {
2925
+ var _a;
2926
+ setStart(null);
2927
+ onPreview(null);
2928
+ clickDetector.reset();
2929
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
2930
+ },
2931
+ onPointerCancel: (_, evt) => {
2932
+ var _a;
2933
+ setStart(null);
2934
+ onPreview(null);
2935
+ clickDetector.reset();
2936
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
2915
2937
  }
2916
- return { ...ctx.changes, rect: tightRect };
2917
- }
2918
- default:
2919
- return ctx.changes;
2938
+ };
2920
2939
  }
2921
2940
  };
2922
- const patchPolyline = (orig, ctx) => {
2923
- switch (ctx.type) {
2924
- case "vertex-edit":
2925
- if (ctx.changes.vertices && ctx.changes.vertices.length) {
2926
- const rawVertices = ctx.changes.vertices;
2927
- const rawRect = lineRectWithEndings(rawVertices, orig.strokeWidth, orig.lineEndings);
2928
- const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
2929
- const rect = lineRectWithEndings(compensated, orig.strokeWidth, orig.lineEndings);
2930
- return {
2931
- ...resolveVertexEditRects(orig, rect),
2932
- vertices: compensated
2933
- };
2934
- }
2935
- return ctx.changes;
2936
- case "move": {
2937
- if (!ctx.changes.rect) return ctx.changes;
2938
- const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
2939
- return {
2940
- ...rects,
2941
- vertices: orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }))
2942
- };
2943
- }
2944
- case "resize": {
2945
- if (!ctx.changes.rect) return ctx.changes;
2946
- const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
2947
- orig,
2948
- ctx.changes.rect,
2949
- ctx.metadata
2950
- );
2951
- return {
2952
- ...rects,
2953
- vertices: orig.vertices.map((v) => ({
2954
- x: resolvedRect.origin.x + (v.x - oldRect.origin.x) * scaleX,
2955
- y: resolvedRect.origin.y + (v.y - oldRect.origin.y) * scaleY
2956
- }))
2957
- };
2958
- }
2959
- case "rotate": {
2960
- const result = baseRotateChanges(orig, ctx);
2961
- if (!result) return ctx.changes;
2962
- const { dx, dy } = rotateOrbitDelta(orig, result);
2941
+ const linkHandlerFactory = {
2942
+ annotationType: PdfAnnotationSubtype.LINK,
2943
+ create(context) {
2944
+ const { pageIndex, onCommit, onPreview, getTool, pageSize } = context;
2945
+ const [getStart, setStart] = useState(null);
2946
+ const clampToPage = (pos) => ({
2947
+ x: clamp(pos.x, 0, pageSize.width),
2948
+ y: clamp(pos.y, 0, pageSize.height)
2949
+ });
2950
+ const getDefaults = () => {
2951
+ const tool = getTool();
2952
+ if (!tool) return null;
2963
2953
  return {
2964
- ...result,
2965
- vertices: orig.vertices.map((v) => ({ x: v.x + dx, y: v.y + dy }))
2954
+ ...tool.defaults,
2955
+ flags: tool.defaults.flags ?? ["print"],
2956
+ strokeWidth: tool.defaults.strokeWidth ?? 2,
2957
+ strokeColor: tool.defaults.strokeColor ?? "#0000FF",
2958
+ strokeStyle: tool.defaults.strokeStyle ?? PdfAnnotationBorderStyle.UNDERLINE,
2959
+ strokeDashArray: tool.defaults.strokeDashArray ?? []
2966
2960
  };
2967
- }
2968
- case "property-update": {
2969
- const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0 || ctx.changes.rotation !== void 0;
2970
- if (!needsRectUpdate) return ctx.changes;
2971
- const merged = { ...orig, ...ctx.changes };
2972
- const tightRect = lineRectWithEndings(
2973
- merged.vertices,
2974
- merged.strokeWidth,
2975
- merged.lineEndings
2976
- );
2977
- const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
2978
- if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
2979
- return {
2980
- ...ctx.changes,
2981
- unrotatedRect: tightRect,
2982
- rect: calculateRotatedRectAABBAroundPoint(
2983
- tightRect,
2984
- effectiveRotation,
2985
- resolveAnnotationRotationCenter(orig)
2986
- )
2961
+ };
2962
+ const clickDetector = useClickDetector({
2963
+ threshold: 5,
2964
+ getTool,
2965
+ onClickDetected: (pos, tool) => {
2966
+ const defaults = getDefaults();
2967
+ if (!defaults) return;
2968
+ const clickConfig = tool.clickBehavior;
2969
+ if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
2970
+ const { width, height } = clickConfig.defaultSize;
2971
+ const halfWidth = width / 2;
2972
+ const halfHeight = height / 2;
2973
+ const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
2974
+ const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
2975
+ const rect = {
2976
+ origin: { x, y },
2977
+ size: { width, height }
2987
2978
  };
2988
- }
2989
- return { ...ctx.changes, rect: tightRect };
2990
- }
2991
- default:
2992
- return ctx.changes;
2993
- }
2994
- };
2995
- function getPolygonPad(intensity, strokeWidth) {
2996
- if ((intensity ?? 0) > 0) {
2997
- return getCloudyBorderExtent(intensity, strokeWidth, false);
2998
- }
2999
- return strokeWidth / 2;
3000
- }
3001
- const patchPolygon = (orig, ctx) => {
3002
- switch (ctx.type) {
3003
- case "vertex-edit":
3004
- if (ctx.changes.vertices && ctx.changes.vertices.length) {
3005
- const pad = getPolygonPad(orig.cloudyBorderIntensity, orig.strokeWidth);
3006
- const rawVertices = ctx.changes.vertices;
3007
- const rawRect = expandRect(rectFromPoints(rawVertices), pad);
3008
- const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
3009
- const rect = expandRect(rectFromPoints(compensated), pad);
3010
- return {
3011
- ...resolveVertexEditRects(orig, rect),
3012
- vertices: compensated
2979
+ const anno = {
2980
+ ...defaults,
2981
+ type: PdfAnnotationSubtype.LINK,
2982
+ target: void 0,
2983
+ created: /* @__PURE__ */ new Date(),
2984
+ id: uuidV4(),
2985
+ pageIndex,
2986
+ rect
3013
2987
  };
2988
+ onCommit(anno);
3014
2989
  }
3015
- return ctx.changes;
3016
- case "move": {
3017
- if (!ctx.changes.rect) return ctx.changes;
3018
- const { dx, dy, rects } = baseMoveChanges(orig, ctx.changes.rect);
3019
- return {
3020
- ...rects,
3021
- vertices: orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }))
3022
- };
3023
- }
3024
- case "resize": {
3025
- if (!ctx.changes.rect) return ctx.changes;
3026
- const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
3027
- orig,
3028
- ctx.changes.rect,
3029
- ctx.metadata
3030
- );
3031
- return {
3032
- ...rects,
3033
- vertices: orig.vertices.map((v) => ({
3034
- x: resolvedRect.origin.x + (v.x - oldRect.origin.x) * scaleX,
3035
- y: resolvedRect.origin.y + (v.y - oldRect.origin.y) * scaleY
3036
- }))
2990
+ });
2991
+ const getPreview = (current) => {
2992
+ const p1 = getStart();
2993
+ if (!p1) return null;
2994
+ const minX = Math.min(p1.x, current.x);
2995
+ const minY = Math.min(p1.y, current.y);
2996
+ const width = Math.abs(p1.x - current.x);
2997
+ const height = Math.abs(p1.y - current.y);
2998
+ const defaults = getDefaults();
2999
+ if (!defaults) return null;
3000
+ const rect = {
3001
+ origin: { x: minX, y: minY },
3002
+ size: { width, height }
3037
3003
  };
3038
- }
3039
- case "rotate": {
3040
- const result = baseRotateChanges(orig, ctx);
3041
- if (!result) return ctx.changes;
3042
- const { dx, dy } = rotateOrbitDelta(orig, result);
3043
3004
  return {
3044
- ...result,
3045
- vertices: orig.vertices.map((v) => ({ x: v.x + dx, y: v.y + dy }))
3046
- };
3047
- }
3048
- case "property-update": {
3049
- const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3050
- const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0 || cloudyChanged;
3051
- if (!needsRectUpdate) return ctx.changes;
3052
- const merged = { ...orig, ...ctx.changes };
3053
- const pad = getPolygonPad(merged.cloudyBorderIntensity, merged.strokeWidth);
3054
- const tightRect = expandRect(rectFromPoints(merged.vertices), pad);
3055
- let patch = ctx.changes;
3056
- const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3057
- if (cloudyChanged || ctx.changes.strokeWidth !== void 0 && hasCloudy) {
3058
- const intensity = merged.cloudyBorderIntensity ?? 0;
3059
- if (intensity > 0) {
3060
- const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
3061
- patch = {
3062
- ...patch,
3063
- rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3064
- };
3065
- } else {
3066
- patch = { ...patch, rectangleDifferences: void 0 };
3005
+ type: PdfAnnotationSubtype.LINK,
3006
+ bounds: rect,
3007
+ data: {
3008
+ rect,
3009
+ strokeColor: defaults.strokeColor,
3010
+ strokeWidth: defaults.strokeWidth,
3011
+ strokeStyle: defaults.strokeStyle,
3012
+ strokeDashArray: defaults.strokeDashArray
3067
3013
  }
3068
- }
3069
- const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
3070
- if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
3071
- return {
3072
- ...patch,
3073
- unrotatedRect: tightRect,
3074
- rect: calculateRotatedRectAABBAroundPoint(
3075
- tightRect,
3076
- effectiveRotation,
3077
- resolveAnnotationRotationCenter(orig)
3078
- )
3079
- };
3080
- }
3081
- return { ...patch, rect: tightRect };
3082
- }
3083
- default:
3084
- return ctx.changes;
3085
- }
3086
- };
3087
- const patchCircle = (orig, ctx) => {
3088
- switch (ctx.type) {
3089
- case "move":
3090
- if (!ctx.changes.rect) return ctx.changes;
3091
- return baseMoveChanges(orig, ctx.changes.rect).rects;
3092
- case "resize":
3093
- if (!ctx.changes.rect) return ctx.changes;
3094
- return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
3095
- case "rotate":
3096
- return baseRotateChanges(orig, ctx) ?? ctx.changes;
3097
- case "property-update": {
3098
- let patch = ctx.changes;
3099
- const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3100
- const strokeChanged = ctx.changes.strokeWidth !== void 0;
3101
- const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3102
- if (cloudyChanged || strokeChanged && hasCloudy) {
3103
- const merged = { ...orig, ...ctx.changes };
3104
- const intensity = merged.cloudyBorderIntensity ?? 0;
3105
- if (intensity > 0) {
3106
- const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, true);
3107
- patch = {
3108
- ...patch,
3109
- rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3110
- };
3014
+ };
3015
+ };
3016
+ return {
3017
+ onPointerDown: (pos, evt) => {
3018
+ var _a;
3019
+ const clampedPos = clampToPage(pos);
3020
+ setStart(clampedPos);
3021
+ clickDetector.onStart(clampedPos);
3022
+ onPreview(getPreview(clampedPos));
3023
+ (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
3024
+ },
3025
+ onPointerMove: (pos) => {
3026
+ const clampedPos = clampToPage(pos);
3027
+ clickDetector.onMove(clampedPos);
3028
+ if (getStart() && clickDetector.hasMoved()) {
3029
+ onPreview(getPreview(clampedPos));
3030
+ }
3031
+ },
3032
+ onPointerUp: (pos, evt) => {
3033
+ var _a;
3034
+ const p1 = getStart();
3035
+ if (!p1) return;
3036
+ const defaults = getDefaults();
3037
+ if (!defaults) return;
3038
+ const clampedPos = clampToPage(pos);
3039
+ if (!clickDetector.hasMoved()) {
3040
+ clickDetector.onEnd(clampedPos);
3111
3041
  } else {
3112
- patch = { ...patch, rectangleDifferences: void 0 };
3042
+ const preview = getPreview(clampedPos);
3043
+ if (preview) {
3044
+ const anno = {
3045
+ ...defaults,
3046
+ type: PdfAnnotationSubtype.LINK,
3047
+ target: void 0,
3048
+ created: /* @__PURE__ */ new Date(),
3049
+ id: uuidV4(),
3050
+ pageIndex,
3051
+ rect: preview.data.rect
3052
+ };
3053
+ onCommit(anno);
3054
+ }
3113
3055
  }
3056
+ setStart(null);
3057
+ onPreview(null);
3058
+ clickDetector.reset();
3059
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
3060
+ },
3061
+ onPointerLeave: (_, evt) => {
3062
+ var _a;
3063
+ setStart(null);
3064
+ onPreview(null);
3065
+ clickDetector.reset();
3066
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
3067
+ },
3068
+ onPointerCancel: (_, evt) => {
3069
+ var _a;
3070
+ setStart(null);
3071
+ onPreview(null);
3072
+ clickDetector.reset();
3073
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
3114
3074
  }
3115
- if (ctx.changes.rotation !== void 0) {
3116
- patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3117
- }
3118
- return patch;
3119
- }
3120
- default:
3121
- return ctx.changes;
3075
+ };
3122
3076
  }
3123
3077
  };
3124
- const patchSquare = (orig, ctx) => {
3125
- switch (ctx.type) {
3126
- case "move":
3127
- if (!ctx.changes.rect) return ctx.changes;
3128
- return baseMoveChanges(orig, ctx.changes.rect).rects;
3129
- case "resize":
3130
- if (!ctx.changes.rect) return ctx.changes;
3131
- return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
3132
- case "rotate":
3133
- return baseRotateChanges(orig, ctx) ?? ctx.changes;
3134
- case "property-update": {
3135
- let patch = ctx.changes;
3136
- const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
3137
- const strokeChanged = ctx.changes.strokeWidth !== void 0;
3138
- const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
3139
- if (cloudyChanged || strokeChanged && hasCloudy) {
3140
- const merged = { ...orig, ...ctx.changes };
3141
- const intensity = merged.cloudyBorderIntensity ?? 0;
3142
- if (intensity > 0) {
3143
- const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
3144
- patch = {
3145
- ...patch,
3146
- rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
3147
- };
3148
- } else {
3149
- patch = { ...patch, rectangleDifferences: void 0 };
3078
+ const textMarkupSelectionHandler = {
3079
+ toolId: "__textMarkup__",
3080
+ handle(context, selections, getText) {
3081
+ const tool = context.getTool();
3082
+ if (!tool) return;
3083
+ for (const selection of selections) {
3084
+ const id = uuidV4();
3085
+ getText().then((text) => {
3086
+ var _a;
3087
+ context.createAnnotation(selection.pageIndex, {
3088
+ ...tool.defaults,
3089
+ rect: selection.rect,
3090
+ segmentRects: selection.segmentRects,
3091
+ pageIndex: selection.pageIndex,
3092
+ created: /* @__PURE__ */ new Date(),
3093
+ id,
3094
+ ...text != null && { custom: { text } }
3095
+ });
3096
+ if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
3097
+ context.selectAnnotation(selection.pageIndex, id);
3150
3098
  }
3151
- }
3152
- if (ctx.changes.rotation !== void 0) {
3153
- patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3154
- }
3155
- return patch;
3099
+ });
3156
3100
  }
3157
- default:
3158
- return ctx.changes;
3159
3101
  }
3160
3102
  };
3161
- const patchFreeText = (orig, ctx) => {
3162
- switch (ctx.type) {
3163
- case "move":
3164
- if (!ctx.changes.rect) return ctx.changes;
3165
- return baseMoveChanges(orig, ctx.changes.rect).rects;
3166
- case "resize":
3167
- if (!ctx.changes.rect) return ctx.changes;
3168
- return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
3169
- case "rotate":
3170
- return baseRotateChanges(orig, ctx) ?? ctx.changes;
3171
- case "property-update":
3172
- if (ctx.changes.rotation !== void 0) {
3173
- return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3174
- }
3175
- return ctx.changes;
3176
- default:
3177
- return ctx.changes;
3103
+ function computeCaretRect(lastSegRect) {
3104
+ const lineHeight = lastSegRect.size.height;
3105
+ const height = lineHeight / 2;
3106
+ const width = height;
3107
+ const lineEndX = lastSegRect.origin.x + lastSegRect.size.width;
3108
+ return {
3109
+ origin: {
3110
+ x: lineEndX - width / 2,
3111
+ y: lastSegRect.origin.y + lineHeight / 2
3112
+ },
3113
+ size: { width, height }
3114
+ };
3115
+ }
3116
+ const insertTextSelectionHandler = {
3117
+ toolId: "insertText",
3118
+ handle(context, selections, getText) {
3119
+ const tool = context.getTool();
3120
+ if (!tool) return;
3121
+ const getDefaults = () => ({
3122
+ strokeColor: tool.defaults.strokeColor ?? "#E44234",
3123
+ opacity: tool.defaults.opacity ?? 1,
3124
+ flags: tool.defaults.flags ?? ["print"]
3125
+ });
3126
+ for (const selection of selections) {
3127
+ const lastSegRect = selection.segmentRects[selection.segmentRects.length - 1];
3128
+ if (!lastSegRect) continue;
3129
+ const caretRect = computeCaretRect(lastSegRect);
3130
+ const caretId = uuidV4();
3131
+ const defaults = getDefaults();
3132
+ getText().then((text) => {
3133
+ var _a;
3134
+ context.createAnnotation(selection.pageIndex, {
3135
+ type: PdfAnnotationSubtype.CARET,
3136
+ id: caretId,
3137
+ pageIndex: selection.pageIndex,
3138
+ rect: caretRect,
3139
+ strokeColor: defaults.strokeColor,
3140
+ opacity: defaults.opacity,
3141
+ intent: "Insert",
3142
+ rectangleDifferences: { left: 0.5, top: 0.5, right: 0.5, bottom: 0.5 },
3143
+ created: /* @__PURE__ */ new Date(),
3144
+ flags: defaults.flags,
3145
+ ...text != null && { custom: { text } }
3146
+ });
3147
+ if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
3148
+ context.selectAnnotation(selection.pageIndex, caretId);
3149
+ }
3150
+ });
3151
+ }
3178
3152
  }
3179
3153
  };
3180
- const patchStamp = (orig, ctx) => {
3181
- switch (ctx.type) {
3182
- case "move":
3183
- if (!ctx.changes.rect) return ctx.changes;
3184
- return baseMoveChanges(orig, ctx.changes.rect).rects;
3185
- case "resize":
3186
- if (!ctx.changes.rect) return ctx.changes;
3187
- return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
3188
- case "rotate":
3189
- return baseRotateChanges(orig, ctx) ?? ctx.changes;
3190
- case "property-update":
3191
- if (ctx.changes.rotation !== void 0) {
3192
- return { ...ctx.changes, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
3193
- }
3194
- return ctx.changes;
3195
- default:
3196
- return ctx.changes;
3154
+ const replaceTextSelectionHandler = {
3155
+ toolId: "replaceText",
3156
+ handle(context, selections, getText) {
3157
+ const tool = context.getTool();
3158
+ if (!tool) return;
3159
+ const getDefaults = () => ({
3160
+ strokeColor: tool.defaults.strokeColor ?? "#E44234",
3161
+ opacity: tool.defaults.opacity ?? 1,
3162
+ flags: tool.defaults.flags ?? ["print"]
3163
+ });
3164
+ for (const selection of selections) {
3165
+ const lastSegRect = selection.segmentRects[selection.segmentRects.length - 1];
3166
+ if (!lastSegRect) continue;
3167
+ const caretRect = computeCaretRect(lastSegRect);
3168
+ const caretId = uuidV4();
3169
+ const strikeoutId = uuidV4();
3170
+ const defaults = getDefaults();
3171
+ getText().then((text) => {
3172
+ var _a;
3173
+ context.createAnnotation(selection.pageIndex, {
3174
+ type: PdfAnnotationSubtype.CARET,
3175
+ id: caretId,
3176
+ pageIndex: selection.pageIndex,
3177
+ rect: caretRect,
3178
+ strokeColor: defaults.strokeColor,
3179
+ opacity: defaults.opacity,
3180
+ intent: "Replace",
3181
+ rectangleDifferences: { left: 0.5, top: 0.5, right: 0.5, bottom: 0.5 },
3182
+ created: /* @__PURE__ */ new Date(),
3183
+ flags: defaults.flags
3184
+ });
3185
+ context.createAnnotation(selection.pageIndex, {
3186
+ type: PdfAnnotationSubtype.STRIKEOUT,
3187
+ id: strikeoutId,
3188
+ pageIndex: selection.pageIndex,
3189
+ rect: selection.rect,
3190
+ segmentRects: selection.segmentRects,
3191
+ strokeColor: defaults.strokeColor,
3192
+ opacity: defaults.opacity,
3193
+ intent: "StrikeOutTextEdit",
3194
+ inReplyToId: caretId,
3195
+ replyType: PdfAnnotationReplyType.Group,
3196
+ created: /* @__PURE__ */ new Date(),
3197
+ flags: defaults.flags,
3198
+ ...text != null && { custom: { text } }
3199
+ });
3200
+ if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
3201
+ context.selectAnnotation(selection.pageIndex, caretId);
3202
+ }
3203
+ });
3204
+ }
3197
3205
  }
3198
3206
  };
3199
3207
  const textMarkupTools = [
@@ -4358,6 +4366,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
4358
4366
  getSelectedAnnotations: () => this.getSelectedAnnotationsMethod(),
4359
4367
  getSelectedAnnotationIds: () => this.getSelectedAnnotationIdsMethod(),
4360
4368
  getAnnotationById: (id) => this.getAnnotationById(id),
4369
+ getAnnotations: (options) => this.getAnnotationsMethod(options),
4361
4370
  selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id),
4362
4371
  toggleSelection: (pageIndex, id) => this.toggleSelectionMethod(pageIndex, id),
4363
4372
  addToSelection: (pageIndex, id) => this.addToSelectionMethod(pageIndex, id),
@@ -4430,6 +4439,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
4430
4439
  getSelectedAnnotations: () => this.getSelectedAnnotationsMethod(documentId),
4431
4440
  getSelectedAnnotationIds: () => this.getSelectedAnnotationIdsMethod(documentId),
4432
4441
  getAnnotationById: (id) => this.getAnnotationById(id, documentId),
4442
+ getAnnotations: (options) => this.getAnnotationsMethod(options, documentId),
4433
4443
  selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id, documentId),
4434
4444
  toggleSelection: (pageIndex, id) => this.toggleSelectionMethod(pageIndex, id, documentId),
4435
4445
  addToSelection: (pageIndex, id) => this.addToSelectionMethod(pageIndex, id, documentId),
@@ -4607,6 +4617,15 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
4607
4617
  const docState = this.getDocumentState(documentId);
4608
4618
  return getAnnotationByUid(docState, id);
4609
4619
  }
4620
+ getAnnotationsMethod(options, documentId) {
4621
+ const docState = this.getDocumentState(documentId);
4622
+ if ((options == null ? void 0 : options.pageIndex) !== void 0) {
4623
+ return getAnnotationsByPageIndex(docState, options.pageIndex).filter(
4624
+ (ta) => ta !== void 0
4625
+ );
4626
+ }
4627
+ return Object.values(docState.byUid);
4628
+ }
4610
4629
  renderAnnotation({ pageIndex, annotation, options }, documentId) {
4611
4630
  const id = documentId ?? this.getActiveDocumentId();
4612
4631
  const docState = this.getCoreDocument(id);