squashed commits for new barcodescanner
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// BarcodeScanner.h
|
||||
//
|
||||
// Created by Matt Kane on 12/01/2011.
|
||||
// Copyright 2011 Matt Kane. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#ifdef PHONEGAP_FRAMEWORK
|
||||
#import <PhoneGap/PGPlugin.h>
|
||||
#else
|
||||
#import "PGPlugin.h"
|
||||
#endif
|
||||
#import "ZXingWidgetController.h"
|
||||
#import "QRCodeReader.h"
|
||||
|
||||
|
||||
@interface BarcodeScanner : PGPlugin <ZXingDelegate> {
|
||||
NSString* successCallback;
|
||||
NSString* failCallback;
|
||||
}
|
||||
|
||||
@property (nonatomic, copy) NSString* successCallback;
|
||||
@property (nonatomic, copy) NSString* failCallback;
|
||||
|
||||
- (void) scan:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
|
||||
|
||||
@end
|
||||
@@ -1,51 +0,0 @@
|
||||
//
|
||||
// BarcodeScanner.m
|
||||
//
|
||||
// Created by Matt Kane on 12/01/2011.
|
||||
// Copyright 2011 Matt Kane. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BarcodeScanner.h"
|
||||
|
||||
|
||||
@implementation BarcodeScanner
|
||||
|
||||
@synthesize successCallback, failCallback;
|
||||
|
||||
- (void) scan:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
|
||||
{
|
||||
NSUInteger argc = [arguments count];
|
||||
|
||||
if (argc < 1) {
|
||||
return;
|
||||
}
|
||||
self.successCallback = [arguments objectAtIndex:0];
|
||||
if (argc > 1) {
|
||||
self.failCallback = [arguments objectAtIndex:1];
|
||||
}
|
||||
//if (argc > 2) {
|
||||
// TODO: Choose the readers to load
|
||||
//}
|
||||
|
||||
ZXingWidgetController *widgetController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
|
||||
QRCodeReader* qrcodeReader = [[QRCodeReader alloc] init];
|
||||
NSSet *readers = [[NSSet alloc ] initWithObjects:qrcodeReader,nil];
|
||||
[qrcodeReader release];
|
||||
widgetController.readers = readers;
|
||||
[readers release];
|
||||
[[super appViewController ] presentModalViewController:widgetController animated:YES];
|
||||
[widgetController release];
|
||||
}
|
||||
|
||||
- (void)zxingController:(ZXingWidgetController*)controller didScanResult:(NSString *)result {
|
||||
NSString* jsCallBack = [NSString stringWithFormat:@"%@(\"%@\");", self.successCallback, [result stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] ];
|
||||
[self writeJavascript: jsCallBack];
|
||||
[[super appViewController ] dismissModalViewControllerAnimated:NO];
|
||||
}
|
||||
|
||||
- (void)zxingControllerDidCancel:(ZXingWidgetController*)controller {
|
||||
[self writeJavascript: [NSString stringWithFormat:@"%@();", self.failCallback]];
|
||||
[[super appViewController ] dismissModalViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
766
iPhone/BarcodeScanner/PGBarcodeScanner.mm
Normal file
@@ -0,0 +1,766 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright 2011 Matt Kane. All rights reserved.
|
||||
* Copyright (c) 2011, IBM Corporation
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <AssetsLibrary/AssetsLibrary.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// use the all-in-one version of zxing that we built
|
||||
//------------------------------------------------------------------------------
|
||||
#import "zxing-all-in-one.h"
|
||||
|
||||
#ifdef PHONEGAP_FRAMEWORK
|
||||
#import <PhoneGap/PGPlugin.h>
|
||||
#import <PhoneGap/JSON.h>
|
||||
#else
|
||||
#import "PGPlugin.h"
|
||||
#import "JSON.h"
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Adds a shutter button to the UI, and changes the scan from continuous to
|
||||
// only performing a scan when you click the shutter button. For testing.
|
||||
//------------------------------------------------------------------------------
|
||||
#define USE_SHUTTER 0
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@class PGbcsProcessor;
|
||||
@class PGbcsViewController;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// plugin class
|
||||
//------------------------------------------------------------------------------
|
||||
@interface PGBarcodeScanner : PGPlugin {}
|
||||
- (NSString*)isScanNotPossible;
|
||||
- (void)scan:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
|
||||
- (void)encode:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
|
||||
- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format cancelled:(BOOL)cancelled callback:(NSString*)callback;
|
||||
- (void)returnError:(NSString*)message callback:(NSString*)callback;
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// class that does the grunt work
|
||||
//------------------------------------------------------------------------------
|
||||
@interface PGbcsProcessor : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> {}
|
||||
@property (nonatomic, retain) PGBarcodeScanner* plugin;
|
||||
@property (nonatomic, retain) NSString* callback;
|
||||
@property (nonatomic, retain) UIViewController* parentViewController;
|
||||
@property (nonatomic, retain) PGbcsViewController* viewController;
|
||||
@property (nonatomic, retain) AVCaptureSession* captureSession;
|
||||
@property (nonatomic, retain) AVCaptureVideoPreviewLayer* previewLayer;
|
||||
@property (nonatomic) BOOL is1D;
|
||||
@property (nonatomic) BOOL is2D;
|
||||
@property (nonatomic) BOOL capturing;
|
||||
|
||||
- (id)initWithPlugin:(PGBarcodeScanner*)plugin callback:(NSString*)callback parentViewController:(UIViewController*)parentViewController;
|
||||
- (void)scanBarcode;
|
||||
- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format;
|
||||
- (void)barcodeScanFailed:(NSString*)message;
|
||||
- (void)barcodeScanCancelled;
|
||||
- (void)openDialog;
|
||||
- (NSString*)setUpCaptureSession;
|
||||
- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection;
|
||||
- (NSString*)formatStringFrom:(zxing::BarcodeFormat)format;
|
||||
- (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer;
|
||||
- (zxing::Ref<zxing::LuminanceSource>) getLuminanceSourceFromSample:(CMSampleBufferRef)sampleBuffer imageBytes:(uint8_t**)ptr;
|
||||
- (UIImage*) getImageFromLuminanceSource:(zxing::LuminanceSource*)luminanceSource;
|
||||
- (void)dumpImage:(UIImage*)image;
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// view controller for the ui
|
||||
//------------------------------------------------------------------------------
|
||||
@interface PGbcsViewController : UIViewController {}
|
||||
@property (nonatomic, retain) PGbcsProcessor* processor;
|
||||
@property (nonatomic) BOOL shutterPressed;
|
||||
|
||||
- (id)initWithProcessor:(PGbcsProcessor*)processor;
|
||||
- (void)startCapturing;
|
||||
- (UIView*)buildOverlayView;
|
||||
- (UIImage*)buildReticleImage;
|
||||
- (void)shutterButtonPressed;
|
||||
- (void)cancelButtonPressed;
|
||||
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// plugin class
|
||||
//------------------------------------------------------------------------------
|
||||
@implementation PGBarcodeScanner
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (NSString*)isScanNotPossible {
|
||||
NSString* result = nil;
|
||||
|
||||
Class aClass = NSClassFromString(@"AVCaptureSession");
|
||||
if (aClass == nil) {
|
||||
return @"AVFoundation Framework not available";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)scan:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
|
||||
PGbcsProcessor* processor;
|
||||
NSString* callback;
|
||||
NSString* capabilityError;
|
||||
|
||||
callback = [arguments objectAtIndex:0];
|
||||
|
||||
capabilityError = [self isScanNotPossible];
|
||||
if (capabilityError) {
|
||||
[self returnError:capabilityError callback:callback];
|
||||
return;
|
||||
}
|
||||
|
||||
processor = [[PGbcsProcessor alloc]
|
||||
initWithPlugin:self
|
||||
callback:callback
|
||||
parentViewController:[self appViewController]
|
||||
];
|
||||
|
||||
// queue [processor scanBarcode] to run on the event loop
|
||||
[processor performSelector:@selector(scanBarcode) withObject:nil afterDelay:0];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)encode:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
|
||||
[self returnError:@"encode function not supported" callback:[arguments objectAtIndex:0]];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)returnSuccess:(NSString*)scannedText format:(NSString*)format cancelled:(BOOL)cancelled callback:(NSString*)callback {
|
||||
NSNumber* cancelledNumber = [NSNumber numberWithInt:(cancelled?1:0)];
|
||||
|
||||
NSMutableDictionary* resultDict = [[[NSMutableDictionary alloc] init] autorelease];
|
||||
[resultDict setObject:scannedText forKey:@"text"];
|
||||
[resultDict setObject:format forKey:@"format"];
|
||||
[resultDict setObject:cancelledNumber forKey:@"cancelled"];
|
||||
|
||||
PluginResult* result = [PluginResult
|
||||
resultWithStatus: PGCommandStatus_OK
|
||||
messageAsDictionary: resultDict
|
||||
];
|
||||
|
||||
NSString* js = [result toSuccessCallbackString:callback];
|
||||
|
||||
[self writeJavascript:js];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)returnError:(NSString*)message callback:(NSString*)callback {
|
||||
PluginResult* result = [PluginResult
|
||||
resultWithStatus: PGCommandStatus_OK
|
||||
messageAsString: message
|
||||
];
|
||||
|
||||
NSString* js = [result toErrorCallbackString:callback];
|
||||
|
||||
[self writeJavascript:js];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// class that does the grunt work
|
||||
//------------------------------------------------------------------------------
|
||||
@implementation PGbcsProcessor
|
||||
|
||||
@synthesize plugin = _plugin;
|
||||
@synthesize callback = _callback;
|
||||
@synthesize parentViewController = _parentViewController;
|
||||
@synthesize viewController = _viewController;
|
||||
@synthesize captureSession = _captureSession;
|
||||
@synthesize previewLayer = _previewLayer;
|
||||
@synthesize is1D = _is1D;
|
||||
@synthesize is2D = _is2D;
|
||||
@synthesize capturing = _capturing;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (id)initWithPlugin:(PGBarcodeScanner*)plugin
|
||||
callback:(NSString*)callback
|
||||
parentViewController:(UIViewController*)parentViewController {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
self.plugin = plugin;
|
||||
self.callback = callback;
|
||||
self.parentViewController = parentViewController;
|
||||
|
||||
self.is1D = YES;
|
||||
self.is2D = YES;
|
||||
self.capturing = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)dealloc {
|
||||
self.plugin = nil;
|
||||
self.callback = nil;
|
||||
self.parentViewController = nil;
|
||||
self.viewController = nil;
|
||||
self.captureSession = nil;
|
||||
self.previewLayer = nil;
|
||||
|
||||
self.capturing = NO;
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)scanBarcode {
|
||||
NSString* errorMessage = [self setUpCaptureSession];
|
||||
if (errorMessage) {
|
||||
[self barcodeScanFailed:errorMessage];
|
||||
return;
|
||||
}
|
||||
|
||||
self.viewController = [[[PGbcsViewController alloc] initWithProcessor: self] autorelease];
|
||||
|
||||
// delayed [self openDialog];
|
||||
[self performSelector:@selector(openDialog) withObject:nil afterDelay:1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)openDialog {
|
||||
[self.parentViewController
|
||||
presentModalViewController:self.viewController
|
||||
animated:YES
|
||||
];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)barcodeScanDone {
|
||||
self.capturing = NO;
|
||||
[self.captureSession stopRunning];
|
||||
[self.parentViewController dismissModalViewControllerAnimated: YES];
|
||||
|
||||
// viewcontroller holding onto a reference to us, release them so they
|
||||
// will release us
|
||||
self.viewController = nil;
|
||||
|
||||
// delayed [self release];
|
||||
[self performSelector:@selector(release) withObject:nil afterDelay:1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)barcodeScanSucceeded:(NSString*)text format:(NSString*)format {
|
||||
[self barcodeScanDone];
|
||||
[self.plugin returnSuccess:text format:format cancelled:FALSE callback:self.callback];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)barcodeScanFailed:(NSString*)message {
|
||||
[self barcodeScanDone];
|
||||
[self.plugin returnError:message callback:self.callback];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)barcodeScanCancelled {
|
||||
[self barcodeScanDone];
|
||||
[self.plugin returnSuccess:@"" format:@"" cancelled:TRUE callback:self.callback];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (NSString*)setUpCaptureSession {
|
||||
NSError* error = nil;
|
||||
|
||||
AVCaptureSession* captureSession = [[[AVCaptureSession alloc] init] autorelease];
|
||||
self.captureSession = captureSession;
|
||||
|
||||
AVCaptureDevice* device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||
if (!device) return @"unable to obtain video capture device";
|
||||
|
||||
AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
|
||||
if (!input) return @"unable to obtain video capture device input";
|
||||
|
||||
AVCaptureVideoDataOutput* output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
|
||||
if (!output) return @"unable to obtain video capture output";
|
||||
|
||||
NSDictionary* videoOutputSettings = [NSDictionary
|
||||
dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA]
|
||||
forKey:(id)kCVPixelBufferPixelFormatTypeKey
|
||||
];
|
||||
|
||||
output.alwaysDiscardsLateVideoFrames = YES;
|
||||
output.videoSettings = videoOutputSettings;
|
||||
|
||||
[output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
|
||||
|
||||
if (![captureSession canSetSessionPreset:AVCaptureSessionPresetMedium]) {
|
||||
return @"unable to preset medium quality video capture";
|
||||
}
|
||||
|
||||
captureSession.sessionPreset = AVCaptureSessionPresetMedium;
|
||||
|
||||
if ([captureSession canAddInput:input]) {
|
||||
[captureSession addInput:input];
|
||||
}
|
||||
else {
|
||||
return @"unable to add video capture device input to session";
|
||||
}
|
||||
|
||||
if ([captureSession canAddOutput:output]) {
|
||||
[captureSession addOutput:output];
|
||||
}
|
||||
else {
|
||||
return @"unable to add video capture output to session";
|
||||
}
|
||||
|
||||
// setup capture preview layer
|
||||
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
|
||||
|
||||
// run on next event loop pass [captureSession startRunning]
|
||||
[captureSession performSelector:@selector(startRunning) withObject:nil afterDelay:0];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// this method gets sent the captured frames
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection {
|
||||
|
||||
if (!self.capturing) return;
|
||||
|
||||
#if USE_SHUTTER
|
||||
if (!self.viewController.shutterPressed) return;
|
||||
self.viewController.shutterPressed = NO;
|
||||
|
||||
UIView* flashView = [[[UIView alloc] initWithFrame:self.viewController.view.frame] autorelease];
|
||||
[flashView setBackgroundColor:[UIColor whiteColor]];
|
||||
[self.viewController.view.window addSubview:flashView];
|
||||
|
||||
[UIView
|
||||
animateWithDuration:.4f
|
||||
animations:^{
|
||||
[flashView setAlpha:0.f];
|
||||
}
|
||||
completion:^(BOOL finished){
|
||||
[flashView removeFromSuperview];
|
||||
}
|
||||
];
|
||||
|
||||
// [self dumpImage: [[self getImageFromSample:sampleBuffer] autorelease]];
|
||||
#endif
|
||||
|
||||
|
||||
using namespace zxing;
|
||||
|
||||
// LuminanceSource is pretty dumb; we have to give it a pointer to
|
||||
// a byte array, but then can't get it back out again. We need to
|
||||
// get it back to free it. Saving it in imageBytes.
|
||||
uint8_t* imageBytes;
|
||||
|
||||
// NSTimeInterval timeStart = [NSDate timeIntervalSinceReferenceDate];
|
||||
|
||||
try {
|
||||
DecodeHints decodeHints;
|
||||
decodeHints.addFormat(BarcodeFormat_QR_CODE);
|
||||
decodeHints.addFormat(BarcodeFormat_DATA_MATRIX);
|
||||
decodeHints.addFormat(BarcodeFormat_UPC_E);
|
||||
decodeHints.addFormat(BarcodeFormat_UPC_A);
|
||||
decodeHints.addFormat(BarcodeFormat_EAN_8);
|
||||
decodeHints.addFormat(BarcodeFormat_EAN_13);
|
||||
decodeHints.addFormat(BarcodeFormat_CODE_128);
|
||||
decodeHints.addFormat(BarcodeFormat_CODE_39);
|
||||
// decodeHints.addFormat(BarcodeFormat_ITF); causing crashes
|
||||
|
||||
// here's the meat of the decode process
|
||||
Ref<LuminanceSource> luminanceSource ([self getLuminanceSourceFromSample: sampleBuffer imageBytes:&imageBytes]);
|
||||
// [self dumpImage: [[self getImageFromLuminanceSource:luminanceSource] autorelease]];
|
||||
Ref<Binarizer> binarizer (new HybridBinarizer(luminanceSource));
|
||||
Ref<BinaryBitmap> bitmap (new BinaryBitmap(binarizer));
|
||||
Ref<MultiFormatReader> reader (new MultiFormatReader());
|
||||
Ref<Result> result (reader->decode(bitmap, decodeHints));
|
||||
Ref<String> resultText (result->getText());
|
||||
BarcodeFormat formatVal = result->getBarcodeFormat();
|
||||
NSString* format = [self formatStringFrom:formatVal];
|
||||
|
||||
|
||||
const char* cString = resultText->getText().c_str();
|
||||
NSString* resultString = [[[NSString alloc] initWithCString:cString encoding:NSUTF8StringEncoding] autorelease];
|
||||
|
||||
[self barcodeScanSucceeded:resultString format:format];
|
||||
|
||||
}
|
||||
catch (zxing::ReaderException &rex) {
|
||||
// NSString *message = [[[NSString alloc] initWithCString:rex.what() encoding:NSUTF8StringEncoding] autorelease];
|
||||
// NSLog(@"decoding: ReaderException: %@", message);
|
||||
}
|
||||
catch (zxing::IllegalArgumentException &iex) {
|
||||
// NSString *message = [[[NSString alloc] initWithCString:iex.what() encoding:NSUTF8StringEncoding] autorelease];
|
||||
// NSLog(@"decoding: IllegalArgumentException: %@", message);
|
||||
}
|
||||
catch (...) {
|
||||
// NSLog(@"decoding: unknown exception");
|
||||
// [self barcodeScanFailed:@"unknown exception decoding barcode"];
|
||||
}
|
||||
|
||||
// NSTimeInterval timeElapsed = [NSDate timeIntervalSinceReferenceDate] - timeStart;
|
||||
// NSLog(@"decoding completed in %dms", (int) (timeElapsed * 1000));
|
||||
|
||||
// free the buffer behind the LuminanceSource
|
||||
if (imageBytes) {
|
||||
free(imageBytes);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// convert barcode format to string
|
||||
//--------------------------------------------------------------------------
|
||||
- (NSString*)formatStringFrom:(zxing::BarcodeFormat)format {
|
||||
if (format == zxing::BarcodeFormat_QR_CODE) return @"QR_CODE";
|
||||
if (format == zxing::BarcodeFormat_DATA_MATRIX) return @"DATA_MATRIX";
|
||||
if (format == zxing::BarcodeFormat_UPC_E) return @"UPC_E";
|
||||
if (format == zxing::BarcodeFormat_UPC_A) return @"UPC_A";
|
||||
if (format == zxing::BarcodeFormat_EAN_8) return @"EAN_8";
|
||||
if (format == zxing::BarcodeFormat_EAN_13) return @"EAN_13";
|
||||
if (format == zxing::BarcodeFormat_CODE_128) return @"CODE_128";
|
||||
if (format == zxing::BarcodeFormat_CODE_39) return @"CODE_39";
|
||||
if (format == zxing::BarcodeFormat_ITF) return @"ITF";
|
||||
return @"???";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// convert capture's sample buffer (scanned picture) into the thing that
|
||||
// zxing needs.
|
||||
//--------------------------------------------------------------------------
|
||||
- (zxing::Ref<zxing::LuminanceSource>) getLuminanceSourceFromSample:(CMSampleBufferRef)sampleBuffer imageBytes:(uint8_t**)ptr {
|
||||
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
||||
CVPixelBufferLockBaseAddress(imageBuffer, 0);
|
||||
|
||||
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
|
||||
size_t width = CVPixelBufferGetWidth(imageBuffer);
|
||||
size_t height = CVPixelBufferGetHeight(imageBuffer);
|
||||
uint8_t* baseAddress = (uint8_t*) CVPixelBufferGetBaseAddress(imageBuffer);
|
||||
|
||||
// only going to get 90% of the min(width,height) of the captured image
|
||||
size_t greyWidth = 9 * MIN(width, height) / 10;
|
||||
uint8_t* greyData = (uint8_t*) malloc(greyWidth * greyWidth);
|
||||
|
||||
// remember this pointer so we can free it later
|
||||
*ptr = greyData;
|
||||
|
||||
if (!greyData) {
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
|
||||
throw new zxing::ReaderException("out of memory");
|
||||
}
|
||||
|
||||
size_t offsetX = (width - greyWidth) / 2;
|
||||
size_t offsetY = (height - greyWidth) / 2;
|
||||
|
||||
// pixel-by-pixel ...
|
||||
for (size_t i=0; i<greyWidth; i++) {
|
||||
for (size_t j=0; j<greyWidth; j++) {
|
||||
// i,j are the coordinates from the sample buffer
|
||||
// ni, nj are the coordinates in the LuminanceSource
|
||||
// in this case, there's a rotation taking place
|
||||
size_t ni = greyWidth-j;
|
||||
size_t nj = i;
|
||||
|
||||
size_t baseOffset = (j+offsetY)*bytesPerRow + (i + offsetX)*4;
|
||||
|
||||
// convert from color to grayscale
|
||||
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
|
||||
size_t value = 0.11 * baseAddress[baseOffset] +
|
||||
0.59 * baseAddress[baseOffset + 1] +
|
||||
0.30 * baseAddress[baseOffset + 2];
|
||||
|
||||
greyData[nj*greyWidth + ni] = value;
|
||||
}
|
||||
}
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
|
||||
|
||||
using namespace zxing;
|
||||
|
||||
Ref<LuminanceSource> luminanceSource (
|
||||
new GreyscaleLuminanceSource(greyData, greyWidth, greyWidth, 0, 0, greyWidth, greyWidth)
|
||||
);
|
||||
|
||||
return luminanceSource;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// for debugging
|
||||
//--------------------------------------------------------------------------
|
||||
- (UIImage*) getImageFromLuminanceSource:(zxing::LuminanceSource*)luminanceSource {
|
||||
unsigned char* bytes = luminanceSource->getMatrix();
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
|
||||
CGContextRef context = CGBitmapContextCreate(
|
||||
bytes,
|
||||
luminanceSource->getWidth(), luminanceSource->getHeight(), 8, luminanceSource->getWidth(),
|
||||
colorSpace,
|
||||
kCGImageAlphaNone
|
||||
);
|
||||
|
||||
CGImageRef cgImage = CGBitmapContextCreateImage(context);
|
||||
UIImage* image = [[UIImage alloc] initWithCGImage:cgImage];
|
||||
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGImageRelease(cgImage);
|
||||
free(bytes);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// for debugging
|
||||
//--------------------------------------------------------------------------
|
||||
- (UIImage*)getImageFromSample:(CMSampleBufferRef)sampleBuffer {
|
||||
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
||||
CVPixelBufferLockBaseAddress(imageBuffer, 0);
|
||||
|
||||
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
|
||||
size_t width = CVPixelBufferGetWidth(imageBuffer);
|
||||
size_t height = CVPixelBufferGetHeight(imageBuffer);
|
||||
|
||||
uint8_t* baseAddress = (uint8_t*) CVPixelBufferGetBaseAddress(imageBuffer);
|
||||
int length = height * bytesPerRow;
|
||||
uint8_t* newBaseAddress = (uint8_t*) malloc(length);
|
||||
memcpy(newBaseAddress, baseAddress, length);
|
||||
baseAddress = newBaseAddress;
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(
|
||||
baseAddress,
|
||||
width, height, 8, bytesPerRow,
|
||||
colorSpace,
|
||||
kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst
|
||||
);
|
||||
|
||||
CGImageRef cgImage = CGBitmapContextCreateImage(context);
|
||||
UIImage* image = [[UIImage alloc] initWithCGImage:cgImage];
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGImageRelease(cgImage);
|
||||
|
||||
free(baseAddress);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// for debugging
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)dumpImage:(UIImage*)image {
|
||||
NSLog(@"writing image to library: %dx%d", (int)image.size.width, (int)image.size.height);
|
||||
ALAssetsLibrary* assetsLibrary = [[[ALAssetsLibrary alloc] init] autorelease];
|
||||
[assetsLibrary
|
||||
writeImageToSavedPhotosAlbum:image.CGImage
|
||||
orientation:ALAssetOrientationUp
|
||||
completionBlock:^(NSURL* assetURL, NSError* error){
|
||||
if (error) NSLog(@" error writing image to library");
|
||||
else NSLog(@" wrote image to library %@", assetURL);
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// view controller for the ui
|
||||
//------------------------------------------------------------------------------
|
||||
@implementation PGbcsViewController
|
||||
@synthesize processor = _processor;
|
||||
@synthesize shutterPressed = _shutterPressed;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (id)initWithProcessor:(PGbcsProcessor*)processor {
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
self.processor = processor;
|
||||
self.shutterPressed = NO;
|
||||
return self;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)dealloc {
|
||||
self.view = nil;
|
||||
self.processor = nil;
|
||||
self.shutterPressed = NO;
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)loadView {
|
||||
self.view = [[[UIView alloc] initWithFrame: self.processor.parentViewController.view.frame] autorelease];
|
||||
|
||||
// setup capture preview layer
|
||||
AVCaptureVideoPreviewLayer* previewLayer = self.processor.previewLayer;
|
||||
previewLayer.frame = self.view.bounds;
|
||||
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
|
||||
|
||||
if ([previewLayer isOrientationSupported]) {
|
||||
[previewLayer setOrientation:AVCaptureVideoOrientationPortrait];
|
||||
}
|
||||
|
||||
[self.view.layer insertSublayer:previewLayer below:[[self.view.layer sublayers] objectAtIndex:0]];
|
||||
|
||||
[self.view addSubview:[self buildOverlayView]];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[self startCapturing];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)startCapturing {
|
||||
self.processor.capturing = YES;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
|
||||
// rotation currently not supported
|
||||
if (interfaceOrientation == UIInterfaceOrientationPortrait) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)shutterButtonPressed {
|
||||
self.shutterPressed = YES;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (void)cancelButtonPressed {
|
||||
[self.processor performSelector:@selector(barcodeScanCancelled) withObject:nil afterDelay:0];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
- (UIView*)buildOverlayView {
|
||||
CGRect bounds = self.view.bounds;
|
||||
bounds = CGRectMake(0, 0, bounds.size.width, bounds.size.height);
|
||||
|
||||
UIView* overlayView = [[[UIView alloc] initWithFrame:bounds] autorelease];
|
||||
overlayView.autoresizesSubviews = YES;
|
||||
overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
overlayView.opaque = NO;
|
||||
|
||||
UIToolbar* toolbar = [[[UIToolbar alloc] init] autorelease];
|
||||
toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
|
||||
|
||||
id cancelButton = [[[UIBarButtonItem alloc] autorelease]
|
||||
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
|
||||
target:(id)self
|
||||
action:@selector(cancelButtonPressed)
|
||||
];
|
||||
|
||||
id flexSpace = [[[UIBarButtonItem alloc] autorelease]
|
||||
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
||||
target:nil
|
||||
action:nil
|
||||
];
|
||||
|
||||
#if USE_SHUTTER
|
||||
id shutterButton = [[UIBarButtonItem alloc]
|
||||
initWithBarButtonSystemItem:UIBarButtonSystemItemCamera
|
||||
target:(id)self
|
||||
action:@selector(shutterButtonPressed)
|
||||
];
|
||||
|
||||
toolbar.items = [NSArray arrayWithObjects:flexSpace,cancelButton,flexSpace,shutterButton,nil];
|
||||
#else
|
||||
toolbar.items = [NSArray arrayWithObjects:flexSpace,cancelButton,flexSpace,nil];
|
||||
#endif
|
||||
bounds = overlayView.bounds;
|
||||
|
||||
[toolbar sizeToFit];
|
||||
CGFloat toolbarHeight = [toolbar frame].size.height;
|
||||
CGFloat rootViewHeight = CGRectGetHeight(bounds);
|
||||
CGFloat rootViewWidth = CGRectGetWidth(bounds);
|
||||
CGRect rectArea = CGRectMake(0, rootViewHeight - toolbarHeight, rootViewWidth, toolbarHeight);
|
||||
[toolbar setFrame:rectArea];
|
||||
|
||||
[overlayView addSubview: toolbar];
|
||||
|
||||
UIImage* reticleImage = [self buildReticleImage];
|
||||
UIView* reticleView = [[[UIImageView alloc] initWithImage: reticleImage] autorelease];
|
||||
CGFloat minAxis = MIN(rootViewHeight, rootViewWidth);
|
||||
|
||||
rectArea = CGRectMake(
|
||||
0.5 * (rootViewWidth - minAxis),
|
||||
0.5 * (rootViewHeight - minAxis),
|
||||
minAxis,
|
||||
minAxis
|
||||
);
|
||||
|
||||
[reticleView setFrame:rectArea];
|
||||
|
||||
reticleView.opaque = NO;
|
||||
reticleView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
reticleView.autoresizingMask = 0
|
||||
| UIViewAutoresizingFlexibleLeftMargin
|
||||
| UIViewAutoresizingFlexibleRightMargin
|
||||
| UIViewAutoresizingFlexibleTopMargin
|
||||
| UIViewAutoresizingFlexibleBottomMargin
|
||||
;
|
||||
|
||||
[overlayView addSubview: reticleView];
|
||||
|
||||
return overlayView;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#define RETICLE_SIZE 500.0f
|
||||
#define RETICLE_WIDTH 10.0f
|
||||
#define RETICLE_OFFSET 60.0f
|
||||
#define RETICLE_ALPHA 0.4f
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// builds the green box and red line
|
||||
//-------------------------------------------------------------------------
|
||||
- (UIImage*)buildReticleImage {
|
||||
UIImage* result;
|
||||
UIGraphicsBeginImageContext(CGSizeMake(RETICLE_SIZE, RETICLE_SIZE));
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
if (self.processor.is1D) {
|
||||
UIColor* color = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:RETICLE_ALPHA];
|
||||
CGContextSetStrokeColorWithColor(context, color.CGColor);
|
||||
CGContextSetLineWidth(context, RETICLE_WIDTH);
|
||||
CGContextBeginPath(context);
|
||||
CGFloat lineOffset = RETICLE_OFFSET+(0.5*RETICLE_WIDTH);
|
||||
CGContextMoveToPoint(context, lineOffset, RETICLE_SIZE/2);
|
||||
CGContextAddLineToPoint(context, RETICLE_SIZE-lineOffset, 0.5*RETICLE_SIZE);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
if (self.processor.is2D) {
|
||||
UIColor* color = [UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:RETICLE_ALPHA];
|
||||
CGContextSetStrokeColorWithColor(context, color.CGColor);
|
||||
CGContextSetLineWidth(context, RETICLE_WIDTH);
|
||||
CGContextStrokeRect(context,
|
||||
CGRectMake(
|
||||
RETICLE_OFFSET,
|
||||
RETICLE_OFFSET,
|
||||
RETICLE_SIZE-2*RETICLE_OFFSET,
|
||||
RETICLE_SIZE-2*RETICLE_OFFSET
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
result = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,53 +1,120 @@
|
||||
# Barcode Scanner plugin for Phonegap #
|
||||
By Matt Kane
|
||||
|
||||
## Adding the Plugin to your project ##
|
||||
Copy the .h and .mm file to the Plugins directory in your project. Copy the .js file to your www directory and reference it from your html file(s). You also need to add the ZXing library to your project.
|
||||
Originally by Matt Kane
|
||||
|
||||
## Adding ZXing to your project ##
|
||||
First, [download the ZXing zip file](http://code.google.com/p/zxing/), unpack it, and put it somewhere safe such as your Documents folder. You'll need to whole package, not just the iphone folder. Then add it to your project, using the instructions in the zxing-1.x/iphone/README file.
|
||||
Modifications by IBM
|
||||
|
||||
## Using the plugin ##
|
||||
The plugin creates the object `window.plugins.barcodeScanner` with one method `scan(types, success, fail)`
|
||||
`types` is a comma-separated list of barcode types that the scanner should accept. If you pass null then any
|
||||
barcode type will be accepted. Currently only `QR_CODE` is supported, but that should change soon.
|
||||
|
||||
`success` and `fail` are callback functions. Success is passed the decoded barcode as a string.
|
||||
The plugin requires the AVFoundation Framework, which is only available under
|
||||
iOS 4.x and greater. The plugin tests to make sure AVFoundation Framework
|
||||
is available before using it, so you should be able to link this into an
|
||||
application targeting iOS 3.x or earlier (though the barcode scanning will
|
||||
not work).
|
||||
|
||||
The plugin creates the object `window.plugins.barcodescanner` with two methods:
|
||||
|
||||
scan(success, fail)
|
||||
encode(type, data, success, fail, options)
|
||||
|
||||
## `scan()` method ##
|
||||
|
||||
The `scan` function is invoked as follows:
|
||||
|
||||
scan(success, fail)
|
||||
|
||||
`success` and `fail` are callback functions.
|
||||
|
||||
`success()` is passed an object with the following properties:
|
||||
|
||||
* `text` - the scanned text as a string
|
||||
* `format` - the format of the barcode scanned as a string
|
||||
* `cancelled` - indication of whether the scan was cancelled as a boolean
|
||||
|
||||
`fail()` is passed a string containing an error message, if the scan failed
|
||||
|
||||
A full example could be:
|
||||
|
||||
window.plugins.barcodeScanner.scan( BarcodeScanner.Type.QR_CODE, function(result) {
|
||||
alert("We got a barcode: " + result);
|
||||
}, function(error) {
|
||||
alert("Scanning failed: " + error);
|
||||
}
|
||||
);
|
||||
window.plugins.barcodescanner.scan(
|
||||
function(result) {
|
||||
if (result.cancelled)
|
||||
alert("the user cancelled the scan")
|
||||
else
|
||||
alert("we got a barcode: " + result.text)
|
||||
},
|
||||
function(error) {
|
||||
alert("scanning failed: " + error)
|
||||
}
|
||||
)
|
||||
|
||||
## `encode()` method ##
|
||||
|
||||
The `encode` function is not supported on iOS.
|
||||
|
||||
## barcode formats supported ##
|
||||
|
||||
The following barcode types are supported; the names come from the
|
||||
Barcode formats constants in the zxing code.
|
||||
|
||||
* QR_CODE
|
||||
* DATA_MATRIX
|
||||
* UPC_E
|
||||
* UPC_A
|
||||
* EAN_8
|
||||
* EAN_13
|
||||
* CODE_128
|
||||
* CODE_39
|
||||
|
||||
## Adding the plugin to your project ##
|
||||
|
||||
* Copy the .h, .cpp and .mm files to the Plugins directory in your project.
|
||||
* Copy the .js file to your www directory and reference it from your html file(s).
|
||||
* In the `Supporting Files` directory of your project, add a new plugin:
|
||||
* key: `com.phonegap.barcodeScanner`
|
||||
* value: `PGBarcodeScanner`
|
||||
* Add the following libraries to your Xcode project, if not already there:
|
||||
* AVFoundation.framework
|
||||
* AssetsLibrary.framework
|
||||
* CoreVideo.framework
|
||||
* libiconv.dylib
|
||||
* to add these libraries, select your target, and then display the Build Phases.
|
||||
Under Link Binary With Libraries, click the add button and then
|
||||
select the frameworks above. To support being able to link against
|
||||
iOS 3.x, these libraries should be marked as Optional, not Required.
|
||||
|
||||
* You may need to set the compile options for zxing-all-in-one.cc to turn off optimization.
|
||||
|
||||
## Building ##
|
||||
|
||||
To make life a little easier for folks using this plugin, all the of zxing
|
||||
code is combined into a single file, rather than dealing with Xcode subprojects.
|
||||
To build the `zxing-all-in-one.cpp` and
|
||||
corresponding `.h` file, cd into the `build` directory and run `make`.
|
||||
|
||||
## Testing ##
|
||||
|
||||
Under the `test` directory is a test case you can use to test the plugin.
|
||||
It has two parts - a desktop web page, and a phonegap app.
|
||||
|
||||
To build the phonegap app, drop all the files in `test/phonegap-app` into
|
||||
a new PhoneGap project and build it per the instructions above.
|
||||
|
||||
Once the phonegap app has build built, run it on your device.
|
||||
|
||||
Next, run the desktop app, by opening the `test/desktop-app/index.html` file in a browser.
|
||||
|
||||
The desktop app and phone app are synchronized to display a barcode and then
|
||||
have you scan that barcode. The results of scanning the barcodes is provided
|
||||
once all the barcodes have been scanned. If you can't get the scanner to
|
||||
recognized a particular barcode, use the `Cancel` button (which will cause
|
||||
that test to fail).
|
||||
|
||||
## BUGS AND CONTRIBUTIONS ##
|
||||
The latest bleeding-edge version is available [on GitHub](http://github.com/ascorbic/phonegap-plugins/tree/master/iPhone/)
|
||||
If you have a patch, fork my repo and send me a pull request. Submit bug reports on GitHub, please.
|
||||
|
||||
## Licence ##
|
||||
|
||||
The MIT License
|
||||
This plugin includes source code from the [zxing](http://code.google.com/p/zxing/)
|
||||
project, which is licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
Copyright (c) 2011 Matt Kane
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
The other code in this plugin is licensed as below:
|
||||
|
||||
PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
|
||||
@@ -1,50 +1,80 @@
|
||||
/**
|
||||
* Phonegap Barcode Scanner plugin
|
||||
* Copyright (c) Matt Kane 2011
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) Matt Kane 2010
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
var BarcodeScanner = function() {
|
||||
|
||||
;(function(){
|
||||
|
||||
if (PhoneGap.hasResource("barcodeScanner")) return
|
||||
|
||||
PhoneGap.addResource("barcodeScanner")
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
var BarcodeScanner = function() {
|
||||
}
|
||||
|
||||
BarcodeScanner.prototype.callbackMap = {};
|
||||
BarcodeScanner.prototype.callbackIdx = 0;
|
||||
|
||||
/* That's your lot for the moment */
|
||||
|
||||
BarcodeScanner.Type = {
|
||||
QR_CODE: "QR_CODE"
|
||||
//-------------------------------------------------------------------
|
||||
BarcodeScanner.Encode = {
|
||||
TEXT_TYPE: "TEXT_TYPE",
|
||||
EMAIL_TYPE: "EMAIL_TYPE",
|
||||
PHONE_TYPE: "PHONE_TYPE",
|
||||
SMS_TYPE: "SMS_TYPE",
|
||||
CONTACT_TYPE: "CONTACT_TYPE",
|
||||
LOCATION_TYPE: "LOCATION_TYPE"
|
||||
}
|
||||
|
||||
/* Types are ignored at the moment until I implement any other than QR Code */
|
||||
//-------------------------------------------------------------------
|
||||
BarcodeScanner.prototype.scan = function(success, fail, options) {
|
||||
function successWrapper(result) {
|
||||
result.cancelled = (result.cancelled == 1)
|
||||
success.call(null, result)
|
||||
}
|
||||
|
||||
BarcodeScanner.prototype.scan = function(types, success, fail) {
|
||||
|
||||
var plugin = window.plugins.barcodeScanner,
|
||||
cbMap = plugin.callbackMap,
|
||||
key = 'scan' + plugin.callbackIdx++;
|
||||
|
||||
cbMap[key] = {
|
||||
success: function(result) {
|
||||
delete cbMap[key];
|
||||
success(result);
|
||||
},
|
||||
fail: function(result) {
|
||||
delete cbMap[key];
|
||||
fail(result);
|
||||
}
|
||||
};
|
||||
|
||||
var cbPrefix = 'window.plugins.barcodeScanner.callbackMap.' + key;
|
||||
|
||||
return PhoneGap.exec("BarcodeScanner.scan", cbPrefix + ".success", cbPrefix + ".fail", types);
|
||||
};
|
||||
if (!fail) { fail = function() {}}
|
||||
|
||||
PhoneGap.addConstructor(function()
|
||||
{
|
||||
if(!window.plugins)
|
||||
{
|
||||
window.plugins = {};
|
||||
}
|
||||
window.plugins.barcodeScanner = new BarcodeScanner();
|
||||
});
|
||||
if (typeof fail != "function") {
|
||||
console.log("BarcodeScanner.scan failure: failure parameter not a function")
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof success != "function") {
|
||||
fail("success callback parameter must be a function")
|
||||
return
|
||||
}
|
||||
|
||||
return PhoneGap.exec(successWrapper, fail, "com.phonegap.barcodeScanner", "scan", [])
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
BarcodeScanner.prototype.encode = function(type, data, success, fail, options) {
|
||||
if (!fail) { fail = function() {}}
|
||||
|
||||
if (typeof fail != "function") {
|
||||
console.log("BarcodeScanner.scan failure: failure parameter not a function")
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof success != "function") {
|
||||
fail("success callback parameter must be a function")
|
||||
return
|
||||
}
|
||||
|
||||
return PhoneGap.exec(success, fail, "com.phonegap.barcodeScanner", "encode", [{type: type, data: data, options: options}])
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (!window.plugins) window.plugins = {}
|
||||
|
||||
if (!window.plugins.barcodeScanner) {
|
||||
window.plugins.barcodeScanner = new BarcodeScanner()
|
||||
}
|
||||
else {
|
||||
console.log("Not installing barcodeScanner: window.plugins.barcodeScanner already exists")
|
||||
}
|
||||
})
|
||||
|
||||
})();
|
||||
2
iPhone/BarcodeScanner/build/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
zxing
|
||||
tmp
|
||||
33
iPhone/BarcodeScanner/build/Makefile
Normal file
@@ -0,0 +1,33 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
ZXING_VERS = 1.7
|
||||
ZXING_URL = http://zxing.googlecode.com/files/ZXing-$(ZXING_VERS).zip
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
all: help
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
build: tmp/zxing.zip
|
||||
-@rm -rf zxing
|
||||
mkdir zxing
|
||||
cp -R tmp/zip/zxing/cpp/core/src/zxing/* zxing
|
||||
python onefile-zxing.py zxing ..
|
||||
# rm -rf zxing
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
tmp/zxing.zip:
|
||||
-@rm -rf tmp
|
||||
mkdir tmp
|
||||
curl --output tmp/zxing.zip $(ZXING_URL)
|
||||
unzip -d tmp/zip tmp/zxing.zip
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
watch:
|
||||
run-when-changed "make build" *
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
help:
|
||||
@echo make targets available:
|
||||
@echo " help print this help"
|
||||
@echo " watch run 'make build' when a file changes"
|
||||
@echo " build build the zxing-all-in-one files"
|
||||
|
||||
318
iPhone/BarcodeScanner/build/onefile-zxing.py
Executable file
@@ -0,0 +1,318 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
PROGRAM = os.path.basename(sys.argv[0])
|
||||
OFILEBASE = "zxing-all-in-one"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
if len(args) != 2:
|
||||
error("expecting arguments src-dir out-dir")
|
||||
|
||||
srcDir = args[0]
|
||||
outDir = args[1]
|
||||
|
||||
oCppFileName = os.path.join(outDir, OFILEBASE + ".cpp")
|
||||
oHFileName = os.path.join(outDir, OFILEBASE + ".h")
|
||||
|
||||
print "processing %s to produce %s and %s" % (srcDir, oCppFileName, oHFileName)
|
||||
|
||||
gatherFiles(srcDir)
|
||||
uniqueize(["logDigits", "LUMINANCE_BITS", "LUMINANCE_SHIFT", "LUMINANCE_BUCKETS"])
|
||||
|
||||
systemIncludes = getSystemIncludes()
|
||||
|
||||
oFile = open(oCppFileName, "w")
|
||||
|
||||
oFile.write('\n#include "%s.h"\n\n' % OFILEBASE)
|
||||
|
||||
for sFile in SourceFile.getSources():
|
||||
oFile.write("// file: %s\n\n" % sFile.getFileName())
|
||||
oFile.write(sFile.getSource())
|
||||
oFile.write("\n\n")
|
||||
|
||||
oFile.close()
|
||||
|
||||
includes = SourceFile.getIncludes()
|
||||
includes = getIncludeOrder(includes)
|
||||
|
||||
oFile = open(oHFileName, "w")
|
||||
|
||||
for include in systemIncludes:
|
||||
oFile.write("#include <%s>\n" % include)
|
||||
|
||||
oFile.write("\n")
|
||||
|
||||
for sFile in includes:
|
||||
oFile.write("// file: %s\n\n" % sFile.getFileName())
|
||||
oFile.write(sFile.getSource())
|
||||
oFile.write("\n\n")
|
||||
|
||||
oFile.close()
|
||||
|
||||
|
||||
# print ".h files:"
|
||||
# for sFile in SourceFile.getIncludes():
|
||||
# print " %s" % sFile.getFileName()
|
||||
|
||||
# print ".cpp files:"
|
||||
# for sFile in SourceFile.getSources():
|
||||
# print " %s" % sFile.getFileName()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def getSystemIncludes():
|
||||
systemIncludes = {}
|
||||
|
||||
for sFile in SourceFile.getIncludes():
|
||||
for include in sFile.getSystemIncludes():
|
||||
systemIncludes[include] = True
|
||||
|
||||
for sFile in SourceFile.getSources():
|
||||
for include in sFile.getSystemIncludes():
|
||||
systemIncludes[include] = True
|
||||
|
||||
return systemIncludes.keys()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def gatherFiles(srcDir):
|
||||
|
||||
allFilesOs = os.walk(srcDir)
|
||||
|
||||
for (dirPath, dirNames, fileNames) in allFilesOs:
|
||||
for fileName in fileNames:
|
||||
fileName = os.path.join(dirPath, fileName)
|
||||
if fileName[-2:] == ".h":
|
||||
SourceFile.addInclude(fileName, dirPath)
|
||||
elif fileName[-4:] == ".cpp":
|
||||
SourceFile.addSource(fileName, dirPath)
|
||||
else:
|
||||
error("unknown file type for: %s" % fileName)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def uniqueize(strings):
|
||||
|
||||
files = SourceFile.getSources()
|
||||
files.extend(SourceFile.getIncludes())
|
||||
|
||||
patterns = {}
|
||||
for string in strings:
|
||||
patterns[string] = re.compile(r"(\W*)(" + string + ")(\W*)")
|
||||
|
||||
counter = 0
|
||||
for file in files:
|
||||
counter = counter + 1
|
||||
|
||||
for string in strings:
|
||||
pattern = patterns[string]
|
||||
replace = r'\1\2_%d\3' % (counter)
|
||||
|
||||
file.source = pattern.sub(replace, file.source)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
class SourceFile:
|
||||
sources = []
|
||||
includes = []
|
||||
includesMap = {}
|
||||
|
||||
@staticmethod
|
||||
def getInclude(fileName):
|
||||
return SourceFile.includesMap.get(fileName, None)
|
||||
|
||||
@staticmethod
|
||||
def getSources():
|
||||
return SourceFile.sources[:]
|
||||
|
||||
@staticmethod
|
||||
def getIncludes():
|
||||
return SourceFile.includes[:]
|
||||
|
||||
@staticmethod
|
||||
def addSource(fileName, dirName):
|
||||
sourceFile = SourceFile(fileName, dirName)
|
||||
SourceFile.sources.append(sourceFile)
|
||||
|
||||
@staticmethod
|
||||
def addInclude(fileName, dirName):
|
||||
sourceFile = SourceFile(fileName, dirName)
|
||||
SourceFile.includes.append(sourceFile)
|
||||
SourceFile.includesMap[fileName] = sourceFile
|
||||
|
||||
def __init__(self, fileName, dirName):
|
||||
self.fileName = fileName
|
||||
self.dirName = dirName
|
||||
self.pIncludes = []
|
||||
self.sIncludes = []
|
||||
self.source = ""
|
||||
|
||||
self._process()
|
||||
|
||||
def getFileName(self):
|
||||
return self.fileName
|
||||
|
||||
def getSource(self):
|
||||
return self.source
|
||||
|
||||
def getProjectIncludes(self):
|
||||
return self.pIncludes[:]
|
||||
pass
|
||||
|
||||
def getSystemIncludes(self):
|
||||
return self.sIncludes[:]
|
||||
|
||||
def _process(self):
|
||||
iFile = file(self.getFileName())
|
||||
lines = iFile.readlines()
|
||||
iFile.close()
|
||||
|
||||
lines = [line.rstrip() for line in lines]
|
||||
|
||||
patternInclude1 = re.compile(r'\s*#include\s*<(\s*.*?\s*)>\s*')
|
||||
patternInclude2 = re.compile(r'\s*#include\s*"(\s*.*?\s*)"\s*')
|
||||
patternDefine1 = re.compile(r'\s*#define\s*__.*_H__\s*')
|
||||
|
||||
for lineNo in range(len(lines)):
|
||||
line = lines[lineNo]
|
||||
|
||||
match = patternDefine1.match(line)
|
||||
if match:
|
||||
lines[lineNo] = "// " + lines[lineNo]
|
||||
|
||||
match = patternInclude1.match(line)
|
||||
if match:
|
||||
fileName = match.group(1)
|
||||
if fileName.startswith("zxing/"):
|
||||
self._addProjectInclude(fileName)
|
||||
else:
|
||||
self._addSystemInclude(fileName)
|
||||
lines[lineNo] = "// " + lines[lineNo]
|
||||
continue
|
||||
|
||||
match = patternInclude2.match(line)
|
||||
if match:
|
||||
fileName = match.group(1)
|
||||
fileName = os.path.join(self.dirName, fileName)
|
||||
self._addProjectInclude(fileName)
|
||||
lines[lineNo] = "// " + lines[lineNo]
|
||||
continue
|
||||
|
||||
self.source = "\n".join(lines)
|
||||
|
||||
def _addProjectInclude(self, include):
|
||||
self.pIncludes.append(include)
|
||||
|
||||
def _addSystemInclude(self, include):
|
||||
self.sIncludes.append(include)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def getIncludeOrder(foundIncludes):
|
||||
foundIncludes = [include.getFileName() for include in foundIncludes]
|
||||
|
||||
orderedIncludes = """
|
||||
zxing/Exception.h
|
||||
zxing/common/IllegalArgumentException.h
|
||||
zxing/common/Counted.h
|
||||
zxing/common/BitArray.h
|
||||
zxing/common/BitMatrix.h
|
||||
zxing/common/Array.h
|
||||
zxing/common/Str.h
|
||||
zxing/common/BitSource.h
|
||||
zxing/common/DecoderResult.h
|
||||
zxing/common/PerspectiveTransform.h
|
||||
zxing/ResultPoint.h
|
||||
zxing/common/DetectorResult.h
|
||||
zxing/common/Point.h
|
||||
zxing/common/EdgeDetector.h
|
||||
zxing/LuminanceSource.h
|
||||
zxing/Binarizer.h
|
||||
zxing/common/GlobalHistogramBinarizer.h
|
||||
zxing/common/GreyscaleLuminanceSource.h
|
||||
zxing/common/GreyscaleRotatedLuminanceSource.h
|
||||
zxing/common/GridSampler.h
|
||||
zxing/common/HybridBinarizer.h
|
||||
zxing/common/reedsolomon/GF256.h
|
||||
zxing/common/reedsolomon/GF256Poly.h
|
||||
zxing/common/reedsolomon/ReedSolomonDecoder.h
|
||||
zxing/common/reedsolomon/ReedSolomonException.h
|
||||
zxing/BarcodeFormat.h
|
||||
zxing/BinaryBitmap.h
|
||||
zxing/ResultPointCallback.h
|
||||
zxing/DecodeHints.h
|
||||
zxing/Result.h
|
||||
zxing/Reader.h
|
||||
zxing/MultiFormatReader.h
|
||||
zxing/ReaderException.h
|
||||
zxing/datamatrix/decoder/Decoder.h
|
||||
zxing/datamatrix/DataMatrixReader.h
|
||||
zxing/datamatrix/Version.h
|
||||
zxing/datamatrix/decoder/BitMatrixParser.h
|
||||
zxing/datamatrix/decoder/DataBlock.h
|
||||
zxing/datamatrix/decoder/DecodedBitStreamParser.h
|
||||
zxing/datamatrix/detector/CornerPoint.h
|
||||
zxing/datamatrix/detector/MonochromeRectangleDetector.h
|
||||
zxing/datamatrix/detector/Detector.h
|
||||
zxing/oned/OneDReader.h
|
||||
zxing/oned/Code128Reader.h
|
||||
zxing/oned/Code39Reader.h
|
||||
zxing/oned/UPCEANReader.h
|
||||
zxing/oned/EAN13Reader.h
|
||||
zxing/oned/EAN8Reader.h
|
||||
zxing/oned/ITFReader.h
|
||||
zxing/oned/MultiFormatOneDReader.h
|
||||
zxing/oned/MultiFormatUPCEANReader.h
|
||||
zxing/oned/OneDResultPoint.h
|
||||
zxing/oned/UPCAReader.h
|
||||
zxing/oned/UPCEReader.h
|
||||
zxing/qrcode/ErrorCorrectionLevel.h
|
||||
zxing/qrcode/FormatInformation.h
|
||||
zxing/qrcode/decoder/Decoder.h
|
||||
zxing/qrcode/QRCodeReader.h
|
||||
zxing/qrcode/Version.h
|
||||
zxing/qrcode/decoder/BitMatrixParser.h
|
||||
zxing/qrcode/decoder/DataBlock.h
|
||||
zxing/qrcode/decoder/DataMask.h
|
||||
zxing/qrcode/decoder/Mode.h
|
||||
zxing/qrcode/decoder/DecodedBitStreamParser.h
|
||||
zxing/qrcode/detector/AlignmentPattern.h
|
||||
zxing/qrcode/detector/AlignmentPatternFinder.h
|
||||
zxing/qrcode/detector/Detector.h
|
||||
zxing/qrcode/detector/FinderPattern.h
|
||||
zxing/qrcode/detector/FinderPatternInfo.h
|
||||
zxing/qrcode/detector/FinderPatternFinder.h
|
||||
zxing/qrcode/detector/QREdgeDetector.h
|
||||
""".split()
|
||||
|
||||
foundError = False
|
||||
|
||||
for include in foundIncludes:
|
||||
if include not in orderedIncludes:
|
||||
foundError = True
|
||||
log("error: found include not listed in ordered includes: %s" % include)
|
||||
|
||||
includes = []
|
||||
for include in orderedIncludes:
|
||||
if include in foundIncludes:
|
||||
includes.append(SourceFile.getInclude(include))
|
||||
else:
|
||||
foundError = True
|
||||
log("error: ordered include not found: %s" % include)
|
||||
|
||||
if foundError:
|
||||
error("exiting")
|
||||
|
||||
return includes
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def log(message):
|
||||
print "%s: %s" % (PROGRAM, message)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def error(message):
|
||||
log("error: %s" % (message))
|
||||
sys.exit(-1)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
main()
|
||||
20
iPhone/BarcodeScanner/test/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
all: help
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
clean:
|
||||
-@chmod +w desktop-app/*.json.js phonegap-app/*.json.js phonegap-app/images/*
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
build: clean
|
||||
@python rebuild-tests-json.py
|
||||
@cp desktop-app/images/* phonegap-app/images
|
||||
@cp desktop-app/*.json.js phonegap-app
|
||||
@chmod -w desktop-app/*.json.js phonegap-app/*.json.js phonegap-app/images/*
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
help:
|
||||
@echo make targets available:
|
||||
@echo " help print this help"
|
||||
@echo " build build the tests"
|
||||
@echo " clean remove the transient test files"
|
||||
|
||||
32
iPhone/BarcodeScanner/test/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
PhoneGap BarcodeScanner test applications
|
||||
=========================================
|
||||
|
||||
This directory contains a set of test applications
|
||||
for the PhoneGap BarcodeScanner plugin. It consists
|
||||
of two pieces:
|
||||
|
||||
* desktop app
|
||||
* phonegap device app
|
||||
|
||||
Running the tests
|
||||
-----------------
|
||||
|
||||
To run the tests,
|
||||
|
||||
* build the tests (see below)
|
||||
* create a new PhoneGap app using [the phonegap app](phonegap-app/index.html)
|
||||
* fire up the desktop app by opening [the desktop app](desktop-app/index.html)
|
||||
* run the PhoneGap app
|
||||
* the PhoneGap app will prompt you to scan an image currently displayed in the
|
||||
desktop app. Presumably, it will scan, and check the scan output. Once
|
||||
scanned, or if you cancel, it will move to the next image to test. The final
|
||||
tally is displayed when all the images have been scanned.
|
||||
|
||||
Building the tests
|
||||
-----------------
|
||||
|
||||
Run `make` in this directory. It will:
|
||||
|
||||
* copy the `desktop-app/images` files to `phonegap-app/images`
|
||||
* create a `.json` file with the image scan results, copying it
|
||||
into `desktop-app` and `phonegap-app`
|
||||
1
iPhone/BarcodeScanner/test/desktop-app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.json.js
|
||||
70
iPhone/BarcodeScanner/test/desktop-app/desktop-app.js
Normal file
@@ -0,0 +1,70 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
// MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
//
|
||||
// Copyright (c) 2011, IBM Corporation
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
var onLoad
|
||||
var displayNextTest
|
||||
|
||||
;(function(){
|
||||
|
||||
var TestsTotal
|
||||
var TestCurrent
|
||||
var LastStatus
|
||||
var Template
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function onLoadInner() {
|
||||
Template = $("template").innerHTML
|
||||
TestsTotal = ImageData.length + 1
|
||||
TestCurrent = -1
|
||||
LastStatus = ""
|
||||
|
||||
displayNextTest()
|
||||
}
|
||||
|
||||
onLoad = onLoadInner
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function displayNextTestInner() {
|
||||
TestCurrent += 1
|
||||
|
||||
if (TestCurrent >= TestsTotal - 1) {
|
||||
$("content").innerHTML = "press cancel<p><a href=''>re-run</a>"
|
||||
return
|
||||
}
|
||||
|
||||
$("content").innerHTML = fillTemplate(Template)
|
||||
}
|
||||
|
||||
displayNextTest = displayNextTestInner
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function fillTemplate(template) {
|
||||
template = template.replace(/{{current}}/g, TestCurrent + 1)
|
||||
template = template.replace(/{{total}}/g, TestsTotal)
|
||||
template = template.replace(/{{image}}/g, escapeHTML(ImageData[TestCurrent].image))
|
||||
template = template.replace(/{{text}}/g, escapeHTML(ImageData[TestCurrent].text))
|
||||
template = template.replace(/{{format}}/g, escapeHTML(ImageData[TestCurrent].format))
|
||||
|
||||
return template
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function $(id) {
|
||||
var element = document.getElementById(id)
|
||||
if (!element) alert("can't find element with id '" + id + "'")
|
||||
return element
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function escapeHTML(string) {
|
||||
return string
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
}
|
||||
|
||||
})();
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/code_128-1.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1 @@
|
||||
123456
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/code_39-1.gif
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1 @@
|
||||
TEST-SHEET
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/data_matrix-1.jpg
Executable file
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1 @@
|
||||
http://google.com/m
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/ean_13-1.jpg
Executable file
|
After Width: | Height: | Size: 9.2 KiB |
@@ -0,0 +1 @@
|
||||
9780764544200
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/ean_8-1.gif
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1 @@
|
||||
48512343
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/qr_code-large.gif
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,12 @@
|
||||
BEGIN:VCARD
|
||||
N:Kennedy;Steve
|
||||
TEL:+44 (0)7775 755503
|
||||
ADR;HOME:;;Flat 2, 43 Howitt Road, Belsize Park;London;;NW34LU;UK
|
||||
ORG:NetTek Ltd;
|
||||
TITLE:Consultant
|
||||
EMAIL:steve@nettek.co.uk
|
||||
URL:www.nettek.co.uk
|
||||
EMAIL;IM:MSN:steve@gbnet.net
|
||||
NOTE:Testing 1 2 3
|
||||
BDAY:19611105
|
||||
END:VCARD
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/qr_code-medium.jpg
Executable file
|
After Width: | Height: | Size: 9.9 KiB |
@@ -0,0 +1 @@
|
||||
MECARD:N:Google 411,;TEL:18665881077;;
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/qr_code-small.png
Normal file
|
After Width: | Height: | Size: 490 B |
@@ -0,0 +1 @@
|
||||
Morden
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/upc_a-1.jpg
Executable file
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1 @@
|
||||
456314319671
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/upc_a-2.jpg
Executable file
|
After Width: | Height: | Size: 63 KiB |
@@ -0,0 +1 @@
|
||||
752919460009
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/upc_e-1.gif
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1 @@
|
||||
00123457
|
||||
BIN
iPhone/BarcodeScanner/test/desktop-app/images/upc_e-2.jpg
Executable file
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1 @@
|
||||
05096893
|
||||
51
iPhone/BarcodeScanner/test/desktop-app/index.html
Normal file
@@ -0,0 +1,51 @@
|
||||
<!--
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2011, IBM Corporation
|
||||
*
|
||||
-->
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>PhoneGap BarcodeScanner desktop test app</title>
|
||||
<script src="image-data.json.js"></script>
|
||||
<script src="desktop-app.js"></script>
|
||||
<style>
|
||||
#template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.xtest-case {
|
||||
border: thin solid black;
|
||||
margin: 1em;
|
||||
padding: 1em;
|
||||
float: left;
|
||||
max-width: 25%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="onLoad()">
|
||||
|
||||
<div id="content">
|
||||
</div>
|
||||
|
||||
<div id="template">
|
||||
<p><b>({{current}}/{{total}}): {{image}} </b>
|
||||
|
||||
<p>click on image to proceed
|
||||
|
||||
<p><img onclick="displayNextTest()" src="images/{{image}}">
|
||||
|
||||
<p>result should be:
|
||||
<br><tt>{{format}} - "{{text}}"</tt>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
1
iPhone/BarcodeScanner/test/phonegap-app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.json.js
|
||||
4
iPhone/BarcodeScanner/test/phonegap-app/images/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.jpg
|
||||
*.gif
|
||||
*.png
|
||||
*.txt
|
||||
50
iPhone/BarcodeScanner/test/phonegap-app/index.html
Normal file
@@ -0,0 +1,50 @@
|
||||
<!--
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2011, IBM Corporation
|
||||
*
|
||||
-->
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>PhoneGap BarcodeScanner device test app</title>
|
||||
<script src="phonegap-1.1.0.js"></script>
|
||||
<script src="barcodescanner.js"></script>
|
||||
<script src="image-data.json.js"></script>
|
||||
<script src="phonegap-app.js"></script>
|
||||
<style>
|
||||
button {
|
||||
font-size: 120%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="onLoad()">
|
||||
|
||||
<div id="running-bits">
|
||||
|
||||
<button id="scan-button">scan: ?</button>
|
||||
|
||||
<p>(<span id="test-count-current">?</span>
|
||||
/
|
||||
<span id="test-count-total">?</span>)
|
||||
|
||||
<p><img id="image" src="">
|
||||
|
||||
<p>expected text:
|
||||
<br><tt><span id="test-text">?</span></tt>
|
||||
|
||||
</div>
|
||||
|
||||
<p><b id="test-done"></b>
|
||||
<br><button id="start-over">start over</button>
|
||||
|
||||
<p>Results:
|
||||
<ul id="results-list">
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
180
iPhone/BarcodeScanner/test/phonegap-app/phonegap-app.js
Normal file
@@ -0,0 +1,180 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
// MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
//
|
||||
// Copyright (c) 2011, IBM Corporation
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
var thisImage = -1
|
||||
var total = ImageData.length
|
||||
|
||||
var successes = []
|
||||
var failures = []
|
||||
|
||||
var resultsList
|
||||
|
||||
for (var i=0; i<total; i++) {
|
||||
ImageData[i].type = ImageData[i].format
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function scannerFake(success, failure) {
|
||||
if (thisImage == null) {
|
||||
success({cancelled: true})
|
||||
}
|
||||
else {
|
||||
success({
|
||||
cancelled: false,
|
||||
text: ImageData[thisImage].text,
|
||||
format: ImageData[thisImage].format
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function onLoad() {
|
||||
if (window.PhoneGap) {
|
||||
document.addEventListener("deviceready",onDeviceReady,false);
|
||||
}
|
||||
else {
|
||||
if (!window.plugins) window.plugins = {}
|
||||
if (!window.plugins.barcodeScanner) window.plugins.barcodeScanner = {
|
||||
scan: scannerFake
|
||||
}
|
||||
setTimeout(onDeviceReady,10)
|
||||
}
|
||||
|
||||
$("scan-button").onclick = scanNext
|
||||
$("start-over").onclick = rerun
|
||||
|
||||
updateText("test-count-total", total+1)
|
||||
|
||||
resultsList = $("results-list")
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function onDeviceReady() {
|
||||
next()
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function rerun() {
|
||||
thisImage = -1
|
||||
resultsList.innerHTML = ""
|
||||
updateText("test-done", "")
|
||||
$("running-bits").style.display = "block"
|
||||
next()
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function scanNext() {
|
||||
try {
|
||||
window.plugins.barcodeScanner.scan(scannerSuccess, scannerFailure)
|
||||
}
|
||||
catch (e) {
|
||||
scannerFailure("exception scanning: " + e)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function scannerSuccess(result) {
|
||||
console.log("scanner returned: " + JSON.stringify(result))
|
||||
|
||||
if (null == thisImage) {
|
||||
if (!result.cancelled) {
|
||||
scannerFailure("result does not indicate cancelled: " + result.cancelled)
|
||||
return
|
||||
}
|
||||
|
||||
var newItem = "<li style='color:green'>cancelled"
|
||||
resultsList.innerHTML += newItem
|
||||
}
|
||||
|
||||
else {
|
||||
if (result.cancelled) {
|
||||
scannerFailure("cancelled instead of scanning: '" + ImageData[thisImage].text + "'")
|
||||
return
|
||||
}
|
||||
|
||||
if (result.text != ImageData[thisImage].text) {
|
||||
scannerFailure("scanned wrong text: '" + result.text + "'")
|
||||
return
|
||||
}
|
||||
|
||||
if (result.format != ImageData[thisImage].format) {
|
||||
scannerFailure("scanned wrong format: '" + result.format + "'")
|
||||
return
|
||||
}
|
||||
|
||||
var newItem = "<li style='color:green'>" + ImageData[thisImage].image
|
||||
resultsList.innerHTML += newItem
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function scannerFailure(message) {
|
||||
console.log("BarcodeScanner failure: " + message)
|
||||
|
||||
var newItem
|
||||
if (thisImage != null) {
|
||||
newItem = "<li style='color:red'>" + ImageData[thisImage].image + " : " + message
|
||||
}
|
||||
else {
|
||||
newItem = "<li style='color:red'>" + message
|
||||
}
|
||||
|
||||
resultsList.innerHTML += newItem
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function next() {
|
||||
if (thisImage === null) {
|
||||
updateText("test-done", "All Done!")
|
||||
$("running-bits").style.display = "none"
|
||||
return
|
||||
}
|
||||
|
||||
thisImage++
|
||||
|
||||
if (thisImage == total) {
|
||||
thisImage = null
|
||||
updateImage("")
|
||||
updateText("scan-button", "scan: {press cancel}")
|
||||
updateText("test-text", "{press cancel}")
|
||||
updateText("test-count-current", total+1)
|
||||
return
|
||||
}
|
||||
|
||||
updateImage(ImageData[thisImage].image)
|
||||
updateText("scan-button", "scan: " + ImageData[thisImage].image)
|
||||
updateText("test-text", ImageData[thisImage].text)
|
||||
updateText("test-count-current", thisImage + 1)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function updateText(id, text) {
|
||||
$(id).innerText = text
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function updateImage(image) {
|
||||
var element = $("image")
|
||||
|
||||
if (image == null) {
|
||||
element.parentNode.removeChild(element)
|
||||
return
|
||||
}
|
||||
|
||||
element.src = "images/" + image
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function $(id) {
|
||||
var element = document.getElementById(id)
|
||||
if (!element) alert("can't find element with id '" + id + "'")
|
||||
return element
|
||||
}
|
||||
79
iPhone/BarcodeScanner/test/rebuild-tests-json.py
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
# MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
#
|
||||
# Copyright (c) 2011, IBM Corporation
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
|
||||
PROGRAM = os.path.basename(sys.argv[0])
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def main():
|
||||
|
||||
iDirName = "desktop-app/images"
|
||||
oFileName = "desktop-app/image-data.json.js"
|
||||
|
||||
entries = os.listdir(iDirName)
|
||||
if len(entries) == 0:
|
||||
error("no image files found!")
|
||||
|
||||
images = {}
|
||||
for entry in entries:
|
||||
(baseName, ext) = os.path.splitext(entry)
|
||||
if ext in [".txt", ".jpg", ".gif", ".png"]:
|
||||
if not baseName in images:
|
||||
images[baseName] = {"text":"", "image":""}
|
||||
|
||||
if ext != ".txt":
|
||||
images[baseName]["image"] = entry
|
||||
pattern = re.compile(r"(.*?)-.*")
|
||||
match = pattern.match(baseName)
|
||||
if match:
|
||||
images[baseName]["format"] = match.group(1).upper()
|
||||
else:
|
||||
error("file name not formatted to include type: %s" % entry)
|
||||
else:
|
||||
iFile = open(os.path.join(iDirName, entry))
|
||||
contents = iFile.read().strip()
|
||||
iFile.close()
|
||||
images[baseName]["text"] = contents
|
||||
|
||||
|
||||
newImages = []
|
||||
for name, data in images.iteritems():
|
||||
if data["image"] == "":
|
||||
error("no image for %s" % name)
|
||||
if data["text"] == "":
|
||||
error("no text for %s" % name)
|
||||
|
||||
newImages.append(data)
|
||||
|
||||
images = newImages
|
||||
images.sort(lambda x,y: cmp(x["image"],y["image"]))
|
||||
|
||||
contents = "ImageData = %s\n" % json.dumps(images, indent=4)
|
||||
|
||||
oFile = open(oFileName, "w")
|
||||
oFile.write(contents)
|
||||
oFile.close()
|
||||
|
||||
log("generated file: %s" % oFileName)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def log(message):
|
||||
print "%s: %s" % (PROGRAM, message)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def error(message):
|
||||
log("error: %s" % (message))
|
||||
sys.exit(-1)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
main()
|
||||