@sanctum-key/react-native-sdk 1.0.14 → 1.0.15
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/build/package.json +1 -1
- package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.js +148 -8
- package/build/src/components/EnhancedCameraView.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +42 -15
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/utils/cropByObb.d.ts +0 -12
- package/build/src/utils/cropByObb.d.ts.map +1 -1
- package/build/src/utils/cropByObb.js +73 -98
- package/build/src/utils/cropByObb.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +190 -33
- package/src/components/KYCElements/IDCardCapture.tsx +49 -15
- package/src/utils/cropByObb.ts +90 -127
package/src/utils/cropByObb.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Image as RNImage, Platform } from 'react-native';
|
|
2
2
|
import * as ImageManipulator from "expo-image-manipulator";
|
|
3
|
-
|
|
4
|
-
import { logger } from './logger';
|
|
3
|
+
|
|
5
4
|
|
|
6
5
|
type Point = [number, number];
|
|
7
6
|
|
|
@@ -30,24 +29,13 @@ export function getObbConfidence(cardObb: any): number | null {
|
|
|
30
29
|
return typeof score === 'number' ? score : null;
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
/**
|
|
34
|
-
* Extracts the card_in_frame flag from card_obb when present.
|
|
35
|
-
* Returns:
|
|
36
|
-
* - true => card fully in frame
|
|
37
|
-
* - false => card not fully in frame
|
|
38
|
-
* - null => field not present (backward compatibility)
|
|
39
|
-
*/
|
|
40
32
|
/**
|
|
41
33
|
* Extracts the framing data from card_obb.
|
|
42
34
|
* STRICT CHECK: Returns true ONLY if card_in_frame is true AND cropped_sides is empty.
|
|
43
|
-
* Returns:
|
|
44
|
-
* - true => card fully in frame (no sides cropped)
|
|
45
|
-
* - false => card not fully in frame OR edges are cropped
|
|
46
|
-
* - null => field not present (backward compatibility)
|
|
47
35
|
*/
|
|
48
36
|
export function getCardInFrame(cardObb: any): boolean | null {
|
|
49
37
|
if (!cardObb) return null;
|
|
50
|
-
|
|
38
|
+
|
|
51
39
|
// card_obb can be an array of objects or a single object
|
|
52
40
|
const first = Array.isArray(cardObb) ? cardObb[0] : cardObb;
|
|
53
41
|
|
|
@@ -60,11 +48,11 @@ export function getCardInFrame(cardObb: any): boolean | null {
|
|
|
60
48
|
if (hasCroppedSides) {
|
|
61
49
|
console.warn(`Card is cropped on sides: ${first.cropped_sides.join(', ')}`);
|
|
62
50
|
}
|
|
63
|
-
return false;
|
|
51
|
+
return false;
|
|
64
52
|
}
|
|
65
53
|
|
|
66
54
|
// If we made it here, card_in_frame is true AND cropped_sides is empty.
|
|
67
|
-
return true;
|
|
55
|
+
return true;
|
|
68
56
|
}
|
|
69
57
|
|
|
70
58
|
// Backward compatibility check
|
|
@@ -101,14 +89,8 @@ async function cropWeb(uri: string, points: Point[]): Promise<string> {
|
|
|
101
89
|
if (!ctx) return reject(new Error('Canvas context not available'));
|
|
102
90
|
ctx.drawImage(
|
|
103
91
|
img,
|
|
104
|
-
minX, //
|
|
105
|
-
|
|
106
|
-
width, // sw
|
|
107
|
-
height, // sh
|
|
108
|
-
0,
|
|
109
|
-
0,
|
|
110
|
-
canvas.width,
|
|
111
|
-
canvas.height
|
|
92
|
+
minX, minY, width, height, // Source
|
|
93
|
+
0, 0, canvas.width, canvas.height // Destination
|
|
112
94
|
);
|
|
113
95
|
resolve(canvas.toDataURL('image/jpeg', 0.92));
|
|
114
96
|
};
|
|
@@ -117,10 +99,9 @@ async function cropWeb(uri: string, points: Point[]): Promise<string> {
|
|
|
117
99
|
});
|
|
118
100
|
}
|
|
119
101
|
|
|
120
|
-
// Fallback: return original for native
|
|
102
|
+
// Fallback: return original for native
|
|
121
103
|
export async function cropByObb(uri: string, cardObb: any): Promise<{ base64?: string; bbox?: { minX: number; minY: number; width: number; height: number } }> {
|
|
122
104
|
try {
|
|
123
|
-
// card_obb format: [ [ [p1,p2,p3,p4], score ] ] — if confidence too low, don't crop (send full image)
|
|
124
105
|
const confidence = getObbConfidence(cardObb);
|
|
125
106
|
if (confidence !== null && confidence < OBB_CONFIDENCE_THRESHOLD) {
|
|
126
107
|
return {};
|
|
@@ -135,7 +116,7 @@ export async function cropByObb(uri: string, cardObb: any): Promise<{ base64?: s
|
|
|
135
116
|
const base64 = await cropWeb(uri, points);
|
|
136
117
|
return { base64, bbox: { minX, minY, width, height } };
|
|
137
118
|
}
|
|
138
|
-
|
|
119
|
+
|
|
139
120
|
return { bbox: { minX, minY, width, height } };
|
|
140
121
|
} catch (e) {
|
|
141
122
|
return {};
|
|
@@ -143,112 +124,94 @@ export async function cropByObb(uri: string, cardObb: any): Promise<{ base64?: s
|
|
|
143
124
|
}
|
|
144
125
|
|
|
145
126
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// exemple d'appel :
|
|
149
|
-
export async function cropImageWithBBox(uri: string, bbox: any) {
|
|
150
|
-
// 1️⃣ Récupère la taille originale de l'image
|
|
151
|
-
await RNImage.getSize(uri, (width, height) => {
|
|
152
|
-
console.log("Image originale:", width, height);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// // 2️⃣ Suppose que ton bbox vient d'une image affichée dans `displayedSize`
|
|
156
|
-
// const scaleX = originalWidth / displayedSize.width;
|
|
157
|
-
// const scaleY = originalHeight / displayedSize.height;
|
|
158
|
-
|
|
159
|
-
// 3️⃣ Convertir le bbox à la taille réelle scale 0.10 = 10%
|
|
160
|
-
const crop = {
|
|
161
|
-
originX: bbox.minX,
|
|
162
|
-
originY: bbox.minY,
|
|
163
|
-
width: bbox.width,
|
|
164
|
-
height: bbox.height,
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
// 4️⃣ Appliquer le crop
|
|
168
|
-
// @ts-ignore - manipulateAsync is deprecated but still functional, new API (useImageManipulator) is a React hook and not suitable for utility functions
|
|
169
|
-
const result = await ImageManipulator.manipulateAsync(
|
|
170
|
-
uri,
|
|
171
|
-
[{ crop }],
|
|
172
|
-
{ compress: 1, format: ImageManipulator.SaveFormat.PNG }
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
console.log("Image recadrée:", result.uri);
|
|
176
|
-
return result.uri;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Fonction pour rogner avec une tolérance de 10% autour du bbox
|
|
180
|
-
export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tolerance: number = 0.1): Promise<string> {
|
|
181
|
-
console.log("cropImageWithBBoxWithTolerance", JSON.stringify(truncateFields({ uri, bbox, tolerance }), null, 2));
|
|
182
|
-
|
|
183
|
-
return new Promise<string>((resolve, reject) => {
|
|
127
|
+
export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tolerance: number = 0.02): Promise<string> {
|
|
128
|
+
return new Promise<string>((resolve) => {
|
|
184
129
|
let isResolved = false;
|
|
185
|
-
|
|
186
|
-
// Timeout
|
|
130
|
+
|
|
131
|
+
// Safety Timeout (10 seconds)
|
|
187
132
|
const timeout = setTimeout(() => {
|
|
188
133
|
if (!isResolved) {
|
|
189
134
|
isResolved = true;
|
|
190
|
-
console.warn("
|
|
191
|
-
resolve(uri);
|
|
135
|
+
console.warn("Crop timeout, returning original.");
|
|
136
|
+
resolve(uri);
|
|
192
137
|
}
|
|
193
|
-
},
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
138
|
+
}, 10000);
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
RNImage.getSize(uri, async (actualWidth, actualHeight) => {
|
|
142
|
+
if (isResolved) return;
|
|
143
|
+
|
|
144
|
+
// 1. Detect if bbox is percentage (e.g., 0.15) or absolute pixels (e.g., 800)
|
|
145
|
+
// Since we pass UI percentages, this will be true.
|
|
146
|
+
const isPercentageBased = bbox.width <= 1.5 && bbox.height <= 1.5;
|
|
147
|
+
|
|
148
|
+
let pixelMinX, pixelMinY, pixelWidth, pixelHeight;
|
|
149
|
+
|
|
150
|
+
if (isPercentageBased) {
|
|
151
|
+
pixelMinX = bbox.minX * actualWidth;
|
|
152
|
+
pixelMinY = bbox.minY * actualHeight;
|
|
153
|
+
pixelWidth = bbox.width * actualWidth;
|
|
154
|
+
pixelHeight = bbox.height * actualHeight;
|
|
155
|
+
} else {
|
|
156
|
+
// Fallback if backend pixels somehow bypass the check
|
|
157
|
+
pixelMinX = bbox.minX;
|
|
158
|
+
pixelMinY = bbox.minY;
|
|
159
|
+
pixelWidth = bbox.width;
|
|
160
|
+
pixelHeight = bbox.height;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 2. Add Tolerance (Expands the box slightly to avoid cutting off physical card edges)
|
|
164
|
+
const toleranceX = pixelWidth * tolerance;
|
|
165
|
+
const toleranceY = pixelHeight * tolerance;
|
|
166
|
+
|
|
167
|
+
// Clamp values to ensure we don't try to crop outside the image bounds
|
|
168
|
+
const newMinX = Math.max(0, pixelMinX - toleranceX);
|
|
169
|
+
const newMinY = Math.max(0, pixelMinY - toleranceY);
|
|
170
|
+
const newMaxX = Math.min(actualWidth, pixelMinX + pixelWidth + toleranceX);
|
|
171
|
+
const newMaxY = Math.min(actualHeight, pixelMinY + pixelHeight + toleranceY);
|
|
172
|
+
|
|
173
|
+
const finalCrop = {
|
|
174
|
+
originX: Math.floor(newMinX),
|
|
175
|
+
originY: Math.floor(newMinY),
|
|
176
|
+
width: Math.floor(newMaxX - newMinX),
|
|
177
|
+
height: Math.floor(newMaxY - newMinY),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// 3. Perform Final Crop
|
|
181
|
+
try {
|
|
182
|
+
// @ts-ignore - Required for utility functions outside React components
|
|
183
|
+
const result = await ImageManipulator.manipulateAsync(
|
|
184
|
+
uri,
|
|
185
|
+
[{ crop: finalCrop }],
|
|
186
|
+
{ compress: 0.95, format: ImageManipulator.SaveFormat.JPEG }
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (!isResolved) {
|
|
190
|
+
isResolved = true;
|
|
191
|
+
clearTimeout(timeout);
|
|
192
|
+
resolve(result.uri);
|
|
193
|
+
}
|
|
194
|
+
} catch (err) {
|
|
195
|
+
if (!isResolved) {
|
|
196
|
+
isResolved = true;
|
|
197
|
+
clearTimeout(timeout);
|
|
198
|
+
console.error("ImageManipulator failed, returning raw image", err);
|
|
199
|
+
resolve(uri);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}, () => {
|
|
203
|
+
if (!isResolved) {
|
|
239
204
|
isResolved = true;
|
|
240
205
|
clearTimeout(timeout);
|
|
241
|
-
console.error("Erreur lors du rognage avec tolérance:", error);
|
|
242
|
-
// En cas d'erreur, retourner l'URI original au lieu de rejeter
|
|
243
206
|
resolve(uri);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
isResolved
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
} catch (e) {
|
|
210
|
+
if (!isResolved) {
|
|
211
|
+
isResolved = true;
|
|
212
|
+
clearTimeout(timeout);
|
|
213
|
+
resolve(uri);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
253
216
|
});
|
|
254
|
-
}
|
|
217
|
+
}
|