squashed commits for new barcodescanner

This commit is contained in:
Patrick Mueller
2011-10-24 13:28:26 -04:00
parent feff34c532
commit 991cd7f000
44 changed files with 16166 additions and 158 deletions

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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.

View File

@@ -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")
}
})
})();

View File

@@ -0,0 +1,2 @@
zxing
tmp

View 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"

View 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()

View 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"

View 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`

View File

@@ -0,0 +1 @@
*.json.js

View 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, '&lt;')
.replace(/>/g, '&gt;')
.replace(/&/g, '&amp;')
}
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1 @@
123456

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -0,0 +1 @@
TEST-SHEET

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1 @@
http://google.com/m

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1 @@
9780764544200

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -0,0 +1 @@
48512343

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@@ -0,0 +1 @@
MECARD:N:Google 411,;TEL:18665881077;;

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

View File

@@ -0,0 +1 @@
Morden

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -0,0 +1 @@
456314319671

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -0,0 +1 @@
752919460009

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1 @@
00123457

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1 @@
05096893

View 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>

View File

@@ -0,0 +1 @@
*.json.js

View File

@@ -0,0 +1,4 @@
*.jpg
*.gif
*.png
*.txt

View 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>

View 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
}

View 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()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff