Skip to content

Commit

Permalink
feat: Add more code types to decode
Browse files Browse the repository at this point in the history
  • Loading branch information
gevgasparyan committed Oct 20, 2021
1 parent 39deae5 commit 7c335bb
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 48 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,24 @@ payload
| Property | Type | Description |
| :------------- | :------: | :----------- |
| values | string[] | Array of detected QR code values. Empty if nothing found.
| type | string | Type of detected code.

The following barcode types are currently supported for decoding:

* UPC-A and UPC-E
* EAN-8 and EAN-13
* Code 39
* Code 93
* Code 128
* ITF
* Codabar
* RSS-14 (all variants)
* QR Code
* Data Matrix
* Maxicode
* Aztec ('beta' quality)
* PDF 417 ('beta' quality)



![example](https://user-images.githubusercontent.com/13519034/104821872-50268480-5858-11eb-9e5b-77190f9da71d.gif)
Expand All @@ -132,5 +150,7 @@ RNQRGenerator.generate({

More information about totp can be found [here](https://github.com/google/google-authenticator/wiki/Key-Uri-Format).


This module uses `Zxing` library for encoding and decoding codes ( [ios](https://github.com/zxingify/zxingify-objc), [Android](https://github.com/journeyapps/zxing-android-embedded)).
# Note
Some simulators may not generate qr code properly. Use real device if you get an error.
1 change: 1 addition & 0 deletions RNQrGenerator.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Pod::Spec.new do |s|
s.requires_arc = true

s.dependency "React"
s.dependency "ZXingObjC"
end


Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.graphics.Color;
import android.net.Uri;
import android.util.Base64;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
Expand Down Expand Up @@ -38,14 +39,17 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class RNQrGeneratorModule extends ReactContextBaseJavaModule {

private final ReactApplicationContext reactContext;
private final static String SCHEME_CONTENT = "content";
private final String TAG = "RNQRGenerator";

public RNQrGeneratorModule(ReactApplicationContext reactContext) {
super(reactContext);
Expand Down Expand Up @@ -143,24 +147,68 @@ public void detect(final ReadableMap options, @Nullable Callback failureCallback
}
}
try {
String decoded = scanQRImage(bitmap);
onDetectResult(decoded, successCallback);
Result result = scanQRImage(bitmap);
BarcodeFormat format = result.getBarcodeFormat();
String codeType = getCodeType(format);
onDetectResult(result.getText(), codeType, successCallback);
} catch (Exception e) {
e.printStackTrace();
onDetectResult("", successCallback);
onDetectResult("", "", successCallback);
}
}

private void onDetectResult(String result, Callback successCallback) {
private void onDetectResult(String result, String type, Callback successCallback) {
WritableArray values = Arguments.createArray();
if (result != "") {
values.pushString(result);
}
WritableMap response = Arguments.createMap();
response.putArray("values", values);
response.putString("type", type);
successCallback.invoke(response);
}

private String getCodeType(BarcodeFormat format) {

switch (format) {
case AZTEC:
return "Aztec";
case CODABAR:
return "Codabar";
case CODE_39:
return "Code39";
case CODE_93:
return "Code93";
case CODE_128:
return "Code128";
case DATA_MATRIX:
return "DataMatrix";
case EAN_8:
return "Ean8";
case EAN_13:
return "Ean13";
case ITF:
return "ITF";
case MAXICODE:
return "MaxiCode";
case PDF_417:
return "PDF417";
case QR_CODE:
return "QRCode";
case RSS_14:
return "RSS14";
case RSS_EXPANDED:
return "RSSExpanded";
case UPC_A:
return "UPCA";
case UPC_E:
return "UPCE";
case UPC_EAN_EXTENSION:
return "UPCEANExtension";
default:
return "";
}
}
public static Bitmap generateQrCode(String myCodeText, int qrWidth, int qrHeight, int backgroundColor, int color, String correctionLevel) throws WriterException {
/**
* Allow the zxing engine use the default argument for the margin variable
Expand Down Expand Up @@ -206,9 +254,7 @@ public static Bitmap generateQrCode(String myCodeText, int qrWidth, int qrHeight
return bitmap;
}

public static String scanQRImage(Bitmap bMap) throws Exception {
String contents = null;

public static Result scanQRImage(Bitmap bMap) throws Exception {
int[] intArray = new int[bMap.getWidth()*bMap.getHeight()];
//copy pixel data from the Bitmap into the 'intArray' array
bMap.getPixels(intArray, 0, bMap.getWidth(), 0, 0, bMap.getWidth(), bMap.getHeight());
Expand All @@ -219,12 +265,11 @@ public static String scanQRImage(Bitmap bMap) throws Exception {
Reader reader = new MultiFormatReader();
try {
Result result = reader.decode(bitmap);
contents = result.getText();
}
catch (Exception e) {
return result;
} catch (Exception e) {
Log.e("RNQRGenerator", "Decode Failed:", e);
throw e;
}
return contents;
}

public static File ensureDirExists(File dir) throws IOException {
Expand Down
3 changes: 2 additions & 1 deletion example/QRGeneratorExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ const App: () => React$Node = () => {
setDetectImageUri({uri: response.uri});
RNQRGenerator.detect({uri: response.uri})
.then((res) => {
console.log('Detected', res);
if (res.values.length === 0) {
setDetectedValues(['QR code not found']);
setDetectedValues(['Code not found']);
} else {
setDetectedValues(res.values);
}
Expand Down
2 changes: 1 addition & 1 deletion example/QRGeneratorExample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"react": "16.13.1",
"react-native": "0.63.4",
"react-native-image-picker": "^3.1.3",
"rn-qr-generator": "^1.1.4"
"rn-qr-generator": "^1.2.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",
Expand Down
5 changes: 5 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ export type QRCodeDetectOptions = {
base64?: string,
};

export type CodeType = 'Aztec' | 'Codabar' | 'Code39' | 'Code93' | 'Code128' |
'DataMatrix' | 'Ean8' | 'Ean13' | 'ITF' | 'MaxiCode' | 'PDF417' | 'QRCode' |
'RSS14' | 'RSSExpanded' | 'UPCA' | 'UPCE' | 'UPCEANExtension';

export type QRCodeScanResult = {
values: string[],
type: CodeType
};

export default {
Expand Down
132 changes: 99 additions & 33 deletions ios/RNQrGenerator.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#import "React/RCTConvert.h" // Required when used as a Pod in a Swift project
#endif

#import <ZXingObjC/ZXingObjC.h>

@implementation RNQrGenerator

- (dispatch_queue_t)methodQueue
Expand All @@ -20,7 +22,6 @@ - (dispatch_queue_t)methodQueue
failureCallback:(RCTResponseErrorBlock)failureCallback
successCallback:(RCTResponseSenderBlock)successCallback)
{

NSString *qrData = [RCTConvert NSString:options[@"value"]];
NSString *level = [RCTConvert NSString:options[@"correctionLevel"]];
NSString *fileName = [RCTConvert NSString:options[@"fileName"]];
Expand Down Expand Up @@ -127,40 +128,64 @@ - (dispatch_queue_t)methodQueue
RCTLogWarn(@"key 'uri' or 'base64' are missing in options");
return;
}
CIImage* ciImage = [[CIImage alloc] initWithImage:image];

NSMutableDictionary* detectorOptions;
detectorOptions[CIDetectorAccuracy] = CIDetectorAccuracyHigh;
// detectorOptions[CIDetectorAccuracy] = CIDetectorAccuracyLow; // Fast but superficial

if (@available(iOS 8.0, *)) {
CIDetector* qrDetector = [CIDetector detectorOfType:CIDetectorTypeQRCode
context:NULL
options:options];
if ([[ciImage properties] valueForKey:(NSString*) kCGImagePropertyOrientation] == nil) {
detectorOptions[CIDetectorImageOrientation] = @1;
} else {
id orientation = [[ciImage properties] valueForKey:(NSString*) kCGImagePropertyOrientation];
detectorOptions[CIDetectorImageOrientation] = orientation;
}
ZXLuminanceSource *source = [[ZXCGImageLuminanceSource alloc] initWithCGImage:image.CGImage];
ZXBinaryBitmap *bitmap = [ZXBinaryBitmap binaryBitmapWithBinarizer:[ZXHybridBinarizer binarizerWithSource:source]];

NSArray * features = [qrDetector featuresInImage:ciImage
options:detectorOptions];
NSMutableArray *rawValues = [NSMutableArray array];
[features enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[rawValues addObject: [obj messageString]];
}];
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
response[@"values"] = rawValues;
successCallback(@[response]);
NSError *error = nil;

} else {
NSString *errorMessage = @"QRCode iOS 8+ required";
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedString(errorMessage, nil)};
NSError *error = [NSError errorWithDomain:@"com.rnqrcode" code:1 userInfo:userInfo];
failureCallback(error);
RCTLogWarn(@"Required iOS 8 or later");
}
// There are a number of hints we can give to the reader, including
// possible formats, allowed lengths, and the string encoding.
ZXDecodeHints *hints = [ZXDecodeHints hints];
[hints setTryHarder:TRUE];

ZXMultiFormatReader *reader = [ZXMultiFormatReader reader];
ZXResult *result = [reader decode:bitmap
hints:hints
error:&error];
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
if (result) {
// The coded result as a string. The raw data can be accessed with
// result.rawBytes and result.length.
NSString *contents = result.text;

// The barcode format, such as a QR code or UPC-A
ZXBarcodeFormat format = result.barcodeFormat;
response[@"values"] = @[contents];
response[@"type"] = [self getCodeType:format];
successCallback(@[response]);
} else {
CIImage* ciImage = [[CIImage alloc] initWithImage:image];
NSMutableDictionary* detectorOptions;
detectorOptions[CIDetectorAccuracy] = CIDetectorAccuracyHigh;
if (@available(iOS 8.0, *)) {
CIDetector* qrDetector = [CIDetector detectorOfType:CIDetectorTypeQRCode
context:NULL
options:options];
if ([[ciImage properties] valueForKey:(NSString*) kCGImagePropertyOrientation] == nil) {
detectorOptions[CIDetectorImageOrientation] = @1;
} else {
id orientation = [[ciImage properties] valueForKey:(NSString*) kCGImagePropertyOrientation];
detectorOptions[CIDetectorImageOrientation] = orientation;
}

NSArray * features = [qrDetector featuresInImage:ciImage
options:detectorOptions];
NSMutableArray *rawValues = [NSMutableArray array];
[features enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[rawValues addObject: [obj messageString]];
}];
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
response[@"values"] = rawValues;
response[@"type"] = @"QRCode";
successCallback(@[response]);
} else {
NSString *errorMessage = @"QRCode iOS 8+ required";
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedString(errorMessage, nil)};
NSError *error = [NSError errorWithDomain:@"com.rnqrcode" code:1 userInfo:userInfo];
failureCallback(error);
RCTLogWarn(@"Required iOS 8 or later");
}
}
}

- (NSString *)generatePathInDirectory:(NSString *)directory fileName:(NSString *)name withExtension:(NSString *)extension
Expand All @@ -184,6 +209,47 @@ - (UIImage *)imageFromBase64:(NSString *)base64String
return [UIImage imageWithData:imageData];
}

- (NSString*) getCodeType:(ZXBarcodeFormat) format {
switch (format) {
case kBarcodeFormatAztec:
return @"Aztec";
case kBarcodeFormatCodabar:
return @"Codabar";
case kBarcodeFormatCode39:
return @"Code39";
case kBarcodeFormatCode93:
return @"Code93";
case kBarcodeFormatCode128:
return @"Code128";
case kBarcodeFormatDataMatrix:
return @"DataMatrix";
case kBarcodeFormatEan8:
return @"Ean8";
case kBarcodeFormatEan13:
return @"Ean13";
case kBarcodeFormatITF:
return @"ITF";
case kBarcodeFormatMaxiCode:
return @"MaxiCode";
case kBarcodeFormatPDF417:
return @"PDF417";
case kBarcodeFormatQRCode:
return @"QRCode";
case kBarcodeFormatRSS14:
return @"RSS14";
case kBarcodeFormatRSSExpanded:
return @"RSSExpanded";
case kBarcodeFormatUPCA:
return @"UPCA";
case kBarcodeFormatUPCE:
return @"UPCE";
case kBarcodeFormatUPCEANExtension:
return @"UPCEANExtension";
default:
return @"";
}
}

- (BOOL)ensureDirExistsWithPath:(NSString *)path
{
BOOL isDir = NO;
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "rn-qr-generator",
"version": "1.1.7",
"description": "React native QR Code generator",
"version": "1.2.0",
"description": "React native QR Code generator / reader",
"main": "index.js",
"types": "typings/index.d.ts",
"files": [
Expand All @@ -18,6 +18,8 @@
"react-native",
"qr",
"qrcode",
"Data Matrix",
"DataMatrix",
"qr generator",
"qrcode image",
"qr decode",
Expand Down
5 changes: 5 additions & 0 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,13 @@ export interface QRCodeDetectOptions {
base64?: string;
};

export type CodeType = 'Aztec' | 'Codabar' | 'Code39' | 'Code93' | 'Code128' |
'DataMatrix' | 'Ean8' | 'Ean13' | 'ITF' | 'MaxiCode' | 'PDF417' | 'QRCode' |
'RSS14' | 'RSSExpanded' | 'UPCA' | 'UPCE' | 'UPCEANExtension';

export interface QRCodeScanResult {
values: Array<string>;
type: CodeType;
};

declare namespace RNQRGenerator {
Expand Down

0 comments on commit 7c335bb

Please sign in to comment.