react-native-rectangle-doc-scanner 3.44.2 → 3.44.4

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.
@@ -57,6 +57,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
57
57
  const [processing, setProcessing] = (0, react_1.useState)(false);
58
58
  const [croppedImageData, setCroppedImageData] = (0, react_1.useState)(null);
59
59
  const [isGalleryOpen, setIsGalleryOpen] = (0, react_1.useState)(false);
60
+ const [rectangleDetected, setRectangleDetected] = (0, react_1.useState)(false);
60
61
  const resolvedGridColor = gridColor ?? overlayColor;
61
62
  const docScannerRef = (0, react_1.useRef)(null);
62
63
  const manualCapturePending = (0, react_1.useRef)(false);
@@ -108,26 +109,34 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
108
109
  console.log('[FullDocScanner] handleCapture called:', {
109
110
  origin: document.origin,
110
111
  path: document.path,
112
+ croppedPath: document.croppedPath,
113
+ initialPath: document.initialPath,
111
114
  });
115
+ // Reset manual capture pending flag
112
116
  if (manualCapturePending.current) {
117
+ console.log('[FullDocScanner] Resetting manualCapturePending');
113
118
  manualCapturePending.current = false;
114
119
  }
115
120
  const normalizedDoc = normalizeCapturedDocument(document);
116
121
  // Auto-capture: Use already cropped image, skip cropper
117
122
  if (document.origin === 'auto' && normalizedDoc.croppedPath) {
118
- console.log('[FullDocScanner] Auto-capture: using pre-cropped image');
123
+ console.log('[FullDocScanner] Auto-capture: using pre-cropped image', normalizedDoc.croppedPath);
119
124
  setCroppedImageData({
120
125
  path: normalizedDoc.croppedPath,
121
126
  });
122
127
  }
123
128
  else {
124
129
  // Manual capture or gallery: Open cropper
125
- console.log('[FullDocScanner] Manual capture: opening cropper');
130
+ console.log('[FullDocScanner] Manual/Gallery capture: opening cropper with', normalizedDoc.path);
126
131
  await openCropper(normalizedDoc.path);
127
132
  }
128
133
  }, [openCropper]);
129
134
  const triggerManualCapture = (0, react_1.useCallback)(() => {
130
- console.log('[FullDocScanner] triggerManualCapture called');
135
+ console.log('[FullDocScanner] triggerManualCapture called', {
136
+ processing,
137
+ manualCapturePending: manualCapturePending.current,
138
+ hasRef: !!docScannerRef.current,
139
+ });
131
140
  if (processing) {
132
141
  console.log('[FullDocScanner] Already processing, skipping manual capture');
133
142
  return;
@@ -136,21 +145,33 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
136
145
  console.log('[FullDocScanner] Manual capture already pending, skipping');
137
146
  return;
138
147
  }
139
- console.log('[FullDocScanner] Setting manualCapturePending to true');
148
+ if (!docScannerRef.current) {
149
+ console.error('[FullDocScanner] DocScanner ref not available');
150
+ return;
151
+ }
152
+ console.log('[FullDocScanner] Starting manual capture');
140
153
  manualCapturePending.current = true;
141
- const capturePromise = docScannerRef.current?.capture();
142
- if (capturePromise && typeof capturePromise.then === 'function') {
143
- capturePromise
144
- .then(() => {
145
- console.log('[FullDocScanner] Capture success');
146
- })
147
- .catch((error) => {
154
+ try {
155
+ const capturePromise = docScannerRef.current.capture();
156
+ console.log('[FullDocScanner] Capture promise:', capturePromise);
157
+ if (capturePromise && typeof capturePromise.then === 'function') {
158
+ capturePromise
159
+ .then((result) => {
160
+ console.log('[FullDocScanner] Manual capture success:', result);
161
+ manualCapturePending.current = false;
162
+ })
163
+ .catch((error) => {
164
+ console.error('[FullDocScanner] Manual capture failed:', error);
165
+ manualCapturePending.current = false;
166
+ });
167
+ }
168
+ else {
169
+ console.warn('[FullDocScanner] No promise returned from capture()');
148
170
  manualCapturePending.current = false;
149
- console.warn('[FullDocScanner] manual capture failed', error);
150
- });
171
+ }
151
172
  }
152
- else if (!capturePromise) {
153
- console.warn('[FullDocScanner] No capture promise returned');
173
+ catch (error) {
174
+ console.error('[FullDocScanner] Exception during capture:', error);
154
175
  manualCapturePending.current = false;
155
176
  }
156
177
  }, [processing]);
@@ -193,7 +214,19 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
193
214
  }
194
215
  }, [croppedImageData, onResult]);
195
216
  const handleRetake = (0, react_1.useCallback)(() => {
217
+ console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
196
218
  setCroppedImageData(null);
219
+ setProcessing(false);
220
+ setRectangleDetected(false);
221
+ // Reset DocScanner state
222
+ if (docScannerRef.current?.reset) {
223
+ docScannerRef.current.reset();
224
+ }
225
+ }, []);
226
+ const handleRectangleDetect = (0, react_1.useCallback)((event) => {
227
+ // Update button color based on rectangle detection
228
+ const hasGoodRectangle = event.lastDetectionType === 0 && event.rectangleCoordinates !== null;
229
+ setRectangleDetected(hasGoodRectangle);
197
230
  }, []);
198
231
  return (react_1.default.createElement(react_native_1.View, { style: styles.container },
199
232
  croppedImageData ? (
@@ -205,7 +238,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
205
238
  react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.retake)),
206
239
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.confirmButtonPrimary], onPress: handleConfirm, accessibilityLabel: mergedStrings.confirm, accessibilityRole: "button" },
207
240
  react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.confirm))))) : (react_1.default.createElement(react_native_1.View, { style: styles.flex },
208
- react_1.default.createElement(DocScanner_1.DocScanner, { ref: docScannerRef, autoCapture: !manualCapture && !isGalleryOpen, overlayColor: overlayColor, showGrid: showGrid, gridColor: resolvedGridColor, gridLineWidth: gridLineWidth, minStableFrames: minStableFrames ?? 6, detectionConfig: detectionConfig, onCapture: handleCapture, showManualCaptureButton: false },
241
+ react_1.default.createElement(DocScanner_1.DocScanner, { ref: docScannerRef, autoCapture: !manualCapture && !isGalleryOpen, overlayColor: overlayColor, showGrid: showGrid, gridColor: resolvedGridColor, gridLineWidth: gridLineWidth, minStableFrames: minStableFrames ?? 6, detectionConfig: detectionConfig, onCapture: handleCapture, onRectangleDetect: handleRectangleDetect, showManualCaptureButton: false },
209
242
  react_1.default.createElement(react_native_1.View, { style: styles.overlayTop, pointerEvents: "box-none" },
210
243
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.closeButton, onPress: handleClose, accessibilityLabel: mergedStrings.cancel, accessibilityRole: "button" },
211
244
  react_1.default.createElement(react_native_1.Text, { style: styles.closeButtonLabel }, "\u00D7"))),
@@ -217,7 +250,10 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
217
250
  enableGallery && (react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.galleryButton, processing && styles.buttonDisabled], onPress: handleGalleryPick, disabled: processing, accessibilityLabel: mergedStrings.galleryButton, accessibilityRole: "button" },
218
251
  react_1.default.createElement(react_native_1.Text, { style: styles.galleryButtonText }, "\uD83D\uDCC1"))),
219
252
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.shutterButton, processing && styles.buttonDisabled], onPress: triggerManualCapture, disabled: processing, accessibilityLabel: mergedStrings.manualHint, accessibilityRole: "button" },
220
- react_1.default.createElement(react_native_1.View, { style: styles.shutterInner })))))),
253
+ react_1.default.createElement(react_native_1.View, { style: [
254
+ styles.shutterInner,
255
+ rectangleDetected && { backgroundColor: overlayColor }
256
+ ] })))))),
221
257
  processing && (react_1.default.createElement(react_native_1.View, { style: styles.processingOverlay },
222
258
  react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: overlayColor }),
223
259
  mergedStrings.processing && (react_1.default.createElement(react_native_1.Text, { style: styles.processingText }, mergedStrings.processing))))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.44.2",
3
+ "version": "3.44.4",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -82,6 +82,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
82
82
  const [processing, setProcessing] = useState(false);
83
83
  const [croppedImageData, setCroppedImageData] = useState<{path: string; base64?: string} | null>(null);
84
84
  const [isGalleryOpen, setIsGalleryOpen] = useState(false);
85
+ const [rectangleDetected, setRectangleDetected] = useState(false);
85
86
  const resolvedGridColor = gridColor ?? overlayColor;
86
87
  const docScannerRef = useRef<DocScannerHandle | null>(null);
87
88
  const manualCapturePending = useRef(false);
@@ -151,9 +152,13 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
151
152
  console.log('[FullDocScanner] handleCapture called:', {
152
153
  origin: document.origin,
153
154
  path: document.path,
155
+ croppedPath: document.croppedPath,
156
+ initialPath: document.initialPath,
154
157
  });
155
158
 
159
+ // Reset manual capture pending flag
156
160
  if (manualCapturePending.current) {
161
+ console.log('[FullDocScanner] Resetting manualCapturePending');
157
162
  manualCapturePending.current = false;
158
163
  }
159
164
 
@@ -161,13 +166,13 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
161
166
 
162
167
  // Auto-capture: Use already cropped image, skip cropper
163
168
  if (document.origin === 'auto' && normalizedDoc.croppedPath) {
164
- console.log('[FullDocScanner] Auto-capture: using pre-cropped image');
169
+ console.log('[FullDocScanner] Auto-capture: using pre-cropped image', normalizedDoc.croppedPath);
165
170
  setCroppedImageData({
166
171
  path: normalizedDoc.croppedPath,
167
172
  });
168
173
  } else {
169
174
  // Manual capture or gallery: Open cropper
170
- console.log('[FullDocScanner] Manual capture: opening cropper');
175
+ console.log('[FullDocScanner] Manual/Gallery capture: opening cropper with', normalizedDoc.path);
171
176
  await openCropper(normalizedDoc.path);
172
177
  }
173
178
  },
@@ -175,31 +180,50 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
175
180
  );
176
181
 
177
182
  const triggerManualCapture = useCallback(() => {
178
- console.log('[FullDocScanner] triggerManualCapture called');
183
+ console.log('[FullDocScanner] triggerManualCapture called', {
184
+ processing,
185
+ manualCapturePending: manualCapturePending.current,
186
+ hasRef: !!docScannerRef.current,
187
+ });
188
+
179
189
  if (processing) {
180
190
  console.log('[FullDocScanner] Already processing, skipping manual capture');
181
191
  return;
182
192
  }
193
+
183
194
  if (manualCapturePending.current) {
184
195
  console.log('[FullDocScanner] Manual capture already pending, skipping');
185
196
  return;
186
197
  }
187
198
 
188
- console.log('[FullDocScanner] Setting manualCapturePending to true');
199
+ if (!docScannerRef.current) {
200
+ console.error('[FullDocScanner] DocScanner ref not available');
201
+ return;
202
+ }
203
+
204
+ console.log('[FullDocScanner] Starting manual capture');
189
205
  manualCapturePending.current = true;
190
206
 
191
- const capturePromise = docScannerRef.current?.capture();
192
- if (capturePromise && typeof capturePromise.then === 'function') {
193
- capturePromise
194
- .then(() => {
195
- console.log('[FullDocScanner] Capture success');
196
- })
197
- .catch((error: unknown) => {
198
- manualCapturePending.current = false;
199
- console.warn('[FullDocScanner] manual capture failed', error);
200
- });
201
- } else if (!capturePromise) {
202
- console.warn('[FullDocScanner] No capture promise returned');
207
+ try {
208
+ const capturePromise = docScannerRef.current.capture();
209
+ console.log('[FullDocScanner] Capture promise:', capturePromise);
210
+
211
+ if (capturePromise && typeof capturePromise.then === 'function') {
212
+ capturePromise
213
+ .then((result) => {
214
+ console.log('[FullDocScanner] Manual capture success:', result);
215
+ manualCapturePending.current = false;
216
+ })
217
+ .catch((error: unknown) => {
218
+ console.error('[FullDocScanner] Manual capture failed:', error);
219
+ manualCapturePending.current = false;
220
+ });
221
+ } else {
222
+ console.warn('[FullDocScanner] No promise returned from capture()');
223
+ manualCapturePending.current = false;
224
+ }
225
+ } catch (error) {
226
+ console.error('[FullDocScanner] Exception during capture:', error);
203
227
  manualCapturePending.current = false;
204
228
  }
205
229
  }, [processing]);
@@ -253,7 +277,20 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
253
277
  }, [croppedImageData, onResult]);
254
278
 
255
279
  const handleRetake = useCallback(() => {
280
+ console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
256
281
  setCroppedImageData(null);
282
+ setProcessing(false);
283
+ setRectangleDetected(false);
284
+ // Reset DocScanner state
285
+ if (docScannerRef.current?.reset) {
286
+ docScannerRef.current.reset();
287
+ }
288
+ }, []);
289
+
290
+ const handleRectangleDetect = useCallback((event: any) => {
291
+ // Update button color based on rectangle detection
292
+ const hasGoodRectangle = event.lastDetectionType === 0 && event.rectangleCoordinates !== null;
293
+ setRectangleDetected(hasGoodRectangle);
257
294
  }, []);
258
295
 
259
296
  return (
@@ -297,6 +334,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
297
334
  minStableFrames={minStableFrames ?? 6}
298
335
  detectionConfig={detectionConfig}
299
336
  onCapture={handleCapture}
337
+ onRectangleDetect={handleRectangleDetect}
300
338
  showManualCaptureButton={false}
301
339
  >
302
340
  <View style={styles.overlayTop} pointerEvents="box-none">
@@ -340,7 +378,10 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
340
378
  accessibilityLabel={mergedStrings.manualHint}
341
379
  accessibilityRole="button"
342
380
  >
343
- <View style={styles.shutterInner} />
381
+ <View style={[
382
+ styles.shutterInner,
383
+ rectangleDetected && { backgroundColor: overlayColor }
384
+ ]} />
344
385
  </TouchableOpacity>
345
386
  </View>
346
387
  </DocScanner>
@@ -474,6 +474,12 @@
474
474
 
475
475
  [weakSelf hideGLKView:NO completion:nil];
476
476
  completionHandler(image, initialImage, rectangleFeature);
477
+ } else {
478
+ // No rectangle detected, return original image
479
+ NSLog(@"[IPDFCameraViewController] No rectangle detected during manual capture, returning original image");
480
+ [weakSelf hideGLKView:NO completion:nil];
481
+ UIImage *initialImage = [UIImage imageWithData:imageData];
482
+ completionHandler(initialImage, initialImage, nil);
477
483
  }
478
484
  } else {
479
485
  [weakSelf hideGLKView:NO completion:nil];