1eafa96e0SStanisław Chmiela// Copyright 2016-present 650 Industries. All rights reserved.
2eafa96e0SStanisław Chmiela
3eafa96e0SStanisław Chmiela#import <EXBarCodeScanner/EXBarCodeScanner.h>
4eafa96e0SStanisław Chmiela#import <EXBarCodeScanner/EXBarCodeScannerUtils.h>
5314e850cSTomasz Sapeta#import <ExpoModulesCore/EXDefines.h>
61f9c336aSBartłomiej Bukowski#import <ZXingObjC/ZXingObjCCore.h>
71f9c336aSBartłomiej Bukowski#import <ZXingObjC/ZXingObjCPDF417.h>
81f9c336aSBartłomiej Bukowski#import <ZXingObjC/ZXingObjCOneD.h>
9eafa96e0SStanisław Chmiela
101f9c336aSBartłomiej Bukowski@interface EXBarCodeScanner() <AVCaptureMetadataOutputObjectsDelegate, AVCaptureVideoDataOutputSampleBufferDelegate>
11eafa96e0SStanisław Chmiela
12eafa96e0SStanisław Chmiela@property (nonatomic, strong) AVCaptureMetadataOutput *metadataOutput;
13eafa96e0SStanisław Chmiela@property (nonatomic, weak) AVCaptureSession *session;
14eafa96e0SStanisław Chmiela@property (nonatomic, weak) dispatch_queue_t sessionQueue;
15eafa96e0SStanisław Chmiela@property (nonatomic, copy, nullable) void (^onBarCodeScanned)(NSDictionary*);
16eafa96e0SStanisław Chmiela@property (nonatomic, assign, getter=isScanningBarCodes) BOOL barCodesScanning;
17eafa96e0SStanisław Chmiela@property (nonatomic, strong) NSDictionary<NSString *, id> *settings;
18bd4c8242SŁukasz Kosmaty@property (nonatomic, weak) AVCaptureVideoPreviewLayer *previewLayer;
19eafa96e0SStanisław Chmiela
205a06ec9aSUrata Daiki@property (nonatomic, strong) NSMutableDictionary<NSString *, id<ZXReader>> *zxingBarcodeReaders;
211f9c336aSBartłomiej Bukowski@property (nonatomic, assign) CGFloat zxingFPSProcessed;
221f9c336aSBartłomiej Bukowski@property (nonatomic, strong) AVCaptureVideoDataOutput* videoDataOutput;
231f9c336aSBartłomiej Bukowski@property (nonatomic, strong) dispatch_queue_t zxingCaptureQueue;
241f9c336aSBartłomiej Bukowski@property (nonatomic, assign) BOOL zxingEnabled;
251f9c336aSBartłomiej Bukowski
26eafa96e0SStanisław Chmiela@end
27eafa96e0SStanisław Chmiela
289cded925STomasz SapetaNSString *const EX_BARCODE_TYPES_KEY = @"barCodeTypes";
29eafa96e0SStanisław Chmiela
30eafa96e0SStanisław Chmiela@implementation EXBarCodeScanner
31eafa96e0SStanisław Chmiela
32eafa96e0SStanisław Chmiela- (instancetype)init
33eafa96e0SStanisław Chmiela{
34eafa96e0SStanisław Chmiela  if (self = [super init]) {
35eafa96e0SStanisław Chmiela    _settings = [[NSMutableDictionary alloc] initWithDictionary:[[self class] _getDefaultSettings]];
361f9c336aSBartłomiej Bukowski
371f9c336aSBartłomiej Bukowski    // zxing handles barcodes reading of following types:
385a06ec9aSUrata Daiki    _zxingBarcodeReaders = [@{
391f9c336aSBartłomiej Bukowski      // PDF417 - built-in PDF417 reader doesn't handle u'\0' (null) character - https://github.com/expo/expo/issues/4817
401f9c336aSBartłomiej Bukowski      AVMetadataObjectTypePDF417Code: [ZXPDF417Reader new],
411f9c336aSBartłomiej Bukowski      // Code39 - built-in Code39 reader doesn't read non-ideal (slightly rotated) images like this - https://github.com/expo/expo/pull/5976#issuecomment-545001008
421f9c336aSBartłomiej Bukowski      AVMetadataObjectTypeCode39Code: [ZXCode39Reader new],
435a06ec9aSUrata Daiki    } mutableCopy];
44*7e5da4d1SBartłomiej Bukowski#ifdef __IPHONE_15_4
455a06ec9aSUrata Daiki    // Codabar - available in iOS 15.4+
465a06ec9aSUrata Daiki    if (@available(iOS 15.4, *)) {
475a06ec9aSUrata Daiki      _zxingBarcodeReaders[AVMetadataObjectTypeCodabarCode] = [ZXCodaBarReader new];
485a06ec9aSUrata Daiki    }
49*7e5da4d1SBartłomiej Bukowski#endif
501f9c336aSBartłomiej Bukowski    _zxingFPSProcessed = 6;
511f9c336aSBartłomiej Bukowski    _zxingCaptureQueue = dispatch_queue_create("com.zxing.captureQueue", NULL);
521f9c336aSBartłomiej Bukowski    _zxingEnabled = YES;
53eafa96e0SStanisław Chmiela  }
54eafa96e0SStanisław Chmiela  return self;
55eafa96e0SStanisław Chmiela}
56eafa96e0SStanisław Chmiela
57eafa96e0SStanisław Chmiela# pragma mark - JS properties setters
58eafa96e0SStanisław Chmiela
59eafa96e0SStanisław Chmiela- (void)setSettings:(NSDictionary<NSString *, id> *)settings
60eafa96e0SStanisław Chmiela{
61eafa96e0SStanisław Chmiela  for (NSString *key in settings) {
629cded925STomasz Sapeta    if ([key isEqualToString:EX_BARCODE_TYPES_KEY]) {
63eafa96e0SStanisław Chmiela      NSArray<NSString *> *value = settings[key];
649cded925STomasz Sapeta      NSSet *previousTypes = [NSSet setWithArray:_settings[EX_BARCODE_TYPES_KEY]];
65eafa96e0SStanisław Chmiela      NSSet *newTypes = [NSSet setWithArray:value];
66eafa96e0SStanisław Chmiela      if (![previousTypes isEqualToSet:newTypes]) {
67eafa96e0SStanisław Chmiela        NSMutableDictionary<NSString *, id> *nextSettings = [[NSMutableDictionary alloc] initWithDictionary:_settings];
689cded925STomasz Sapeta        nextSettings[EX_BARCODE_TYPES_KEY] = value;
69eafa96e0SStanisław Chmiela        _settings = nextSettings;
701f9c336aSBartłomiej Bukowski        NSSet *zxingCoveredTypes = [NSSet setWithArray:[_zxingBarcodeReaders allKeys]];
711f9c336aSBartłomiej Bukowski        _zxingEnabled = [zxingCoveredTypes intersectsSet:newTypes];
72314e850cSTomasz Sapeta        EX_WEAKIFY(self);
73f870e5d8SStanisław Chmiela        [self _runBlockIfQueueIsPresent:^{
74314e850cSTomasz Sapeta          EX_ENSURE_STRONGIFY(self);
75eafa96e0SStanisław Chmiela          [self maybeStartBarCodeScanning];
76f870e5d8SStanisław Chmiela        }];
77eafa96e0SStanisław Chmiela      }
78eafa96e0SStanisław Chmiela    }
79eafa96e0SStanisław Chmiela  }
80eafa96e0SStanisław Chmiela}
81eafa96e0SStanisław Chmiela
82eafa96e0SStanisław Chmiela- (void)setIsEnabled:(BOOL)newBarCodeScanning
83eafa96e0SStanisław Chmiela{
84eafa96e0SStanisław Chmiela  if ([self isScanningBarCodes] == newBarCodeScanning) {
85eafa96e0SStanisław Chmiela    return;
86eafa96e0SStanisław Chmiela  }
87eafa96e0SStanisław Chmiela  _barCodesScanning = newBarCodeScanning;
88314e850cSTomasz Sapeta  EX_WEAKIFY(self);
89eafa96e0SStanisław Chmiela  [self _runBlockIfQueueIsPresent:^{
90314e850cSTomasz Sapeta    EX_ENSURE_STRONGIFY(self);
91eafa96e0SStanisław Chmiela    if ([self isScanningBarCodes]) {
92eafa96e0SStanisław Chmiela      if (self.metadataOutput) {
93eafa96e0SStanisław Chmiela        [self _setConnectionsEnabled:true];
94eafa96e0SStanisław Chmiela      } else {
95eafa96e0SStanisław Chmiela        [self maybeStartBarCodeScanning];
96eafa96e0SStanisław Chmiela      }
97eafa96e0SStanisław Chmiela    } else {
98eafa96e0SStanisław Chmiela      [self _setConnectionsEnabled:false];
99eafa96e0SStanisław Chmiela    }
100eafa96e0SStanisław Chmiela  }];
101eafa96e0SStanisław Chmiela}
102eafa96e0SStanisław Chmiela
103eafa96e0SStanisław Chmiela# pragma mark - Public API
104eafa96e0SStanisław Chmiela
105eafa96e0SStanisław Chmiela- (void)maybeStartBarCodeScanning
106eafa96e0SStanisław Chmiela{
107eafa96e0SStanisław Chmiela  if (!_session || !_sessionQueue || ![self isScanningBarCodes]) {
108eafa96e0SStanisław Chmiela    return;
109eafa96e0SStanisław Chmiela  }
110eafa96e0SStanisław Chmiela
1111f9c336aSBartłomiej Bukowski  if (!_metadataOutput || !_videoDataOutput) {
112eafa96e0SStanisław Chmiela    [_session beginConfiguration];
113eafa96e0SStanisław Chmiela
1141f9c336aSBartłomiej Bukowski    if (!_metadataOutput) {
115eafa96e0SStanisław Chmiela      AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
116eafa96e0SStanisław Chmiela      [metadataOutput setMetadataObjectsDelegate:self queue:_sessionQueue];
117eafa96e0SStanisław Chmiela      if ([_session canAddOutput:metadataOutput]) {
118eafa96e0SStanisław Chmiela        [_session addOutput:metadataOutput];
119eafa96e0SStanisław Chmiela        _metadataOutput = metadataOutput;
120eafa96e0SStanisław Chmiela      }
1211f9c336aSBartłomiej Bukowski    }
1221f9c336aSBartłomiej Bukowski
1231f9c336aSBartłomiej Bukowski    if (!_videoDataOutput) {
1241f9c336aSBartłomiej Bukowski      AVCaptureVideoDataOutput *videoDataOutput = [AVCaptureVideoDataOutput new];
1251f9c336aSBartłomiej Bukowski      [videoDataOutput setVideoSettings:@{
1261f9c336aSBartłomiej Bukowski        (NSString *)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
1271f9c336aSBartłomiej Bukowski      }];
1281f9c336aSBartłomiej Bukowski      [videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];
1291f9c336aSBartłomiej Bukowski      [videoDataOutput setSampleBufferDelegate:self queue:_zxingCaptureQueue];
1301f9c336aSBartłomiej Bukowski      if ([_session canAddOutput:videoDataOutput]) {
1311f9c336aSBartłomiej Bukowski        [_session addOutput:videoDataOutput];
1321f9c336aSBartłomiej Bukowski        _videoDataOutput = videoDataOutput;
1331f9c336aSBartłomiej Bukowski      }
1341f9c336aSBartłomiej Bukowski    }
1351f9c336aSBartłomiej Bukowski
136eafa96e0SStanisław Chmiela    [_session commitConfiguration];
137eafa96e0SStanisław Chmiela
138eafa96e0SStanisław Chmiela    if (!_metadataOutput) {
139eafa96e0SStanisław Chmiela      return;
140eafa96e0SStanisław Chmiela    }
141eafa96e0SStanisław Chmiela  }
142eafa96e0SStanisław Chmiela
143eafa96e0SStanisław Chmiela  NSArray<AVMetadataObjectType> *availableRequestedObjectTypes = @[];
144eafa96e0SStanisław Chmiela  NSArray<AVMetadataObjectType> *requestedObjectTypes = @[];
145eafa96e0SStanisław Chmiela  NSArray<AVMetadataObjectType> *availableObjectTypes = _metadataOutput.availableMetadataObjectTypes;
1469cded925STomasz Sapeta  if (_settings && _settings[EX_BARCODE_TYPES_KEY]) {
1479cded925STomasz Sapeta    requestedObjectTypes = [[NSArray alloc] initWithArray:_settings[EX_BARCODE_TYPES_KEY]];
148eafa96e0SStanisław Chmiela  }
149eafa96e0SStanisław Chmiela
150eafa96e0SStanisław Chmiela  for(AVMetadataObjectType objectType in requestedObjectTypes) {
151eafa96e0SStanisław Chmiela    if ([availableObjectTypes containsObject:objectType]) {
152eafa96e0SStanisław Chmiela      availableRequestedObjectTypes = [availableRequestedObjectTypes arrayByAddingObject:objectType];
153eafa96e0SStanisław Chmiela    }
154eafa96e0SStanisław Chmiela  }
155eafa96e0SStanisław Chmiela
156eafa96e0SStanisław Chmiela  [_metadataOutput setMetadataObjectTypes:availableRequestedObjectTypes];
157eafa96e0SStanisław Chmiela}
158eafa96e0SStanisław Chmiela
159eafa96e0SStanisław Chmiela- (void)stopBarCodeScanning
160eafa96e0SStanisław Chmiela{
161eafa96e0SStanisław Chmiela  if (!_session) {
162eafa96e0SStanisław Chmiela    return;
163eafa96e0SStanisław Chmiela  }
164eafa96e0SStanisław Chmiela
165eafa96e0SStanisław Chmiela  [_session beginConfiguration];
166eafa96e0SStanisław Chmiela
167eafa96e0SStanisław Chmiela  if ([_session.outputs containsObject:_metadataOutput]) {
168eafa96e0SStanisław Chmiela    [_session removeOutput:_metadataOutput];
169eafa96e0SStanisław Chmiela    _metadataOutput = nil;
170eafa96e0SStanisław Chmiela  }
171eafa96e0SStanisław Chmiela
1721f9c336aSBartłomiej Bukowski  if ([_session.outputs containsObject:_videoDataOutput]) {
1731f9c336aSBartłomiej Bukowski    [_session removeOutput:_videoDataOutput];
1741f9c336aSBartłomiej Bukowski    _videoDataOutput = nil;
1751f9c336aSBartłomiej Bukowski  }
1761f9c336aSBartłomiej Bukowski
177eafa96e0SStanisław Chmiela  [_session commitConfiguration];
178eafa96e0SStanisław Chmiela
179eafa96e0SStanisław Chmiela  if ([self isScanningBarCodes] && _onBarCodeScanned) {
180eafa96e0SStanisław Chmiela    _onBarCodeScanned(nil);
181eafa96e0SStanisław Chmiela  }
182eafa96e0SStanisław Chmiela}
183eafa96e0SStanisław Chmiela
184eafa96e0SStanisław Chmiela# pragma mark - Private API
185eafa96e0SStanisław Chmiela
186eafa96e0SStanisław Chmiela- (void)_setConnectionsEnabled:(BOOL)enabled
187eafa96e0SStanisław Chmiela{
188eafa96e0SStanisław Chmiela  if (!_metadataOutput) {
189eafa96e0SStanisław Chmiela    return;
190eafa96e0SStanisław Chmiela  }
191eafa96e0SStanisław Chmiela  for (AVCaptureConnection *connection in _metadataOutput.connections) {
192eafa96e0SStanisław Chmiela    connection.enabled = enabled;
193eafa96e0SStanisław Chmiela  }
194eafa96e0SStanisław Chmiela}
195eafa96e0SStanisław Chmiela
196eafa96e0SStanisław Chmiela- (void)_runBlockIfQueueIsPresent:(void (^)(void))block
197eafa96e0SStanisław Chmiela{
198eafa96e0SStanisław Chmiela  if (_sessionQueue) {
199eafa96e0SStanisław Chmiela    dispatch_async(_sessionQueue, block);
200eafa96e0SStanisław Chmiela  }
201eafa96e0SStanisław Chmiela}
202eafa96e0SStanisław Chmiela
203eafa96e0SStanisław Chmiela# pragma mark - AVCaptureMetadataOutputObjectsDelegate
204eafa96e0SStanisław Chmiela
205eafa96e0SStanisław Chmiela- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
206eafa96e0SStanisław Chmiela       fromConnection:(AVCaptureConnection *)connection
207eafa96e0SStanisław Chmiela{
2089cded925STomasz Sapeta  if (!_settings || !_settings[EX_BARCODE_TYPES_KEY] || !_metadataOutput) {
209eafa96e0SStanisław Chmiela    return;
210eafa96e0SStanisław Chmiela  }
211eafa96e0SStanisław Chmiela
212eafa96e0SStanisław Chmiela  for (AVMetadataObject *metadata in metadataObjects) {
213eafa96e0SStanisław Chmiela    if ([metadata isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
214bd4c8242SŁukasz Kosmaty      AVMetadataMachineReadableCodeObject *codeMetadata;
215bd4c8242SŁukasz Kosmaty      if (_previewLayer) {
216bd4c8242SŁukasz Kosmaty        codeMetadata = (AVMetadataMachineReadableCodeObject *)[_previewLayer transformedMetadataObjectForMetadataObject:metadata];
217bd4c8242SŁukasz Kosmaty      } else {
218bd4c8242SŁukasz Kosmaty        codeMetadata = (AVMetadataMachineReadableCodeObject *)metadata;
219bd4c8242SŁukasz Kosmaty      }
220bd4c8242SŁukasz Kosmaty
2219cded925STomasz Sapeta      for (id barcodeType in _settings[EX_BARCODE_TYPES_KEY]) {
2221f9c336aSBartłomiej Bukowski        // some barcodes aren't handled properly by iOS SDK build-in reader -> zxing handles it in separate flow
2231f9c336aSBartłomiej Bukowski        if ([_zxingBarcodeReaders objectForKey:barcodeType]) {
2241f9c336aSBartłomiej Bukowski          continue;
2251f9c336aSBartłomiej Bukowski        }
226e60eb2afSStanisław Chmiela        if (codeMetadata.stringValue && [codeMetadata.type isEqualToString:barcodeType]) {
227eafa96e0SStanisław Chmiela          if (_onBarCodeScanned) {
228bd4c8242SŁukasz Kosmaty            _onBarCodeScanned([EXBarCodeScannerUtils avMetadataCodeObjectToDicitionary:codeMetadata]);
229eafa96e0SStanisław Chmiela          }
230eafa96e0SStanisław Chmiela          return;
231eafa96e0SStanisław Chmiela        }
232eafa96e0SStanisław Chmiela      }
233eafa96e0SStanisław Chmiela    }
234eafa96e0SStanisław Chmiela  }
235eafa96e0SStanisław Chmiela}
236eafa96e0SStanisław Chmiela
2371f9c336aSBartłomiej Bukowski# pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate for ZXing
2381f9c336aSBartłomiej Bukowski
2391f9c336aSBartłomiej Bukowski- (void)captureOutput:(AVCaptureVideoDataOutput *)output
2401f9c336aSBartłomiej BukowskididOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
2411f9c336aSBartłomiej Bukowski       fromConnection:(AVCaptureConnection *)connection
2421f9c336aSBartłomiej Bukowski{
2431f9c336aSBartłomiej Bukowski  if (!_settings || !_settings[EX_BARCODE_TYPES_KEY] || !_metadataOutput) {
2441f9c336aSBartłomiej Bukowski    return;
2451f9c336aSBartłomiej Bukowski  }
2461f9c336aSBartłomiej Bukowski  // do not use ZXing library if not scanning for predefined barcodes
2471f9c336aSBartłomiej Bukowski  if (!_zxingEnabled) {
2481f9c336aSBartłomiej Bukowski    return;
2491f9c336aSBartłomiej Bukowski  }
2501f9c336aSBartłomiej Bukowski
2511f9c336aSBartłomiej Bukowski  // below code is mostly taken from ZXing library itself
2521f9c336aSBartłomiej Bukowski  float kMinMargin = 1.0 / _zxingFPSProcessed;
2531f9c336aSBartłomiej Bukowski
2541f9c336aSBartłomiej Bukowski  // Gets the timestamp for each frame.
2551f9c336aSBartłomiej Bukowski  CMTime presentTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
2561f9c336aSBartłomiej Bukowski
2571f9c336aSBartłomiej Bukowski  @autoreleasepool {
2581f9c336aSBartłomiej Bukowski    static double curFrameTimeStamp = 0;
2591f9c336aSBartłomiej Bukowski    static double lastFrameTimeStamp = 0;
2601f9c336aSBartłomiej Bukowski
2611f9c336aSBartłomiej Bukowski    curFrameTimeStamp = (double)presentTimeStamp.value / presentTimeStamp.timescale;
2621f9c336aSBartłomiej Bukowski
2631f9c336aSBartłomiej Bukowski    if (curFrameTimeStamp - lastFrameTimeStamp > kMinMargin) {
2641f9c336aSBartłomiej Bukowski      lastFrameTimeStamp = curFrameTimeStamp;
2651f9c336aSBartłomiej Bukowski
2661f9c336aSBartłomiej Bukowski      CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
2671f9c336aSBartłomiej Bukowski      CGImageRef videoFrameImage = [ZXCGImageLuminanceSource createImageFromBuffer:videoFrame];
268bd4c8242SŁukasz Kosmaty      [self scanBarcodesFromImage:videoFrameImage withCompletion:^(ZXResult *barCodeScannerResult, NSError *error) {
2691f9c336aSBartłomiej Bukowski        if (self->_onBarCodeScanned) {
270bd4c8242SŁukasz Kosmaty          self->_onBarCodeScanned([EXBarCodeScannerUtils zxResultToDicitionary:barCodeScannerResult]);
2711f9c336aSBartłomiej Bukowski        }
2721f9c336aSBartłomiej Bukowski      }];
2731f9c336aSBartłomiej Bukowski    }
2741f9c336aSBartłomiej Bukowski  }
2751f9c336aSBartłomiej Bukowski}
2761f9c336aSBartłomiej Bukowski
2771f9c336aSBartłomiej Bukowski- (void)scanBarcodesFromImage:(CGImageRef)image
278bd4c8242SŁukasz Kosmaty               withCompletion:(void(^)(ZXResult *barCodeResult, NSError *error))completion
2791f9c336aSBartłomiej Bukowski{
2801f9c336aSBartłomiej Bukowski  ZXCGImageLuminanceSource *source = [[ZXCGImageLuminanceSource alloc] initWithCGImage:image];
2811f9c336aSBartłomiej Bukowski  CGImageRelease(image);
2821f9c336aSBartłomiej Bukowski
2831f9c336aSBartłomiej Bukowski  ZXHybridBinarizer *binarizer = [[ZXHybridBinarizer alloc] initWithSource:source];
2841f9c336aSBartłomiej Bukowski  ZXBinaryBitmap *bitmap = [[ZXBinaryBitmap alloc] initWithBinarizer:binarizer];
2851f9c336aSBartłomiej Bukowski
2861f9c336aSBartłomiej Bukowski  NSError *error = nil;
2871f9c336aSBartłomiej Bukowski  ZXResult *result;
2881f9c336aSBartłomiej Bukowski
2891f9c336aSBartłomiej Bukowski  for (id<ZXReader> reader in [_zxingBarcodeReaders allValues]) {
2901f9c336aSBartłomiej Bukowski    result = [reader decode:bitmap hints:nil error:&error];
2911f9c336aSBartłomiej Bukowski    if (result) {
2921f9c336aSBartłomiej Bukowski      break;
2931f9c336aSBartłomiej Bukowski    }
2941f9c336aSBartłomiej Bukowski  }
2951f9c336aSBartłomiej Bukowski  // rotate bitmap by 90° only, becasue zxing rotates bitmap by 180° internally, so that each possible orientation is covered
2961f9c336aSBartłomiej Bukowski  if (!result && [bitmap rotateSupported]) {
2971f9c336aSBartłomiej Bukowski    ZXBinaryBitmap *rotatedBitmap = [bitmap rotateCounterClockwise];
2981f9c336aSBartłomiej Bukowski    for (id<ZXReader> reader in [_zxingBarcodeReaders allValues]) {
2991f9c336aSBartłomiej Bukowski      result = [reader decode:rotatedBitmap hints:nil error:&error];
3001f9c336aSBartłomiej Bukowski      if (result) {
3011f9c336aSBartłomiej Bukowski        break;
3021f9c336aSBartłomiej Bukowski      }
3031f9c336aSBartłomiej Bukowski    }
3041f9c336aSBartłomiej Bukowski  }
3051f9c336aSBartłomiej Bukowski
3061f9c336aSBartłomiej Bukowski  if (result) {
307bd4c8242SŁukasz Kosmaty    completion(result, error);
3081f9c336aSBartłomiej Bukowski  }
3091f9c336aSBartłomiej Bukowski}
3101f9c336aSBartłomiej Bukowski
3111f9c336aSBartłomiej Bukowski+ (NSString *)zxingFormatToString:(ZXBarcodeFormat)format
3121f9c336aSBartłomiej Bukowski{
3131f9c336aSBartłomiej Bukowski  switch (format) {
3141f9c336aSBartłomiej Bukowski    case kBarcodeFormatPDF417:
3151f9c336aSBartłomiej Bukowski      return AVMetadataObjectTypePDF417Code;
3161f9c336aSBartłomiej Bukowski    case kBarcodeFormatCode39:
3171f9c336aSBartłomiej Bukowski      return AVMetadataObjectTypeCode39Code;
3185a06ec9aSUrata Daiki    case kBarcodeFormatCodabar:
319*7e5da4d1SBartłomiej Bukowski#ifdef __IPHONE_15_4
3205a06ec9aSUrata Daiki      if (@available(iOS 15.4, *)) {
3215a06ec9aSUrata Daiki        return AVMetadataObjectTypeCodabarCode;
3225a06ec9aSUrata Daiki      }
323*7e5da4d1SBartłomiej Bukowski#endif
324*7e5da4d1SBartłomiej Bukowski      return @"unknown";
3251f9c336aSBartłomiej Bukowski    default:
3261f9c336aSBartłomiej Bukowski      return @"unknown";
3271f9c336aSBartłomiej Bukowski  }
3281f9c336aSBartłomiej Bukowski}
3291f9c336aSBartłomiej Bukowski
330eafa96e0SStanisław Chmiela# pragma mark - default settings
331eafa96e0SStanisław Chmiela
332eafa96e0SStanisław Chmiela+ (NSDictionary *)_getDefaultSettings
333eafa96e0SStanisław Chmiela{
334eafa96e0SStanisław Chmiela  return @{
3359cded925STomasz Sapeta           EX_BARCODE_TYPES_KEY: [[EXBarCodeScannerUtils validBarCodeTypes] allValues],
336eafa96e0SStanisław Chmiela           };
337eafa96e0SStanisław Chmiela}
338eafa96e0SStanisław Chmiela
339eafa96e0SStanisław Chmiela@end
340