react-native-rectangle-doc-scanner 3.111.0 → 3.112.0
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/dist/FullDocScanner.d.ts +2 -0
- package/dist/FullDocScanner.js +12 -90
- package/package.json +1 -1
- package/src/FullDocScanner.tsx +14 -112
package/dist/FullDocScanner.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface FullDocScannerResult {
|
|
|
8
8
|
base64?: string;
|
|
9
9
|
/** Original captured document info */
|
|
10
10
|
original?: CapturedDocument;
|
|
11
|
+
/** Rotation angle applied by user (0, 90, 180, 270) */
|
|
12
|
+
rotationDegrees?: number;
|
|
11
13
|
}
|
|
12
14
|
export interface FullDocScannerStrings {
|
|
13
15
|
captureHint?: string;
|
package/dist/FullDocScanner.js
CHANGED
|
@@ -52,9 +52,9 @@ catch (error) {
|
|
|
52
52
|
}
|
|
53
53
|
let expoManipulatorUnavailable = false;
|
|
54
54
|
const isExpoImageManipulatorAvailable = () => !!ImageManipulator?.manipulateAsync && !expoManipulatorUnavailable;
|
|
55
|
-
|
|
55
|
+
// 회전은 항상 지원됨 (회전 각도를 반환하고 tdb 앱에서 처리)
|
|
56
|
+
const isImageRotationSupported = () => true;
|
|
56
57
|
const stripFileUri = (value) => value.replace(/^file:\/\//, '');
|
|
57
|
-
const ensureFileUri = (value) => (value.startsWith('file://') ? value : `file://${value}`);
|
|
58
58
|
const CROPPER_TIMEOUT_MS = 8000;
|
|
59
59
|
const CROPPER_TIMEOUT_CODE = 'cropper_timeout';
|
|
60
60
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -374,94 +374,16 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
374
374
|
if (!croppedImageData) {
|
|
375
375
|
return;
|
|
376
376
|
}
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
setProcessing(true);
|
|
388
|
-
// 회전 각도 정규화 (0, 90, 180, 270)
|
|
389
|
-
const rotationNormalized = ((rotationDegrees % 360) + 360) % 360;
|
|
390
|
-
console.log('[FullDocScanner] Rotation degrees:', rotationDegrees, 'normalized:', rotationNormalized);
|
|
391
|
-
if (rotationNormalized === 0) {
|
|
392
|
-
console.log('[FullDocScanner] Normalized to 0, returning original image');
|
|
393
|
-
onResult({
|
|
394
|
-
path: croppedImageData.path,
|
|
395
|
-
base64: croppedImageData.base64,
|
|
396
|
-
});
|
|
397
|
-
setProcessing(false);
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
// expo-image-manipulator 시도
|
|
401
|
-
if (isExpoImageManipulatorAvailable() && ImageManipulator?.manipulateAsync) {
|
|
402
|
-
try {
|
|
403
|
-
console.log('[FullDocScanner] Using expo-image-manipulator for rotation');
|
|
404
|
-
const inputUri = ensureFileUri(croppedImageData.path);
|
|
405
|
-
const result = await ImageManipulator.manipulateAsync(inputUri, [{ rotate: rotationNormalized }], {
|
|
406
|
-
compress: 0.9,
|
|
407
|
-
format: ImageManipulator.SaveFormat.JPEG,
|
|
408
|
-
base64: true,
|
|
409
|
-
});
|
|
410
|
-
console.log('[FullDocScanner] Rotation complete via expo-image-manipulator:', {
|
|
411
|
-
path: result.uri,
|
|
412
|
-
hasBase64: !!result.base64,
|
|
413
|
-
});
|
|
414
|
-
onResult({
|
|
415
|
-
path: stripFileUri(result.uri),
|
|
416
|
-
base64: result.base64,
|
|
417
|
-
});
|
|
418
|
-
setProcessing(false);
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
catch (manipulatorError) {
|
|
422
|
-
const code = manipulatorError && typeof manipulatorError === 'object' && 'code' in manipulatorError
|
|
423
|
-
? String(manipulatorError.code)
|
|
424
|
-
: undefined;
|
|
425
|
-
console.error('[FullDocScanner] expo-image-manipulator error:', manipulatorError);
|
|
426
|
-
if (code === 'ERR_UNAVAILABLE') {
|
|
427
|
-
expoManipulatorUnavailable = true;
|
|
428
|
-
console.warn('[FullDocScanner] expo-image-manipulator unavailable at runtime. Trying react-native-image-rotate.');
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
throw manipulatorError;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
// expo-image-manipulator를 사용할 수 없는 경우 에러 처리
|
|
436
|
-
console.error('[FullDocScanner] Rotation requested but expo-image-manipulator is not available.');
|
|
437
|
-
setProcessing(false);
|
|
438
|
-
react_native_1.Alert.alert('회전 불가', 'expo-image-manipulator가 설치되지 않아 이미지 회전을 수행할 수 없습니다.\n\n패키지를 설치해주세요:\nnpm install expo-image-manipulator', [
|
|
439
|
-
{
|
|
440
|
-
text: '원본 사용',
|
|
441
|
-
onPress: () => {
|
|
442
|
-
onResult({
|
|
443
|
-
path: croppedImageData.path,
|
|
444
|
-
base64: croppedImageData.base64,
|
|
445
|
-
});
|
|
446
|
-
},
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
text: '취소',
|
|
450
|
-
style: 'cancel',
|
|
451
|
-
},
|
|
452
|
-
]);
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
console.error('[FullDocScanner] Image rotation error on confirm:', error);
|
|
456
|
-
setProcessing(false);
|
|
457
|
-
const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : String(error);
|
|
458
|
-
react_native_1.Alert.alert('회전 실패', `이미지 회전 중 오류가 발생했습니다.\n원본 이미지를 사용합니다.\n\n오류: ${errorMessage}`);
|
|
459
|
-
// 에러 발생 시 원본 이미지 반환
|
|
460
|
-
onResult({
|
|
461
|
-
path: croppedImageData.path,
|
|
462
|
-
base64: croppedImageData.base64,
|
|
463
|
-
});
|
|
464
|
-
}
|
|
377
|
+
// 회전 각도 정규화 (0, 90, 180, 270)
|
|
378
|
+
const rotationNormalized = ((rotationDegrees % 360) + 360) % 360;
|
|
379
|
+
console.log('[FullDocScanner] Confirm - rotation degrees:', rotationDegrees, 'normalized:', rotationNormalized);
|
|
380
|
+
// 원본 이미지와 회전 각도를 함께 반환
|
|
381
|
+
// tdb 앱에서 expo-image-manipulator를 사용해서 회전 처리
|
|
382
|
+
onResult({
|
|
383
|
+
path: croppedImageData.path,
|
|
384
|
+
base64: croppedImageData.base64,
|
|
385
|
+
rotationDegrees: rotationNormalized,
|
|
386
|
+
});
|
|
465
387
|
}, [croppedImageData, rotationDegrees, onResult]);
|
|
466
388
|
const handleRetake = (0, react_1.useCallback)(() => {
|
|
467
389
|
console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
|
package/package.json
CHANGED
package/src/FullDocScanner.tsx
CHANGED
|
@@ -37,10 +37,10 @@ try {
|
|
|
37
37
|
let expoManipulatorUnavailable = false;
|
|
38
38
|
const isExpoImageManipulatorAvailable = () =>
|
|
39
39
|
!!ImageManipulator?.manipulateAsync && !expoManipulatorUnavailable;
|
|
40
|
-
|
|
40
|
+
// 회전은 항상 지원됨 (회전 각도를 반환하고 tdb 앱에서 처리)
|
|
41
|
+
const isImageRotationSupported = () => true;
|
|
41
42
|
|
|
42
43
|
const stripFileUri = (value: string) => value.replace(/^file:\/\//, '');
|
|
43
|
-
const ensureFileUri = (value: string) => (value.startsWith('file://') ? value : `file://${value}`);
|
|
44
44
|
|
|
45
45
|
const CROPPER_TIMEOUT_MS = 8000;
|
|
46
46
|
const CROPPER_TIMEOUT_CODE = 'cropper_timeout';
|
|
@@ -122,6 +122,8 @@ export interface FullDocScannerResult {
|
|
|
122
122
|
base64?: string;
|
|
123
123
|
/** Original captured document info */
|
|
124
124
|
original?: CapturedDocument;
|
|
125
|
+
/** Rotation angle applied by user (0, 90, 180, 270) */
|
|
126
|
+
rotationDegrees?: number;
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
export interface FullDocScannerStrings {
|
|
@@ -500,117 +502,17 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
500
502
|
return;
|
|
501
503
|
}
|
|
502
504
|
|
|
503
|
-
//
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
onResult({
|
|
507
|
-
path: croppedImageData.path,
|
|
508
|
-
base64: croppedImageData.base64,
|
|
509
|
-
});
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
try {
|
|
514
|
-
setProcessing(true);
|
|
515
|
-
|
|
516
|
-
// 회전 각도 정규화 (0, 90, 180, 270)
|
|
517
|
-
const rotationNormalized = ((rotationDegrees % 360) + 360) % 360;
|
|
518
|
-
console.log('[FullDocScanner] Rotation degrees:', rotationDegrees, 'normalized:', rotationNormalized);
|
|
519
|
-
|
|
520
|
-
if (rotationNormalized === 0) {
|
|
521
|
-
console.log('[FullDocScanner] Normalized to 0, returning original image');
|
|
522
|
-
onResult({
|
|
523
|
-
path: croppedImageData.path,
|
|
524
|
-
base64: croppedImageData.base64,
|
|
525
|
-
});
|
|
526
|
-
setProcessing(false);
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// expo-image-manipulator 시도
|
|
531
|
-
if (isExpoImageManipulatorAvailable() && ImageManipulator?.manipulateAsync) {
|
|
532
|
-
try {
|
|
533
|
-
console.log('[FullDocScanner] Using expo-image-manipulator for rotation');
|
|
534
|
-
const inputUri = ensureFileUri(croppedImageData.path);
|
|
535
|
-
|
|
536
|
-
const result = await ImageManipulator.manipulateAsync(
|
|
537
|
-
inputUri,
|
|
538
|
-
[{ rotate: rotationNormalized }],
|
|
539
|
-
{
|
|
540
|
-
compress: 0.9,
|
|
541
|
-
format: ImageManipulator.SaveFormat.JPEG,
|
|
542
|
-
base64: true,
|
|
543
|
-
},
|
|
544
|
-
);
|
|
505
|
+
// 회전 각도 정규화 (0, 90, 180, 270)
|
|
506
|
+
const rotationNormalized = ((rotationDegrees % 360) + 360) % 360;
|
|
507
|
+
console.log('[FullDocScanner] Confirm - rotation degrees:', rotationDegrees, 'normalized:', rotationNormalized);
|
|
545
508
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
base64: result.base64,
|
|
554
|
-
});
|
|
555
|
-
setProcessing(false);
|
|
556
|
-
return;
|
|
557
|
-
} catch (manipulatorError) {
|
|
558
|
-
const code =
|
|
559
|
-
manipulatorError && typeof manipulatorError === 'object' && 'code' in manipulatorError
|
|
560
|
-
? String((manipulatorError as any).code)
|
|
561
|
-
: undefined;
|
|
562
|
-
|
|
563
|
-
console.error('[FullDocScanner] expo-image-manipulator error:', manipulatorError);
|
|
564
|
-
|
|
565
|
-
if (code === 'ERR_UNAVAILABLE') {
|
|
566
|
-
expoManipulatorUnavailable = true;
|
|
567
|
-
console.warn(
|
|
568
|
-
'[FullDocScanner] expo-image-manipulator unavailable at runtime. Trying react-native-image-rotate.',
|
|
569
|
-
);
|
|
570
|
-
} else {
|
|
571
|
-
throw manipulatorError;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// expo-image-manipulator를 사용할 수 없는 경우 에러 처리
|
|
577
|
-
console.error(
|
|
578
|
-
'[FullDocScanner] Rotation requested but expo-image-manipulator is not available.',
|
|
579
|
-
);
|
|
580
|
-
setProcessing(false);
|
|
581
|
-
Alert.alert(
|
|
582
|
-
'회전 불가',
|
|
583
|
-
'expo-image-manipulator가 설치되지 않아 이미지 회전을 수행할 수 없습니다.\n\n패키지를 설치해주세요:\nnpm install expo-image-manipulator',
|
|
584
|
-
[
|
|
585
|
-
{
|
|
586
|
-
text: '원본 사용',
|
|
587
|
-
onPress: () => {
|
|
588
|
-
onResult({
|
|
589
|
-
path: croppedImageData.path,
|
|
590
|
-
base64: croppedImageData.base64,
|
|
591
|
-
});
|
|
592
|
-
},
|
|
593
|
-
},
|
|
594
|
-
{
|
|
595
|
-
text: '취소',
|
|
596
|
-
style: 'cancel',
|
|
597
|
-
},
|
|
598
|
-
],
|
|
599
|
-
);
|
|
600
|
-
} catch (error) {
|
|
601
|
-
console.error('[FullDocScanner] Image rotation error on confirm:', error);
|
|
602
|
-
setProcessing(false);
|
|
603
|
-
|
|
604
|
-
const errorMessage =
|
|
605
|
-
error && typeof error === 'object' && 'message' in error ? (error as Error).message : String(error);
|
|
606
|
-
Alert.alert('회전 실패', `이미지 회전 중 오류가 발생했습니다.\n원본 이미지를 사용합니다.\n\n오류: ${errorMessage}`);
|
|
607
|
-
|
|
608
|
-
// 에러 발생 시 원본 이미지 반환
|
|
609
|
-
onResult({
|
|
610
|
-
path: croppedImageData.path,
|
|
611
|
-
base64: croppedImageData.base64,
|
|
612
|
-
});
|
|
613
|
-
}
|
|
509
|
+
// 원본 이미지와 회전 각도를 함께 반환
|
|
510
|
+
// tdb 앱에서 expo-image-manipulator를 사용해서 회전 처리
|
|
511
|
+
onResult({
|
|
512
|
+
path: croppedImageData.path,
|
|
513
|
+
base64: croppedImageData.base64,
|
|
514
|
+
rotationDegrees: rotationNormalized,
|
|
515
|
+
});
|
|
614
516
|
}, [croppedImageData, rotationDegrees, onResult]);
|
|
615
517
|
|
|
616
518
|
const handleRetake = useCallback(() => {
|