react-native-rectangle-doc-scanner 3.67.0 → 3.68.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/DocScanner.js +35 -32
- package/package.json +1 -1
- package/src/DocScanner.tsx +37 -34
- package/vendor/react-native-document-scanner/ios/DocumentScannerView.h +2 -0
- package/vendor/react-native-document-scanner/ios/DocumentScannerView.m +85 -43
- package/vendor/react-native-document-scanner/ios/IPDFCameraViewController.m +33 -122
- package/vendor/react-native-document-scanner/ios/RNPdfScannerManager.m +6 -3
- package/vendor/react-native-document-scanner/ios.js +8 -6
package/dist/DocScanner.js
CHANGED
|
@@ -168,46 +168,49 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
|
|
|
168
168
|
captureOriginRef.current = 'auto';
|
|
169
169
|
return Promise.reject(new Error('capture_in_progress'));
|
|
170
170
|
}
|
|
171
|
-
console.log('[DocScanner] Calling native capture method...');
|
|
172
|
-
let result;
|
|
171
|
+
console.log('[DocScanner] Calling native capture method (now returns Promise)...');
|
|
173
172
|
try {
|
|
174
|
-
result = instance.capture();
|
|
173
|
+
const result = instance.capture();
|
|
175
174
|
console.log('[DocScanner] Native capture method called, result type:', typeof result, 'isPromise:', !!(result && typeof result.then === 'function'));
|
|
175
|
+
if (result && typeof result.then === 'function') {
|
|
176
|
+
console.log('[DocScanner] Native returned a promise, waiting for resolution...');
|
|
177
|
+
return result
|
|
178
|
+
.then((payload) => {
|
|
179
|
+
console.log('[DocScanner] Native promise resolved with payload:', {
|
|
180
|
+
hasCroppedImage: !!payload.croppedImage,
|
|
181
|
+
hasInitialImage: !!payload.initialImage,
|
|
182
|
+
});
|
|
183
|
+
handlePictureTaken(payload);
|
|
184
|
+
return payload;
|
|
185
|
+
})
|
|
186
|
+
.catch((error) => {
|
|
187
|
+
console.error('[DocScanner] Native promise rejected:', error);
|
|
188
|
+
captureOriginRef.current = 'auto';
|
|
189
|
+
throw error;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// Fallback for legacy event-based approach
|
|
193
|
+
console.warn('[DocScanner] Native did not return a promise, using callback-based approach (legacy)');
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
captureResolvers.current = {
|
|
196
|
+
resolve: (value) => {
|
|
197
|
+
console.log('[DocScanner] Callback resolver called with:', value);
|
|
198
|
+
captureOriginRef.current = 'auto';
|
|
199
|
+
resolve(value);
|
|
200
|
+
},
|
|
201
|
+
reject: (reason) => {
|
|
202
|
+
console.error('[DocScanner] Callback rejector called with:', reason);
|
|
203
|
+
captureOriginRef.current = 'auto';
|
|
204
|
+
reject(reason);
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
});
|
|
176
208
|
}
|
|
177
209
|
catch (error) {
|
|
178
210
|
console.error('[DocScanner] Native capture threw error:', error);
|
|
179
211
|
captureOriginRef.current = 'auto';
|
|
180
212
|
return Promise.reject(error);
|
|
181
213
|
}
|
|
182
|
-
if (result && typeof result.then === 'function') {
|
|
183
|
-
console.log('[DocScanner] Native returned a promise, waiting for resolution...');
|
|
184
|
-
return result
|
|
185
|
-
.then((payload) => {
|
|
186
|
-
console.log('[DocScanner] Native promise resolved with payload:', payload);
|
|
187
|
-
handlePictureTaken(payload);
|
|
188
|
-
return payload;
|
|
189
|
-
})
|
|
190
|
-
.catch((error) => {
|
|
191
|
-
console.error('[DocScanner] Native promise rejected:', error);
|
|
192
|
-
captureOriginRef.current = 'auto';
|
|
193
|
-
throw error;
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
console.log('[DocScanner] Native did not return a promise, using callback-based approach');
|
|
197
|
-
return new Promise((resolve, reject) => {
|
|
198
|
-
captureResolvers.current = {
|
|
199
|
-
resolve: (value) => {
|
|
200
|
-
console.log('[DocScanner] Callback resolver called with:', value);
|
|
201
|
-
captureOriginRef.current = 'auto';
|
|
202
|
-
resolve(value);
|
|
203
|
-
},
|
|
204
|
-
reject: (reason) => {
|
|
205
|
-
console.error('[DocScanner] Callback rejector called with:', reason);
|
|
206
|
-
captureOriginRef.current = 'auto';
|
|
207
|
-
reject(reason);
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
});
|
|
211
214
|
}, [handlePictureTaken]);
|
|
212
215
|
const handleManualCapture = (0, react_1.useCallback)(() => {
|
|
213
216
|
captureOriginRef.current = 'manual';
|
package/package.json
CHANGED
package/src/DocScanner.tsx
CHANGED
|
@@ -247,47 +247,50 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
247
247
|
return Promise.reject(new Error('capture_in_progress'));
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
console.log('[DocScanner] Calling native capture method...');
|
|
251
|
-
let result: any;
|
|
250
|
+
console.log('[DocScanner] Calling native capture method (now returns Promise)...');
|
|
252
251
|
try {
|
|
253
|
-
result = instance.capture();
|
|
252
|
+
const result = instance.capture();
|
|
254
253
|
console.log('[DocScanner] Native capture method called, result type:', typeof result, 'isPromise:', !!(result && typeof result.then === 'function'));
|
|
254
|
+
|
|
255
|
+
if (result && typeof result.then === 'function') {
|
|
256
|
+
console.log('[DocScanner] Native returned a promise, waiting for resolution...');
|
|
257
|
+
return result
|
|
258
|
+
.then((payload: PictureEvent) => {
|
|
259
|
+
console.log('[DocScanner] Native promise resolved with payload:', {
|
|
260
|
+
hasCroppedImage: !!payload.croppedImage,
|
|
261
|
+
hasInitialImage: !!payload.initialImage,
|
|
262
|
+
});
|
|
263
|
+
handlePictureTaken(payload);
|
|
264
|
+
return payload;
|
|
265
|
+
})
|
|
266
|
+
.catch((error: unknown) => {
|
|
267
|
+
console.error('[DocScanner] Native promise rejected:', error);
|
|
268
|
+
captureOriginRef.current = 'auto';
|
|
269
|
+
throw error;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Fallback for legacy event-based approach
|
|
274
|
+
console.warn('[DocScanner] Native did not return a promise, using callback-based approach (legacy)');
|
|
275
|
+
return new Promise<PictureEvent>((resolve, reject) => {
|
|
276
|
+
captureResolvers.current = {
|
|
277
|
+
resolve: (value) => {
|
|
278
|
+
console.log('[DocScanner] Callback resolver called with:', value);
|
|
279
|
+
captureOriginRef.current = 'auto';
|
|
280
|
+
resolve(value);
|
|
281
|
+
},
|
|
282
|
+
reject: (reason) => {
|
|
283
|
+
console.error('[DocScanner] Callback rejector called with:', reason);
|
|
284
|
+
captureOriginRef.current = 'auto';
|
|
285
|
+
reject(reason);
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
});
|
|
255
289
|
} catch (error) {
|
|
256
290
|
console.error('[DocScanner] Native capture threw error:', error);
|
|
257
291
|
captureOriginRef.current = 'auto';
|
|
258
292
|
return Promise.reject(error);
|
|
259
293
|
}
|
|
260
|
-
|
|
261
|
-
if (result && typeof result.then === 'function') {
|
|
262
|
-
console.log('[DocScanner] Native returned a promise, waiting for resolution...');
|
|
263
|
-
return result
|
|
264
|
-
.then((payload: PictureEvent) => {
|
|
265
|
-
console.log('[DocScanner] Native promise resolved with payload:', payload);
|
|
266
|
-
handlePictureTaken(payload);
|
|
267
|
-
return payload;
|
|
268
|
-
})
|
|
269
|
-
.catch((error: unknown) => {
|
|
270
|
-
console.error('[DocScanner] Native promise rejected:', error);
|
|
271
|
-
captureOriginRef.current = 'auto';
|
|
272
|
-
throw error;
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
console.log('[DocScanner] Native did not return a promise, using callback-based approach');
|
|
277
|
-
return new Promise<PictureEvent>((resolve, reject) => {
|
|
278
|
-
captureResolvers.current = {
|
|
279
|
-
resolve: (value) => {
|
|
280
|
-
console.log('[DocScanner] Callback resolver called with:', value);
|
|
281
|
-
captureOriginRef.current = 'auto';
|
|
282
|
-
resolve(value);
|
|
283
|
-
},
|
|
284
|
-
reject: (reason) => {
|
|
285
|
-
console.error('[DocScanner] Callback rejector called with:', reason);
|
|
286
|
-
captureOriginRef.current = 'auto';
|
|
287
|
-
reject(reason);
|
|
288
|
-
},
|
|
289
|
-
};
|
|
290
|
-
});
|
|
291
294
|
}, [handlePictureTaken]);
|
|
292
295
|
|
|
293
296
|
const handleManualCapture = useCallback(() => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#import "IPDFCameraViewController.h"
|
|
2
2
|
#import <React/RCTViewManager.h>
|
|
3
|
+
#import <React/RCTBridgeModule.h>
|
|
3
4
|
|
|
4
5
|
@interface DocumentScannerView : IPDFCameraViewController <IPDFCameraViewControllerDelegate>
|
|
5
6
|
|
|
@@ -14,5 +15,6 @@
|
|
|
14
15
|
@property (nonatomic, assign) BOOL manualOnly;
|
|
15
16
|
|
|
16
17
|
- (void) capture;
|
|
18
|
+
- (void) captureWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
|
|
17
19
|
|
|
18
20
|
@end
|
|
@@ -124,6 +124,87 @@
|
|
|
124
124
|
self.onRectangleDetect(payload);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
// Helper method to process captured images and prepare response data
|
|
128
|
+
- (NSDictionary *)processAndPrepareImageData:(UIImage *)croppedImage
|
|
129
|
+
initialImage:(UIImage *)initialImage
|
|
130
|
+
rectangleFeature:(CIRectangleFeature *)rectangleFeature {
|
|
131
|
+
CGFloat imageQuality = MAX(self.quality, 0.95);
|
|
132
|
+
NSData *croppedImageData = UIImageJPEGRepresentation(croppedImage, imageQuality);
|
|
133
|
+
|
|
134
|
+
if (initialImage.imageOrientation != UIImageOrientationUp) {
|
|
135
|
+
UIGraphicsBeginImageContextWithOptions(initialImage.size, false, initialImage.scale);
|
|
136
|
+
[initialImage drawInRect:CGRectMake(0, 0, initialImage.size.width, initialImage.size.height)];
|
|
137
|
+
initialImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
138
|
+
UIGraphicsEndImageContext();
|
|
139
|
+
}
|
|
140
|
+
NSData *initialImageData = UIImageJPEGRepresentation(initialImage, imageQuality);
|
|
141
|
+
|
|
142
|
+
NSDictionary *rectangleCoordinatesDict = [self dictionaryForRectangleFeature:rectangleFeature];
|
|
143
|
+
id rectangleCoordinates = rectangleCoordinatesDict ? rectangleCoordinatesDict : [NSNull null];
|
|
144
|
+
|
|
145
|
+
if (self.useBase64) {
|
|
146
|
+
return @{
|
|
147
|
+
@"croppedImage": [croppedImageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength],
|
|
148
|
+
@"initialImage": [initialImageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength],
|
|
149
|
+
@"rectangleCoordinates": rectangleCoordinates
|
|
150
|
+
};
|
|
151
|
+
} else {
|
|
152
|
+
NSString *dir = NSTemporaryDirectory();
|
|
153
|
+
if (self.saveInAppDocument) {
|
|
154
|
+
dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
|
155
|
+
}
|
|
156
|
+
NSString *croppedFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"cropped_img_%i.jpeg",(int)[NSDate date].timeIntervalSince1970]];
|
|
157
|
+
NSString *initialFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"initial_img_%i.jpeg",(int)[NSDate date].timeIntervalSince1970]];
|
|
158
|
+
|
|
159
|
+
[croppedImageData writeToFile:croppedFilePath atomically:YES];
|
|
160
|
+
[initialImageData writeToFile:initialFilePath atomically:YES];
|
|
161
|
+
|
|
162
|
+
return @{
|
|
163
|
+
@"croppedImage": croppedFilePath,
|
|
164
|
+
@"initialImage": initialFilePath,
|
|
165
|
+
@"rectangleCoordinates": rectangleCoordinates
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Promise-based capture method - NEW
|
|
171
|
+
- (void)captureWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
|
172
|
+
NSLog(@"[DocumentScanner] captureWithResolver called");
|
|
173
|
+
|
|
174
|
+
[self captureImageWithCompletionHander:^(UIImage *croppedImage, UIImage *initialImage, CIRectangleFeature *rectangleFeature) {
|
|
175
|
+
NSLog(@"[DocumentScanner] captureImageWithCompletionHander callback - croppedImage: %@, initialImage: %@", croppedImage ? @"YES" : @"NO", initialImage ? @"YES" : @"NO");
|
|
176
|
+
|
|
177
|
+
if (!croppedImage && initialImage) {
|
|
178
|
+
croppedImage = initialImage;
|
|
179
|
+
} else if (!initialImage && croppedImage) {
|
|
180
|
+
initialImage = croppedImage;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!croppedImage || !initialImage) {
|
|
184
|
+
NSLog(@"[DocumentScanner] capture failed - missing image data");
|
|
185
|
+
reject(@"CAPTURE_FAILED", @"Failed to capture image", nil);
|
|
186
|
+
|
|
187
|
+
if (!self.captureMultiple) {
|
|
188
|
+
[self stop];
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
NSLog(@"[DocumentScanner] Processing captured images");
|
|
194
|
+
NSDictionary *result = [self processAndPrepareImageData:croppedImage
|
|
195
|
+
initialImage:initialImage
|
|
196
|
+
rectangleFeature:rectangleFeature];
|
|
197
|
+
|
|
198
|
+
NSLog(@"[DocumentScanner] Resolving promise with result");
|
|
199
|
+
resolve(result);
|
|
200
|
+
|
|
201
|
+
if (!self.captureMultiple) {
|
|
202
|
+
[self stop];
|
|
203
|
+
}
|
|
204
|
+
}];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Event-based capture method - LEGACY (for backwards compatibility)
|
|
127
208
|
- (void) capture {
|
|
128
209
|
NSLog(@"[DocumentScanner] capture called");
|
|
129
210
|
[self captureImageWithCompletionHander:^(UIImage *croppedImage, UIImage *initialImage, CIRectangleFeature *rectangleFeature) {
|
|
@@ -151,49 +232,10 @@
|
|
|
151
232
|
|
|
152
233
|
if (self.onPictureTaken) {
|
|
153
234
|
NSLog(@"[DocumentScanner] Calling onPictureTaken");
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (initialImage.imageOrientation != UIImageOrientationUp) {
|
|
160
|
-
UIGraphicsBeginImageContextWithOptions(initialImage.size, false, initialImage.scale);
|
|
161
|
-
[initialImage drawInRect:CGRectMake(0, 0, initialImage.size.width
|
|
162
|
-
, initialImage.size.height)];
|
|
163
|
-
initialImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
164
|
-
UIGraphicsEndImageContext();
|
|
165
|
-
}
|
|
166
|
-
NSData *initialImageData = UIImageJPEGRepresentation(initialImage, imageQuality);
|
|
167
|
-
|
|
168
|
-
/*
|
|
169
|
-
RectangleCoordinates expects a rectanle viewed from portrait,
|
|
170
|
-
while rectangleFeature returns a rectangle viewed from landscape, which explains the nonsense of the mapping below.
|
|
171
|
-
Sorry about that.
|
|
172
|
-
*/
|
|
173
|
-
NSDictionary *rectangleCoordinatesDict = [self dictionaryForRectangleFeature:rectangleFeature];
|
|
174
|
-
id rectangleCoordinates = rectangleCoordinatesDict ? rectangleCoordinatesDict : [NSNull null];
|
|
175
|
-
if (self.useBase64) {
|
|
176
|
-
self.onPictureTaken(@{
|
|
177
|
-
@"croppedImage": [croppedImageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength],
|
|
178
|
-
@"initialImage": [initialImageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength],
|
|
179
|
-
@"rectangleCoordinates": rectangleCoordinates });
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
NSString *dir = NSTemporaryDirectory();
|
|
183
|
-
if (self.saveInAppDocument) {
|
|
184
|
-
dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
|
185
|
-
}
|
|
186
|
-
NSString *croppedFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"cropped_img_%i.jpeg",(int)[NSDate date].timeIntervalSince1970]];
|
|
187
|
-
NSString *initialFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"initial_img_%i.jpeg",(int)[NSDate date].timeIntervalSince1970]];
|
|
188
|
-
|
|
189
|
-
[croppedImageData writeToFile:croppedFilePath atomically:YES];
|
|
190
|
-
[initialImageData writeToFile:initialFilePath atomically:YES];
|
|
191
|
-
|
|
192
|
-
self.onPictureTaken(@{
|
|
193
|
-
@"croppedImage": croppedFilePath,
|
|
194
|
-
@"initialImage": initialFilePath,
|
|
195
|
-
@"rectangleCoordinates": rectangleCoordinates });
|
|
196
|
-
}
|
|
235
|
+
NSDictionary *result = [self processAndPrepareImageData:croppedImage
|
|
236
|
+
initialImage:initialImage
|
|
237
|
+
rectangleFeature:rectangleFeature];
|
|
238
|
+
self.onPictureTaken(result);
|
|
197
239
|
}
|
|
198
240
|
|
|
199
241
|
if (!self.captureMultiple) {
|
|
@@ -34,7 +34,6 @@ static inline void dispatch_async_main_queue(dispatch_block_t block)
|
|
|
34
34
|
@property (nonatomic,strong) AVCaptureDevice *captureDevice;
|
|
35
35
|
@property (nonatomic,strong) EAGLContext *context;
|
|
36
36
|
|
|
37
|
-
@property (nonatomic, strong) AVCapturePhotoOutput* photoOutput;
|
|
38
37
|
@property (nonatomic, strong) AVCaptureStillImageOutput* stillImageOutput; // Kept for backward compatibility
|
|
39
38
|
|
|
40
39
|
@property (nonatomic, assign) BOOL forceStop;
|
|
@@ -192,29 +191,14 @@ static inline void dispatch_async_main_queue(dispatch_block_t block)
|
|
|
192
191
|
[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
|
|
193
192
|
[session addOutput:dataOutput];
|
|
194
193
|
|
|
195
|
-
// Use
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if ([session canAddOutput:self.photoOutput]) {
|
|
199
|
-
[session addOutput:self.photoOutput];
|
|
200
|
-
NSLog(@"[IPDFCamera] Using AVCapturePhotoOutput (modern API)");
|
|
201
|
-
} else {
|
|
202
|
-
NSLog(@"[IPDFCamera] WARNING: Cannot add AVCapturePhotoOutput, falling back to AVCaptureStillImageOutput");
|
|
203
|
-
self.photoOutput = nil;
|
|
204
|
-
// Fallback to legacy API
|
|
205
|
-
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
|
|
206
|
-
if ([session canAddOutput:self.stillImageOutput]) {
|
|
207
|
-
[session addOutput:self.stillImageOutput];
|
|
208
|
-
NSLog(@"[IPDFCamera] Fallback successful: Using AVCaptureStillImageOutput");
|
|
209
|
-
} else {
|
|
210
|
-
NSLog(@"[IPDFCamera] CRITICAL ERROR: Cannot add any capture output!");
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
} else {
|
|
214
|
-
// Fallback for older iOS versions (< iOS 10)
|
|
215
|
-
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
|
|
194
|
+
// Use legacy AVCaptureStillImageOutput for reliable manual captures on all supported iOS versions
|
|
195
|
+
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
|
|
196
|
+
if ([session canAddOutput:self.stillImageOutput]) {
|
|
216
197
|
[session addOutput:self.stillImageOutput];
|
|
217
|
-
NSLog(@"[IPDFCamera] Using AVCaptureStillImageOutput (
|
|
198
|
+
NSLog(@"[IPDFCamera] Using AVCaptureStillImageOutput (manual capture)");
|
|
199
|
+
} else {
|
|
200
|
+
NSLog(@"[IPDFCamera] CRITICAL ERROR: Cannot add AVCaptureStillImageOutput to session");
|
|
201
|
+
self.stillImageOutput = nil;
|
|
218
202
|
}
|
|
219
203
|
|
|
220
204
|
AVCaptureConnection *connection = [dataOutput.connections firstObject];
|
|
@@ -520,122 +504,49 @@ static inline void dispatch_async_main_queue(dispatch_block_t block)
|
|
|
520
504
|
// Store completion handler for delegate callback
|
|
521
505
|
self.captureCompletionHandler = completionHandler;
|
|
522
506
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
NSLog(@"[IPDFCameraViewController] photoOutput is nil, trying fallback to stillImageOutput");
|
|
533
|
-
// Fallback to legacy API if photoOutput is not available
|
|
507
|
+
if (!self.stillImageOutput) {
|
|
508
|
+
NSLog(@"[IPDFCameraViewController] ERROR: stillImageOutput is nil");
|
|
509
|
+
NSError *error = [NSError errorWithDomain:@"IPDFCameraViewController"
|
|
510
|
+
code:-201
|
|
511
|
+
userInfo:@{ NSLocalizedDescriptionKey: @"missing_still_image_output" }];
|
|
512
|
+
[self completeCaptureWithCroppedImage:nil initialImage:nil rectangle:nil error:error];
|
|
513
|
+
return;
|
|
534
514
|
}
|
|
535
515
|
|
|
536
|
-
|
|
516
|
+
AVCaptureConnection *videoConnection = nil;
|
|
517
|
+
for (AVCaptureConnection *connection in self.stillImageOutput.connections)
|
|
537
518
|
{
|
|
538
|
-
|
|
539
|
-
NSLog(@"[IPDFCameraViewController] ERROR: stillImageOutput is nil");
|
|
540
|
-
NSError *error = [NSError errorWithDomain:@"IPDFCameraViewController"
|
|
541
|
-
code:-201
|
|
542
|
-
userInfo:@{ NSLocalizedDescriptionKey: @"missing_still_image_output" }];
|
|
543
|
-
[self completeCaptureWithCroppedImage:nil initialImage:nil rectangle:nil error:error];
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
AVCaptureConnection *videoConnection = nil;
|
|
548
|
-
for (AVCaptureConnection *connection in self.stillImageOutput.connections)
|
|
519
|
+
for (AVCaptureInputPort *port in [connection inputPorts])
|
|
549
520
|
{
|
|
550
|
-
|
|
521
|
+
if ([[port mediaType] isEqual:AVMediaTypeVideo] )
|
|
551
522
|
{
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
videoConnection = connection;
|
|
555
|
-
break;
|
|
556
|
-
}
|
|
523
|
+
videoConnection = connection;
|
|
524
|
+
break;
|
|
557
525
|
}
|
|
558
|
-
if (videoConnection) break;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (!videoConnection) {
|
|
562
|
-
NSLog(@"[IPDFCameraViewController] ERROR: No video connection found");
|
|
563
|
-
NSError *error = [NSError errorWithDomain:@"IPDFCameraViewController"
|
|
564
|
-
code:-202
|
|
565
|
-
userInfo:@{ NSLocalizedDescriptionKey: @"no_video_connection" }];
|
|
566
|
-
[self completeCaptureWithCroppedImage:nil initialImage:nil rectangle:nil error:error];
|
|
567
|
-
return;
|
|
568
526
|
}
|
|
569
|
-
|
|
570
|
-
NSLog(@"[IPDFCameraViewController] Using AVCaptureStillImageOutput (legacy)");
|
|
571
|
-
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
|
|
572
|
-
{
|
|
573
|
-
[weakSelf handleCapturedImageData:imageSampleBuffer error:error];
|
|
574
|
-
}];
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// AVCapturePhotoCaptureDelegate method for iOS 11+
|
|
579
|
-
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error API_AVAILABLE(ios(11.0)) {
|
|
580
|
-
NSLog(@"[IPDFCameraViewController] didFinishProcessingPhoto called, error=%@", error);
|
|
581
|
-
|
|
582
|
-
if (error) {
|
|
583
|
-
NSLog(@"[IPDFCameraViewController] ERROR in didFinishProcessingPhoto: %@", error);
|
|
584
|
-
[self completeCaptureWithCroppedImage:nil initialImage:nil rectangle:nil error:error];
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// iOS 11+ uses fileDataRepresentation
|
|
589
|
-
NSData *imageData = [photo fileDataRepresentation];
|
|
590
|
-
if (!imageData) {
|
|
591
|
-
NSLog(@"[IPDFCameraViewController] ERROR: Failed to get image data from photo");
|
|
592
|
-
NSError *dataError = [NSError errorWithDomain:@"IPDFCameraViewController"
|
|
593
|
-
code:-203
|
|
594
|
-
userInfo:@{ NSLocalizedDescriptionKey: @"no_image_data_from_photo" }];
|
|
595
|
-
[self completeCaptureWithCroppedImage:nil initialImage:nil rectangle:nil error:dataError];
|
|
596
|
-
return;
|
|
527
|
+
if (videoConnection) break;
|
|
597
528
|
}
|
|
598
529
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings error:(NSError *)error API_DEPRECATED("Use -captureOutput:didFinishProcessingPhoto:error: instead.", ios(10.0, 11.0)) {
|
|
605
|
-
NSLog(@"[IPDFCameraViewController] didFinishProcessingPhotoSampleBuffer called (iOS 10)");
|
|
606
|
-
|
|
607
|
-
if (error) {
|
|
608
|
-
NSLog(@"[IPDFCameraViewController] ERROR in didFinishProcessingPhotoSampleBuffer: %@", error);
|
|
530
|
+
if (!videoConnection) {
|
|
531
|
+
NSLog(@"[IPDFCameraViewController] ERROR: No video connection found");
|
|
532
|
+
NSError *error = [NSError errorWithDomain:@"IPDFCameraViewController"
|
|
533
|
+
code:-202
|
|
534
|
+
userInfo:@{ NSLocalizedDescriptionKey: @"no_video_connection" }];
|
|
609
535
|
[self completeCaptureWithCroppedImage:nil initialImage:nil rectangle:nil error:error];
|
|
610
536
|
return;
|
|
611
537
|
}
|
|
612
538
|
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
NSError *bufferError = [NSError errorWithDomain:@"IPDFCameraViewController"
|
|
616
|
-
code:-204
|
|
617
|
-
userInfo:@{ NSLocalizedDescriptionKey: @"photo_sample_buffer_nil" }];
|
|
618
|
-
[self completeCaptureWithCroppedImage:nil initialImage:nil rectangle:nil error:bufferError];
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// iOS 10: Use AVCapturePhotoOutput's method for converting sample buffer
|
|
623
|
-
NSData *imageData = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
|
|
624
|
-
|
|
625
|
-
if (!imageData) {
|
|
626
|
-
NSLog(@"[IPDFCameraViewController] ERROR: Failed to create JPEG data from photo sample buffer");
|
|
627
|
-
NSError *dataError = [NSError errorWithDomain:@"IPDFCameraViewController"
|
|
628
|
-
code:-205
|
|
629
|
-
userInfo:@{ NSLocalizedDescriptionKey: @"jpeg_conversion_failed" }];
|
|
630
|
-
[self completeCaptureWithCroppedImage:nil initialImage:nil rectangle:nil error:dataError];
|
|
631
|
-
return;
|
|
539
|
+
if (videoConnection.isVideoOrientationSupported) {
|
|
540
|
+
videoConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
|
|
632
541
|
}
|
|
633
542
|
|
|
634
|
-
NSLog(@"[IPDFCameraViewController]
|
|
635
|
-
[self
|
|
543
|
+
NSLog(@"[IPDFCameraViewController] Capturing image via AVCaptureStillImageOutput");
|
|
544
|
+
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
|
|
545
|
+
[weakSelf handleCapturedImageData:imageSampleBuffer error:error];
|
|
546
|
+
}];
|
|
636
547
|
}
|
|
637
548
|
|
|
638
|
-
// Helper method for legacy AVCaptureStillImageOutput
|
|
549
|
+
// Helper method for legacy AVCaptureStillImageOutput
|
|
639
550
|
- (void)handleCapturedImageData:(CMSampleBufferRef)sampleBuffer error:(NSError *)error {
|
|
640
551
|
NSLog(@"[IPDFCameraViewController] handleCapturedImageData called (legacy), error=%@, buffer=%@", error, sampleBuffer ? @"YES" : @"NO");
|
|
641
552
|
|
|
@@ -34,8 +34,10 @@ RCT_EXPORT_VIEW_PROPERTY(quality, float)
|
|
|
34
34
|
RCT_EXPORT_VIEW_PROPERTY(brightness, float)
|
|
35
35
|
RCT_EXPORT_VIEW_PROPERTY(contrast, float)
|
|
36
36
|
|
|
37
|
-
// Main capture method -
|
|
38
|
-
RCT_EXPORT_METHOD(capture:(nullable NSNumber *)reactTag
|
|
37
|
+
// Main capture method - returns a Promise
|
|
38
|
+
RCT_EXPORT_METHOD(capture:(nullable NSNumber *)reactTag
|
|
39
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
40
|
+
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
39
41
|
NSLog(@"[RNPdfScannerManager] capture called with reactTag: %@", reactTag);
|
|
40
42
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
41
43
|
DocumentScannerView *targetView = nil;
|
|
@@ -59,11 +61,12 @@ RCT_EXPORT_METHOD(capture:(nullable NSNumber *)reactTag) {
|
|
|
59
61
|
|
|
60
62
|
if (!targetView) {
|
|
61
63
|
NSLog(@"[RNPdfScannerManager] ERROR: No scanner view available for capture");
|
|
64
|
+
reject(@"NO_VIEW", @"No scanner view available for capture", nil);
|
|
62
65
|
return;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
NSLog(@"[RNPdfScannerManager] Calling capture on view: %@", targetView);
|
|
66
|
-
[targetView
|
|
69
|
+
[targetView captureWithResolver:resolve rejecter:reject];
|
|
67
70
|
});
|
|
68
71
|
}
|
|
69
72
|
|
|
@@ -30,13 +30,15 @@ class PdfScanner extends React.Component {
|
|
|
30
30
|
console.log('[PdfScanner/ios.js] capture called, ref:', this.scannerRef.current);
|
|
31
31
|
const handle = findNodeHandle(this.scannerRef.current);
|
|
32
32
|
console.log('[PdfScanner/ios.js] node handle (reactTag):', handle);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
|
|
34
|
+
if (!handle) {
|
|
35
|
+
console.error('[PdfScanner/ios.js] ERROR: No handle found for scanner ref');
|
|
36
|
+
return Promise.reject(new Error('No handle found for scanner view'));
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
|
|
39
|
+
// Call native method with reactTag - now returns a Promise
|
|
40
|
+
console.log('[PdfScanner/ios.js] Calling native capture with handle:', handle);
|
|
41
|
+
return NativeModules.RNPdfScannerManager.capture(handle);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
render() {
|