react-native-expo-cropper 1.2.41 → 1.2.43

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.
@@ -445,13 +445,14 @@ var ImageCropper = function ImageCropper(_ref) {
445
445
  var imageRectY = wrapper.y + imageRect.y;
446
446
 
447
447
  // ✅ PRIORITY RULE #3: IF image comes from camera → Use EXACT green frame coordinates
448
- // Image is displayed in "cover" mode (full wrapper), so green frame coords = white frame coords
448
+ // Image is displayed in "cover" mode (full wrapper), so green frame coords = white frame coords.
449
+ // Store points in WRAPPER-RELATIVE coordinates (greenFrame.x, greenFrame.y) so they match
450
+ // touch events (locationX/locationY are relative to the wrapper). This keeps display→image
451
+ // conversion correct in Confirm.
449
452
  if (cameraFrameData.current && cameraFrameData.current.greenFrame && originalImageDimensions.current.width > 0) {
450
453
  var greenFrame = cameraFrameData.current.greenFrame;
451
-
452
- // Image fills wrapper (cover mode), so use green frame position directly in wrapper space
453
- var _boxX = wrapper.x + greenFrame.x;
454
- var _boxY = wrapper.y + greenFrame.y;
454
+ var _boxX = greenFrame.x;
455
+ var _boxY = greenFrame.y;
455
456
  var _boxWidth = greenFrame.width;
456
457
  var _boxHeight = greenFrame.height;
457
458
 
@@ -464,7 +465,7 @@ var ImageCropper = function ImageCropper(_ref) {
464
465
  return;
465
466
  }
466
467
 
467
- // ✅ Create points using EXACT greenFrame coordinates (mapped to ImageCropper wrapper)
468
+ // ✅ Points in wrapper-relative coords (same as touch events)
468
469
  var _newPoints = [{
469
470
  x: _boxX,
470
471
  y: _boxY
@@ -507,7 +508,7 @@ var ImageCropper = function ImageCropper(_ref) {
507
508
  width: _boxWidth.toFixed(2),
508
509
  height: _boxHeight.toFixed(2)
509
510
  },
510
- note: "Image in cover mode - white frame = same position/size as green frame, same content"
511
+ note: "Points in wrapper-relative coords - same as touch events and crop_image_size"
511
512
  });
512
513
  setPoints(_newPoints);
513
514
  hasInitializedCropBox.current = true; // ✅ CRITICAL: Mark as initialized
@@ -1243,6 +1244,15 @@ var ImageCropper = function ImageCropper(_ref) {
1243
1244
  });
1244
1245
  case 4:
1245
1246
  rotated = _context.v;
1247
+ // ✅ Send rotated image to backend: use rotated URI and dimensions so crop bbox matches
1248
+ sourceImageUri.current = rotated.uri;
1249
+ originalImageDimensions.current = {
1250
+ width: rotated.width,
1251
+ height: rotated.height
1252
+ };
1253
+ cameraFrameData.current = null; // rotated image is no longer "camera preview" frame
1254
+ imageSource.current = 'gallery'; // so layout callbacks run initializeCropBox() and show the white box
1255
+
1246
1256
  setPoints([]);
1247
1257
  hasInitializedCropBox.current = false;
1248
1258
  setImage(rotated.uri);
@@ -1470,7 +1480,7 @@ var ImageCropper = function ImageCropper(_ref) {
1470
1480
  }, "Reset")), /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
1471
1481
  style: _ImageCropperStyles["default"].button,
1472
1482
  onPress: /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
1473
- var actualImageWidth, actualImageHeight, layout, contentRect, displayedWidth, displayedHeight, isCoverMode, scale, coverOffsetX, coverOffsetY, scaledWidth, scaledHeight, originalUri, cropMeta, imagePoints, minX, minY, maxX, maxY, cropX, cropY, cropEndX, cropEndY, cropWidth, cropHeight, bbox, polygon, name, _t2;
1483
+ var _cameraFrameData$curr4, actualImageWidth, actualImageHeight, isCoverMode, captured, layout, contentRect, displayedWidth, displayedHeight, scale, coverOffsetX, coverOffsetY, scaledWidth, scaledHeight, originalUri, cropMeta, imagePoints, minX, minY, maxX, maxY, cropX, cropY, cropEndX, cropEndY, cropWidth, cropHeight, bbox, polygon, name, _t2;
1474
1484
  return _regenerator().w(function (_context2) {
1475
1485
  while (1) switch (_context2.p = _context2.n) {
1476
1486
  case 0:
@@ -1478,7 +1488,25 @@ var ImageCropper = function ImageCropper(_ref) {
1478
1488
  _context2.p = 1;
1479
1489
  console.log("=== Starting pixel-perfect metadata export (no bitmap crop on mobile) ===");
1480
1490
  actualImageWidth = originalImageDimensions.current.width;
1481
- actualImageHeight = originalImageDimensions.current.height;
1491
+ actualImageHeight = originalImageDimensions.current.height; // ✅ CRITICAL: Camera JPEGs often have EXIF 6 (90° CW). The Image component displays
1492
+ // the EXIF-corrected view (3120×4160 portrait) but takePictureAsync returns raw (4160×3120).
1493
+ // Use swapped dimensions for coordinate conversion so bbox matches what the user sees.
1494
+ isCoverMode = !!(cameraFrameData.current && cameraFrameData.current.greenFrame);
1495
+ captured = (_cameraFrameData$curr4 = cameraFrameData.current) === null || _cameraFrameData$curr4 === void 0 ? void 0 : _cameraFrameData$curr4.capturedImageSize;
1496
+ if (isCoverMode && captured && captured.width > captured.height) {
1497
+ actualImageWidth = captured.height;
1498
+ actualImageHeight = captured.width;
1499
+ console.log("✅ Using EXIF-swapped dimensions for bbox (raw was landscape, display is portrait):", {
1500
+ raw: {
1501
+ w: captured.width,
1502
+ h: captured.height
1503
+ },
1504
+ display: {
1505
+ w: actualImageWidth,
1506
+ h: actualImageHeight
1507
+ }
1508
+ });
1509
+ }
1482
1510
  if (!(actualImageWidth === 0 || actualImageHeight === 0)) {
1483
1511
  _context2.n = 2;
1484
1512
  break;
@@ -1514,7 +1542,6 @@ var ImageCropper = function ImageCropper(_ref) {
1514
1542
  case 3:
1515
1543
  throw new Error("Displayed image dimensions not available.");
1516
1544
  case 4:
1517
- isCoverMode = !!(cameraFrameData.current && cameraFrameData.current.greenFrame);
1518
1545
  coverOffsetX = 0, coverOffsetY = 0;
1519
1546
  if (isCoverMode) {
1520
1547
  scale = Math.max(displayedWidth / actualImageWidth, displayedHeight / actualImageHeight);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-expo-cropper",
3
- "version": "1.2.41",
3
+ "version": "1.2.43",
4
4
  "description": "Recadrage polygonal d'images.",
5
5
  "main": "index.js",
6
6
  "author": "PCS AGRI",
@@ -334,13 +334,15 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
334
334
  const imageRectY = wrapper.y + imageRect.y;
335
335
 
336
336
  // ✅ PRIORITY RULE #3: IF image comes from camera → Use EXACT green frame coordinates
337
- // Image is displayed in "cover" mode (full wrapper), so green frame coords = white frame coords
337
+ // Image is displayed in "cover" mode (full wrapper), so green frame coords = white frame coords.
338
+ // Store points in WRAPPER-RELATIVE coordinates (greenFrame.x, greenFrame.y) so they match
339
+ // touch events (locationX/locationY are relative to the wrapper). This keeps display→image
340
+ // conversion correct in Confirm.
338
341
  if (cameraFrameData.current && cameraFrameData.current.greenFrame && originalImageDimensions.current.width > 0) {
339
342
  const greenFrame = cameraFrameData.current.greenFrame;
340
343
 
341
- // Image fills wrapper (cover mode), so use green frame position directly in wrapper space
342
- const boxX = wrapper.x + greenFrame.x;
343
- const boxY = wrapper.y + greenFrame.y;
344
+ const boxX = greenFrame.x;
345
+ const boxY = greenFrame.y;
344
346
  const boxWidth = greenFrame.width;
345
347
  const boxHeight = greenFrame.height;
346
348
 
@@ -353,7 +355,7 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
353
355
  return;
354
356
  }
355
357
 
356
- // ✅ Create points using EXACT greenFrame coordinates (mapped to ImageCropper wrapper)
358
+ // ✅ Points in wrapper-relative coords (same as touch events)
357
359
  const newPoints = [
358
360
  { x: boxX, y: boxY }, // Top-left
359
361
  { x: boxX + boxWidth, y: boxY }, // Top-right
@@ -371,7 +373,7 @@ const ImageCropper = ({ onConfirm, openCameraFirst, initialImage, addheight, rot
371
373
  console.log("✅ Initializing crop box for camera image (COVER MODE - exact green frame):", {
372
374
  greenFrame: { x: greenFrame.x, y: greenFrame.y, width: greenFrame.width, height: greenFrame.height },
373
375
  whiteFrame: { x: boxX.toFixed(2), y: boxY.toFixed(2), width: boxWidth.toFixed(2), height: boxHeight.toFixed(2) },
374
- note: "Image in cover mode - white frame = same position/size as green frame, same content",
376
+ note: "Points in wrapper-relative coords - same as touch events and crop_image_size",
375
377
  });
376
378
 
377
379
  setPoints(newPoints);
@@ -1048,6 +1050,15 @@ const rotatePreviewImage = async (degrees) => {
1048
1050
  }
1049
1051
  );
1050
1052
 
1053
+ // ✅ Send rotated image to backend: use rotated URI and dimensions so crop bbox matches
1054
+ sourceImageUri.current = rotated.uri;
1055
+ originalImageDimensions.current = {
1056
+ width: rotated.width,
1057
+ height: rotated.height,
1058
+ };
1059
+ cameraFrameData.current = null; // rotated image is no longer "camera preview" frame
1060
+ imageSource.current = 'gallery'; // so layout callbacks run initializeCropBox() and show the white box
1061
+
1051
1062
  setPoints([]);
1052
1063
  hasInitializedCropBox.current = false;
1053
1064
  setImage(rotated.uri);
@@ -1255,8 +1266,22 @@ const rotatePreviewImage = async (degrees) => {
1255
1266
  try {
1256
1267
  console.log("=== Starting pixel-perfect metadata export (no bitmap crop on mobile) ===");
1257
1268
 
1258
- const actualImageWidth = originalImageDimensions.current.width;
1259
- const actualImageHeight = originalImageDimensions.current.height;
1269
+ let actualImageWidth = originalImageDimensions.current.width;
1270
+ let actualImageHeight = originalImageDimensions.current.height;
1271
+
1272
+ // ✅ CRITICAL: Camera JPEGs often have EXIF 6 (90° CW). The Image component displays
1273
+ // the EXIF-corrected view (3120×4160 portrait) but takePictureAsync returns raw (4160×3120).
1274
+ // Use swapped dimensions for coordinate conversion so bbox matches what the user sees.
1275
+ const isCoverMode = !!(cameraFrameData.current && cameraFrameData.current.greenFrame);
1276
+ const captured = cameraFrameData.current?.capturedImageSize;
1277
+ if (isCoverMode && captured && captured.width > captured.height) {
1278
+ actualImageWidth = captured.height;
1279
+ actualImageHeight = captured.width;
1280
+ console.log("✅ Using EXIF-swapped dimensions for bbox (raw was landscape, display is portrait):", {
1281
+ raw: { w: captured.width, h: captured.height },
1282
+ display: { w: actualImageWidth, h: actualImageHeight },
1283
+ });
1284
+ }
1260
1285
 
1261
1286
  if (actualImageWidth === 0 || actualImageHeight === 0) {
1262
1287
  throw new Error("Original image dimensions not available. Please wait for image to load.");
@@ -1287,7 +1312,6 @@ const rotatePreviewImage = async (degrees) => {
1287
1312
  }
1288
1313
  }
1289
1314
 
1290
- const isCoverMode = !!(cameraFrameData.current && cameraFrameData.current.greenFrame);
1291
1315
  let scale, coverOffsetX = 0, coverOffsetY = 0;
1292
1316
  if (isCoverMode) {
1293
1317
  scale = Math.max(displayedWidth / actualImageWidth, displayedHeight / actualImageHeight);