react-native-expo-cropper 1.0.30 → 1.0.31

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.
@@ -88,7 +88,9 @@ function CustomCamera(_ref) {
88
88
  case 0:
89
89
  _context2.p = 0;
90
90
  _context2.n = 1;
91
- return ImageManipulator.manipulateAsync(uri, [], {
91
+ return ImageManipulator.manipulateAsync(uri, [],
92
+ // Empty array = apply EXIF orientation automatically
93
+ {
92
94
  compress: 1,
93
95
  format: ImageManipulator.SaveFormat.PNG
94
96
  });
@@ -13,21 +13,26 @@ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.
13
13
  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
14
14
  var enhanceImage = exports.enhanceImage = /*#__PURE__*/function () {
15
15
  var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(uri, addheight) {
16
- var imageInfo, ratio, maxHeight, newWidth, newHeight, result, _t;
16
+ var fixedOrientationImage, ratio, maxHeight, newWidth, newHeight, result, _t;
17
17
  return _regenerator().w(function (_context) {
18
18
  while (1) switch (_context.p = _context.n) {
19
19
  case 0:
20
20
  _context.p = 0;
21
21
  _context.n = 1;
22
- return ImageManipulator.manipulateAsync(uri, []);
22
+ return ImageManipulator.manipulateAsync(uri, [], {
23
+ compress: 1,
24
+ format: ImageManipulator.SaveFormat.PNG
25
+ });
23
26
  case 1:
24
- imageInfo = _context.v;
25
- ratio = imageInfo.height / imageInfo.width;
27
+ fixedOrientationImage = _context.v;
28
+ // Get the correct dimensions after orientation fix
29
+ // The width and height properties reflect the actual image dimensions after orientation fix
30
+ ratio = fixedOrientationImage.height / fixedOrientationImage.width;
26
31
  maxHeight = addheight;
27
32
  newWidth = Math.round(maxHeight / ratio);
28
- newHeight = Math.round(newWidth * ratio);
33
+ newHeight = Math.round(newWidth * ratio); // Resize using the orientation-fixed image
29
34
  _context.n = 2;
30
- return ImageManipulator.manipulateAsync(uri, [{
35
+ return ImageManipulator.manipulateAsync(fixedOrientationImage.uri, [{
31
36
  resize: {
32
37
  width: newWidth,
33
38
  height: newHeight
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-expo-cropper",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "description": "Recadrage polygonal d'images.",
5
5
  "main": "index.js",
6
6
  "author": "PCS AGRI",
@@ -43,7 +43,7 @@
43
43
  "homepage": "https://github.com/pcsagri/react-native-expo-cropper#readme",
44
44
  "scripts": {
45
45
  "test": "echo \"Error: no test specified\" && exit 1",
46
- "build": "babel src --out-dir dist"
46
+ "build": "npx babel src --out-dir dist"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@babel/cli": "^7.28.3",
@@ -1,224 +1,232 @@
1
- import React, { useState, useRef, useEffect } from 'react';
2
- import {
3
- StyleSheet,
4
- Text,
5
- View,
6
- TouchableOpacity,
7
- Alert,
8
- SafeAreaView,
9
- Dimensions,
10
- Image,
11
- } from 'react-native';
12
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
13
- import { Camera, CameraView } from 'expo-camera';
14
- import * as ImageManipulator from 'expo-image-manipulator';
15
- const { width } = Dimensions.get('window');
16
-
17
- export default function CustomCamera({ onPhotoCaptured}) {
18
- const [isReady, setIsReady] = useState(false);
19
- const [loadingBeforeCapture, setLoadingBeforeCapture] = useState(false);
20
- const [setHasPermission] = useState(null);
21
- const cameraRef = useRef(null);
22
- const insets = useSafeAreaInsets();
23
-
24
-
25
-
26
- useEffect(() => {
27
- (async () => {
28
- const { status } = await Camera.requestCameraPermissionsAsync();
29
- setHasPermission(status === 'granted');
30
- })();
31
- }, []);
32
-
33
-
34
-
35
- // Helper function to wait for multiple render cycles (works on iOS)
36
- const waitForRender = (cycles = 5) => {
37
- return new Promise((resolve) => {
38
- let count = 0;
39
- const tick = () => {
40
- count++;
41
- if (count >= cycles) {
42
- resolve();
43
- } else {
44
- setImmediate(tick);
45
- }
46
- };
47
- setImmediate(tick);
48
- });
49
- };
50
-
51
- // Helper function to fix image orientation (removes EXIF orientation)
52
- const fixImageOrientation = async (uri) => {
53
- try {
54
- // Use manipulateAsync with empty array to fix orientation based on EXIF data
55
- const fixedImage = await ImageManipulator.manipulateAsync(uri, [], {
56
- compress: 1,
57
- format: ImageManipulator.SaveFormat.PNG
58
- });
59
- return fixedImage.uri;
60
- } catch (error) {
61
- console.error("Error fixing image orientation:", error);
62
- return uri; // Return original if fixing fails
63
- }
64
- };
65
-
66
- const takePicture = async () => {
67
- if (cameraRef.current) {
68
- try {
69
- // Show loading after a delay (using setImmediate for iOS compatibility)
70
- waitForRender(20).then(() => {
71
- setLoadingBeforeCapture(true);
72
- });
73
-
74
- // Wait a bit before taking picture (works on iOS)
75
- await waitForRender(3);
76
-
77
- const photo = await cameraRef.current.takePictureAsync({
78
- quality: 1,
79
- shutterSound: false,
80
- });
81
-
82
- // Fix orientation to preserve vertical/horizontal position as taken
83
- const fixedUri = await fixImageOrientation(photo.uri);
84
-
85
- onPhotoCaptured(fixedUri);
86
-
87
- setLoadingBeforeCapture(false);
88
- } catch (error) {
89
- setLoadingBeforeCapture(false);
90
- Alert.alert("Erreur");
91
- }
92
- }
93
- };
94
-
95
-
96
- return (
97
- <SafeAreaView style={styles.outerContainer}>
98
- <View style={styles.cameraWrapper}>
99
- <CameraView
100
- style={styles.camera}
101
- facing="back"
102
- ref={cameraRef}
103
- onCameraReady={() => setIsReady(true)}
104
- />
105
-
106
- {/* Loading overlay */}
107
- {loadingBeforeCapture && (
108
- <>
109
- <View style={styles.loadingOverlay}>
110
- <Image
111
- source={require('../src/assets/loadingCamera.gif')}
112
- style={styles.loadingGif}
113
- resizeMode="contain"
114
- />
115
- </View>
116
- <View style={styles.touchBlocker} pointerEvents="auto" />
117
- </>
118
- )}
119
-
120
- {/* Cadre de scan */}
121
- <View style={styles.scanFrame} />
122
- </View>
123
-
124
- <View style={[styles.buttonContainer, { bottom: (insets?.bottom || 0) + 16, marginBottom: 0 }]}>
125
- <TouchableOpacity
126
- style={styles.button}
127
- onPress={takePicture}
128
- disabled={!isReady || loadingBeforeCapture}
129
- />
130
- </View>
131
- </SafeAreaView>
132
- );
133
- }
134
-
135
- const PRIMARY_GREEN = '#198754';
136
- const DEEP_BLACK = '#0B0B0B';
137
- const GLOW_WHITE = 'rgba(255, 255, 255, 0.85)';
138
-
139
- const styles = StyleSheet.create({
140
- outerContainer: {
141
- flex: 1,
142
- backgroundColor: DEEP_BLACK,
143
- justifyContent: 'center',
144
- alignItems: 'center',
145
- },
146
- cameraWrapper: {
147
- width: width,
148
- aspectRatio: 9 / 16,
149
- borderRadius: 30,
150
- overflow: 'hidden',
151
- alignItems: 'center',
152
- justifyContent: 'center',
153
- position: 'relative',
154
- },
155
- camera: {
156
- ...StyleSheet.absoluteFillObject,
157
- },
158
- scanFrame: {
159
- position: 'absolute',
160
- width: '95%',
161
- height: '80%',
162
- borderWidth: 4,
163
- borderColor: PRIMARY_GREEN,
164
- borderRadius: 5,
165
- backgroundColor: 'rgba(0, 0, 0, 0.1)',
166
- },
167
- loadingOverlay: {
168
- ...StyleSheet.absoluteFillObject,
169
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
170
- zIndex: 20,
171
- justifyContent: 'center',
172
- alignItems: 'center',
173
- },
174
- loadingGif: {
175
- width: 100,
176
- height: 100,
177
- },
178
- touchBlocker: {
179
- ...StyleSheet.absoluteFillObject,
180
- zIndex: 21,
181
- backgroundColor: 'transparent',
182
- },
183
- cancelIcon: {
184
- position: 'absolute',
185
- top: 20,
186
- left: 20,
187
- backgroundColor: PRIMARY_GREEN,
188
- borderRadius: 5,
189
- padding: 8,
190
- },
191
- buttonContainer: {
192
- position: 'absolute',
193
- bottom: 0,
194
- marginBottom: 20,
195
- flexDirection: 'row',
196
- justifyContent: 'center',
197
- },
198
- button: {
199
- width: 80,
200
- height: 80,
201
- borderRadius: 50,
202
- backgroundColor: GLOW_WHITE,
203
- borderWidth: 5,
204
- borderColor: PRIMARY_GREEN,
205
- alignItems: 'center',
206
- justifyContent: 'center',
207
- },
208
- text: {
209
- fontSize: 18,
210
- color: GLOW_WHITE,
211
- },
212
- container: {
213
- flex: 1,
214
- backgroundColor: DEEP_BLACK,
215
- justifyContent: 'center',
216
- alignItems: 'center',
217
- padding: 20,
218
- },
219
- iconText: {
220
- fontSize: 18,
221
- color: GLOW_WHITE,
222
- fontWeight: '600',
223
- },
224
- });
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import {
3
+ StyleSheet,
4
+ Text,
5
+ View,
6
+ TouchableOpacity,
7
+ Alert,
8
+ SafeAreaView,
9
+ Dimensions,
10
+ Image,
11
+ } from 'react-native';
12
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
13
+ import { Camera, CameraView } from 'expo-camera';
14
+ import * as ImageManipulator from 'expo-image-manipulator';
15
+ const { width } = Dimensions.get('window');
16
+
17
+ export default function CustomCamera({ onPhotoCaptured}) {
18
+ const [isReady, setIsReady] = useState(false);
19
+ const [loadingBeforeCapture, setLoadingBeforeCapture] = useState(false);
20
+ const [setHasPermission] = useState(null);
21
+ const cameraRef = useRef(null);
22
+ const insets = useSafeAreaInsets();
23
+
24
+
25
+
26
+ useEffect(() => {
27
+ (async () => {
28
+ const { status } = await Camera.requestCameraPermissionsAsync();
29
+ setHasPermission(status === 'granted');
30
+ })();
31
+ }, []);
32
+
33
+
34
+
35
+ // Helper function to wait for multiple render cycles (works on iOS)
36
+ const waitForRender = (cycles = 5) => {
37
+ return new Promise((resolve) => {
38
+ let count = 0;
39
+ const tick = () => {
40
+ count++;
41
+ if (count >= cycles) {
42
+ resolve();
43
+ } else {
44
+ setImmediate(tick);
45
+ }
46
+ };
47
+ setImmediate(tick);
48
+ });
49
+ };
50
+
51
+ // Helper function to fix image orientation (removes EXIF orientation)
52
+ const fixImageOrientation = async (uri) => {
53
+ try {
54
+ // Use manipulateAsync with empty array to fix orientation based on EXIF data
55
+ // Empty array [] tells manipulateAsync to automatically apply EXIF orientation
56
+ // This ensures horizontal photos stay horizontal and vertical photos stay vertical
57
+ // The EXIF orientation tag is removed and the image is physically rotated if needed
58
+ const fixedImage = await ImageManipulator.manipulateAsync(
59
+ uri,
60
+ [], // Empty array = apply EXIF orientation automatically
61
+ {
62
+ compress: 1,
63
+ format: ImageManipulator.SaveFormat.PNG
64
+ }
65
+ );
66
+
67
+ return fixedImage.uri;
68
+ } catch (error) {
69
+ console.error("Error fixing image orientation:", error);
70
+ return uri; // Return original if fixing fails
71
+ }
72
+ };
73
+
74
+ const takePicture = async () => {
75
+ if (cameraRef.current) {
76
+ try {
77
+ // Show loading after a delay (using setImmediate for iOS compatibility)
78
+ waitForRender(20).then(() => {
79
+ setLoadingBeforeCapture(true);
80
+ });
81
+
82
+ // Wait a bit before taking picture (works on iOS)
83
+ await waitForRender(3);
84
+
85
+ const photo = await cameraRef.current.takePictureAsync({
86
+ quality: 1,
87
+ shutterSound: false,
88
+ });
89
+
90
+ // Fix orientation to preserve vertical/horizontal position as taken
91
+ const fixedUri = await fixImageOrientation(photo.uri);
92
+
93
+ onPhotoCaptured(fixedUri);
94
+
95
+ setLoadingBeforeCapture(false);
96
+ } catch (error) {
97
+ setLoadingBeforeCapture(false);
98
+ Alert.alert("Erreur");
99
+ }
100
+ }
101
+ };
102
+
103
+
104
+ return (
105
+ <SafeAreaView style={styles.outerContainer}>
106
+ <View style={styles.cameraWrapper}>
107
+ <CameraView
108
+ style={styles.camera}
109
+ facing="back"
110
+ ref={cameraRef}
111
+ onCameraReady={() => setIsReady(true)}
112
+ />
113
+
114
+ {/* Loading overlay */}
115
+ {loadingBeforeCapture && (
116
+ <>
117
+ <View style={styles.loadingOverlay}>
118
+ <Image
119
+ source={require('../src/assets/loadingCamera.gif')}
120
+ style={styles.loadingGif}
121
+ resizeMode="contain"
122
+ />
123
+ </View>
124
+ <View style={styles.touchBlocker} pointerEvents="auto" />
125
+ </>
126
+ )}
127
+
128
+ {/* Cadre de scan */}
129
+ <View style={styles.scanFrame} />
130
+ </View>
131
+
132
+ <View style={[styles.buttonContainer, { bottom: (insets?.bottom || 0) + 16, marginBottom: 0 }]}>
133
+ <TouchableOpacity
134
+ style={styles.button}
135
+ onPress={takePicture}
136
+ disabled={!isReady || loadingBeforeCapture}
137
+ />
138
+ </View>
139
+ </SafeAreaView>
140
+ );
141
+ }
142
+
143
+ const PRIMARY_GREEN = '#198754';
144
+ const DEEP_BLACK = '#0B0B0B';
145
+ const GLOW_WHITE = 'rgba(255, 255, 255, 0.85)';
146
+
147
+ const styles = StyleSheet.create({
148
+ outerContainer: {
149
+ flex: 1,
150
+ backgroundColor: DEEP_BLACK,
151
+ justifyContent: 'center',
152
+ alignItems: 'center',
153
+ },
154
+ cameraWrapper: {
155
+ width: width,
156
+ aspectRatio: 9 / 16,
157
+ borderRadius: 30,
158
+ overflow: 'hidden',
159
+ alignItems: 'center',
160
+ justifyContent: 'center',
161
+ position: 'relative',
162
+ },
163
+ camera: {
164
+ ...StyleSheet.absoluteFillObject,
165
+ },
166
+ scanFrame: {
167
+ position: 'absolute',
168
+ width: '95%',
169
+ height: '80%',
170
+ borderWidth: 4,
171
+ borderColor: PRIMARY_GREEN,
172
+ borderRadius: 5,
173
+ backgroundColor: 'rgba(0, 0, 0, 0.1)',
174
+ },
175
+ loadingOverlay: {
176
+ ...StyleSheet.absoluteFillObject,
177
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
178
+ zIndex: 20,
179
+ justifyContent: 'center',
180
+ alignItems: 'center',
181
+ },
182
+ loadingGif: {
183
+ width: 100,
184
+ height: 100,
185
+ },
186
+ touchBlocker: {
187
+ ...StyleSheet.absoluteFillObject,
188
+ zIndex: 21,
189
+ backgroundColor: 'transparent',
190
+ },
191
+ cancelIcon: {
192
+ position: 'absolute',
193
+ top: 20,
194
+ left: 20,
195
+ backgroundColor: PRIMARY_GREEN,
196
+ borderRadius: 5,
197
+ padding: 8,
198
+ },
199
+ buttonContainer: {
200
+ position: 'absolute',
201
+ bottom: 0,
202
+ marginBottom: 20,
203
+ flexDirection: 'row',
204
+ justifyContent: 'center',
205
+ },
206
+ button: {
207
+ width: 80,
208
+ height: 80,
209
+ borderRadius: 50,
210
+ backgroundColor: GLOW_WHITE,
211
+ borderWidth: 5,
212
+ borderColor: PRIMARY_GREEN,
213
+ alignItems: 'center',
214
+ justifyContent: 'center',
215
+ },
216
+ text: {
217
+ fontSize: 18,
218
+ color: GLOW_WHITE,
219
+ },
220
+ container: {
221
+ flex: 1,
222
+ backgroundColor: DEEP_BLACK,
223
+ justifyContent: 'center',
224
+ alignItems: 'center',
225
+ padding: 20,
226
+ },
227
+ iconText: {
228
+ fontSize: 18,
229
+ color: GLOW_WHITE,
230
+ fontWeight: '600',
231
+ },
232
+ });
@@ -2,15 +2,25 @@ import * as ImageManipulator from 'expo-image-manipulator';
2
2
 
3
3
  export const enhanceImage = async (uri , addheight) => {
4
4
  try {
5
- const imageInfo = await ImageManipulator.manipulateAsync(uri, []);
6
- const ratio = imageInfo.height / imageInfo.width;
5
+ // First, fix orientation by applying EXIF orientation data
6
+ // Empty array [] tells manipulateAsync to automatically apply EXIF orientation
7
+ // This ensures the image is correctly oriented and EXIF tag is removed
8
+ const fixedOrientationImage = await ImageManipulator.manipulateAsync(uri, [], {
9
+ compress: 1,
10
+ format: ImageManipulator.SaveFormat.PNG
11
+ });
12
+
13
+ // Get the correct dimensions after orientation fix
14
+ // The width and height properties reflect the actual image dimensions after orientation fix
15
+ const ratio = fixedOrientationImage.height / fixedOrientationImage.width;
7
16
 
8
17
  const maxHeight = addheight;
9
18
  const newWidth = Math.round(maxHeight / ratio);
10
19
  const newHeight = Math.round(newWidth * ratio);
11
20
 
21
+ // Resize using the orientation-fixed image
12
22
  const result = await ImageManipulator.manipulateAsync(
13
- uri,
23
+ fixedOrientationImage.uri,
14
24
  [
15
25
  { resize: { width: newWidth, height: newHeight } },
16
26
  ],