@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.
@@ -1,7 +1,6 @@
1
1
  import { Image as RNImage, Platform } from 'react-native';
2
2
  import * as ImageManipulator from "expo-image-manipulator";
3
- import { truncateFields } from '../modules/api/KYCService';
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, // sx
105
- minY, // sy
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 (no dependency added); caller can still use bbox to draw overlay
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
- // Native: return bbox only; keep base64 undefined
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 de sécurité (15 secondes)
130
+
131
+ // Safety Timeout (10 seconds)
187
132
  const timeout = setTimeout(() => {
188
133
  if (!isResolved) {
189
134
  isResolved = true;
190
- console.warn("Timeout lors du rognage avec tolérance, utilisation de l'image originale");
191
- resolve(uri); // Retourner l'URI original en cas de timeout
135
+ console.warn("Crop timeout, returning original.");
136
+ resolve(uri);
192
137
  }
193
- }, 15000);
194
-
195
- // 1️⃣ Récupère la taille originale de l'image
196
- RNImage.getSize(uri, (imageWidth, imageHeight) => {
197
- if (isResolved) return;
198
-
199
- // 2️⃣ Calculer la tolérance en pixels (10% de chaque côté)
200
- const toleranceX = bbox.width * tolerance;
201
- const toleranceY = bbox.height * tolerance;
202
-
203
- // 3️⃣ Ajuster le bbox avec la tolérance
204
- // Calculer les nouvelles coordonnées avec tolérance
205
- const newMinX = Math.max(0, bbox.minX - toleranceX);
206
- const newMinY = Math.max(0, bbox.minY - toleranceY);
207
- const newMaxX = Math.min(imageWidth, bbox.minX + bbox.width + toleranceX);
208
- const newMaxY = Math.min(imageHeight, bbox.minY + bbox.height + toleranceY);
209
-
210
- // Calculer la largeur et hauteur finales
211
- const finalWidth = newMaxX - newMinX;
212
- const finalHeight = newMaxY - newMinY;
213
-
214
- const crop = {
215
- originX: newMinX,
216
- originY: newMinY,
217
- width: finalWidth,
218
- height: finalHeight,
219
- };
220
-
221
- logger.log("cropImageWithBBoxWithTolerance - crop params", JSON.stringify(truncateFields({ uri, crop, tolerance, imageWidth, imageHeight }), null, 2));
222
-
223
- // 4️⃣ Appliquer le crop avec tolérance
224
- // @ts-ignore - manipulateAsync is deprecated but still functional, new API (useImageManipulator) is a React hook and not suitable for utility functions
225
- ImageManipulator.manipulateAsync(
226
- uri,
227
- [{ crop }],
228
- { compress: 0.95, format: ImageManipulator.SaveFormat.JPEG }
229
- )
230
- .then((result) => {
231
- if (isResolved) return;
232
- isResolved = true;
233
- clearTimeout(timeout);
234
- console.log("Image recadrée avec tolérance:", result.uri);
235
- resolve(result.uri);
236
- })
237
- .catch((error) => {
238
- if (isResolved) return;
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
- }, (error) => {
246
- if (isResolved) return;
247
- isResolved = true;
248
- clearTimeout(timeout);
249
- console.error("Erreur lors de la récupération de la taille de l'image:", error);
250
- // En cas d'erreur, retourner l'URI original au lieu de rejeter
251
- resolve(uri);
252
- });
207
+ }
208
+ });
209
+ } catch (e) {
210
+ if (!isResolved) {
211
+ isResolved = true;
212
+ clearTimeout(timeout);
213
+ resolve(uri);
214
+ }
215
+ }
253
216
  });
254
- }
217
+ }