From 2ef3267f156cd97718364b78b065e4d81d18f3e6 Mon Sep 17 00:00:00 2001 From: Markus Fisch Date: Fri, 17 Nov 2023 17:22:46 +0100 Subject: [PATCH 1/2] ios: improve exception/error handling Make `read()` throw so C++ exceptions can be caught in Swift. So an app can't crash silently anymore when a C++ exception is thrown while trying to read a barcode. This also adds a function to properly set a NSError from a C++ exception to avoid code duplication in ZXIBarcodeReader/Writer. --- .../Sources/Wrapper/Reader/ZXIBarcodeReader.h | 16 +++-- .../Wrapper/Reader/ZXIBarcodeReader.mm | 65 +++++++++++-------- .../Wrapper/Writer/ZXIBarcodeWriter.mm | 7 +- wrappers/ios/Sources/Wrapper/ZXIErrors.h | 6 ++ wrappers/ios/Sources/Wrapper/ZXIErrors.mm | 25 +++++++ wrappers/ios/demo/demo/ViewController.swift | 2 +- 6 files changed, 82 insertions(+), 39 deletions(-) create mode 100644 wrappers/ios/Sources/Wrapper/ZXIErrors.mm diff --git a/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.h b/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.h index f2cca0ba21..f72e6691ed 100644 --- a/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.h +++ b/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.h @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 +#import #import #import #import "ZXIResult.h" @@ -12,10 +13,17 @@ NS_ASSUME_NONNULL_BEGIN @interface ZXIBarcodeReader : NSObject @property(nonatomic, strong) ZXIDecodeHints *hints; -- (instancetype)initWithHints:(ZXIDecodeHints*)options; -- (NSArray *)readCIImage:(nonnull CIImage *)image; -- (NSArray *)readCGImage:(nonnull CGImageRef)image; -- (NSArray *)readCVPixelBuffer:(nonnull CVPixelBufferRef)pixelBuffer; +-(instancetype)initWithHints:(ZXIDecodeHints*)options; + +-(nullable NSArray *)readCIImage:(nonnull CIImage *)image + error:(NSError *__autoreleasing _Nullable *)error; + +-(nullable NSArray *)readCGImage:(nonnull CGImageRef)image + error:(NSError *__autoreleasing _Nullable *)error; + +-(nullable NSArray *)readCVPixelBuffer:(nonnull CVPixelBufferRef)pixelBuffer + error:(NSError *__autoreleasing _Nullable *)error; + @end NS_ASSUME_NONNULL_END diff --git a/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm b/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm index c13b150e8c..1783305c4d 100644 --- a/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm +++ b/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm @@ -9,6 +9,7 @@ #import "GTIN.h" #import "ZXIFormatHelper.h" #import "ZXIPosition+Helper.h" +#import "ZXIErrors.h" using namespace ZXing; @@ -44,7 +45,8 @@ - (instancetype)initWithHints:(ZXIDecodeHints*)hints{ return self; } -- (NSArray *)readCVPixelBuffer:(nonnull CVPixelBufferRef)pixelBuffer { +- (NSArray *)readCVPixelBuffer:(nonnull CVPixelBufferRef)pixelBuffer + error:(NSError *__autoreleasing _Nullable *)error { OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer); // We tried to work with all luminance based formats listed in kCVPixelFormatType @@ -64,24 +66,26 @@ - (instancetype)initWithHints:(ZXIDecodeHints*)hints{ ImageFormat::Lum, static_cast(bytesPerRow), 0); - NSArray* results = [self readImageView:imageView]; + NSArray* results = [self readImageView:imageView error:error]; CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); return results; } // If given pixel format is not a supported type with a luminance channel we just use the // default method - return [self readCIImage:[[CIImage alloc] initWithCVImageBuffer:pixelBuffer]]; + return [self readCIImage:[[CIImage alloc] initWithCVImageBuffer:pixelBuffer] error:error]; } -- (NSArray *)readCIImage:(nonnull CIImage *)image { +- (NSArray *)readCIImage:(nonnull CIImage *)image + error:(NSError *__autoreleasing _Nullable *)error { CGImageRef cgImage = [self.ciContext createCGImage:image fromRect:image.extent]; - auto results = [self readCGImage:cgImage]; + auto results = [self readCGImage:cgImage error:error]; CGImageRelease(cgImage); return results; } -- (NSArray *)readCGImage: (nonnull CGImageRef)image { +- (NSArray *)readCGImage:(nonnull CGImageRef)image + error:(NSError *__autoreleasing _Nullable *)error { CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); CGFloat cols = CGImageGetWidth(image); CGFloat rows = CGImageGetHeight(image); @@ -104,7 +108,7 @@ - (instancetype)initWithHints:(ZXIDecodeHints*)hints{ static_cast(cols), static_cast(rows), ImageFormat::Lum); - return [self readImageView:imageView]; + return [self readImageView:imageView error:error]; } + (DecodeHints)DecodeHintsFromZXIOptions:(ZXIDecodeHints*)hints { @@ -126,28 +130,33 @@ + (DecodeHints)DecodeHintsFromZXIOptions:(ZXIDecodeHints*)hints { return resultingHints; } -- (NSArray *)readImageView: (ImageView)imageView { - Results results = ReadBarcodes(imageView, [ZXIBarcodeReader DecodeHintsFromZXIOptions:self.hints]); - - NSMutableArray* zxiResults = [NSMutableArray array]; - for (auto result: results) { - [zxiResults addObject: - [[ZXIResult alloc] init:stringToNSString(result.text()) - format:ZXIFormatFromBarcodeFormat(result.format()) - bytes:[[NSData alloc] initWithBytes:result.bytes().data() length:result.bytes().size()] - position:[[ZXIPosition alloc]initWithPosition: result.position()] - orientation:result.orientation() - ecLevel:stringToNSString(result.ecLevel()) - symbologyIdentifier:stringToNSString(result.symbologyIdentifier()) - sequenceSize:result.sequenceSize() - sequenceIndex:result.sequenceIndex() - sequenceId:stringToNSString(result.sequenceId()) - readerInit:result.readerInit() - lineCount:result.lineCount() - gtin:getGTIN(result)] - ]; +- (NSArray *)readImageView:(ImageView)imageView + error:(NSError *__autoreleasing _Nullable *)error { + try { + Results results = ReadBarcodes(imageView, [ZXIBarcodeReader DecodeHintsFromZXIOptions:self.hints]); + NSMutableArray* zxiResults = [NSMutableArray array]; + for (auto result: results) { + [zxiResults addObject: + [[ZXIResult alloc] init:stringToNSString(result.text()) + format:ZXIFormatFromBarcodeFormat(result.format()) + bytes:[[NSData alloc] initWithBytes:result.bytes().data() length:result.bytes().size()] + position:[[ZXIPosition alloc]initWithPosition: result.position()] + orientation:result.orientation() + ecLevel:stringToNSString(result.ecLevel()) + symbologyIdentifier:stringToNSString(result.symbologyIdentifier()) + sequenceSize:result.sequenceSize() + sequenceIndex:result.sequenceIndex() + sequenceId:stringToNSString(result.sequenceId()) + readerInit:result.readerInit() + lineCount:result.lineCount() + gtin:getGTIN(result)] + ]; + } + return zxiResults; + } catch(std::exception &e) { + SetNSError(error, ZXIReaderError, e.what()); + return nil; } - return zxiResults; } @end diff --git a/wrappers/ios/Sources/Wrapper/Writer/ZXIBarcodeWriter.mm b/wrappers/ios/Sources/Wrapper/Writer/ZXIBarcodeWriter.mm index 433994fb59..dfaf706ccc 100644 --- a/wrappers/ios/Sources/Wrapper/Writer/ZXIBarcodeWriter.mm +++ b/wrappers/ios/Sources/Wrapper/Writer/ZXIBarcodeWriter.mm @@ -75,12 +75,7 @@ -(CGImageRef)encode:(std::wstring)content BitMatrix bitMatrix = writer.encode(content, width, height); return [self inflate:&bitMatrix]; } catch(std::exception &e) { - if (error != nil) { - const char *errorCString = e.what(); - NSString *errorDescription = errorCString ? [NSString stringWithUTF8String:errorCString] : @"Unknown error"; - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: errorDescription }; - *error = [NSError errorWithDomain:ZXIErrorDomain code:ZXIWriterError userInfo:userInfo]; - } + SetNSError(error, ZXIWriterError, e.what()); return nil; } } diff --git a/wrappers/ios/Sources/Wrapper/ZXIErrors.h b/wrappers/ios/Sources/Wrapper/ZXIErrors.h index 7f1ebb6e26..583df50771 100644 --- a/wrappers/ios/Sources/Wrapper/ZXIErrors.h +++ b/wrappers/ios/Sources/Wrapper/ZXIErrors.h @@ -8,8 +8,14 @@ NS_ASSUME_NONNULL_BEGIN #define ZXIErrorDomain @"ZXIErrorDomain" +typedef NS_ENUM(NSInteger, ZXIBarcodeReaderError) { + ZXIReaderError, +}; + typedef NS_ENUM(NSInteger, ZXIBarcodeWriterError) { ZXIWriterError, }; +void SetNSError(NSError *__autoreleasing _Nullable* error, NSInteger code, const char* message); + NS_ASSUME_NONNULL_END diff --git a/wrappers/ios/Sources/Wrapper/ZXIErrors.mm b/wrappers/ios/Sources/Wrapper/ZXIErrors.mm new file mode 100644 index 0000000000..6b88bcb1de --- /dev/null +++ b/wrappers/ios/Sources/Wrapper/ZXIErrors.mm @@ -0,0 +1,25 @@ +// Copyright 2023 KURZ Digital Solutions GmbH +// +// SPDX-License-Identifier: Apache-2.0 + +#import "ZXIErrors.h" + +void SetNSError(NSError *__autoreleasing _Nullable* error, + NSInteger code, + const char* message) { + if (error == nil) { + return; + } + NSString *errorDescription = @"Unknown C++ error"; + if (message && strlen(message) > 0) { + try { + errorDescription = [NSString stringWithUTF8String: message]; + } catch (NSException *exception) { + errorDescription = @"Unknown ObjC error"; + } + } + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: errorDescription }; + *error = [NSError errorWithDomain:ZXIErrorDomain + code:code + userInfo:userInfo]; +} diff --git a/wrappers/ios/demo/demo/ViewController.swift b/wrappers/ios/demo/demo/ViewController.swift index 964ca6010d..30d437eb31 100644 --- a/wrappers/ios/demo/demo/ViewController.swift +++ b/wrappers/ios/demo/demo/ViewController.swift @@ -70,7 +70,7 @@ extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate { return } let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! - if let result = reader.read(imageBuffer).first { + if let result = try? reader.read(imageBuffer).first { print("Found barcode of format", result.format.rawValue, "with text", result.text) } self.zxingLock.signal() From 53c73e12f50ab1b8abd6b00f6addd6eff4809450 Mon Sep 17 00:00:00 2001 From: Markus Fisch Date: Fri, 17 Nov 2023 17:30:24 +0100 Subject: [PATCH 2/2] ios: catch exceptions from invalid GTIN data Because invalid GTIN data can lead to exceptions (e.g. from std::stoi), in which case we don't want to discard the whole result. --- .../Wrapper/Reader/ZXIBarcodeReader.mm | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm b/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm index 1783305c4d..9a964be3df 100644 --- a/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm +++ b/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm @@ -18,14 +18,20 @@ } ZXIGTIN *getGTIN(const Result &result) { - auto country = GTIN::LookupCountryIdentifier(result.text(TextMode::Plain), result.format()); - auto addOn = GTIN::EanAddOn(result); - return country.empty() - ? nullptr - : [[ZXIGTIN alloc]initWithCountry:stringToNSString(country) - addOn:stringToNSString(addOn) - price:stringToNSString(GTIN::Price(addOn)) - issueNumber:stringToNSString(GTIN::IssueNr(addOn))]; + try { + auto country = GTIN::LookupCountryIdentifier(result.text(TextMode::Plain), result.format()); + auto addOn = GTIN::EanAddOn(result); + return country.empty() + ? nullptr + : [[ZXIGTIN alloc]initWithCountry:stringToNSString(country) + addOn:stringToNSString(addOn) + price:stringToNSString(GTIN::Price(addOn)) + issueNumber:stringToNSString(GTIN::IssueNr(addOn))]; + } catch (std::exception e) { + // Because invalid GTIN data can lead to exceptions, in which case + // we don't want to discard the whole result. + return nullptr; + } } @interface ZXIBarcodeReader() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy