@sanctum-key/react-native-sdk 1.0.58 → 1.0.59

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.
Files changed (29) hide show
  1. package/build/package.json +7 -2
  2. package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
  3. package/build/src/components/EnhancedCameraView.js +5 -1
  4. package/build/src/components/EnhancedCameraView.js.map +1 -1
  5. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  6. package/build/src/components/KYCElements/IDCardCapture.js +34 -13
  7. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  8. package/build/src/components/TemplateKYCExample.d.ts +2 -1
  9. package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
  10. package/build/src/components/TemplateKYCExample.js +2 -2
  11. package/build/src/components/TemplateKYCExample.js.map +1 -1
  12. package/build/src/components/TemplateKYCFlowRefactored.d.ts +1 -0
  13. package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  14. package/build/src/components/TemplateKYCFlowRefactored.js +56 -12
  15. package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
  16. package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
  17. package/build/src/modules/api/CardAuthentification.js +10 -2
  18. package/build/src/modules/api/CardAuthentification.js.map +1 -1
  19. package/build/src/utils/cropByObb.d.ts +1 -1
  20. package/build/src/utils/cropByObb.d.ts.map +1 -1
  21. package/build/src/utils/cropByObb.js +112 -5
  22. package/build/src/utils/cropByObb.js.map +1 -1
  23. package/package.json +7 -2
  24. package/src/components/EnhancedCameraView.tsx +4 -0
  25. package/src/components/KYCElements/IDCardCapture.tsx +49 -21
  26. package/src/components/TemplateKYCExample.tsx +4 -1
  27. package/src/components/TemplateKYCFlowRefactored.tsx +66 -11
  28. package/src/modules/api/CardAuthentification.ts +11 -3
  29. package/src/utils/cropByObb.ts +160 -6
@@ -4,6 +4,22 @@ import * as ImageManipulator from "expo-image-manipulator";
4
4
 
5
5
  type Point = [number, number];
6
6
 
7
+ type PixelBbox = {
8
+ minX: number;
9
+ minY: number;
10
+ width: number;
11
+ height: number;
12
+ };
13
+
14
+ type BboxOrientation = 'direct' | 'clockwise' | 'counter-clockwise' | 'upside-down';
15
+
16
+ type OrientedBboxCandidate = {
17
+ bbox: PixelBbox;
18
+ orientation: BboxOrientation;
19
+ inFrameRatio: number;
20
+ score: number;
21
+ };
22
+
7
23
  export const OBB_CONFIDENCE_THRESHOLD = 0.85;
8
24
 
9
25
 
@@ -84,6 +100,105 @@ function computeAabb(points: Point[]) {
84
100
  return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };
85
101
  }
86
102
 
103
+ function getInFrameRatio(bbox: PixelBbox, imageWidth: number, imageHeight: number): number {
104
+ const left = Math.max(0, bbox.minX);
105
+ const top = Math.max(0, bbox.minY);
106
+ const right = Math.min(imageWidth, bbox.minX + bbox.width);
107
+ const bottom = Math.min(imageHeight, bbox.minY + bbox.height);
108
+ const intersectionArea = Math.max(0, right - left) * Math.max(0, bottom - top);
109
+ const bboxArea = bbox.width * bbox.height;
110
+
111
+ return bboxArea > 0 ? intersectionArea / bboxArea : 0;
112
+ }
113
+
114
+ /**
115
+ * The API can return OBB coordinates in the camera sensor's pixel space while
116
+ * ImageManipulator returns an EXIF-normalized image. On phones held sideways,
117
+ * those spaces are 90 degrees apart. Select the representation which best fits
118
+ * the normalized image instead of applying the server coordinates blindly.
119
+ */
120
+ function resolveBboxOrientation(
121
+ bbox: PixelBbox,
122
+ imageWidth: number,
123
+ imageHeight: number,
124
+ preferredCardOrientation: 'landscape' | 'portrait' | 'either' = 'landscape',
125
+ ): OrientedBboxCandidate {
126
+ const candidates: Array<{ bbox: PixelBbox; orientation: BboxOrientation; priority: number }> = [
127
+ {
128
+ bbox,
129
+ orientation: 'direct',
130
+ priority: 0.75,
131
+ },
132
+ {
133
+ // Source canvas is imageHeight x imageWidth and is rotated clockwise.
134
+ bbox: {
135
+ minX: imageWidth - bbox.minY - bbox.height,
136
+ minY: bbox.minX,
137
+ width: bbox.height,
138
+ height: bbox.width,
139
+ },
140
+ orientation: 'clockwise',
141
+ priority: 0,
142
+ },
143
+ {
144
+ // Source canvas is imageHeight x imageWidth and is rotated counter-clockwise.
145
+ bbox: {
146
+ minX: bbox.minY,
147
+ minY: imageHeight - bbox.minX - bbox.width,
148
+ width: bbox.height,
149
+ height: bbox.width,
150
+ },
151
+ orientation: 'counter-clockwise',
152
+ priority: 0,
153
+ },
154
+ {
155
+ bbox: {
156
+ minX: imageWidth - bbox.minX - bbox.width,
157
+ minY: imageHeight - bbox.minY - bbox.height,
158
+ width: bbox.width,
159
+ height: bbox.height,
160
+ },
161
+ orientation: 'upside-down',
162
+ priority: -0.25,
163
+ },
164
+ ];
165
+
166
+ const scoredCandidates = candidates.map((candidate) => {
167
+ const candidateBbox = candidate.bbox;
168
+ const inFrameRatio = getInFrameRatio(candidateBbox, imageWidth, imageHeight);
169
+ const centerX = candidateBbox.minX + candidateBbox.width / 2;
170
+ const centerY = candidateBbox.minY + candidateBbox.height / 2;
171
+ const normalizedCenterDistance = Math.hypot(
172
+ (centerX - imageWidth / 2) / Math.max(1, imageWidth / 2),
173
+ (centerY - imageHeight / 2) / Math.max(1, imageHeight / 2),
174
+ );
175
+ const centerScore = 1 - Math.min(1, normalizedCenterDistance);
176
+
177
+ const isLandscapeCard = candidateBbox.width >= candidateBbox.height;
178
+ const preferredOrientationScore = preferredCardOrientation === 'either'
179
+ ? 0
180
+ : ((preferredCardOrientation === 'landscape') === isLandscapeCard ? 1 : 0);
181
+ const areaRatio = (candidateBbox.width * candidateBbox.height) / Math.max(1, imageWidth * imageHeight);
182
+ const plausibleAreaScore = Math.min(1, areaRatio / 0.08);
183
+
184
+ return {
185
+ bbox: candidateBbox,
186
+ orientation: candidate.orientation,
187
+ inFrameRatio,
188
+ score:
189
+ inFrameRatio * 100
190
+ + centerScore * 3
191
+ + preferredOrientationScore * 2
192
+ + plausibleAreaScore
193
+ + candidate.priority,
194
+ };
195
+ });
196
+
197
+ return scoredCandidates.reduce((best, candidate) => (
198
+ candidate.score > best.score ? candidate : best
199
+ ));
200
+ }
201
+
87
202
  async function cropWeb(uri: string, points: Point[]): Promise<string> {
88
203
  return new Promise((resolve, reject) => {
89
204
  const img = new Image();
@@ -159,7 +274,12 @@ async function normalizeOrientationAndGetDimensions(
159
274
  }
160
275
 
161
276
 
162
- export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tolerance: number = 0.02): Promise<string> {
277
+ export async function cropImageWithBBoxWithTolerance(
278
+ uri: string,
279
+ bbox: any,
280
+ tolerance: number = 0.02,
281
+ preferredCardOrientation: 'landscape' | 'portrait' | 'either' = 'landscape',
282
+ ): Promise<string> {
163
283
  return new Promise<string>((resolve) => {
164
284
  let isResolved = false;
165
285
 
@@ -195,10 +315,44 @@ export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tol
195
315
  pixelWidth = bbox.width * actualWidth;
196
316
  pixelHeight = bbox.height * actualHeight;
197
317
  } else {
198
- pixelMinX = bbox.minX;
199
- pixelMinY = bbox.minY;
200
- pixelWidth = bbox.width;
201
- pixelHeight = bbox.height;
318
+ const resolvedBbox = resolveBboxOrientation(
319
+ {
320
+ minX: bbox.minX,
321
+ minY: bbox.minY,
322
+ width: bbox.width,
323
+ height: bbox.height,
324
+ },
325
+ actualWidth,
326
+ actualHeight,
327
+ preferredCardOrientation,
328
+ );
329
+
330
+ if (resolvedBbox.inFrameRatio < 0.6) {
331
+ if (!isResolved) {
332
+ isResolved = true;
333
+ clearTimeout(timeout);
334
+ console.warn('OBB does not match the normalized image bounds; returning the full image.', {
335
+ bbox,
336
+ imageSize: { width: actualWidth, height: actualHeight },
337
+ bestOrientation: resolvedBbox.orientation,
338
+ inFrameRatio: resolvedBbox.inFrameRatio,
339
+ });
340
+ resolve(normalizedUri);
341
+ }
342
+ return;
343
+ }
344
+
345
+ if (resolvedBbox.orientation !== 'direct') {
346
+ console.info('Adjusted OBB coordinates for the normalized camera orientation.', {
347
+ orientation: resolvedBbox.orientation,
348
+ imageSize: { width: actualWidth, height: actualHeight },
349
+ });
350
+ }
351
+
352
+ pixelMinX = resolvedBbox.bbox.minX;
353
+ pixelMinY = resolvedBbox.bbox.minY;
354
+ pixelWidth = resolvedBbox.bbox.width;
355
+ pixelHeight = resolvedBbox.bbox.height;
202
356
  }
203
357
 
204
358
  // STEP 3: Add tolerance and clamp to image bounds.
@@ -309,4 +463,4 @@ export async function cropToCenterScanArea(
309
463
  }
310
464
  })();
311
465
  });
312
- }
466
+ }