comparison mupdf-source/thirdparty/zxing-cpp/wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright 2022 KURZ Digital Solutions GmbH
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 #import "ZXIBarcodeReader.h"
6 #import "ReadBarcode.h"
7 #import "ImageView.h"
8 #import "Barcode.h"
9 #import "GTIN.h"
10 #import "ZXIFormatHelper.h"
11 #import "ZXIPosition+Helper.h"
12 #import "ZXIErrors.h"
13
14 using namespace ZXing;
15
16 NSString *stringToNSString(const std::string &text) {
17 return [[NSString alloc]initWithBytes:text.data() length:text.size() encoding:NSUTF8StringEncoding];
18 }
19
20 ZXIGTIN *getGTIN(const Result &result) {
21 try {
22 auto country = GTIN::LookupCountryIdentifier(result.text(TextMode::Plain), result.format());
23 auto addOn = GTIN::EanAddOn(result);
24 return country.empty()
25 ? nullptr
26 : [[ZXIGTIN alloc]initWithCountry:stringToNSString(country)
27 addOn:stringToNSString(addOn)
28 price:stringToNSString(GTIN::Price(addOn))
29 issueNumber:stringToNSString(GTIN::IssueNr(addOn))];
30 } catch (std::exception e) {
31 // Because invalid GTIN data can lead to exceptions, in which case
32 // we don't want to discard the whole result.
33 return nullptr;
34 }
35 }
36
37 @interface ZXIReaderOptions()
38 @property(nonatomic) ZXing::ReaderOptions cppOpts;
39 @end
40
41 @interface ZXIBarcodeReader()
42 @property (nonatomic, strong) CIContext* ciContext;
43 @end
44
45 @implementation ZXIBarcodeReader
46
47 - (instancetype)init {
48 return [self initWithOptions: [[ZXIReaderOptions alloc] init]];
49 }
50
51 - (instancetype)initWithOptions:(ZXIReaderOptions*)options{
52 self = [super init];
53 self.ciContext = [[CIContext alloc] initWithOptions:@{kCIContextWorkingColorSpace: [NSNull new]}];
54 self.options = options;
55 return self;
56 }
57
58 - (NSArray<ZXIResult *> *)readCVPixelBuffer:(nonnull CVPixelBufferRef)pixelBuffer
59 error:(NSError *__autoreleasing _Nullable *)error {
60 OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
61
62 // We tried to work with all luminance based formats listed in kCVPixelFormatType
63 // but only the following ones seem to be supported on iOS.
64 switch (pixelFormat) {
65 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
66 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
67 NSInteger cols = CVPixelBufferGetWidth(pixelBuffer);
68 NSInteger rows = CVPixelBufferGetHeight(pixelBuffer);
69 NSInteger bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
70 CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
71 const uint8_t * bytes = static_cast<const uint8_t *>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0));
72 ImageView imageView = ImageView(
73 static_cast<const uint8_t *>(bytes),
74 static_cast<int>(cols),
75 static_cast<int>(rows),
76 ImageFormat::Lum,
77 static_cast<int>(bytesPerRow),
78 0);
79 NSArray* results = [self readImageView:imageView error:error];
80 CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
81 return results;
82 }
83
84 // If given pixel format is not a supported type with a luminance channel we just use the
85 // default method
86 return [self readCIImage:[[CIImage alloc] initWithCVImageBuffer:pixelBuffer] error:error];
87 }
88
89 - (NSArray<ZXIResult *> *)readCIImage:(nonnull CIImage *)image
90 error:(NSError *__autoreleasing _Nullable *)error {
91 CGImageRef cgImage = [self.ciContext createCGImage:image fromRect:image.extent];
92 auto results = [self readCGImage:cgImage error:error];
93 CGImageRelease(cgImage);
94 return results;
95 }
96
97 - (NSArray<ZXIResult *> *)readCGImage:(nonnull CGImageRef)image
98 error:(NSError *__autoreleasing _Nullable *)error {
99 CGFloat cols = CGImageGetWidth(image);
100 CGFloat rows = CGImageGetHeight(image);
101 NSMutableData *data = [NSMutableData dataWithLength: cols * rows];
102
103 CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
104 CGContextRef contextRef = CGBitmapContextCreate(data.mutableBytes,// Pointer to backing data
105 cols, // Width of bitmap
106 rows, // Height of bitmap
107 8, // Bits per component
108 cols, // Bytes per row
109 colorSpace, // Colorspace
110 kCGBitmapByteOrderDefault); // Bitmap info flags
111 CGColorSpaceRelease(colorSpace);
112 CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image);
113 CGContextRelease(contextRef);
114
115 ImageView imageView = ImageView(
116 static_cast<const uint8_t *>(data.bytes),
117 static_cast<int>(cols),
118 static_cast<int>(rows),
119 ImageFormat::Lum);
120 return [self readImageView:imageView error:error];
121 }
122
123 - (NSArray<ZXIResult*> *)readImageView:(ImageView)imageView
124 error:(NSError *__autoreleasing _Nullable *)error {
125 try {
126 Barcodes results = ReadBarcodes(imageView, self.options.cppOpts);
127 NSMutableArray* zxiResults = [NSMutableArray array];
128 for (auto result: results) {
129 [zxiResults addObject:
130 [[ZXIResult alloc] init:stringToNSString(result.text())
131 format:ZXIFormatFromBarcodeFormat(result.format())
132 bytes:[[NSData alloc] initWithBytes:result.bytes().data() length:result.bytes().size()]
133 position:[[ZXIPosition alloc]initWithPosition: result.position()]
134 orientation:result.orientation()
135 ecLevel:stringToNSString(result.ecLevel())
136 symbologyIdentifier:stringToNSString(result.symbologyIdentifier())
137 sequenceSize:result.sequenceSize()
138 sequenceIndex:result.sequenceIndex()
139 sequenceId:stringToNSString(result.sequenceId())
140 readerInit:result.readerInit()
141 lineCount:result.lineCount()
142 gtin:getGTIN(result)]
143 ];
144 }
145 return zxiResults;
146 } catch(std::exception &e) {
147 SetNSError(error, ZXIReaderError, e.what());
148 return nil;
149 } catch (...) {
150 SetNSError(error, ZXIReaderError, "An unknown error occurred");
151 return nil;
152 }
153 }
154
155 @end