Adding the actionscript3/Flex conversion of the zxing library plus a demo app.

See the readme.txt file and the comments in the zxing client project for more details, hints and remarks.


git-svn-id: https://zxing.googlecode.com/svn/trunk@1116 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
bas5winkel 2009-11-20 15:00:00 +00:00
parent f337db0b35
commit 2893fb3804
185 changed files with 24729 additions and 0 deletions

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<actionScriptProperties mainApplicationPath="zxing.as" version="3">
<compiler additionalCompilerArguments="" copyDependentFiles="false" enableModuleDebug="true" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersion="9.0.28" htmlPlayerVersionCheck="true" outputFolderPath="bin" strict="true" useApolloConfig="false" verifyDigests="true" warn="true">
<compilerSourcePath/>
<libraryPath defaultLinkType="1">
<libraryPathEntry kind="4" path=""/>
</libraryPath>
<sourceAttachmentPath/>
</compiler>
<applications>
<application path="zxing.as"/>
</applications>
<modules/>
<buildCSSFiles/>
</actionScriptProperties>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<flexLibProperties version="1">
<includeClasses/>
<includeResources/>
<namespaceManifests/>
</flexLibProperties>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>zxing</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.adobe.flexbuilder.project.flexbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.adobe.flexbuilder.project.flexlibnature</nature>
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,3 @@
#Thu Nov 05 11:41:58 CET 2009
eclipse.preferences.version=1
encoding/<project>=utf-8

View file

@ -0,0 +1,81 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* Enumerates barcode formats known to this package.
*
* @author Sean Owen
*/
public class BarcodeFormat
{
 // No, we can't use an enum here. J2ME doesn't support it.
/** QR Code 2D barcode format. */
public static var QR_CODE:BarcodeFormat = new BarcodeFormat("QR_CODE");
/** DataMatrix 2D barcode format. */
public static var DATAMATRIX:BarcodeFormat = new BarcodeFormat("DATAMATRIX");
/** UPC-E 1D format. */
public static var UPC_E:BarcodeFormat = new BarcodeFormat("UPC_E");
/** UPC-A 1D format. */
public static var UPC_A:BarcodeFormat = new BarcodeFormat("UPC_A");
/** EAN-8 1D format. */
public static var EAN_8:BarcodeFormat = new BarcodeFormat("EAN_8");
/** EAN-13 1D format. */
public static var EAN_13:BarcodeFormat = new BarcodeFormat("EAN_13");
/** Code 128 1D format. */
public static var CODE_128:BarcodeFormat = new BarcodeFormat("CODE_128");
/** Code 39 1D format. */
public static var CODE_39:BarcodeFormat = new BarcodeFormat("CODE_39");
/** ITF (Interleaved Two of Five) 1D format. */
public static var ITF:BarcodeFormat = new BarcodeFormat("ITF");
/** PDF417 format. */
public static var PDF417:BarcodeFormat = new BarcodeFormat("PDF417");
private var _name:String;
public function BarcodeFormat(name:String)
{
this._name = name;
}
public function toString():String
{
return this._name;
}
/*
public function get name():String
{
return this._name;
}
/*
public function Equals(other:BarcodeFormat):Boolean
{
if (this._name == other.name)
{
return true;
}
else
{
return false;
}
}
*/
}
}

View file

@ -0,0 +1,82 @@
package com.google.zxing
{
/**
* This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
* It allows the algorithm to vary polymorphically, for example allowing a very expensive
* thresholding technique for servers and a fast one for mobile. It also permits the implementation
* to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public class Binarizer
{
import com.google.zxing.common.BitArray;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private var source:LuminanceSource;
public function Binarizer(source:LuminanceSource)
{
if (source == null)
{
throw new IllegalArgumentException("Source must be non-null.");
}
this.source = source;
}
public function getLuminanceSource():LuminanceSource
{
return source;
}
/**
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
* and passed in with each call for performance. However it is legal to keep more than one row
* at a time if needed.
*
* @param y The row to fetch, 0 <= y < bitmap height.
* @param row An optional preallocated array. If null or too small, it will be ignored.
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
* @return The array of bits for this row (true means black).
*/
public function getBlackRow(y:int, row:BitArray):BitArray
{
return null;
}
/**
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
* fetched using getBlackRow(), so don't mix and match between them.
*
* @return The 2D array of bits for the image (true means black).
*/
public function getBlackMatrix():BitMatrix
{
return null
}
/**
* Creates a new object with the same type as this Binarizer implementation, but with pristine
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
*
* @param source The LuminanceSource this Binarizer will operate on.
* @return A new concrete Binarizer implementation object.
*/
public function createBinarizer(source:LuminanceSource ):Binarizer
{
return null
}
}
}

View file

@ -0,0 +1,141 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
* accept a BinaryBitmap and attempt to decode it.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
import com.google.zxing.common.BitArray;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
public final class BinaryBitmap
{
private var binarizer:Binarizer;
private var matrix:BitMatrix;
public function BinaryBitmap( binarizer:Binarizer)
{
if (binarizer == null)
{
throw new IllegalArgumentException("Binarizer must be non-null.");
}
this.binarizer = binarizer;
matrix = null;
}
/**
* @return The width of the bitmap.
*/
public function getWidth():int
{
return binarizer.getLuminanceSource().getWidth();
}
/**
* @return The height of the bitmap.
*/
public function getHeight():int
{
return binarizer.getLuminanceSource().getHeight();
}
/**
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
*
* @param y The row to fetch, 0 <= y < bitmap height.
* @param row An optional preallocated array. If null or too small, it will be ignored.
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
* @return The array of bits for this row (true means black).
*/
public function getBlackRow(y:int, row:BitArray) :BitArray
{
return binarizer.getBlackRow(y, row);
}
/**
* Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
* fetched using getBlackRow(), so don't mix and match between them.
*
* @return The 2D array of bits for the image (true means black).
*/
public function getBlackMatrix():BitMatrix
{
// The matrix is created on demand the first time it is requested, then cached. There are two
// reasons for this:
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
// 1D Reader finds a barcode before the 2D Readers run.
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
if (matrix == null)
{
matrix = binarizer.getBlackMatrix();
}
return matrix;
}
/**
* @return Whether this bitmap can be cropped.
*/
public function isCropSupported():Boolean
{
return binarizer.getLuminanceSource().isCropSupported();
}
/**
* Returns a new object with cropped image data. Implementations may keep a reference to the
* original data rather than a copy. Only callable if isCropSupported() is true.
*
* @param left The left coordinate, 0 <= left < getWidth().
* @param top The top coordinate, 0 <= top <= getHeight().
* @param width The width of the rectangle to crop.
* @param height The height of the rectangle to crop.
* @return A cropped version of this object.
*/
public function crop(left:int, top:int, width:int, height:int ):BinaryBitmap {
var newSource:LuminanceSource = binarizer.getLuminanceSource().crop(left, top, width, height);
return new BinaryBitmap(binarizer.createBinarizer(newSource));
}
/**
* @return Whether this bitmap supports counter-clockwise rotation.
*/
public function isRotateSupported():Boolean
{
return binarizer.getLuminanceSource().isRotateSupported();
}
/**
* Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
*
* @return A rotated version of this object.
*/
public function rotateCounterClockwise():BinaryBitmap
{
var newSource:LuminanceSource = binarizer.getLuminanceSource().rotateCounterClockwise();
return new BinaryBitmap(binarizer.createBinarizer(newSource));
}
}
}

View file

@ -0,0 +1,169 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
import com.google.zxing.LuminanceSource;
import mx.controls.Image;
import flash.display.BitmapData;
/**
* This LuminanceSource implementation is meant for J2SE clients and our blackbox unit tests.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
public final class BufferedImageLuminanceSource extends LuminanceSource
{
private var image:BitmapData ;
private var left:int;
private var top:int;
private var rgbData:Array;
public function getRGB(left:int, top:int, width:int, height:int, rgb:Array, value:int, numPixels:int):void
{
var cntr:int = 0;
for (var i:int=top;i<(top+height);i++)
{
for (var j:int=left;j<(left+width);j++)
{
rgb[cntr] = image.getPixel(j,i);
cntr++;
}
}
}
public function BufferedImageLuminanceSource(image:BitmapData, left:int=0, top:int=0, width:int=0, height:int=0)
{
if ((width==0) && (height==0))
{
width = image.width;
height = image.height;
}
super(width,height);
var sourceWidth:int = image.width;
var sourceHeight:int = image.height;
if (left + width > sourceWidth || top + height > sourceHeight) {
throw new Error("Crop rectangle does not fit within image data.");
}
this.image = image;
this.left = left;
this.top = top;
}
// These methods use an integer calculation for luminance derived from:
// <code>Y = 0.299R + 0.587G + 0.114B</code>
public override function getRow(y:int, row:Array):Array {
if (y < 0 || y >= image.height) {
throw new Error("Requested row is outside the image: " + y);
}
var width:int = image.width;
if (row == null || row.length < width) {
row = new Array(width);
}
if (rgbData == null || rgbData.length < width)
{
rgbData = new Array(width);
}
this.getRGB(left, top + y, width, 1, rgbData, 0, image.width);
for (var x:int = 0; x < width; x++) {
var pixel:int = rgbData[x];
var luminance:int = (306 * ((pixel >> 16) & 0xFF) +
601 * ((pixel >> 8) & 0xFF) +
117 * (pixel & 0xFF)) >> 10;
row[x] = luminance;
}
return row;
}
public override function getMatrix():Array {
var width:int = image.width;
var height:int = image.height;
var area:int = width * height;
var matrix:Array = new Array(area);
var rgb:Array = new Array(area);
this.getRGB(left, top, width, height, rgb, 0, image.width);
for (var y:int = 0; y < height; y++) {
var offset:int = y * width;
for (var x:int = 0; x < width; x++) {
var pixel:int = rgb[offset + x];
var luminance:int = (306 * ((pixel >> 16) & 0xFF) +
601 * ((pixel >> 8) & 0xFF) +
117 * (pixel & 0xFF)) >> 10;
matrix[offset + x] = luminance;
}
}
return matrix;
}
public override function isCropSupported():Boolean
{
return false;
}
public override function crop(left:int, top:int, width:int, height:int):LuminanceSource
{
// BAS : todo
return new BufferedImageLuminanceSource(image, left, top, width, height);
}
// Can't run AffineTransforms on images of unknown format.
public override function isRotateSupported():Boolean
{
return false;
//Bas : TOO
//return image.getType() != BufferedImage.TYPE_CUSTOM;
}
public override function rotateCounterClockwise():LuminanceSource
{
if (!isRotateSupported())
{
throw new Error("Rotate not supported");
}
// Bas : todo
return null;
/*
var sourceWidth:int = image.getWidth();
var sourceHeight:int = image.getHeight();
// Rotate 90 degrees counterclockwise.
var transform:AffineTransform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);
var op:BufferedImageOp = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
// Note width/height are flipped since we are rotating 90 degrees.
var rotatedImage:BufferedImage = new BufferedImage(sourceHeight, sourceWidth, image.getType());
op.filter(image, rotatedImage);
// Maintain the cropped region, but rotate it too.
var width:int = getWidth();
return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width),
getHeight(), width);
*/
}
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* Encapsulates a type of hint that a caller may pass to a barcode reader to help it
* more quickly or accurately decode it. It is up to implementations to decide what,
* if anything, to do with the information that is supplied.
*
* @author Sean Owen
* @author dswitkin@google.com (Daniel Switkin)
* @see Reader#decode(BinaryBitmap,java.util.Hashtable)
*/
public class DecodeHintType
{
/**
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
*/
public static var OTHER:DecodeHintType = new DecodeHintType();
/**
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
* use {@link Boolean#TRUE}.
*/
public static var PURE_BARCODE:DecodeHintType = new DecodeHintType();
/**
*
* Image is known to be of one of a few possible formats.
* Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s.
*/
public static var POSSIBLE_FORMATS:DecodeHintType = new DecodeHintType();
/**
* Spend more time to try to find a barcode; optimize for accuracy, not speed.
* Doesn't matter what it maps to; use {@link Boolean#TRUE}.
*/
public static var TRY_HARDER:DecodeHintType = new DecodeHintType();
/**
* Allowed lengths of encoded data -- reject anything else. Maps to an int[].
*/
public static var ALLOWED_LENGTHS:DecodeHintType = new DecodeHintType();
public function DecodeHintType() {
}
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* These are a set of hints that you may pass to Writers to specify their behavior.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public class EncodeHintType
{
/**
* Specifies what degree of error correction to use, for example in QR Codes (type Integer).
*/
public static var ERROR_CORRECTION:EncodeHintType = new EncodeHintType();
/**
* Specifies what character encoding to use where applicable (type String)
*/
public static var CHARACTER_SET:EncodeHintType = new EncodeHintType();
public function EncodeHintType()
{
}
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
public class LuminanceSource
{
/**
* The purpose of this class hierarchy is to abstract different bitmap implementations across
* platforms into a standard interface for requesting greyscale luminance values. The interface
* only provides immutable methods; therefore crop and rotation create copies. This is to ensure
* that one Reader does not modify the original luminance source and leave it in an unknown state
* for other Readers in the chain.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
private var width:int;
private var height:int;
public function LuminanceSource(width:int, height:int)
{
this.width = width;
this.height = height;
}
/**
* Fetches one row of luminance data from the underlying platform's bitmap. Values range from
* 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
* to bitwise and with 0xff for each value. It is preferrable for implementations of this method
* to only fetch this row rather than the whole image, since no 2D Readers may be installed and
* getMatrix() may never be called.
*
* @param y The row to fetch, 0 <= y < getHeight().
* @param row An optional preallocated array. If null or too small, it will be ignored.
* Always use the returned object, and ignore the .length of the array.
* @return An array containing the luminance data.
*/
public function getRow(y:int, row:Array):Array{ return null};
/**
* Fetches luminance data for the underlying bitmap. Values should be fetched using:
* int luminance = array[y * width + x] & 0xff;
*
* @return A row-major 2D array of luminance values. Do not use result.length as it may be
* larger than width * height bytes on some platforms. Do not modify the contents
* of the result.
*/
public function getMatrix():Array{ return null};
/**
* @return The width of the bitmap.
*/
public final function getWidth():int {
return width;
}
/**
* @return The height of the bitmap.
*/
public final function getHeight():int {
return height;
}
/**
* @return Whether this subclass supports cropping.
*/
public function isCropSupported():Boolean {
return false;
}
/**
* Returns a new object with cropped image data. Implementations may keep a reference to the
* original data rather than a copy. Only callable if isCropSupported() is true.
*
* @param left The left coordinate, 0 <= left < getWidth().
* @param top The top coordinate, 0 <= top <= getHeight().
* @param width The width of the rectangle to crop.
* @param height The height of the rectangle to crop.
* @return A cropped version of this object.
*/
public function crop(left:int , top:int , width:int , height:int ):LuminanceSource {
throw new Error("This luminance source does not support cropping.");
}
/**
* @return Whether this subclass supports counter-clockwise rotation.
*/
public function isRotateSupported():Boolean {
return false;
}
/**
* Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
*
* @return A rotated version of this object.
*/
public function rotateCounterClockwise():LuminanceSource {
throw new Error("This luminance source does not support rotation.");
}
}
}

View file

@ -0,0 +1,168 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.oned.MultiFormatOneDReader;
import com.google.zxing.pdf417.PDF417Reader;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.datamatrix.DataMatrixReader;
import com.google.zxing.Reader;
/**
* MultiFormatReader is a convenience class and the main entry point into the library for most uses.
* By default it attempts to decode all barcode formats that the library supports. Optionally, you
* can provide a hints object to request different behavior, for example only decoding QR codes.
*
* @author Sean Owen
* @author dswitkin@google.com (Daniel Switkin)
*/
public class MultiFormatReader implements Reader
{
protected var hints:HashTable;
protected var readers:ArrayList;
/**
* Decode an image using the hints provided. Does not honor existing state.
*
* @param image The pixel data to decode
* @param hints The hints to use, clearing the previous state.
* @return The contents of the image
* @throws ReaderException Any errors which occurred
*/
public function decode( image:BinaryBitmap, hints:HashTable=null):Result
{
setHints(hints);
return decodeInternal(image);
}
/**
* Decode an image using the state set up by calling setHints() previously. Continuous scan
* clients will get a <b>large</b> speed increase by using this instead of decode().
*
* @param image The pixel data to decode
* @return The contents of the image
* @throws ReaderException Any errors which occurred
*/
public function decodeWithState(image:BinaryBitmap):Result{
// Make sure to set up the default state so we don't crash
if (readers == null) {
setHints(null);
}
return decodeInternal(image);
}
/**
* This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls
* to decodeWithState(image) can reuse the same set of readers without reallocating memory. This
* is important for performance in continuous scan clients.
*
* @param hints The set of hints to use for subsequent calls to decode(image)
*/
public function setHints(hints:HashTable):void
{
this.hints = hints;
var tryHarder:Boolean = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER);
var formats:ArrayList = ((hints == null) ? null : hints.getValuesByKey(DecodeHintType.POSSIBLE_FORMATS));
readers = new ArrayList();
if (formats != null)
{
var addOneDReader:Boolean =
(formats.indexOf(BarcodeFormat.UPC_A) != -1)||
(formats.indexOf(BarcodeFormat.UPC_E) != -1)||
(formats.indexOf(BarcodeFormat.ITF) != -1)||
(formats.indexOf(BarcodeFormat.EAN_13) != -1)||
(formats.indexOf(BarcodeFormat.EAN_8) != -1)||
(formats.indexOf(BarcodeFormat.CODE_39) != -1)||
(formats.indexOf(BarcodeFormat.CODE_128) != -1);
// Put 1D readers upfront in "normal" mode
if (addOneDReader && !tryHarder)
{
readers.Add(new MultiFormatOneDReader(hints));
}
if (formats.indexOf(BarcodeFormat.QR_CODE) != -1)
{
readers.Add(new QRCodeReader());
}
if (formats.indexOf(BarcodeFormat.PDF417) != -1) {
readers.addElement(new PDF417Reader());
}
// TODO re-enable once Data Matrix is ready
if (formats.indexOf(BarcodeFormat.DATAMATRIX) != -1) {
readers.Add(new DataMatrixReader());
}
// At end in "try harder" mode
if (addOneDReader && tryHarder)
{
readers.Add(new MultiFormatOneDReader(hints));
}
}
if (readers.Count == 0)
{
if (!tryHarder)
{
var reader:MultiFormatOneDReader = new MultiFormatOneDReader(hints);
readers.Add(reader);
}
readers.Add(new QRCodeReader());
// TODO re-enable once Data Matrix is ready
readers.Add(new DataMatrixReader());
// TODO: Enable once PDF417 has passed QA
readers.addElement(new PDF417Reader());
if (tryHarder)
{
readers.Add(new MultiFormatOneDReader(hints));
}
}
}
private function decodeInternal( image:BinaryBitmap):Result
{
var size:int = readers.Count;
for (var i:int = 0; i < size; i++)
{
var reader:Reader = (readers.getObjectByIndex(i)) as Reader;
try
{
var res:Result = reader.decode(image, hints);
return res;
}
catch ( re:ReaderException)
{
// continue
var a:int=0;
}
}
// no decoder could decode the barcode
return null;
//throw new ReaderException("MultiFormatReader : decodeInternal :could not decode");
}
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
public class MultiFormatWriter implements Writer
{
import com.google.zxing.common.ByteMatrix;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.oned.EAN13Writer;
import com.google.zxing.oned.EAN8Writer;
public function encode(contents:String, format:BarcodeFormat=null,width:int=0,height:int=0, hints:HashTable=null):Object{
if (format == BarcodeFormat.EAN_8) {
return (new EAN8Writer()).encode(contents, format, width, height, hints);
} else if (format == BarcodeFormat.EAN_13) {
return (new EAN13Writer()).encode(contents, format, width, height, hints);
} else if (format == BarcodeFormat.QR_CODE) {
return (new QRCodeWriter()).encode(contents, format, width, height, hints);
} else {
throw new IllegalArgumentException("No encoder available for format " + format);
}
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* Implementations of this interface can decode an image of a barcode in some format into
* the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
* decode a QR code. The decoder may optionally receive hints from the caller which may help
* it decode more quickly or accurately.
*
* See {@link com.google.zxing.MultiFormatReader}, which attempts to determine what barcode
* format is present within the image as well, and then decodes it accordingly.
*
* @author Sean Owen
* @author dswitkin@google.com (Daniel Switkin)
*/
public interface Reader
{
import com.google.zxing.common.flexdatatypes.HashTable;
/**
* Locates and decodes a barcode in some format within an image. This method also accepts
* hints, each possibly associated to some data, which may help the implementation decode.
*
* @param image image of barcode to decode
* @param hints passed as a {@link java.util.Hashtable} from {@link com.google.zxing.DecodeHintType} to aribtrary data. The
* meaning of the data depends upon the hint type. The implementation may or may not do
* anything with these hints.
* @return String which the barcode encodes
* @throws ReaderException if the barcode cannot be located or decoded for any reason
*/
function decode(image:BinaryBitmap, hints:HashTable=null):Result;
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* The general exception class throw when something goes wrong during decoding of a barcode.
* This includes, but is not limited to, failing checksums / error correction algorithms, being
* unable to locate finder timing patterns, and so on.
*
* @author Sean Owen
*/
public class ReaderException extends Error
{
// TODO: Currently we throw up to 400 ReaderExceptions while scanning a single 240x240 image before
// rejecting it. This involves a lot of overhead and memory allocation, and affects both performance
// and latency on continuous scan clients. In the future, we should change all the decoders not to
// throw exceptions for routine events, like not finding a barcode on a given row. Instead, we
// should return error codes back to the callers, and simply delete this class. In the mean time, I
// have altered this class to be as lightweight as possible, by ignoring the exception string, and
// by disabling the generation of stack traces, which is especially time consuming. These are just
// temporary measures, pending the big cleanup.
private static var instance:ReaderException = new ReaderException();
public function ReaderException(message:String=""):void {
super(message);
// do nothing
}
public static function getInstance():ReaderException {
return instance;
}
// Prevent stack traces from being taken
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
/*public function fillInStackTrace():Throwable {
return null;
}*/
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* <p>Encapsulates the result of decoding a barcode within an image.</p>
*
* @author Sean Owen
*/
public class Result
{
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
//BAS : made public for debugging
private var text:String;
private var rawBytes:Array;
private var resultPoints:Array;
private var format:BarcodeFormat;
private var resultMetadata:HashTable;
public function Result( text:String,
rawBytes:Array,
resultPoints:Array,
format:BarcodeFormat)
{
if (text == null && rawBytes == null) {
throw new IllegalArgumentException("Result : Text and bytes are both null");
}
this.text = text;
this.rawBytes = rawBytes;
this.resultPoints = resultPoints;
this.format = format;
this.resultMetadata = null;
}
/**
* @return raw text encoded by the barcode, if applicable, otherwise <code>null</code>
*/
public function getText():String {
return text;
}
/**
* @return raw bytes encoded by the barcode, if applicable, otherwise <code>null</code>
*/
public function getRawBytes():Array {
return rawBytes;
}
/**
* @return points related to the barcode in the image. These are typically points
* identifying finder patterns or the corners of the barcode. The exact meaning is
* specific to the type of barcode that was decoded.
*/
public function getResultPoints():Array {
return resultPoints;
}
/**
* @return {@link BarcodeFormat} representing the format of the barcode that was recognized and decoded
*/
public function getBarcodeFormat():BarcodeFormat {
return format;
}
/**
* @return {@link HashTable} mapping {@link ResultMetadataType} keys to values. May be <code>null</code>.
* This contains optional metadata about what was detected about the barcode, like orientation.
*/
public function getResultMetadata():HashTable {
return resultMetadata;
}
public function putMetadata(type:ResultMetadataType, value:Object ):void {
if (resultMetadata == null) {
resultMetadata = new HashTable(3);
}
resultMetadata.Add(type, value);
}
public function toString():String {
if (text == null) {
return "[" + rawBytes.length + " bytes]";
} else {
return text;
}
}
}
}

View file

@ -0,0 +1,45 @@
package com.google.zxing
{
public class ResultMetadataType
{
// No, we can't use an enum here. J2ME doesn't support it.
/**
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
*/
public static var OTHER:ResultMetadataType = new ResultMetadataType();
/**
* Denotes the likely approximate orientation of the barcode in the image. This value
* is given as degrees rotated clockwise from the normal, upright orientation.
* For example a 1D barcode which was found by reading top-to-bottom would be
* said to have orientation "90". This key maps to an {@link Integer} whose
* value is in the range [0,360).
*/
public static var ORIENTATION:ResultMetadataType = new ResultMetadataType();
/**
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
* which is sometimes used to encode binary data. While {@link Result} makes available
* the complete raw bytes in the barcode for these formats, it does not offer the bytes
* from the byte segments alone.</p>
*
* <p>This maps to a {@link java.util.Vector} of byte arrays corresponding to the
* raw bytes in the byte segments in the barcode, in order.</p>
*/
public static var BYTE_SEGMENTS:ResultMetadataType = new ResultMetadataType();
/**
* Error correction level used, if applicable. The value type depends on the
* format, but is typically a String.
*/
public static var ERROR_CORRECTION_LEVEL:ResultMetadataType = new ResultMetadataType();
public function ResultMetadataType() {
}
}
}

View file

@ -0,0 +1,150 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* <p>Encapsulates a point of interest in an image containing a barcode. Typically, this
* would be the location of a finder pattern or the corner of the barcode, for example.</p>
*
* @author Sean Owen
*/
import com.google.zxing.common.flexdatatypes.StringBuilder;
public class ResultPoint
{
private var x:Number;
private var y:Number;
public function ResultPoint(x:Number, y:Number)
{
this.x = x;
this.y = y;
}
public final function getX():Number
{
return x;
}
public final function getY():Number
{
return y;
}
public function equals(other:Object ):Boolean
{
if (other is ResultPoint)
{
var otherPoint:ResultPoint = other as ResultPoint;
return x == otherPoint.x && y == otherPoint.y;
}
return false;
}
/* no default method to determine a hashcode for Number in Actionscript
public function hashCode():int
{
return 31 * identityHashCode(x) + identityHashCode(y);
}
*/
public function toString():String
{
var result:StringBuilder = new StringBuilder(25);
result.Append('(');
result.Append(x);
result.Append(',');
result.Append(y);
result.Append(')');
return result.toString();
}
/**
* <p>Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and
* BC < AC and the angle between BC and BA is less than 180 degrees.
*/
public static function orderBestPatterns(patterns:Array):void
{
// Find distances between pattern centers
var zeroOneDistance:Number = distance(patterns[0], patterns[1]);
var oneTwoDistance:Number = distance(patterns[1], patterns[2]);
var zeroTwoDistance:Number = distance(patterns[0], patterns[2]);
var pointA:ResultPoint;
var pointB:ResultPoint;
var pointC:ResultPoint;
// Assume one closest to other two is B; A and C will just be guesses at first
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance)
{
pointB = patterns[0];
pointA = patterns[1];
pointC = patterns[2];
}
else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance)
{
pointB = patterns[1];
pointA = patterns[0];
pointC = patterns[2];
}
else
{
pointB = patterns[2];
pointA = patterns[0];
pointC = patterns[1];
}
// Use cross product to figure out whether A and C are correct or flipped.
// This asks whether BC x BA has a positive z component, which is the arrangement
// we want for A, B, C. If it's negative, then we've got it flipped around and
// should swap A and C.
if (crossProductZ(pointA, pointB, pointC) < 0.0) {
var temp:ResultPoint = pointA;
pointA = pointC;
pointC = temp;
}
patterns[0] = pointA;
patterns[1] = pointB;
patterns[2] = pointC;
}
/**
* @return distance between two points
*/
public static function distance(pattern1:ResultPoint, pattern2:ResultPoint):Number
{
var xDiff:Number = pattern1.getX() - pattern2.getX();
var yDiff:Number = pattern1.getY() - pattern2.getY();
return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
}
/**
* Returns the z component of the cross product between vectors BC and BA.
*/
public static function crossProductZ(pointA:ResultPoint, pointB:ResultPoint, pointC:ResultPoint):Number
{
var bX:Number = pointB.x;
var bY:Number = pointB.y;
return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
}
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* The base class for all objects which encode/generate a barcode image.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public interface Writer
{
import com.google.zxing.common.ByteMatrix;
import com.google.zxing.common.flexdatatypes.HashTable;
/**
*
* @param contents The contents to encode in the barcode
* @param format The barcode format to generate
* @param width The preferred width in pixels
* @param height The preferred height in pixels
* @param hints Additional parameters to supply to the encoder
* @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
*/
function encode(contents:String, format:BarcodeFormat=null, width:int=0, height:int=0, hints:HashTable=null):Object
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing
{
/**
* A base class which covers the range of exceptions which may occur when encoding a barcode using
* the Writer framework.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public class WriterException extends Error
{
public function WriterException(message:String=undefined)
{
super(message);
}
}
}

View file

@ -0,0 +1,43 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>See
* <a href="http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/about/s2.html">
* DoCoMo's documentation</a> about the result types represented by subclasses of this class.</p>
*
* <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
* on exception-based mechanisms during parsing.</p>
*
* @author Sean Owen
*/
public class AbstractDoCoMoResultParser extends ResultParser
{
public static function matchDoCoMoPrefixedField(prefix:String , rawText:String, trim:Boolean):Array {
return matchPrefixedField(prefix, rawText, ';', trim);
}
public static function matchSingleDoCoMoPrefixedField(prefix:String, rawText:String, trim:Boolean):String {
return matchSinglePrefixedField(prefix, rawText, ';', trim);
}
}
}

View file

@ -0,0 +1,74 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.Result;
/**
* Implements KDDI AU's address book format. See
* <a href="http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html">
* http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html</a>.
* (Thanks to Yuzo for translating!)
*
* @author Sean Owen
*/
public final class AddressBookDocomoResultParser extends ResultParser {
public static function parse(result:Result ):AddressBookParsedResult {
var rawText:String = result.getText();
// MEMORY is mandatory; seems like a decent indicator, as does end-of-record separator CR/LF
if (rawText == null || rawText.indexOf("MEMORY") < 0 || rawText.indexOf("\r\n") < 0) {
return null;
}
// NAME1 and NAME2 have specific uses, namely written name and pronunciation, respectively.
// Therefore we treat them specially instead of as an array of names.
var name:String = matchSinglePrefixedField("NAME1:", rawText, '\r', true);
var pronunciation:String = matchSinglePrefixedField("NAME2:", rawText, '\r', true);
var phoneNumbers:Array = matchMultipleValuePrefix("TEL", 3, rawText, true);
var emails:Array = matchMultipleValuePrefix("MAIL", 3, rawText, true);
var note:String = matchSinglePrefixedField("MEMORY:", rawText, '\r', false);
var address:String = matchSinglePrefixedField("ADD:", rawText, '\r', true);
return new AddressBookParsedResult(maybeWrap(name), pronunciation, phoneNumbers, emails, note,
address, null, null, null, null);
}
public static function matchMultipleValuePrefix(prefix:String , max:int , rawText:String , trim:Boolean ):Array
{
var values:ArrayList = null;
for (var i:int = 1; i <= max; i++)
{
var value:String = matchSinglePrefixedField(prefix + i + ':', rawText, '\r', trim);
if (value == null) {
break;
}
if (values == null) {
values = new ArrayList(max); // lazy init
}
values.addElement(value);
}
if (values == null) {
return null;
}
return toStringArray(values);
}
}
}

View file

@ -0,0 +1,127 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.common.flexdatatypes.StringBuilder;
/**
* @author Sean Owen
*/
public final class AddressBookParsedResult extends ParsedResult
{
private var names:Array;
private var pronunciation:String;
private var phoneNumbers:Array;
private var emails:Array;
private var note:String;
private var address:String;
private var org:String;
private var birthday:String;
private var title:String;
private var url:String;
public function AddressBookParsedResult(names:Array,
pronunciation:String,
phoneNumbers:Array,
emails:Array,
note:String,
address:String,
org:String,
birthday:String,
title:String,
url:String) {
super(ParsedResultType.ADDRESSBOOK);
this.names = names;
this.pronunciation = pronunciation;
this.phoneNumbers = phoneNumbers;
this.emails = emails;
this.note = note;
this.address = address;
this.org = org;
this.birthday = birthday;
this.title = title;
this.url = url;
}
public function getNames():Array {
return names;
}
/**
* In Japanese, the name is written in kanji, which can have multiple readings. Therefore a hint
* is often provided, called furigana, which spells the name phonetically.
*
* @return The pronunciation of the getNames() field, often in hiragana or katakana.
*/
public function getPronunciation():String {
return pronunciation;
}
public function getPhoneNumbers():Array {
return phoneNumbers;
}
public function getEmails():Array {
return emails;
}
public function getNote():String {
return note;
}
public function getAddress():String {
return address;
}
public function getTitle():String {
return title;
}
public function getOrg():String {
return org;
}
public function getURL():String {
return url;
}
/**
* @return birthday formatted as yyyyMMdd (e.g. 19780917)
*/
public function getBirthday():String {
return birthday;
}
public override function getDisplayResult():String {
var result:StringBuilder = new StringBuilder();
maybeAppend(names, result);
maybeAppend(pronunciation, result);
maybeAppend(title, result);
maybeAppend(org, result);
maybeAppend(address, result);
maybeAppend(phoneNumbers, result);
maybeAppend(emails, result);
maybeAppend(url, result);
maybeAppend(birthday, result);
maybeAppend(note, result);
return result.toString();
}
}
}

View file

@ -0,0 +1,98 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.Result;
/**
* Implements the "BIZCARD" address book entry format, though this has been
* largely reverse-engineered from examples observed in the wild -- still
* looking for a definitive reference.
*
* @author Sean Owen
*/
public final class BizcardResultParser extends AbstractDoCoMoResultParser
{
// Yes, we extend AbstractDoCoMoResultParser since the format is very much
// like the DoCoMo MECARD format, but this is not technically one of
// DoCoMo's proposed formats
public static function parse(result:Result):AddressBookParsedResult {
var rawText:String = result.getText();
if (rawText == null || !(rawText.substr(0,8) == "BIZCARD:")) {
return null;
}
var firstName:String = matchSingleDoCoMoPrefixedField("N:", rawText, true);
var lastName:String = matchSingleDoCoMoPrefixedField("X:", rawText, true);
var fullName:String = buildName(firstName, lastName);
var title:String = matchSingleDoCoMoPrefixedField("T:", rawText, true);
var org:String = matchSingleDoCoMoPrefixedField("C:", rawText, true);
var address:String = matchSingleDoCoMoPrefixedField("A:", rawText, true);
var phoneNumber1:String = matchSingleDoCoMoPrefixedField("B:", rawText, true);
var phoneNumber2:String = matchSingleDoCoMoPrefixedField("M:", rawText, true);
var phoneNumber3:String = matchSingleDoCoMoPrefixedField("F:", rawText, true);
var email:String = matchSingleDoCoMoPrefixedField("E:", rawText, true);
return new AddressBookParsedResult(maybeWrap(fullName),
null,
buildPhoneNumbers(phoneNumber1, phoneNumber2, phoneNumber3),
maybeWrap(email),
null,
address,
org,
null,
title,
null);
}
private static function buildPhoneNumbers(number1:String, number2:String, number3:String ):Array {
var numbers:ArrayList = new ArrayList(3);
if (number1 != null) {
numbers.addElement(number1);
}
if (number2 != null) {
numbers.addElement(number2);
}
if (number3 != null) {
numbers.addElement(number3);
}
var size:int = numbers.size();
if (size == 0) {
return null;
}
var result:Array = new Array(size);
for (var i:int = 0; i < size; i++) {
result[i] = String(numbers.elementAt(i));
}
return result;
}
private static function buildName(firstName:String, lastName:String):String
{
if (firstName == null) {
return lastName;
} else {
return lastName == null ? firstName : firstName + ' ' + lastName;
}
}
}
}

View file

@ -0,0 +1,49 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
/**
* @author Sean Owen
*/
public final class BookmarkDoCoMoResultParser extends AbstractDoCoMoResultParser
{
public function BookmarkDoCoMoResultParser()
{
}
public static function parse(result:Result):URIParsedResult {
var rawText:String = result.getText();
if (rawText == null || (rawText.substr(0,6) != "MEBKM:")) {
return null;
}
var title:String = matchSingleDoCoMoPrefixedField("TITLE:", rawText, true);
var rawUri:Array = matchDoCoMoPrefixedField("URL:", rawText, true);
if (rawUri == null) {
return null;
}
var uri:String = rawUri[0];
if (!URIResultParser.isBasicallyValidURI(uri)) {
return null;
}
return new URIParsedResult(uri, title);
}
}
}

View file

@ -0,0 +1,143 @@
package com.google.zxing.client.result
{
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.common.flexdatatypes.Utils;
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Sean Owen
*/
public final class CalendarParsedResult extends ParsedResult
{
private var summary:String;
private var start:String;
private var end:String;
private var location:String;
private var attendee:String;
private var title:String;
public function CalendarParsedResult(summary:String,
start:String,
end:String,
location:String,
attendee:String,
title:String)
{
super(ParsedResultType.CALENDAR);
// Start is required, end is not
if (start == null) {
throw new IllegalArgumentException();
}
validateDate(start);
validateDate(end);
this.summary = summary;
this.start = start;
this.end = end;
this.location = location;
this.attendee = attendee;
this.title = title;
}
public function getSummary():String
{
return summary;
}
/**
* <p>We would return the start and end date as a {@link java.util.Date} except that this code
* needs to work under JavaME / MIDP and there is no date parsing library available there, such
* as <code>java.text.SimpleDateFormat</code>.</p> See validateDate() for the return format.
*
* @return start time formatted as a RFC 2445 DATE or DATE-TIME.</p>
*/
public function getStart():String
{
return start;
}
/**
* @see #getStart(). May return null if the event has no duration.
*/
public function getEnd():String
{
return end;
}
public function getLocation():String
{
return location;
}
public function getAttendee():String {
return attendee;
}
public function getTitle():String {
return title;
}
public override function getDisplayResult():String {
var result:StringBuilder = new StringBuilder();
maybeAppend(summary, result);
maybeAppend(start, result);
maybeAppend(end, result);
maybeAppend(location, result);
maybeAppend(attendee, result);
maybeAppend(title, result);
return result.toString();
}
/**
* RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021) or DATE-TIME
* (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC).
*
* @param date The string to validate
*/
private static function validateDate( date:String):void {
if (date != null) {
var length:int = date.length;
if (length != 8 && length != 15 && length != 16) {
throw new IllegalArgumentException();
}
for (var i:int = 0; i < 8; i++) {
if (!Utils.isDigit(date.charAt(i))) {
throw new IllegalArgumentException();
}
}
if (length > 8) {
if (date.charAt(8) != 'T') {
throw new IllegalArgumentException();
}
for (var ii:int = 9; ii < 15; i++) {
if (!Utils.isDigit(date.charAt(ii))) {
throw new IllegalArgumentException();
}
}
if (length == 16 && date.charAt(15) != 'Z') {
throw new IllegalArgumentException();
}
}
}
}
}
}

View file

@ -0,0 +1,71 @@
package com.google.zxing.client.result
{
import com.google.zxing.common.flexdatatypes.StringBuilder;
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Sean Owen
*/
public final class EmailAddressParsedResult extends ParsedResult
{
private var emailAddress:String;
private var subject:String;
private var body:String;
private var mailtoURI:String;
public function EmailAddressParsedResult(emailAddress:String, subject:String, body:String , mailtoURI:String )
{
super(ParsedResultType.EMAIL_ADDRESS);
this.emailAddress = emailAddress;
this.subject = subject;
this.body = body;
this.mailtoURI = mailtoURI;
}
public function getEmailAddress():String
{
return emailAddress;
}
public function getSubject():String
{
return subject;
}
public function getBody():String
{
return body;
}
public function getMailtoURI():String
{
return mailtoURI;
}
public override function getDisplayResult():String
{
var result:StringBuilder = new StringBuilder();
maybeAppend(emailAddress, result);
maybeAppend(subject, result);
maybeAppend(body, result);
return result.toString();
}
}}

View file

@ -0,0 +1,62 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.HashTable;
/**
* Represents a result that encodes an e-mail address, either as a plain address
* like "joe@example.org" or a mailto: URL like "mailto:joe@example.org".
*
* @author Sean Owen
*/
public final class EmailAddressResultParser extends ResultParser {
public static function parse(result:Result):EmailAddressParsedResult {
var rawText:String = result.getText();
if (rawText == null) {
return null;
}
var emailAddress:String;
if ((rawText.substr(0,7) == "mailto:") || (rawText.substr(0,7) == "MAILTO:")) {
// If it starts with mailto:, assume it is definitely trying to be an email address
emailAddress = rawText.substring(7);
var queryStart:int = emailAddress.indexOf('?');
if (queryStart >= 0) {
emailAddress = emailAddress.substring(0, queryStart);
}
var nameValues:HashTable = parseNameValuePairs(rawText);
var subject:String = null;
var body:String = null;
if (nameValues != null) {
if (emailAddress.length == 0) {
emailAddress = String( nameValues._get("to"));
}
subject = String(nameValues._get("subject"));
body = String(nameValues._get("body"));
}
return new EmailAddressParsedResult(emailAddress, subject, body, rawText);
} else {
if (!EmailDoCoMoResultParser.isBasicallyValidEmailAddress(rawText)) {
return null;
}
emailAddress = rawText;
return new EmailAddressParsedResult(emailAddress, null, null, "mailto:" + emailAddress);
}
}
}}

View file

@ -0,0 +1,88 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* Implements the "MATMSG" email message entry format.
*
* Supported keys: TO, SUB, BODY
*
* @author Sean Owen
*/
public final class EmailDoCoMoResultParser extends AbstractDoCoMoResultParser {
private static var ATEXT_SYMBOLS:Array = ['@','.','!','#','$','%','&','\'','*','+','-','/','=','?','^','_','`','{','|','}','~'];
public static function parse(result:Result):EmailAddressParsedResult {
var rawText:String = result.getText();
if (rawText == null || !Utils.startsWith(rawText,"MATMSG:")) {
return null;
}
var rawTo:Array = matchDoCoMoPrefixedField("TO:", rawText, true);
if (rawTo == null) {
return null;
}
var _to:String = rawTo[0];
if (!isBasicallyValidEmailAddress(_to)) {
return null;
}
var subject:String = matchSingleDoCoMoPrefixedField("SUB:", rawText, false);
var body:String = matchSingleDoCoMoPrefixedField("BODY:", rawText, false);
return new EmailAddressParsedResult(_to, subject, body, "mailto:" + _to);
}
/**
* This implements only the most basic checking for an email address's validity -- that it contains
* an '@' contains no characters disallowed by RFC 2822. This is an overly lenient definition of
* validity. We want to generally be lenient here since this class is only intended to encapsulate what's
* in a barcode, not "judge" it.
*/
public static function isBasicallyValidEmailAddress(email:String):Boolean {
if (email == null) {
return false;
}
var atFound:Boolean = false;
for (var i:int = 0; i < email.length; i++) {
var c:String = email.charAt(i);
if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') &&
!isAtextSymbol(c)) {
return false;
}
if (c == '@') {
if (atFound) {
return false;
}
atFound = true;
}
}
return atFound;
}
private static function isAtextSymbol(c:String):Boolean {
for (var i:int = 0; i < ATEXT_SYMBOLS.length; i++) {
if (c == ATEXT_SYMBOLS[i]) {
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,109 @@
package com.google.zxing.client.result
{
import com.google.zxing.common.flexdatatypes.StringBuilder;
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Sean Owen
*/
public final class GeoParsedResult extends ParsedResult {
private var geoURI:String;
private var latitude:Number;
private var longitude:Number;
private var altitude:Number;
public function GeoParsedResult(geoURI:String, latitude:Number, longitude:Number, altitude:Number ) {
super(ParsedResultType.GEO);
this.geoURI = geoURI;
this.latitude = latitude;
this.longitude = longitude;
this.altitude = altitude;
}
public function getGeoURI():String {
return geoURI;
}
/**
* @return latitude in degrees
*/
public function getLatitude():Number {
return latitude;
}
/**
* @return longitude in degrees
*/
public function getLongitude():Number {
return longitude;
}
/**
* @return altitude in meters. If not specified, in the geo URI, returns 0.0
*/
public function getAltitude():Number {
return altitude;
}
public override function getDisplayResult():String {
var result:StringBuilder = new StringBuilder(50);
result.Append(latitude);
result.Append(", ");
result.Append(longitude);
if (altitude > 0.0) {
result.Append(", ");
result.Append(altitude);
result.Append('m');
}
return result.toString();
}
/**
* @return a URI link to Google Maps which display the point on the Earth described
* by this instance, and sets the zoom level in a way that roughly reflects the
* altitude, if specified
*/
/*
public String getGoogleMapsURI() {
StringBuffer result = new StringBuffer(50);
result.append("http://maps.google.com/?ll=");
result.append(latitude);
result.append(',');
result.append(longitude);
if (altitude > 0.0f) {
// Map altitude to zoom level, cleverly. Roughly, zoom level 19 is like a
// view from 1000ft, 18 is like 2000ft, 17 like 4000ft, and so on.
double altitudeInFeet = altitude * 3.28;
int altitudeInKFeet = (int) (altitudeInFeet / 1000.0);
// No Math.log() available here, so compute log base 2 the old fashioned way
// Here logBaseTwo will take on a value between 0 and 18 actually
int logBaseTwo = 0;
while (altitudeInKFeet > 1 && logBaseTwo < 18) {
altitudeInKFeet >>= 1;
logBaseTwo++;
}
int zoom = 19 - logBaseTwo;
result.append("&z=");
result.append(zoom);
}
return result.toString();
}
*/
}}

View file

@ -0,0 +1,65 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
/**
* Parses a "geo:" URI result, which specifices a location on the surface of
* the Earth as well as an optional altitude above the surface. See
* <a href="http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00">
* http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00</a>.
*
* @author Sean Owen
*/
public final class GeoResultParser extends ResultParser {
public function GeoResultParser() {
}
public static function parse(result:Result ):GeoParsedResult {
var rawText:String = result.getText();
if (rawText == null || (!(rawText.substr(0,4) == "geo:") && !(rawText.substr(0,4) == "GEO:"))) {
return null;
}
// Drop geo, query portion
var queryStart:int = rawText.indexOf('?', 4);
var geoURIWithoutQuery:String = queryStart < 0 ? rawText.substring(4) : rawText.substring(4, queryStart);
var latitudeEnd:int = geoURIWithoutQuery.indexOf(',');
if (latitudeEnd < 0) {
return null;
}
var longitudeEnd:int = geoURIWithoutQuery.indexOf(',', latitudeEnd + 1);
var latitude:Number, longitude:Number, altitude:Number;
try {
latitude = Number(geoURIWithoutQuery.substring(0, latitudeEnd));
if (longitudeEnd < 0) {
longitude = Number(geoURIWithoutQuery.substring(latitudeEnd + 1));
altitude = 0.0;
} else {
longitude = Number(geoURIWithoutQuery.substring(latitudeEnd + 1, longitudeEnd));
altitude = Number(geoURIWithoutQuery.substring(longitudeEnd + 1));
}
} catch (nfe:Error) {
return null;
}
return new GeoParsedResult((rawText.substr(0,4) == "GEO:") ? "geo:" + rawText.substring(4) : rawText,
latitude, longitude, altitude);
}
}
}

View file

@ -0,0 +1,42 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author jbreiden@google.com (Jeff Breidenbach)
*/
public final class ISBNParsedResult extends ParsedResult {
private var isbn:String;
public function ISBNParsedResult(isbn:String) {
super(ParsedResultType.ISBN);
this.isbn = isbn;
}
public function getISBN():String {
return isbn;
}
public override function getDisplayResult():String {
return isbn;
}
}
}

View file

@ -0,0 +1,58 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* Parses strings of digits that represent a ISBN.
*
* @author jbreiden@google.com (Jeff Breidenbach)
*/
public class ISBNResultParser extends ResultParser {
public function ISBNResultParser() {
}
// ISBN-13 For Dummies
// http://www.bisg.org/isbn-13/for.dummies.html
public static function parse(result:Result):ISBNParsedResult
{
var format:BarcodeFormat = result.getBarcodeFormat();
if (BarcodeFormat.EAN_13 != format) {
return null;
}
var rawText:String = result.getText();
if (rawText == null) {
return null;
}
var length:int = rawText.length;
if (length != 13) {
return null;
}
if (!Utils.startsWith(rawText,"978") && !Utils.startsWith(rawText,"979")) {
return null;
}
return new ISBNParsedResult(rawText);
}
}
}

View file

@ -0,0 +1,69 @@
package com.google.zxing.client.result
{/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.common.flexdatatypes.StringBuilder;
/**
* <p>Abstract class representing the result of decoding a barcode, as more than
* a String -- as some type of structured data. This might be a subclass which represents
* a URL, or an e-mail address. {@link ResultParser#parseResult(Result)} will turn a raw
* decoded string into the most appropriate type of structured representation.</p>
*
* <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
* on exception-based mechanisms during parsing.</p>
*
* @author Sean Owen
*/
public class ParsedResult {
private var type:ParsedResultType;
public function ParsedResult(type:ParsedResultType ) {
this.type = type;
}
public function getType():ParsedResultType {
return type;
}
public function getDisplayResult():String{return '';}
public function toString():String
{
return getDisplayResult();
}
public static function maybeAppend(value1:Object, result:StringBuilder ):void
{
var value:Array;
if (value1 is Array) { value = value1 as Array; }
else if (value1 is String) { value = [value1]; }
if (value != null) {
for (var i:int = 0; i < value.length; i++) {
if (value[i] != null && value[i].length > 0) {
if (result.length > 0) {
result.Append('\n');
}
result.Append(value[i]);
}
}
}
}
}
}

View file

@ -0,0 +1,53 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Represents the type of data encoded by a barcode -- from plain text, to a
* URI, to an e-mail address, etc.
*
* @author Sean Owen
*/
public final class ParsedResultType {
public static var ADDRESSBOOK:ParsedResultType = new ParsedResultType("ADDRESSBOOK");
public static var EMAIL_ADDRESS:ParsedResultType = new ParsedResultType("EMAIL_ADDRESS");
public static var PRODUCT:ParsedResultType = new ParsedResultType("PRODUCT");
public static var URI:ParsedResultType = new ParsedResultType("URI");
public static var TEXT:ParsedResultType = new ParsedResultType("TEXT");
public static var ANDROID_INTENT:ParsedResultType = new ParsedResultType("ANDROID_INTENT");
public static var GEO:ParsedResultType = new ParsedResultType("GEO");
public static var TEL:ParsedResultType = new ParsedResultType("TEL");
public static var SMS:ParsedResultType = new ParsedResultType("SMS");
public static var CALENDAR:ParsedResultType = new ParsedResultType("CALENDAR");
// "optional" types
public static var NDEF_SMART_POSTER:ParsedResultType = new ParsedResultType("NDEF_SMART_POSTER");
public static var MOBILETAG_RICH_WEB:ParsedResultType = new ParsedResultType("MOBILETAG_RICH_WEB");
public static var ISBN:ParsedResultType = new ParsedResultType("ISBN");
private var name:String;
public function ParsedResultType(name:String) {
this.name = name;
}
public function toString():String {
return name;
}
}
}

View file

@ -0,0 +1,58 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ProductParsedResult extends ParsedResult {
public var productID:String;
public var normalizedProductID:String;
/*
public function ProductParsedResult(productID:String)
{
this(productID, productID);
}
*/
public function ProductParsedResult(productID:String, normalizedProductID:String='')
{
if (normalizedProductID == '')
{
normalizedProductID = productID
}
super(ParsedResultType.PRODUCT);
this.productID = productID;
this.normalizedProductID = normalizedProductID;
}
public function getProductID():String {
return productID;
}
public function getNormalizedProductID():String {
return normalizedProductID;
}
public override function getDisplayResult():String {
return productID;
}
}
}

View file

@ -0,0 +1,67 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.oned.UPCEReader;
/**
* Parses strings of digits that repesent a UPC code.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ProductResultParser extends ResultParser {
public function ProductResultParser() {
}
// Treat all UPC and EAN variants as UPCs, in the sense that they are all product barcodes.
public static function parse(result:Result):ProductParsedResult {
var format:BarcodeFormat = result.getBarcodeFormat();
if (!((BarcodeFormat.UPC_A == format) || (BarcodeFormat.UPC_E == format) ||
(BarcodeFormat.EAN_8 == format) || (BarcodeFormat.EAN_13 == format))) {
return null;
}
// Really neither of these should happen:
var rawText:String = result.getText();
if (rawText == null) {
return null;
}
var length:int = rawText.length;
for (var x:int = 0; x < length; x++) {
var c:int = rawText.charCodeAt(x);//.charAt(x);
if (c < ('0').charCodeAt(0) || c > ('9').charCodeAt(0)) {
return null;
}
}
// Not actually checking the checksum again here
var normalizedProductID:String;
// Expand UPC-E for purposes of searching
if (BarcodeFormat.UPC_E == format) {
normalizedProductID = UPCEReader.convertUPCEtoUPCA(rawText);
} else {
normalizedProductID = rawText;
}
return new ProductParsedResult(rawText, normalizedProductID);
}
}}

View file

@ -0,0 +1,292 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.Utils;
import mx.utils.StringUtil;
/**
* <p>Abstract class representing the result of decoding a barcode, as more than
* a String -- as some type of structured data. This might be a subclass which represents
* a URL, or an e-mail address. {@link #parseResult(com.google.zxing.Result)} will turn a raw
* decoded string into the most appropriate type of structured representation.</p>
*
* <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
* on exception-based mechanisms during parsing.</p>
*
* @author Sean Owen
*/
public class ResultParser {
public static function parseResult(theResult:Result):ParsedResult {
// This is a bit messy, but given limited options in MIDP / CLDC, this may well be the simplest
// way to go about this. For example, we have no reflection available, really.
// Order is important here.
var result:ParsedResult;
if ((result = BookmarkDoCoMoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = AddressBookDocomoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = EmailDoCoMoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = AddressBookDocomoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = VCardResultParser.parse(theResult)) != null) {
return result;
} else if ((result = BizcardResultParser.parse(theResult)) != null) {
return result;
} else if ((result = VEventResultParser.parse(theResult)) != null) {
return result;
} else if ((result = EmailAddressResultParser.parse(theResult)) != null) {
return result;
} else if ((result = TelResultParser.parse(theResult)) != null) {
return result;
} else if ((result = SMSMMSResultParser.parse(theResult)) != null) {
return result;
} else if ((result = GeoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = URLTOResultParser.parse(theResult)) != null) {
return result;
} else if ((result = URIResultParser.parse(theResult)) != null) {
return result;
} else if ((result = ISBNResultParser.parse(theResult)) != null) {
// We depend on ISBN parsing coming before UPC, as it is a subset.
return result;
} else if ((result = ProductResultParser.parse(theResult)) != null) {
return result;
}
return new TextParsedResult(theResult.getText(), null);
}
protected static function maybeAppend(value:String, result:StringBuilder):void {
if (value != null) {
result.Append('\n');
result.Append(value);
}
}
protected static function maybeWrap(value:String ):Array {
return value == null ? null : [ value ];
}
protected static function unescapeBackslash(escaped:String):String {
if (escaped != null) {
var backslash:int = escaped.indexOf('\\');
if (backslash >= 0) {
var max:int = escaped.length;
var unescaped:StringBuilder = new StringBuilder(max - 1);
unescaped.Append(escaped.split(""), 0, backslash);
var nextIsEscaped:Boolean = false;
for (var i:int = backslash; i < max; i++) {
var c:String = escaped.charAt(i);
if (nextIsEscaped || c != '\\') {
unescaped.Append(c);
nextIsEscaped = false;
} else {
nextIsEscaped = true;
}
}
return unescaped.toString();
}
}
return escaped;
}
public static function urlDecode(escaped:String):String {
// No we can't use java.net.URLDecoder here. JavaME doesn't have it.
if (escaped == null) {
return null;
}
var escapedArray:Array = escaped.split("");
var first:int = findFirstEscape(escapedArray);
if (first < 0) {
return escaped;
}
var max:int = escapedArray.length;
// final length is at most 2 less than original due to at least 1 unescaping
var unescaped:StringBuilder = new StringBuilder(max - 2);
// Can append everything up to first escape character
unescaped.Append(escapedArray, 0, first);
for (var i:int = first; i < max; i++) {
var c:String = escapedArray[i];
if (c == '+') {
// + is translated directly into a space
unescaped.Append(' ');
} else if (c == '%') {
// Are there even two more chars? if not we will just copy the escaped sequence and be done
if (i >= max - 2) {
unescaped.Append('%'); // append that % and move on
} else {
var firstDigitValue:int = parseHexDigit(escapedArray[++i]);
var secondDigitValue:int = parseHexDigit(escapedArray[++i]);
if (firstDigitValue < 0 || secondDigitValue < 0) {
// bad digit, just move on
unescaped.Append('%');
unescaped.Append(escapedArray[i-1]);
unescaped.Append(escapedArray[i]);
}
unescaped.Append(String.fromCharCode((firstDigitValue << 4) + secondDigitValue));
}
} else {
unescaped.Append(c);
}
}
return unescaped.toString();
}
private static function findFirstEscape(escapedArray:Array):int {
var max:int = escapedArray.length;
for (var i:int = 0; i < max; i++) {
var c:String = escapedArray[i];
if (c == '+' || c == '%') {
return i;
}
}
return -1;
}
private static function parseHexDigit(c:String):int {
if (c.charCodeAt(0) >= ('a').charCodeAt(0)) {
if (c.charCodeAt(0) <= ('f').charCodeAt(0)) {
return 10 + (c.charCodeAt(0) - ('a').charCodeAt(0));
}
} else if (c.charCodeAt(0) >= ('A').charCodeAt(0)) {
if (c.charCodeAt(0) <= ('F').charCodeAt(0)) {
return 10 + (c.charCodeAt(0) - ('A').charCodeAt(0));
}
} else if (c.charCodeAt(0) >= ('0').charCodeAt(0)) {
if (c.charCodeAt(0) <= ('9').charCodeAt(0)) {
return c.charCodeAt(0) - ('0').charCodeAt(0);
}
}
return -1;
}
protected static function isStringOfDigits(value:String,length:int):Boolean {
if (value == null) {
return false;
}
var stringLength:int = value.length;
if (length != stringLength) {
return false;
}
for (var i:int = 0; i < length; i++) {
var c:String = value.charAt(i);
if (c < '0' || c > '9') {
return false;
}
}
return true;
}
public static function parseNameValuePairs(uri:String):HashTable {
var paramStart:int = uri.indexOf('?');
if (paramStart < 0) {
return null;
}
var result:HashTable = new HashTable(3);
paramStart++;
var paramEnd:int;
while ((paramEnd = uri.indexOf('&', paramStart)) >= 0) {
appendKeyValue(uri, paramStart, paramEnd, result);
paramStart = paramEnd + 1;
}
appendKeyValue(uri, paramStart, uri.length, result);
return result;
}
private static function appendKeyValue(uri:String , paramStart:int , paramEnd:int, result:HashTable):void {
var separator:int = uri.indexOf('=', paramStart);
if (separator >= 0) {
// key = value
var key:String = uri.substring(paramStart, separator);
var value:String = uri.substring(separator + 1, paramEnd);
value = urlDecode(value);
result._put(key, value);
}
// Can't put key, null into a hashtable
}
public static function matchPrefixedField(prefix:String, rawText:String, endChar:String, trim:Boolean):Array {
var matches:ArrayList = null;
var i:int = 0;
var max:int = rawText.length;
while (i < max) {
i = rawText.indexOf(prefix, i);
if (i < 0) {
break;
}
i += prefix.length; // Skip past this prefix we found to start
var start:int = i; // Found the start of a match here
var done:Boolean = false;
while (!done) {
i = rawText.indexOf( endChar, i);
if (i < 0) {
// No terminating end character? uh, done. Set i such that loop terminates and break
i = rawText.length;
done = true;
} else if (rawText.charAt(i - 1) == '\\') {
// semicolon was escaped so continue
i++;
} else {
// found a match
if (matches == null) {
matches = new ArrayList(3); // lazy init
}
var element:String = unescapeBackslash(rawText.substring(start, i));
if (trim) {
element = StringUtil.trim(element);
}
matches.addElement(element);
i++;
done = true;
}
}
}
if (matches == null || matches.isEmpty()) {
return null;
}
return toStringArray(matches);
}
public static function matchSinglePrefixedField(prefix:String, rawText:String, endChar:String, trim:Boolean ):String
{
var matches:Array = matchPrefixedField(prefix, rawText, endChar, trim);
return matches == null ? null : matches[0];
}
public static function toStringArray(strings:ArrayList):Array {
var size:int = strings.size();
var result:Array = new Array(size);
for (var j:int = 0; j < size; j++) {
result[j] = String(strings.elementAt(j));
}
return result;
}
}
}

View file

@ -0,0 +1,104 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* <p>Parses an "sms:" URI result, which specifies a number to SMS and optional
* "via" number. See <a href="http://gbiv.com/protocols/uri/drafts/draft-antti-gsm-sms-url-04.txt">
* the IETF draft</a> on this.</p>
*
* <p>This actually also parses URIs starting with "mms:", "smsto:", "mmsto:", "SMSTO:", and
* "MMSTO:", and treats them all the same way, and effectively converts them to an "sms:" URI
* for purposes of forwarding to the platform.</p>
*
* @author Sean Owen
*/
public final class SMSMMSResultParser extends ResultParser {
public function SMSMMSResultParser() {
}
public static function parse(result:Result):SMSParsedResult {
var rawText:String = result.getText();
if (rawText == null) {
return null;
}
var prefixLength:int;
if (Utils.startsWith(rawText,"sms:") || Utils.startsWith(rawText,"SMS:") ||
Utils.startsWith(rawText,"mms:") || Utils.startsWith(rawText,"MMS:")) {
prefixLength = 4;
} else if (Utils.startsWith(rawText,"smsto:") || Utils.startsWith(rawText,"SMSTO:") ||
Utils.startsWith(rawText,"mmsto:") || Utils.startsWith(rawText,"MMSTO:")) {
prefixLength = 6;
} else {
return null;
}
// Check up front if this is a URI syntax string with query arguments
var nameValuePairs:HashTable = parseNameValuePairs(rawText);
var subject:String = null;
var body:String = null;
var querySyntax:Boolean = false;
if (nameValuePairs != null && !nameValuePairs.isEmpty()) {
subject = String(nameValuePairs._get("subject"));
body = String( nameValuePairs._get("body"));
querySyntax = true;
}
// Drop sms, query portion
var queryStart:int = rawText.indexOf('?', prefixLength);
var smsURIWithoutQuery:String;
// If it's not query syntax, the question mark is part of the subject or message
if (queryStart < 0 || !querySyntax) {
smsURIWithoutQuery = rawText.substring(prefixLength);
} else {
smsURIWithoutQuery = rawText.substring(prefixLength, queryStart);
}
var numberEnd:int = smsURIWithoutQuery.indexOf(';');
var number:String;
var via:String;
if (numberEnd < 0) {
number = smsURIWithoutQuery;
via = null;
} else {
number = smsURIWithoutQuery.substring(0, numberEnd);
var maybeVia:String = smsURIWithoutQuery.substring(numberEnd + 1);
if (Utils.startsWith(maybeVia,"via=")) {
via = maybeVia.substring(4);
} else {
via = null;
}
}
// Thanks to dominik.wild for suggesting this enhancement to support
// smsto:number:body URIs
if (body == null) {
var bodyStart:int = number.indexOf(':');
if (bodyStart >= 0) {
body = number.substring(bodyStart + 1);
number = number.substring(0, bodyStart);
}
}
return new SMSParsedResult("sms:" + number, number, via, subject, body, null);
}
}}

View file

@ -0,0 +1,77 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.common.flexdatatypes.StringBuilder;
/**
* @author Sean Owen
*/
public final class SMSParsedResult extends ParsedResult {
private var smsURI:String;
private var number:String;
private var via:String;
private var subject:String;
private var body:String;
private var title:String;
public function SMSParsedResult(smsURI:String ,number:String ,via:String,subject:String, body:String, title:String) {
super(ParsedResultType.SMS);
this.smsURI = smsURI;
this.number = number;
this.via = via;
this.subject = subject;
this.body = body;
this.title = title;
}
public function getSMSURI():String {
return smsURI;
}
public function getNumber():String {
return number;
}
public function getVia():String {
return via;
}
public function getSubject():String {
return subject;
}
public function getBody():String {
return body;
}
public function getTitle():String {
return title;
}
public override function getDisplayResult():String {
var result:StringBuilder = new StringBuilder();
maybeAppend(number, result);
maybeAppend(via, result);
maybeAppend(subject, result);
maybeAppend(body, result);
maybeAppend(title, result);
return result.toString();
}
}
}

View file

@ -0,0 +1,54 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.common.flexdatatypes.StringBuilder;
/**
* @author Sean Owen
*/
public final class TelParsedResult extends ParsedResult {
private var number:String;
private var telURI:String;
private var title:String;
public function TelParsedResult(number:String, telURI:String,title:String) {
super(ParsedResultType.TEL);
this.number = number;
this.telURI = telURI;
this.title = title;
}
public function getNumber():String {
return number;
}
public function getTelURI():String {
return telURI;
}
public function getTitle():String {
return title;
}
public override function getDisplayResult():String {
var result:StringBuilder = new StringBuilder();
maybeAppend(number, result);
maybeAppend(title, result);
return result.toString();
}
}}

View file

@ -0,0 +1,46 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* Parses a "tel:" URI result, which specifies a phone number.
*
* @author Sean Owen
*/
public final class TelResultParser extends ResultParser {
public function TelResultParser() {
}
public static function parse(result:Result):TelParsedResult {
var rawText:String = result.getText();
if (rawText == null || (!Utils.startsWith(rawText,"tel:") && !Utils.startsWith(rawText,"TEL:"))) {
return null;
}
// Normalize "TEL:" to "tel:"
var telURI:String = Utils.startsWith(rawText,"TEL:") ? "tel:" + rawText.substring(4) : rawText;
// Drop tel, query portion
var queryStart:int = rawText.indexOf('?', 4);
var number:String = queryStart < 0 ? rawText.substring(4) : rawText.substring(4, queryStart);
return new TelParsedResult(number, telURI, null);
}
}}

View file

@ -0,0 +1,51 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A simple result type encapsulating a string that has no further
* interpretation.
*
* @author Sean Owen
*/
public final class TextParsedResult extends ParsedResult {
private var text:String;
private var language:String;
public function TextParsedResult(text:String, language:String) {
super(ParsedResultType.TEXT);
this.text = text;
this.language = language;
}
public function getText():String {
return text;
}
public function getLanguage():String {
return language;
}
public override function getDisplayResult():String {
return text;
}
}
}

View file

@ -0,0 +1,114 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.common.flexdatatypes.StringBuilder;
/**
* @author Sean Owen
*/
public final class URIParsedResult extends ParsedResult {
private var uri:String;
private var title:String;
public function URIParsedResult(uri:String,title:String) {
super(ParsedResultType.URI);
this.uri = massageURI(uri);
this.title = title;
}
public function getURI():String {
return uri;
}
public function getTitle():String {
return title;
}
/**
* @return true if the URI contains suspicious patterns that may suggest it intends to
* mislead the user about its true nature. At the moment this looks for the presence
* of user/password syntax in the host/authority portion of a URI which may be used
* in attempts to make the URI's host appear to be other than it is. Example:
* http://yourbank.com@phisher.com This URI connects to phisher.com but may appear
* to connect to yourbank.com at first glance.
*/
public function isPossiblyMaliciousURI():Boolean {
return containsUser();
}
private function containsUser():Boolean {
// This method is likely not 100% RFC compliant yet
var hostStart:int = uri.indexOf(':'); // we should always have scheme at this point
hostStart++;
// Skip slashes preceding host
var uriLength:int = uri.length;
while (hostStart < uriLength && uri.charAt(hostStart) == '/') {
hostStart++;
}
var hostEnd:int = uri.indexOf('/', hostStart);
if (hostEnd < 0) {
hostEnd = uriLength;
}
var at:int = uri.indexOf('@', hostStart);
return at >= hostStart && at < hostEnd;
}
public override function getDisplayResult():String {
var result:StringBuilder = new StringBuilder();
maybeAppend(title, result);
maybeAppend(uri, result);
return result.toString();
}
/**
* Transforms a string that represents a URI into something more proper, by adding or canonicalizing
* the protocol.
*/
private static function massageURI(uri:String):String {
var protocolEnd:int = uri.indexOf(':');
if (protocolEnd < 0) {
// No protocol, assume http
uri = "http://" + uri;
} else if (isColonFollowedByPortNumber(uri, protocolEnd)) {
// Found a colon, but it looks like it is after the host, so the protocol is still missing
uri = "http://" + uri;
} else {
// Lowercase protocol to avoid problems
uri = uri.substring(0, protocolEnd).toLowerCase() + uri.substring(protocolEnd);
}
return uri;
}
private static function isColonFollowedByPortNumber(uri:String, protocolEnd:int):Boolean {
var nextSlash:int = uri.indexOf('/', protocolEnd + 1);
if (nextSlash < 0) {
nextSlash = uri.length;
}
if (nextSlash <= protocolEnd + 1) {
return false;
}
for (var x:int = protocolEnd + 1; x < nextSlash; x++) {
if (uri.charAt(x) < '0' || uri.charAt(x) > '9') {
return false;
}
}
return true;
}
}
}

View file

@ -0,0 +1,60 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* Tries to parse results that are a URI of some kind.
*
* @author Sean Owen
*/
public final class URIResultParser extends ResultParser {
public function URIResultParser() {
}
public static function parse(result:Result):URIParsedResult {
var rawText:String = result.getText();
// We specifically handle the odd "URL" scheme here for simplicity
if (rawText != null && Utils.startsWith(rawText,"URL:")) {
rawText = rawText.substring(4);
}
if (!isBasicallyValidURI(rawText)) {
return null;
}
return new URIParsedResult(rawText, null);
}
/**
* Determines whether a string is not obviously not a URI. This implements crude checks; this class does not
* intend to strictly check URIs as its only function is to represent what is in a barcode, but, it does
* need to know when a string is obviously not a URI.
*/
public static function isBasicallyValidURI(uri:String):Boolean {
if (uri == null || uri.indexOf(' ') >= 0 || uri.indexOf('\n') >= 0) {
return false;
}
var period:int = uri.indexOf('.');
// Look for period in a domain but followed by at least a two-char TLD
return period < uri.length - 2 && (period >= 0 || uri.indexOf(':') >= 0);
}
}
}

View file

@ -0,0 +1,48 @@
package com.google.zxing.client.result
{
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* Parses the "URLTO" result format, which is of the form "URLTO:[title]:[url]".
* This seems to be used sometimes, but I am not able to find documentation
* on its origin or official format?
*
* @author Sean Owen
*/
public final class URLTOResultParser {
public function URLTOResultParser() {
}
public static function parse(result:Result):URIParsedResult {
var rawText:String = result.getText();
if (rawText == null || (!Utils.startsWith(rawText,"urlto:") && !Utils.startsWith(rawText,"URLTO:"))) {
return null;
}
var titleEnd:int = rawText.indexOf(':', 6);
if (titleEnd < 0) {
return null;
}
var title:String = titleEnd <= 6 ? null : rawText.substring(6, titleEnd);
var uri:String = rawText.substring(titleEnd + 1);
return new URIParsedResult(uri, title);
}
}
}

View file

@ -0,0 +1,173 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.Utils;
import mx.utils.StringUtil;
/**
* Parses contact information formatted according to the VCard (2.1) format. This is not a complete
* implementation but should parse information as commonly encoded in 2D barcodes.
*
* @author Sean Owen
*/
public final class VCardResultParser extends ResultParser {
public function VCardResultParser() {
}
public static function parse(result:Result):AddressBookParsedResult {
var rawText:String = result.getText();
if (rawText == null || (Utils.startsWith(rawText,"BEGIN:VCARD")) || !Utils.endsWith(rawText,"END:VCARD")) {
return null;
}
var names:Array = matchVCardPrefixedField("FN", rawText, true);
if (names == null) {
// If no display names found, look for regular name fields and format them
names = matchVCardPrefixedField("N", rawText, true);
formatNames(names);
}
var phoneNumbers:Array = matchVCardPrefixedField("TEL", rawText, true);
var emails:Array = matchVCardPrefixedField("EMAIL", rawText, true);
var note:String = matchSingleVCardPrefixedField("NOTE", rawText, false);
var address:String = matchSingleVCardPrefixedField("ADR", rawText, true);
address = formatAddress(address);
var org:String = matchSingleVCardPrefixedField("ORG", rawText, true);
var birthday:String = matchSingleVCardPrefixedField("BDAY", rawText, true);
if (birthday != null && !isStringOfDigits(birthday, 8)) {
return null;
}
var title:String = matchSingleVCardPrefixedField("TITLE", rawText, true);
var url:String = matchSingleVCardPrefixedField("URL", rawText, true);
return new AddressBookParsedResult(names, null, phoneNumbers, emails, note, address, org,
birthday, title, url);
}
private static function matchVCardPrefixedField(prefix:String, rawText:String, trim:Boolean):Array {
var matches:ArrayList = null;
var i:int = 0;
var max:int = rawText.length;
while (i < max) {
i = rawText.indexOf(prefix, i);
if (i < 0) {
break;
}
if (i > 0 && rawText.charAt(i - 1) != '\n') {
// then this didn't start a new token, we matched in the middle of something
i++;
continue;
}
i += prefix.length; // Skip past this prefix we found to start
if (rawText.charAt(i) != ':' && rawText.charAt(i) != ';') {
continue;
}
while (rawText.charAt(i) != ':') { // Skip until a colon
i++;
}
i++; // skip colon
var start:int = i; // Found the start of a match here
i = rawText.indexOf('\n', i); // Really, ends in \r\n
if (i < 0) {
// No terminating end character? uh, done. Set i such that loop terminates and break
i = max;
} else if (i > start) {
// found a match
if (matches == null) {
matches = new ArrayList(3); // lazy init
}
var element:String = rawText.substring(start, i);
if (trim) {
element = StringUtil.trim(element);
}
matches.addElement(element);
i++;
} else {
i++;
}
}
if (matches == null || matches.isEmpty()) {
return null;
}
return toStringArray(matches);
}
public static function matchSingleVCardPrefixedField(prefix:String , rawText:String , trim:Boolean):String {
var values:Array = matchVCardPrefixedField(prefix, rawText, trim);
return values == null ? null : values[0];
}
private static function formatAddress(address:String):String {
if (address == null) {
return null;
}
var length:int = address.length;
var newAddress:StringBuilder = new StringBuilder(length);
for (var j:int = 0; j < length; j++) {
var c:String = address.charAt(j);
if (c == ';') {
newAddress.Append(' ');
} else {
newAddress.Append(c);
}
}
return StringUtil.trim(newAddress.toString());
}
/**
* Formats name fields of the form "Public;John;Q.;Reverend;III" into a form like
* "Reverend John Q. Public III".
*
* @param names name values to format, in place
*/
private static function formatNames(names:Array):void {
if (names != null) {
for (var i:int = 0; i < names.length; i++) {
var name:String = names[i];
var components:Array = new Array(5);
var start:int = 0;
var end:int;
var componentIndex:int = 0;
while ((end = name.indexOf(';', start)) > 0) {
components[componentIndex] = name.substring(start, end);
componentIndex++;
start = end + 1;
}
components[componentIndex] = name.substring(start);
var newName:StringBuilder = new StringBuilder();
maybeAppendComponent(components, 3, newName);
maybeAppendComponent(components, 1, newName);
maybeAppendComponent(components, 2, newName);
maybeAppendComponent(components, 0, newName);
maybeAppendComponent(components, 4, newName);
names[i] = StringUtil.trim(newName.toString());
}
}
}
private static function maybeAppendComponent(components:Array,i:int, newName:StringBuilder):void {
if (components[i] != null) {
newName.Append(' ');
newName.Append(components[i]);
}
}
}
}

View file

@ -0,0 +1,59 @@
package com.google.zxing.client.result
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
/**
* Partially implements the iCalendar format's "VEVENT" format for specifying a
* calendar event. See RFC 2445. This supports SUMMARY, DTSTART and DTEND fields.
*
* @author Sean Owen
*/
public final class VEventResultParser extends ResultParser {
public function VEventResultParser() {
}
public static function parse(result:Result):CalendarParsedResult {
var rawText:String = result.getText();
if (rawText == null) {
return null;
}
var vEventStart:int = rawText.indexOf("BEGIN:VEVENT");
if (vEventStart < 0) {
return null;
}
var vEventEnd:int = rawText.indexOf("END:VEVENT");
if (vEventEnd < 0) {
return null;
}
var summary:String = VCardResultParser.matchSingleVCardPrefixedField("SUMMARY", rawText, true);
var start:String = VCardResultParser.matchSingleVCardPrefixedField("DTSTART", rawText, true);
var end:String = VCardResultParser.matchSingleVCardPrefixedField("DTEND", rawText, true);
try {
return new CalendarParsedResult(summary, start, end, null, null, null);
} catch (iae:IllegalArgumentException) {
}
return null;
}
}
}

View file

@ -0,0 +1,83 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.client.result.ResultParser;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* <p>Superclass for classes encapsulating reader results encoded according
* to the MobileTag Reader International Specification.</p>
*
* @author Sean Owen
*/
public class AbstractMobileTagResultParser extends ResultParser {
public static var ACTION_DO:int = 1;
public static var ACTION_EDIT:int = 2;
public static var ACTION_SAVE:int = 4;
public static function matchDelimitedFields(rawText:String, maxItems:int):Array {
var result:Array = new Array(maxItems);
var item:int = 0;
var i:int = 0;
var max:int = rawText.length;
while (item < maxItems && i < max) {
var start:int = i; // Found the start of a match here
var done:Boolean = false;
while (!done) {
i = rawText.indexOf('|', i);
if (i < 0) {
// No terminating end character? done. Set i such that loop terminates and break
i = rawText.length;
done = true;
} else if (rawText.charAt(i - 1) == '\\') {
// semicolon was escaped so continue
i++;
} else {
// found a match
if (start != i) {
result[item] = unescapeBackslash(rawText.substring(start, i));
}
item++;
i++;
done = true;
}
}
}
if (item < maxItems) {
return null;
}
return result;
}
public static function isDigits(s:String, expectedLength:int):Boolean
{
if (s == null) {
return true;
}
if (s.length != expectedLength) {
return false;
}
for (var i:int = 0; i < expectedLength; i++) {
if (!Utils.isDigit(s.charAt(i))) {
return false;
}
}
return true;
}
}}

View file

@ -0,0 +1,47 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.client.result.ResultParser;
/**
* <p>Superclass for classes encapsulating results in the NDEF format.
* See <a href="http://www.nfc-forum.org/specs/">http://www.nfc-forum.org/specs/</a>.</p>
*
* <p>This code supports a limited subset of NDEF messages, ones that are plausibly
* useful in 2D barcode formats. This generally includes 1-record messages, no chunking,
* "short record" syntax, no ID field.</p>
*
* @author Sean Owen
*/
public class AbstractNDEFResultParser extends ResultParser {
public static function bytesToString(bytes:Array, offset:int, length:int, encoding:String):String {
try {
// can't do this in Actionscript
//return new String(bytes, offset, length, encoding);
throw new Error("Platform does not support encoding");
} catch (uee:Error) {
// This should only be used when 'encoding' is an encoding that must necessarily
// be supported by the JVM, like UTF-8
throw new Error("Platform does not support required encoding: " + uee);
}
throw new Error("Platform does not support encoding");
}
}}

View file

@ -0,0 +1,74 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ResultParser;
import com.google.zxing.client.result.AddressBookParsedResult;
/**
* Implements KDDI AU's address book format. See
* <a href="http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html">
* http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html</a>.
* (Thanks to Yuzo for translating!)
*
* @author Sean Owen
*/
public final class AddressBookAUResultParser extends ResultParser {
public static function parse(result:Result):AddressBookParsedResult {
var rawText:String = result.getText();
// MEMORY is mandatory; seems like a decent indicator, as does end-of-record separator CR/LF
if (rawText == null || rawText.indexOf("MEMORY") < 0 || rawText.indexOf("\r\n") < 0) {
return null;
}
// NAME1 and NAME2 have specific uses, namely written name and pronunciation, respectively.
// Therefore we treat them specially instead of as an array of names.
var name:String = matchSinglePrefixedField("NAME1:", rawText, '\r', true);
var pronunciation:String = matchSinglePrefixedField("NAME2:", rawText, '\r', true);
var phoneNumbers:Array = matchMultipleValuePrefix("TEL", 3, rawText, true);
var emails:Array = matchMultipleValuePrefix("MAIL", 3, rawText, true);
var note:String = matchSinglePrefixedField("MEMORY:", rawText, '\r', false);
var address:String = matchSinglePrefixedField("ADD:", rawText, '\r', true);
return new AddressBookParsedResult(maybeWrap(name), pronunciation, phoneNumbers, emails, note,
address, null, null, null, null);
}
private static function matchMultipleValuePrefix(prefix:String, max:int, rawText:String, trim:Boolean):Array {
var values:ArrayList = null;
for (var i:int = 1; i <= max; i++) {
var value:String = matchSinglePrefixedField(prefix + i + ':', rawText, '\r', trim);
if (value == null) {
break;
}
if (values == null) {
values = new ArrayList(max); // lazy init
}
values.addElement(value);
}
if (values == null) {
return null;
}
return toStringArray(values);
}
}
}

View file

@ -0,0 +1,54 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.client.result.SMSParsedResult;
/**
* <p>Represents a "MMS" result encoded according to section 4.7 of the
* MobileTag Reader International Specification.</p>
*
* @author Sean Owen
*/
public final class MobileTagMMSResultParser extends AbstractMobileTagResultParser {
public static var SERVICE_TYPE:String = "05";
public static function parse(result:Result):SMSParsedResult {
if (result.getBarcodeFormat() != BarcodeFormat.DATAMATRIX) {
return null;
}
var rawText:String = result.getText();
if (rawText.substr(0,(SERVICE_TYPE).length) != SERVICE_TYPE) {
return null;
}
var matches:Array = matchDelimitedFields(rawText.substring(2), 4);
if (matches == null) {
return null;
}
var _to:String = matches[0];
var subject:String = matches[1];
var body:String = matches[2];
var title:String = matches[3];
return new SMSParsedResult("sms:" + _to, _to, null, subject, body, title);
}
}}

View file

@ -0,0 +1,63 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ParsedResultType;
/**
* @author Sean Owen
*/
public final class MobileTagRichWebParsedResult extends ParsedResult {
// Example: "http://www.tagserver.com/script.asp?id="
public static var TAGSERVER_URI_PREFIX:String;// = System.getProperty("zxing.mobiletag.tagserver");
private var id:String;
private var action:int;
public function MobileTagRichWebParsedResult(id:String, action:int, tagserver:String)
{
super(ParsedResultType.MOBILETAG_RICH_WEB);
this.id = id;
this.action = action;
MobileTagRichWebParsedResult.TAGSERVER_URI_PREFIX = tagserver;
}
public static function getTagserverURIPrefix():String {
return MobileTagRichWebParsedResult.TAGSERVER_URI_PREFIX;
}
public function getId():String {
return id;
}
public function getAction():int {
return action;
}
public function getTagserverURI():String {
return TAGSERVER_URI_PREFIX + id;
}
public override function getDisplayResult():String {
return id;
}
}
}

View file

@ -0,0 +1,64 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.common.flexdatatypes.Utils;
import com.google.zxing.Result;
import com.google.zxing.BarcodeFormat;
/**
* <p>Represents a "rich web" result encoded according to section 5 of the
* MobileTag Reader International Specification.</p>
*
* @author Sean Owen
*/
public final class MobileTagRichWebResultParser extends AbstractMobileTagResultParser {
public static var SERVICE_TYPE:String = "54";
private static var DEFAULT_ACTION:int = AbstractMobileTagResultParser.ACTION_DO;
public static function parse(result:Result,tagserver:String):MobileTagRichWebParsedResult {
if (MobileTagRichWebParsedResult.TAGSERVER_URI_PREFIX == null) {
return null;
}
if (result.getBarcodeFormat() != BarcodeFormat.DATAMATRIX) {
return null;
}
var rawText:String = result.getText();
if (!Utils.startsWith(rawText,SERVICE_TYPE)) {
return null;
}
var length:int = rawText.length;
if (!isDigits(rawText, length)) {
return null;
}
var action:int;
var id:String;
if (length == 15) {
action = DEFAULT_ACTION;
id = rawText.substring(0, 2) + action + rawText.substring(2);
} else if (length == 16) {
action = rawText.charCodeAt(2) - ('0').charCodeAt(0);
id = rawText;
} else {
return null;
}
return new MobileTagRichWebParsedResult(id, action,tagserver);
}
}}

View file

@ -0,0 +1,55 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.client.result.SMSParsedResult;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* <p>Represents a "SMS" result encoded according to section 4.6 of the
* MobileTag Reader International Specification.</p>
*
* @author Sean Owen
*/
public final class MobileTagSMSResultParser extends AbstractMobileTagResultParser {
public static var SERVICE_TYPE:String = "03";
public static function parse(result:Result):SMSParsedResult {
if (result.getBarcodeFormat() != BarcodeFormat.DATAMATRIX) {
return null;
}
var rawText:String = result.getText();
if (!Utils.startsWith(rawText,SERVICE_TYPE)) {
return null;
}
var matches:Array = matchDelimitedFields(rawText.substring(2), 3);
if (matches == null) {
return null;
}
var _to:String = matches[0];
var body:String = matches[1];
var title:String = matches[2];
return new SMSParsedResult("sms:" + _to, _to, null, null, body, title);
}
}
}

View file

@ -0,0 +1,70 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.client.result.CalendarParsedResult;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* <p>Represents a "simple calendar" result encoded according to section 4.9 of the
* MobileTag Reader International Specification.</p>
*
* @author Sean Owen
*/
public final class MobileTagSimpleCalendarResultParser extends AbstractMobileTagResultParser {
public static var SERVICE_TYPE:String = "07";
public static function parse(result:Result):CalendarParsedResult {
if (result.getBarcodeFormat() != BarcodeFormat.DATAMATRIX) {
return null;
}
var rawText:String = result.getText();
if (!Utils.startsWith(rawText,SERVICE_TYPE)) {
return null;
}
var matches:Array = matchDelimitedFields(rawText.substring(2), 6);
if (matches == null || !isDigits(matches[1], 10) || !isDigits(matches[2], 10)) {
return null;
}
var summary:String = matches[0];
var start:String = expandDateString(matches[1]);
var end:String = expandDateString(matches[2]);
var location:String = matches[3];
var attendee:String = matches[4];
var title:String = matches[5];
try {
return new CalendarParsedResult(summary, start, end, location, attendee, title);
} catch ( iae:IllegalArgumentException) {
}
return null;
}
private static function expandDateString(date:String):String {
if (date == null) {
return null;
}
// Input is of form YYMMddHHmmss, and needs to be YYYYMMdd'T'HHmmss'Z'
return "20" + date.substring(0, 6) + 'T' + date.substring(6) + "00Z";
}
}}

View file

@ -0,0 +1,71 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.client.result.AddressBookParsedResult;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* <p>Represents a "simple contact" result encoded according to section 4.8 of the
* MobileTag Reader International Specification.</p>
*
* @author Sean Owen
*/
public final class MobileTagSimpleContactResultParser extends AbstractMobileTagResultParser {
public static var SERVICE_TYPE:String = "02";
public static function parse(result:Result):AddressBookParsedResult {
if (result.getBarcodeFormat() != BarcodeFormat.DATAMATRIX) {
return null;
}
var rawText:String = result.getText();
if (!Utils.startsWith(rawText,SERVICE_TYPE)) {
return null;
}
var matches:Array = matchDelimitedFields(rawText.substring(2), 9);
if (matches == null || !isDigits(matches[7], 8)) {
return null;
}
var fullName:String = matches[0];
var telephoneCell:String = matches[1];
var telephone:String = matches[2];
var email1:String = matches[3];
var email2:String = matches[4];
var address:String = matches[5];
var org:String = matches[6];
var birthday:String = matches[7];
if (!isStringOfDigits(birthday, 8)) {
return null;
}
var title:String = matches[8];
return new AddressBookParsedResult([fullName],
null,
[telephoneCell, telephone],
[email1, email2],
null,
address,
org,
birthday,
title,
null);
}
}
}

View file

@ -0,0 +1,73 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.client.result.URIParsedResult;
import com.google.zxing.common.flexdatatypes.Utils;
import com.google.zxing.Result;
import com.google.zxing.BarcodeFormat;
/**
* <p>Represents a "simple web" result encoded according to section 4.11 of the
* MobileTag Reader International Specification.</p>
*
* @author Sean Owen
*/
public final class MobileTagSimpleWebResultParser extends AbstractMobileTagResultParser {
public static var SERVICE_TYPE:String = "04";
private static var URI_PREFIXES:Array = [
null,
"http://",
"http://www.",
"https://",
"https://www.",
"rtsp://",
];
public static function parse(result:Result):URIParsedResult {
if (result.getBarcodeFormat() != BarcodeFormat.DATAMATRIX) {
return null;
}
var rawText:String = result.getText();
if (!Utils.startsWith(rawText,SERVICE_TYPE)) {
return null;
}
var matches:Array = matchDelimitedFields(rawText.substring(2), 2);
if (matches == null) {
return null;
}
var uri:String = matches[0];
var title:String = matches[1];
var maybePrefixChar:String = uri.charAt(2);
if (maybePrefixChar >= '0' && maybePrefixChar <= '9') {
var prefixIndex:int = (maybePrefixChar).charCodeAt(0) - ('0').charCodeAt(0);
// Note that '0' is reserved
if (prefixIndex >= 1 && prefixIndex < URI_PREFIXES.length) {
uri = URI_PREFIXES[prefixIndex] + uri.substring(1);
} else {
uri = uri.substring(1);
}
}
return new URIParsedResult(uri, title);
}
}
}

View file

@ -0,0 +1,53 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.client.result.TelParsedResult;
import com.google.zxing.common.flexdatatypes.Utils;
/**
* <p>Represents a "TEL" result encoded according to section 4.4 of the
* MobileTag Reader International Specification.</p>
*
* @author Sean Owen
*/
public final class MobileTagTelResultParser extends AbstractMobileTagResultParser {
public static var SERVICE_TYPE:String = "01";
public static function parse(result:Result):TelParsedResult {
if (result.getBarcodeFormat() != BarcodeFormat.DATAMATRIX) {
return null;
}
var rawText:String = result.getText();
if (!Utils.startsWith(rawText,SERVICE_TYPE)) {
return null;
}
var matches:Array = matchDelimitedFields(rawText.substring(2), 2);
if (matches == null) {
return null;
}
var number:String = matches[0];
var title:String = matches[1];
return new TelParsedResult(number, "tel:" + number, title);
}
}
}

View file

@ -0,0 +1,90 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>Represents a record in an NDEF message. This class only supports certain types
* of records -- namely, non-chunked records, where ID length is omitted, and only
* "short records".</p>
*
* @author Sean Owen
*/
import com.google.zxing.common.flexdatatypes.Utils;
public final class NDEFRecord {
private static var SUPPORTED_HEADER_MASK:int = 0x3F; // 0 0 1 1 1 111 (the bottom 6 bits matter)
private static var SUPPORTED_HEADER:int = 0x11; // 0 0 0 1 0 001
public static var TEXT_WELL_KNOWN_TYPE:String = "T";
public static var URI_WELL_KNOWN_TYPE:String = "U";
public static var SMART_POSTER_WELL_KNOWN_TYPE:String = "Sp";
public static var ACTION_WELL_KNOWN_TYPE:String = "act";
private var header:int;
private var type:String;
private var payload:Array;
private var totalRecordLength:int;
public function NDEFRecord(header:int, type:String, payload:Array, totalRecordLength:int) {
this.header = header;
this.type = type;
this.payload = payload;
this.totalRecordLength = totalRecordLength;
}
public static function readRecord(bytes:Array, offset:int):NDEFRecord {
var header:int = bytes[offset] & 0xFF;
// Does header match what we support in the bits we care about?
// XOR figures out where we differ, and if any of those are in the mask, fail
if (((header ^ SUPPORTED_HEADER) & SUPPORTED_HEADER_MASK) != 0) {
return null;
}
var typeLength:int = bytes[offset + 1] & 0xFF;
var payloadLength:int = bytes[offset + 2] & 0xFF;
var type:String = AbstractNDEFResultParser.bytesToString(bytes, offset + 3, typeLength, "US-ASCII");
var payload:Array = new Array(payloadLength);
Utils.arraycopy(bytes, offset + 3 + typeLength, payload, 0, payloadLength);
return new NDEFRecord(header, type, payload, 3 + typeLength + payloadLength);
}
public function isMessageBegin():Boolean {
return (header & 0x80) != 0;
}
public function isMessageEnd():Boolean {
return (header & 0x40) != 0;
}
public function getType():String {
return type;
}
public function getPayload():Array {
return payload;
}
public function getTotalRecordLength():int {
return totalRecordLength;
}
}
}

View file

@ -0,0 +1,65 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.client.result.ParsedResultType;
import com.google.zxing.client.result.ParsedResult;
/**
* @author Sean Owen
*/
public final class NDEFSmartPosterParsedResult extends ParsedResult
{
public static var ACTION_UNSPECIFIED:int = -1;
public static var ACTION_DO:int = 0;
public static var ACTION_SAVE:int = 1;
public static var ACTION_OPEN:int = 2;
private var title:String;
private var uri:String;
private var action:int;
public function NDEFSmartPosterParsedResult(action:int, uri:String, title:String) {
super(ParsedResultType.NDEF_SMART_POSTER);
this.action = action;
this.uri = uri;
this.title = title;
}
public function getTitle():String {
return title;
}
public function getURI():String {
return uri;
}
public function getAction():int {
return action;
}
public override function getDisplayResult():String {
if (title == null) {
return uri;
} else {
return title + '\n' + uri;
}
}
}
}

View file

@ -0,0 +1,82 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
/**
* <p>Recognizes an NDEF message that encodes information according to the
* "Smart Poster Record Type Definition" specification.</p>
*
* <p>This actually only supports some parts of the Smart Poster format: title,
* URI, and action records. Icon records are not supported because the size
* of these records are infeasibly large for barcodes. Size and type records
* are not supported. Multiple titles are not supported.</p>
*
* @author Sean Owen
*/
public final class NDEFSmartPosterResultParser extends AbstractNDEFResultParser {
public static function parse(result:Result):NDEFSmartPosterParsedResult {
var bytes:Array = result.getRawBytes();
if (bytes == null) {
return null;
}
var headerRecord:NDEFRecord = NDEFRecord.readRecord(bytes, 0);
// Yes, header record starts and ends a message
if (headerRecord == null || !headerRecord.isMessageBegin() || !headerRecord.isMessageEnd()) {
return null;
}
if (headerRecord.getType() != NDEFRecord.SMART_POSTER_WELL_KNOWN_TYPE) {
return null;
}
var offset:int = 0;
var recordNumber:int = 0;
var ndefRecord:NDEFRecord = null;
var payload:Array = headerRecord.getPayload();
var action:int = NDEFSmartPosterParsedResult.ACTION_UNSPECIFIED;
var title:String = null;
var uri:String = null;
while (offset < payload.length && (ndefRecord = NDEFRecord.readRecord(payload, offset)) != null) {
if (recordNumber == 0 && !ndefRecord.isMessageBegin()) {
return null;
}
var type:String = ndefRecord.getType();
if (NDEFRecord.TEXT_WELL_KNOWN_TYPE == type) {
var languageText:Array = NDEFTextResultParser.decodeTextPayload(ndefRecord.getPayload());
title = languageText[1];
} else if (NDEFRecord.URI_WELL_KNOWN_TYPE == type) {
uri = NDEFURIResultParser.decodeURIPayload(ndefRecord.getPayload());
} else if (NDEFRecord.ACTION_WELL_KNOWN_TYPE == type) {
action = ndefRecord.getPayload()[0];
}
recordNumber++;
offset += ndefRecord.getTotalRecordLength();
}
if (recordNumber == 0 || (ndefRecord != null && !ndefRecord.isMessageEnd())) {
return null;
}
return new NDEFSmartPosterParsedResult(action, uri, title);
}
}
}

View file

@ -0,0 +1,57 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.client.result.TextParsedResult;
import com.google.zxing.Result;
/**
* Recognizes an NDEF message that encodes text according to the
* "Text Record Type Definition" specification.
*
* @author Sean Owen
*/
public final class NDEFTextResultParser extends AbstractNDEFResultParser {
public static function parse(result:Result ):TextParsedResult {
var bytes:Array = result.getRawBytes();
if (bytes == null) {
return null;
}
var ndefRecord:NDEFRecord = NDEFRecord.readRecord(bytes, 0);
if (ndefRecord == null || !ndefRecord.isMessageBegin() || !ndefRecord.isMessageEnd()) {
return null;
}
if (ndefRecord.getType() != NDEFRecord.TEXT_WELL_KNOWN_TYPE) {
return null;
}
var languageText:Array = decodeTextPayload(ndefRecord.getPayload());
return new TextParsedResult(languageText[0], languageText[1]);
}
public static function decodeTextPayload(payload:Array):Array {
var statusByte:int = payload[0];
var isUTF16:Boolean = (statusByte & 0x80) != 0;
var languageLength:int = statusByte & 0x1F;
// language is always ASCII-encoded:
var language:String = bytesToString(payload, 1, languageLength, "US-ASCII");
var encoding:String = isUTF16 ? "UTF-16" : "UTF8";
var text:String = bytesToString(payload, 1 + languageLength, payload.length - languageLength - 1, encoding);
return new [language, text ];
}
}
}

View file

@ -0,0 +1,96 @@
package com.google.zxing.client.result.optional
{
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.google.zxing.Result;
import com.google.zxing.client.result.URIParsedResult;
/**
* Recognizes an NDEF message that encodes a URI according to the
* "URI Record Type Definition" specification.
*
* @author Sean Owen
*/
public final class NDEFURIResultParser extends AbstractNDEFResultParser {
private static var URI_PREFIXES:Array = [
null,
"http://www.",
"https://www.",
"http://",
"https://",
"tel:",
"mailto:",
"ftp://anonymous:anonymous@",
"ftp://ftp.",
"ftps://",
"sftp://",
"smb://",
"nfs://",
"ftp://",
"dav://",
"news:",
"telnet://",
"imap:",
"rtsp://",
"urn:",
"pop:",
"sip:",
"sips:",
"tftp:",
"btspp://",
"btl2cap://",
"btgoep://",
"tcpobex://",
"irdaobex://",
"file://",
"urn:epc:id:",
"urn:epc:tag:",
"urn:epc:pat:",
"urn:epc:raw:",
"urn:epc:",
"urn:nfc:",
];
public static function parse(result:Result ):URIParsedResult {
var bytes:Array = result.getRawBytes();
if (bytes == null) {
return null;
}
var ndefRecord:NDEFRecord = NDEFRecord.readRecord(bytes, 0);
if (ndefRecord == null || !ndefRecord.isMessageBegin() || !ndefRecord.isMessageEnd()) {
return null;
}
if (ndefRecord.getType() != NDEFRecord.URI_WELL_KNOWN_TYPE) {
return null;
}
var fullURI:String = decodeURIPayload(ndefRecord.getPayload());
return new URIParsedResult(fullURI, null);
}
public static function decodeURIPayload(payload:Array):String {
var identifierCode:int = payload[0] & 0xFF;
var prefix:String = null;
if (identifierCode < URI_PREFIXES.length) {
prefix = URI_PREFIXES[identifierCode];
}
var restOfURI:String = bytesToString(payload, 1, payload.length - 1, "UTF8");
return prefix == null ? restOfURI : prefix + restOfURI;
}
}
}

View file

@ -0,0 +1,188 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
*
* @author Sean Owen
*/
public class BitArray
{
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
// TODO: I have changed these members to be public so ProGuard can inline get() and set(). Ideally
// they'd be private and we'd use the -allowaccessmodification flag, but Dalvik rejects the
// resulting binary at runtime on Android. If we find a solution to this, these should be changed
// back to private.
public var bits:Array;
public var Size:int;
public function BitArray(size:int) {
if (size < 1) {
throw new IllegalArgumentException("common : BitArray : size must be at least 1");
}
this.Size = size;
this.bits = makeArray(size);
}
public function getSize():int {
return Size;
}
/**
* @param i bit to get
* @return true iff bit i is set
*/
public function _get(i:int):Boolean {
return (bits[i >> 5] & (1 << (i & 0x1F))) != 0;
}
/**
* Sets bit i.
*
* @param i bit to set
*/
public function _set(i:int):void {
bits[i >> 5] |= 1 << (i & 0x1F);
}
/**
* Flips bit i.
*
* @param i bit to set
*/
public function flip(i:int):void {
bits[i >> 5] ^= 1 << (i & 0x1F);
}
/**
* Sets a block of 32 bits, starting at bit i.
*
* @param i first bit to set
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
* corresponds to bit i, the next-least-significant to i+1, and so on.
*/
public function setBulk(i:int, newBits:int):void {
bits[i >> 5] = newBits;
}
/**
* Clears all bits (sets to false).
*/
public function clear():void {
var max:int = bits.length;
for (var i:int = 0; i < max; i++) {
bits[i] = 0;
}
}
/**
* Efficient method to check if a range of bits is set, or not set.
*
* @param start start of range, inclusive.
* @param end end of range, exclusive
* @param value if true, checks that bits in range are set, otherwise checks that they are not set
* @return true iff all bits are set or not set in range, according to value argument
* @throws IllegalArgumentException if end is less than or equal to start
*/
public function isRange(start:int, end:int, value:Boolean):Boolean {
if (end < start) {
throw new IllegalArgumentException("common : BitArray isRange : end before start");
}
if (end == start) {
return true; // empty range matches
}
end--; // will be easier to treat this as the last actually set bit -- inclusive
var firstInt:int = start >> 5;
var lastInt:int = end >> 5;
for (var i:int = firstInt; i <= lastInt; i++) {
var firstBit:int = i > firstInt ? 0 : start & 0x1F;
var lastBit:int = i < lastInt ? 31 : end & 0x1F;
var mask:int;
if (firstBit == 0 && lastBit == 31) {
mask = -1;
} else {
mask = 0;
for (var j:int = firstBit; j <= lastBit; j++) {
mask |= 1 << j;
}
}
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
if ((bits[i] & mask) != (value ? mask : 0)) {
return false;
}
}
return true;
}
/**
* @return underlying array of ints. The first element holds the first 32 bits, and the least
* significant bit is bit 0.
*/
public function getBitArray():Array {
return bits;
}
// bas : for debugging purposes
public function setBitArray(a:Array):void {
bits = a;
}
public function setSize(siz:int):void {
Size = siz;
}
/**
* Reverses all bits in the array.
*/
public function reverse():void {
var newBits:Array = makeArray(Size);
var max:int = Size;
for (var i:int = 0; i < max; i++) { newBits[i] = 0; }//Flex : makew
var size:int = this.Size;
for (var ii:int = 0; ii < size; ii++) {
if (this._get(size - ii - 1)) {
newBits[ii >> 5] |= 1 << (ii & 0x1F);
}
}
bits = newBits;
}
private static function makeArray(size:int):Array {
var arraySize:int = size >> 5;
if ((size & 0x1F) != 0) {
arraySize++;
}
return new Array(arraySize);
}
public function toString():String
{
var result:StringBuilder = new StringBuilder(this.Size);
for (var i:int = 0; i < this.Size; i++)
{
if ((i & 0x07) == 0)
{
result.Append(' ');
}
result.Append(_get(i) ? 'X' : '.');
}
return result.ToString();
}
}
}

View file

@ -0,0 +1,202 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
* module, x is the column position, and y is the row position. The ordering is always x, y.
* The origin is at the top-left.</p>
*
* <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
* with a new int. This is done intentionally so that we can copy out a row into a BitArray very
* efficiently.</p>
*
* <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
* meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
*
* @author Sean Owen
* @author dswitkin@google.com (Daniel Switkin)
*/
public class BitMatrix
{
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private var width:int;
private var height:int;
private var rowSize:int;
public var bits:Array;
public function BitMatrix(width:int, o:Object= null)
{
var height:int;
if (o == null)
{
height = width;
}
else if (o is int)
{
height = (o as int);
}
if (width < 1 || height < 1)
{
throw new IllegalArgumentException("common : BitMatrix : Both dimensions must be greater than 0");
}
this.width = width;
this.height = height;
var rowSize:int = width >> 5;
if ((width & 0x1f) != 0)
{
rowSize++;
}
this.rowSize = rowSize;
bits = new Array(rowSize * height);
// BAS : initialize the array
for (var i:int=0;i<bits.length;i++) { bits[i] = 0; }
}
/**
* <p>Gets the requested bit, where true means black.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
* @return value of given bit in matrix
*/
public function _get(x:int, y:int):Boolean {
var offset:int = y * rowSize + (x >> 5);
return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;
}
/**
* <p>Sets the given bit to true.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
*/
public function _set(x:int, y:int):void {
var offset:int = y * rowSize + (x >> 5);
bits[offset] |= 1 << (x & 0x1f);
}
/**
* <p>Flips the given bit.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
*/
public function flip(x:int, y:int):void {
var offset:int = y * rowSize + (x >> 5);
bits[offset] ^= 1 << (x & 0x1f);
}
/**
* Clears all bits (sets to false).
*/
public function clear():void {
var max:int = bits.length;
for (var i:int = 0; i < max; i++) {
bits[i] = 0;
}
}
/**
* <p>Sets a square region of the bit matrix to true.</p>
*
* @param left The horizontal position to begin at (inclusive)
* @param top The vertical position to begin at (inclusive)
* @param width The width of the region
* @param height The height of the region
*/
public function setRegion(left:int,top:int, width:int, height:int):void {
if (top < 0 || left < 0) {
throw new IllegalArgumentException("Common : BitMatrix : setRegion : Left and top must be nonnegative");
}
if (height < 1 || width < 1) {
throw new IllegalArgumentException("Common : BitMatrix : setRegion : Height and width must be at least 1");
}
var right:int = left + width;
var bottom:int = top + height;
if (bottom > this.height || right > this.width) {
throw new IllegalArgumentException("Common : BitMatrix : setRegion : The region must fit inside the matrix");
}
for (var y:int = top; y < bottom; y++) {
var offset:int = y * rowSize;
for (var x:int = left; x < right; x++) {
bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
}
}
}
/**
* A fast method to retrieve one row of data from the matrix as a BitArray.
*
* @param y The row to retrieve
* @param row An optional caller-allocated BitArray, will be allocated if null or too small
* @return The resulting BitArray - this reference should always be used even when passing
* your own row
*/
public function getRow(y:int, row:BitArray ):BitArray {
if (row == null || row.getSize() < width) {
row = new BitArray(width);
}
var offset:int = y * rowSize;
for (var x:int = 0; x < rowSize; x++) {
row.setBulk(x << 5, bits[offset + x]);
}
return row;
}
/**
* @return The width of the matrix
*/
public function getWidth():int {
return width;
}
/**
* @return The height of the matrix
*/
public function getHeight():int {
return height;
}
/**
* This method is for compatibility with older code. It's only logical to call if the matrix
* is square, so I'm throwing if that's not the case.
*
* @return row/column dimension of this matrix
*/
public function getDimension():int {
if (width != height) {
throw new Error("Common : BitMatrix : getDimension :Can't call getDimension() on a non-square matrix");
}
return width;
}
public function toString():String {
var result:StringBuilder = new StringBuilder(height * (width + 1));
for (var y:int = 0; y < height; y++) {
for (var x:int = 0; x < width; x++) {
result.Append(_get(x, y) ? "X " : " ");
}
result.Append('\n');
}
return result.toString();
}
}
}

View file

@ -0,0 +1,90 @@
package com.google.zxing.common
{
/// <summary> A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a
/// unsigned container, it's up to you to do byteValue & 0xff at each location.
/// *
/// JAVAPORT: I'm not happy about the argument ordering throughout the file, as I always like to have
/// the horizontal component first, but this is for compatibility with the C++ code. The original
/// code was a 2D array of ints, but since it only ever gets assigned -1, 0, and 1, I'm going to use
/// less memory and go with bytes.
/// *
/// </summary>
/// <author> dswitkin@google.com (Daniel Switkin)
///
/// </author>
public class BitSource
{
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private var bytes:Array;
private var byteOffset:int;
private var bitOffset:int;
/**
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
* Bits are read within a byte from most-significant to least-significant bit.
*/
public function BitSource( bytes:Array) {
this.bytes = bytes;
}
/**
* @param numBits number of bits to read
* @return int representing the bits read. The bits will appear as the least-significant
* bits of the int
* @throws IllegalArgumentException if numBits isn't in [1,32]
*/
public function readBits(numBits:int):int
{
if (numBits < 1 || numBits > 32)
{
throw new IllegalArgumentException("BitSource : numBits out of range");
}
var result:int = 0;
// First, read remainder from current byte
if (bitOffset > 0) {
var bitsLeft:int = 8 - bitOffset;
var toRead:int = numBits < bitsLeft ? numBits : bitsLeft;
var bitsToNotRead:int = bitsLeft - toRead;
var mask:int = (0xFF >> (8 - toRead)) << bitsToNotRead;
result = (bytes[byteOffset] & mask) >> bitsToNotRead;
numBits -= toRead;
bitOffset += toRead;
if (bitOffset == 8) {
bitOffset = 0;
byteOffset++;
}
}
// Next read whole bytes
if (numBits > 0) {
while (numBits >= 8) {
result = (result << 8) | (bytes[byteOffset] & 0xFF);
byteOffset++;
numBits -= 8;
}
// Finally read a partial byte
if (numBits > 0) {
var bitsToNotRead2:int = 8 - numBits;
var mask2:int = (0xFF >> bitsToNotRead2) << bitsToNotRead2;
result = (result << numBits) | ((bytes[byteOffset] & mask2) >> bitsToNotRead2);
bitOffset += numBits;
}
}
return result;
}
/**
* @return number of bits that can be read successfully
*/
public function available():int {
var bits:int = 8 * (bytes.length - byteOffset) - bitOffset;
return bits;
}
}
}

View file

@ -0,0 +1,133 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a
* unsigned container, it's up to you to do byteValue & 0xff at each location.
*
* JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
* -1, 0, and 1, I'm going to use less memory and go with bytes.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public class ByteMatrix
{
import com.google.zxing.common.flexdatatypes.StringBuilder;
private var bytes:Array;
private var Height:int;
private var Width:int;
public function ByteMatrix(height:int, width:int ) {
bytes = new Array(height);
for (var i:int = 0; i < height; i++) {
bytes[i] = new Array(width);
}
this.Height = height;
this.Width = width;
}
public function height():int
{
return Height;
}
public function width():int
{
return Width;
}
public function _get(x:int, y:int):int
{
return bytes[y][x];
}
public function getArray():Array
{
return bytes;
}
//public function _set(y:int,x:int, value:int)
//{
// bytes[y][x] = value;
//}
public function _set(x:int, y:int, value:Object ):void
{
if (value is int)
{
bytes[y][x] = value as int;
}
else
{
throw new Error('ByteMatrix : _set : unknown type of value');
}
}
public function clear(value:int):void
{
for (var y:int = 0; y < Height; ++y)
{
for (var x:int = 0; x < Width; ++x)
{
bytes[y][x] = value;
}
}
}
public function sum():int
{
var result:int = 0;
for (var y:int = 0; y < Height; ++y)
{
for (var x:int = 0; x < Width; ++x)
{
result += bytes[y][x];
}
}
return result;
}
public function toString():String
{
var result:StringBuilder = new StringBuilder();
for (var y:int = 0; y < Height; ++y)
{
for (var x:int = 0; x < Width; ++x)
{
switch (bytes[y][x])
{
case 0:
result.Append("0");
break;
case 1:
result.Append("1");
break;
default:
result.Append(".");
break;
}
}
result.Append('\n');
}
return result.ToString();
}
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
* of ISO 18004.
*
* @author Sean Owen
*/
public class CharacterSetECI extends ECI
{
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private static var VALUE_TO_ECI:HashTable = new HashTable(29);
private static var NAME_TO_ECI:HashTable = new HashTable(29);
private static function initialize():void {
VALUE_TO_ECI = new HashTable(29);
NAME_TO_ECI = new HashTable(29);
// TODO figure out if these values are even right!
addCharacterSet(0, "Cp437");
addCharacterSet(1, ["ISO8859_1", "ISO-8859-1"]);
addCharacterSet(2, "Cp437");
addCharacterSet(3, ["ISO8859_1", "ISO-8859-1"]);
addCharacterSet(4, "ISO8859_2");
addCharacterSet(5, "ISO8859_3");
addCharacterSet(6, "ISO8859_4");
addCharacterSet(7, "ISO8859_5");
addCharacterSet(8, "ISO8859_6");
addCharacterSet(9, "ISO8859_7");
addCharacterSet(10, "ISO8859_8");
addCharacterSet(11, "ISO8859_9");
addCharacterSet(12, "ISO8859_10");
addCharacterSet(13, "ISO8859_11");
addCharacterSet(15, "ISO8859_13");
addCharacterSet(16, "ISO8859_14");
addCharacterSet(17, "ISO8859_15");
addCharacterSet(18, "ISO8859_16");
addCharacterSet(20, ["SJIS", "Shift_JIS"]);
}
private var encodingName:String;
public function CharacterSetECI(value:int, encodingName:String) {
super(value);
this.encodingName = encodingName;
}
public function getEncodingName():String {
return encodingName;
}
private static function addCharacterSet(value:int, encodingNames:Object):void
{
var eci:CharacterSetECI;
if (encodingNames is String)
{
eci = new CharacterSetECI(value, encodingNames as String);
VALUE_TO_ECI._put(value, eci);
NAME_TO_ECI._put(encodingNames as String, eci);
}
else if (encodingNames is Array)
{
eci = new CharacterSetECI(value, encodingNames[0]);
VALUE_TO_ECI._put(value, eci);
for (var i:int = 0; i < encodingNames.length; i++) { NAME_TO_ECI._put(encodingNames[i], eci);}
}
}
/**
* @param value character set ECI value
* @return {@link CharacterSetECI} representing ECI of given value, or null if it is legal but
* unsupported
* @throws IllegalArgumentException if ECI value is invalid
*/
public static function getCharacterSetECIByValue(value:int):CharacterSetECI {
if (VALUE_TO_ECI == null)
{
initialize();
}
if (value < 0 || value >= 900) {
throw new IllegalArgumentException("COMMON : CharacterSetECI : getCharacterSetECIByValue : Bad ECI value: " + value);
}
return VALUE_TO_ECI.getValueByKey(value) as CharacterSetECI;
}
/**
* @param name character set ECI encoding name
* @return {@link CharacterSetECI} representing ECI for character encoding, or null if it is legal
* but unsupported
*/
public static function getCharacterSetECIByName(name:String ):CharacterSetECI {
if (NAME_TO_ECI == null) {
initialize();
}
return (NAME_TO_ECI.getValueByKey(name) as CharacterSetECI);
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* <p>This is basically a substitute for <code>java.util.Collections</code>, which is not
* present in MIDP 2.0 / CLDC 1.1.</p>
*
* @author Sean Owen
*/
public class Collections
{
import com.google.zxing.common.flexdatatypes.ArrayList;
public function Collections()
{
}
/**
* Sorts its argument (destructively) using insert sort; in the context of this package
* insertion sort is simple and efficient given its relatively small inputs.
*
* @param vector vector to sort
* @param comparator comparator to define sort ordering
*/
public static function insertionSort( vector:ArrayList, comparator:Comparator ):void
{
var max:int = vector.Count;
for (var i:int = 1; i < max; i++)
{
var valueA:Object = vector[i];
var j:int = i - 1;
var valueB:Object;
while (j >= 0 && comparator.compare((valueB = vector[j]), valueA) > 0)
{
vector[j + 1] = valueB;
j--;
}
vector[j + 1] = valueA;
}
}
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* This is merely a clone of <code>Comparator</code> since it is not available in
* CLDC 1.1 / MIDP 2.0.
*/
public interface Comparator
{
function compare(o1:Object, o2:Object):int;
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import mx.controls.List;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.common.flexdatatypes.ArrayList;
/**
* <p>Encapsulates the result of decoding a matrix of bits. This typically
* applies to 2D barcode formats. For now it contains the raw bytes obtained,
* as well as a String interpretation of those bytes, if applicable.</p>
*
* @author Sean Owen
*/ public class DecoderResult
{
private var rawBytes:Array;
private var text:String ;
private var byteSegments:ArrayList;
private var ecLevel:ErrorCorrectionLevel;
public function DecoderResult(rawBytes:Array, text:String, byteSegments:ArrayList, ecLevel:ErrorCorrectionLevel)
{
if (rawBytes == null && text == null)
{
throw new IllegalArgumentException("common : DecoderResult : Constructor : rawBytes array contains no data and text == null");
}
this.rawBytes = rawBytes;
this.text = text;
this.byteSegments = byteSegments;
this.ecLevel = ecLevel
}
public function getRawBytes():Array {
return this.rawBytes;
}
public function getText():String {
return text;
}
public function getByteSegments():ArrayList
{
return byteSegments;
}
public function getECLevel():ErrorCorrectionLevel
{
return ecLevel;
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
import com.google.zxing.ResultPoint;
/**
* <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
* matrix of black/white pixels corresponding to the barcode, and possibly points of interest
* in the image, like the location of finder patterns or corners of the barcode in the image.</p>
*
* @author Sean Owen
*/
public class DetectorResult
{
private var bits:BitMatrix;
private var points:Array;
public function DetectorResult( bits:BitMatrix, points:Array ) {
this.bits = bits;
this.points = points;
}
public function getBits():BitMatrix {
return bits;
}
public function getPoints():Array {
return points;
}
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* Superclass of classes encapsulating types ECIs, according to "Extended Channel Interpretations"
* 5.3 of ISO 18004.
*
* @author Sean Owen
*/
public class ECI
{
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private var value:int;
public function ECI( value:int) {
this.value = value;
}
public function getValue():int {
return value;
}
/**
* @param value ECI value
* @return {@link ECI} representing ECI of given value, or null if it is legal but unsupported
* @throws IllegalArgumentException if ECI value is invalid
*/
public static function getECIByValue(value:int ):ECI {
if (value < 0 || value > 999999) {
throw new IllegalArgumentException("Bad ECI value: " + value);
}
if (value < 900) { // Character set ECIs use 000000 - 000899
return CharacterSetECI.getCharacterSetECIByValue(value);
}
return null;
}
}
}

View file

@ -0,0 +1,209 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
import com.google.zxing.Binarizer;
import com.google.zxing.LuminanceSource;
import com.google.zxing.ReaderException;
/**
* This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
* for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
* algorithm. However, because it picks a global black point, it cannot handle difficult shadows
* and gradients.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
public final class GlobalHistogramBinarizer extends Binarizer {
private static var LUMINANCE_BITS:int = 5;
private static var LUMINANCE_SHIFT:int = 8 - LUMINANCE_BITS;
private static var LUMINANCE_BUCKETS:int = 1 << LUMINANCE_BITS;
private var luminances:Array = null;
private var buckets:Array = null;
public function GlobalHistogramBinarizer(source:LuminanceSource ) {
super(source);
}
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
public override function getBlackRow(y:int , row:BitArray ):BitArray {
var source:LuminanceSource = getLuminanceSource();
var width:int = source.getWidth();
if (row == null || row.getSize() < width) {
row = new BitArray(width);
} else {
row.clear();
}
initArrays(width);
var _localLuminances:Array = source.getRow(y, luminances);
var localBuckets:Array = buckets;
for (var x2:int = 0; x2 < width; x2++)
{
var pixel:int = _localLuminances[x2] & 0xff;
localBuckets[pixel >> LUMINANCE_SHIFT]++;
}
var blackPoint:int = estimateBlackPoint(localBuckets);
var left:int = _localLuminances[0] & 0xff;
var center:int = _localLuminances[1] & 0xff;
for (var x:int = 1; x < width - 1; x++)
{
var right:int = _localLuminances[x + 1] & 0xff;
// A simple -1 4 -1 box filter with a weight of 2.
var luminance:int = ((center << 2) - left - right) >> 1;
if (luminance < blackPoint) {
row._set(x);
}
left = center;
center = right;
}
return row;
}
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
public override function getBlackMatrix():BitMatrix {
var source:LuminanceSource = getLuminanceSource();
var width:int = source.getWidth();
var height:int = source.getHeight();
var matrix:BitMatrix = new BitMatrix(width, height);
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
// more robust on the blackbox tests than sampling a diagonal as we used to do.
initArrays(width);
var _localLuminances:Array;
var localBuckets:Array = buckets;//assign empty array
for (var y2:int = 1; y2 < 5; y2++)
{
var row:int = height * y2 / 5;
_localLuminances = source.getRow(row, luminances);
var right:int = (width << 2) / 5;
for (var x:int = width / 5; x < right; x++)
{
var pixel:int = _localLuminances[x] & 0xff;
var index:int = Math.floor(pixel >> LUMINANCE_SHIFT);
localBuckets[index]++;
}
}
var blackPoint:int = estimateBlackPoint(localBuckets);
// We delay reading the entire image luminance until the black point estimation succeeds.
// Although we end up reading four rows twice, it is consistent with our motto of
// "fail quickly" which is necessary for continuous scanning.
_localLuminances = source.getMatrix();
for (var y:int = 0; y < height; y++)
{
var offset:int = y * width;
for (var x2:int = 0; x2< width; x2++)
{
var pixel2:int = _localLuminances[offset + x2] & 0xff;
if (pixel2 < blackPoint)
{
matrix._set(x2, y);
}
}
}
return matrix;
}
public override function createBinarizer(source:LuminanceSource):Binarizer {
return new GlobalHistogramBinarizer(source);
}
private function initArrays(luminanceSize:int):void {
if (luminances == null || luminances.length < luminanceSize)
{
luminances = new Array(luminanceSize);
}
for (var i:int=0;i<luminances.length;i++) { luminances[i]=0;}
if (buckets == null)
{
buckets = new Array(LUMINANCE_BUCKETS);
}
for (var j:int=0;j<buckets.length;j++) { buckets[j]=0;}
}
private static function estimateBlackPoint(buckets:Array):int {
// Find the tallest peak in the histogram.
var numBuckets:int = buckets.length;
var maxBucketCount:int = 0;
var firstPeak:int = 0;
var firstPeakSize:int = 0;
for (var x:int = 0; x < numBuckets; x++) {
if (buckets[x] > firstPeakSize) {
firstPeak = x;
firstPeakSize = buckets[x];
}
if (buckets[x] > maxBucketCount) {
maxBucketCount = buckets[x];
}
}
// Find the second-tallest peak which is somewhat far from the tallest peak.
var secondPeak:int = 0;
var secondPeakScore:int = 0;
for (var x2:int = 0; x2 < numBuckets; x2++) {
var distanceToBiggest:int = x2 - firstPeak;
// Encourage more distant second peaks by multiplying by square of distance.
var score:int = buckets[x2] * distanceToBiggest * distanceToBiggest;
if (score > secondPeakScore) {
secondPeak = x2;
secondPeakScore = score;
}
}
// Make sure firstPeak corresponds to the black peak.
if (firstPeak > secondPeak) {
var temp:int = firstPeak;
firstPeak = secondPeak;
secondPeak = temp;
}
// If there is too little contrast in the image to pick a meaningful black point, throw rather
// than waste time trying to decode the image, and risk false positives.
// TODO: It might be worth comparing the brightest and darkest pixels seen, rather than the
// two peaks, to determine the contrast.
if (secondPeak - firstPeak <= numBuckets >> 4) {
throw new ReaderException("GlobalHistogramBinarizer : estimateBlackPoint");
}
// Find a valley between them that is low and closer to the white peak.
var bestValley:int = secondPeak - 1;
var bestValleyScore:int = -1;
for (var x3:int = secondPeak - 1; x3 > firstPeak; x3--) {
var fromFirst:int = x3 - firstPeak;
var score2:int = fromFirst * fromFirst * (secondPeak - x3) * (maxBucketCount - buckets[x3]);
if (score2 > bestValleyScore) {
bestValley = x3;
bestValleyScore = score2;
}
}
return bestValley << LUMINANCE_SHIFT;
}
}
}

View file

@ -0,0 +1,242 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* Implementations of this class can, given locations of finder patterns for a QR code in an
* image, sample the right points in the image to reconstruct the QR code, accounting for
* perspective distortion. It is abstracted since it is relatively expensive and should be allowed
* to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
* Imaging library, but which may not be available in other environments such as J2ME, and vice
* versa.
*
* The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
* with an instance of a class which implements this interface.
*
* @author Sean Owen
*
*/
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.ReaderException;
public class GridSampler
{
private static var gridSampler:GridSampler = new GridSampler();
/**
* Sets the implementation of {@link GridSampler} used by the library. One global
* instance is stored, which may sound problematic. But, the implementation provided
* ought to be appropriate for the entire platform, and all uses of this library
* in the whole lifetime of the JVM. For instance, an Android activity can swap in
* an implementation that takes advantage of native platform libraries.
*
* @param newGridSampler The platform-specific object to install.
*/
public static function setGridSampler(newGridSampler:GridSampler):void {
if (newGridSampler == null) {
throw new IllegalArgumentException("common : GridSampler : setGridSampler");;
}
gridSampler = newGridSampler;
}
/**
* @return the current implementation of {@link GridSampler}
*/
public static function getGridSamplerInstance():GridSampler
{
return gridSampler;
}
/**
* <p>Samples an image for a square matrix of bits of the given dimension. This is used to extract
* the black/white modules of a 2D barcode like a QR Code found in an image. Because this barcode
* may be rotated or perspective-distorted, the caller supplies four points in the source image
* that define known points in the barcode, so that the image may be sampled appropriately.</p>
*
* <p>The last eight "from" parameters are four X/Y coordinate pairs of locations of points in
* the image that define some significant points in the image to be sample. For example,
* these may be the location of finder pattern in a QR Code.</p>
*
* <p>The first eight "to" parameters are four X/Y coordinate pairs measured in the destination
* {@link BitMatrix}, from the top left, where the known points in the image given by the "from"
* parameters map to.</p>
*
* <p>These 16 parameters define the transformation needed to sample the image.</p>
*
* @param image image to sample
* @param dimension width/height of {@link BitMatrix} to sample from iamge
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
* defined by the "from" parameters
* @throws ReaderException if image can't be sampled, for example, if the transformation defined
* by the given points is invalid or results in sampling outside the image boundaries
*/
public function sampleGrid(image:BitMatrix,
dimension:int,
p1ToX:Number,
p1ToY:Number,
p2ToX:Number,
p2ToY:Number,
p3ToX:Number,
p3ToY:Number,
p4ToX:Number,
p4ToY:Number,
p1FromX:Number,
p1FromY:Number,
p2FromX:Number,
p2FromY:Number,
p3FromX:Number,
p3FromY:Number,
p4FromX:Number,
p4FromY:Number):BitMatrix
{
// BAS : originally in DefaultGridSampler
var transform:PerspectiveTransform = PerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
var bits:BitMatrix = new BitMatrix(dimension);
var points:Array = new Array(dimension << 1);
for (var y:int = 0; y < dimension; y++)
{
var max:int = points.length;
var iValue:Number = Number(y + 0.5);
for (var x:int = 0; x < max; x += 2)
{
points[x] = Number((x >> 1) + 0.5);
points[x + 1] = iValue;
}
points = transform.transformPoints(points);
// Quick check to see if points transformed to something inside the image;
// sufficent to check the endpoints
checkAndNudgePoints(image, points);
try
{
for (var x2:int = 0; x2 < max; x2 += 2)
{
//UPGRADE_WARNING: Narrowing conversions may produce unexpected results in C#. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1042"'
if (image._get( int(points[x2]), int(points[x2 + 1])))
//if (image._get( Math.floor(points[x2]), Math.floor(points[x2 + 1])))
{
// Black(-ish) pixel
bits._set(x2 >> 1,y );
}
}
}
catch (aioobe:RangeError )
{
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
// transform gets "twisted" such that it maps a straight line of points to a set of points
// whose endpoints are in bounds, but others are not. There is probably some mathematical
// way to detect this about the transformation that I don't know yet.
// This results in an ugly runtime exception despite our clever checks above -- can't have that.
// We could check each point's coordinates but that feels duplicative. We settle for
// catching and wrapping ArrayIndexOutOfBoundsException.
throw new ReaderException("DefautGridSampler : sampleGrid : "+aioobe.message);
}
}
return bits;
}
/**
* <p>Checks a set of points that have been transformed to sample points on an image against
* the image's dimensions to see if the point are even within the image.</p>
*
* <p>This method will actually "nudge" the endpoints back onto the image if they are found to be
* barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
* patterns in an image where the QR Code runs all the way to the image border.</p>
*
* <p>For efficiency, the method will check points from either end of the line until one is found
* to be within the image. Because the set of points are assumed to be linear, this is valid.</p>
*
* @param image image into which the points should map
* @param points actual points in x1,y1,...,xn,yn form
* @throws ReaderException if an endpoint is lies outside the image boundaries
*/
public static function checkAndNudgePoints(image:BitMatrix, points:Array):void
{
var width:int = image.getWidth();
var height:int = image.getHeight();
// Check and nudge points from start until we see some that are OK:
var nudged:Boolean = true;
for (var offset:int = 0; offset < points.length && nudged; offset += 2)
{
var x:int = int(points[offset]);
var y:int = int(points[offset + 1]);
if (x < -1 || x > width || y < -1 || y > height)
{
throw new ReaderException("common : GridSampler : checkAndNudgePoints : point out of range ("+x+""+y+") max:"+width+" - "+height);
}
nudged = false;
if (x == -1)
{
points[offset] = 0;
nudged = true;
}
else if (x == width)
{
points[offset] = width - 1;
nudged = true;
}
if (y == -1)
{
points[offset + 1] = 0;
nudged = true;
}
else if (y == height)
{
points[offset + 1] = height - 1;
nudged = true;
}
}
// Check and nudge points from end:
nudged = true;
for (var offset1:int = points.length - 2; offset >= 0 && nudged; offset -= 2)
{
var x1:int = int(points[offset1]);
var y1:int = int(points[offset1 + 1]);
if (x1 < -1 || x1 > width || y1 < -1 || y1 > height)
{
throw new ReaderException("common : GridSampler : checkAndNudgePoints : out of bounds");
}
nudged = false;
if (x1 == -1)
{
points[offset1] = 0;
nudged = true;
}
else if (x1 == width)
{
points[offset1] = width - 1;
nudged = true;
}
if (y1 == -1)
{
points[offset1 + 1] = 0;
nudged = true;
}
else if (y1 == height)
{
points[offset1 + 1] = height - 1;
nudged = true;
}
}
}
}
}

View file

@ -0,0 +1,179 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* This class implements a local thresholding algorithm, which while slower than the
* GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
* high frequency images of barcodes with black data on white backgrounds. For this application,
* it does a much better job than a global blackpoint with severe shadows and gradients.
* However it tends to produce artifacts on lower frequency images and is therefore not
* a good general purpose binarizer for uses outside ZXing.
*
* NOTE: This class is still experimental and may not be ready for prime time yet.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
import com.google.zxing.Binarizer;
import com.google.zxing.LuminanceSource;
public final class LocalBlockBinarizer extends Binarizer {
private var matrix:BitMatrix = null;
public function LocalBlockBinarizer(source:LuminanceSource) {
super(source);
}
// TODO: Consider a different strategy for 1D Readers.
public override function getBlackRow(y:int , row:BitArray ):BitArray {
binarizeEntireImage();
return matrix.getRow(y, row);
}
// TODO: If getBlackRow() calculates its own values, removing sharpening here.
public override function getBlackMatrix():BitMatrix {
binarizeEntireImage();
return matrix;
}
public override function createBinarizer(source:LuminanceSource ):Binarizer {
return new LocalBlockBinarizer(source);
}
// Calculates the final BitMatrix once for all requests. This could be called once from the
// constructor instead, but there are some advantages to doing it lazily, such as making
// profiling easier, and not doing heavy lifting when callers don't expect it.
private function binarizeEntireImage():void {
if (matrix == null) {
var source:LuminanceSource = getLuminanceSource();
var luminances:Array = source.getMatrix();
var width:int = source.getWidth();
var height:int = source.getHeight();
sharpenRow(luminances, width, height);
var subWidth:int = width >> 3;
var subHeight:int = height >> 3;
var blackPoints:Array = calculateBlackPoints(luminances, subWidth, subHeight, width);
matrix = new BitMatrix(width, height);
calculateThresholdForBlock(luminances, subWidth, subHeight, width, blackPoints, matrix);
}
}
// For each 8x8 block in the image, calculate the average black point using a 5x5 grid
// of the blocks around it. Also handles the corner cases, but will ignore up to 7 pixels
// on the right edge and 7 pixels at the bottom of the image if the overall dimsions are not
// multiples of eight. In practice, leaving those pixels white does not seem to be a problem.
private static function calculateThresholdForBlock(luminances:Array , subWidth:int , subHeight:int ,
stride:int , blackPoints:Array , matrix:BitMatrix ):void {
for (var y:int = 0; y < subHeight; y++) {
for (var x:int = 0; x < subWidth; x++) {
var left:int = (x > 1) ? x : 2;
left = (left < subWidth - 2) ? left : subWidth - 3;
var top:int = (y > 1) ? y : 2;
top = (top < subHeight - 2) ? top : subHeight - 3;
var sum:int = 0;
for (var z:int = -2; z <= 2; z++) {
sum += blackPoints[top + z][left - 2];
sum += blackPoints[top + z][left - 1];
sum += blackPoints[top + z][left];
sum += blackPoints[top + z][left + 1];
sum += blackPoints[top + z][left + 2];
}
var average:int = sum / 25;
threshold8x8Block(luminances, x << 3, y << 3, average, stride, matrix);
}
}
}
// Applies a single threshold to an 8x8 block of pixels.
private static function threshold8x8Block(luminances:Array , xoffset:int , yoffset:int , threshold:int ,
stride:int , matrix:BitMatrix ):void {
for (var y:int = 0; y < 8; y++) {
var offset:int = (yoffset + y) * stride + xoffset;
for (var x:int = 0; x < 8; x++) {
var pixel:int = luminances[offset + x] & 0xff;
if (pixel < threshold) {
matrix._set(xoffset + x, yoffset + y);
}
}
}
}
// Calculates a single black point for each 8x8 block of pixels and saves it away.
private static function calculateBlackPoints(luminances:Array, subWidth:int, subHeight:int,
stride:int):Array {
//int[][] blackPoints = new int[subHeight][subWidth];
var blackPoints:Array = new Array();
for (var y:int = 0; y < subHeight; y++) {
for (var x:int = 0; x < subWidth; x++) {
var sum:int = 0;
var min:int = 255;
var max:int = 0;
for (var yy:int = 0; yy < 8; yy++) {
var offset:int = ((y << 3) + yy) * stride + (x << 3);
for (var xx:int = 0; xx < 8; xx++) {
var pixel:int = luminances[offset + xx] & 0xff;
sum += pixel;
if (pixel < min) {
min = pixel;
}
if (pixel > max) {
max = pixel;
}
}
}
// If the contrast is inadequate, use half the minimum, so that this block will be
// treated as part of the white background, but won't drag down neighboring blocks
// too much.
var average:int = (max - min > 24) ? (sum >> 6) : (min >> 1);
blackPoints[y][x] = average;
}
}
return blackPoints;
}
// Applies a simple -1 4 -1 box filter with a weight of 2 to each row.
private static function sharpenRow(luminances:Array, width:int, height:int):void {
for (var y:int = 0; y < height; y++) {
var offset:int = y * width;
var left:int = luminances[offset] & 0xff;
var center:int = luminances[offset + 1] & 0xff;
for (var x:int = 1; x < width - 1; x++) {
var right:int = luminances[offset + x + 1] & 0xff;
var pixel:int = ((center << 2) - left - right) >> 1;
// Must clamp values to 0..255 so they will fit in a byte.
if (pixel > 255) {
pixel = 255;
} else if (pixel < 0) {
pixel = 0;
}
luminances[offset + x] = pixel;
left = center;
center = right;
}
}
}
}
}

View file

@ -0,0 +1,120 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
/**
* <p>This class implements a perspective transform in two dimensions. Given four source and four
* destination points, it will compute the transformation implied between them. The code is based
* directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p>
*
* @author Sean Owen
*/
public class PerspectiveTransform
{
private var a11:Number;
private var a12:Number;
private var a13:Number;
private var a21:Number;
private var a22:Number;
private var a23:Number;
private var a31:Number;
private var a32:Number;
private var a33:Number;
public function PerspectiveTransform( a11:Number, a21:Number,a31:Number, a12:Number, a22:Number, a32:Number, a13:Number, a23:Number, a33:Number)
{
this.a11 = a11;
this.a12 = a12;
this.a13 = a13;
this.a21 = a21;
this.a22 = a22;
this.a23 = a23;
this.a31 = a31;
this.a32 = a32;
this.a33 = a33;
}
public static function quadrilateralToQuadrilateral(x0:Number,y0:Number,x1:Number,y1:Number,x2:Number,y2:Number,x3:Number,y3:Number,x0p:Number,y0p:Number,x1p:Number,y1p:Number,x2p:Number, y2p:Number,x3p:Number,y3p:Number):PerspectiveTransform
{
var qToS:PerspectiveTransform = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
var sToQ:PerspectiveTransform = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
return sToQ.times(qToS);
}
public function transformPoints(points:Array):Array
{
var max:int = points.length;
var a11:Number = this.a11;
var a12:Number = this.a12;
var a13:Number = this.a13;
var a21:Number = this.a21;
var a22:Number = this.a22;
var a23:Number = this.a23;
var a31:Number = this.a31;
var a32:Number = this.a32;
var a33:Number = this.a33;
for (var i:int = 0; i < max; i += 2)
{
var x:Number = points[i];
var y:Number = points[i + 1];
var denominator:Number = a13 * x + a23 * y + a33;
points[i] = (a11 * x + a21 * y + a31) / denominator;
points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
}
return points;
}
public static function squareToQuadrilateral(x0:Number,y0:Number,x1:Number,y1:Number,x2:Number,y2:Number,x3:Number,y3:Number):PerspectiveTransform
{
var dy2:Number = y3 - y2;
var dy3:Number = y0 - y1 + y2 - y3;
if (dy2 == 0 && dy3 == 0)
{
return new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0, 0, 1);
}
else
{
var dx1:Number = x1 - x2;
var dx2:Number = x3 - x2;
var dx3:Number = x0 - x1 + x2 - x3;
var dy1:Number = y1 - y2;
var denominator:Number = dx1 * dy2 - dx2 * dy1;
var a13:Number = (dx3 * dy2 - dx2 * dy3) / denominator;
var a23:Number = (dx1 * dy3 - dx3 * dy1) / denominator;
return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1);
}
}
public static function quadrilateralToSquare(x0:Number,y0:Number,x1:Number,y1:Number,x2:Number,y2:Number,x3:Number,y3:Number):PerspectiveTransform
{
// Here, the adjoint serves as the inverse:
return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
}
public function buildAdjoint():PerspectiveTransform
{
// Adjoint is the transpose of the cofactor matrix:
return new PerspectiveTransform(a22 * a33 - a23 * a32, a23 * a31 - a21 * a33, a21 * a32 - a22 * a31, a13 * a32 - a12 * a33, a11 * a33 - a13 * a31, a12 * a31 - a11 * a32, a12 * a23 - a13 * a22, a13 * a21 - a11 * a23, a11 * a22 - a12 * a21);
}
private function times(other:PerspectiveTransform ):PerspectiveTransform
{
return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13, a11 * other.a21 + a21 * other.a22 + a31 * other.a23, a11 * other.a31 + a21 * other.a32 + a31 * other.a33, a12 * other.a11 + a22 * other.a12 + a32 * other.a13, a12 * other.a21 + a22 * other.a22 + a32 * other.a23, a12 * other.a31 + a22 * other.a32 + a32 * other.a33, a13 * other.a11 + a23 * other.a12 + a33 * other.a13, a13 * other.a21 + a23 * other.a22 + a33 * other.a23, a13 * other.a31 + a23 * other.a32 + a33 * other.a33);
}
}
}

View file

@ -0,0 +1,217 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common.detector
{
import com.google.zxing.common.BitMatrix;
import com.google.zxing.ResultPoint;
import com.google.zxing.ReaderException;
/**
* <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
* It looks within a mostly white region of an image for a region of black and white, but mostly
* black. It returns the four corners of the region, as best it can determine.</p>
*
* @author Sean Owen
*/
public final class MonochromeRectangleDetector
{
private static var MAX_MODULES:int = 32;
private var image:BitMatrix;
public function MonochromeRectangleDetector(image:BitMatrix)
{
this.image = image;
}
/**
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
* white, in an image.</p>
*
* @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
* last points are opposed on the diagonal, as are the second and third. The first point will be
* the topmost point and the last, the bottommost. The second point will be leftmost and the
* third, the rightmost
* @throws ReaderException if no Data Matrix Code can be found
*/
public function detect():Array
{
var height:int = image.getHeight();
var width:int = image.getWidth();
var halfHeight:int = height >> 1;
var halfWidth:int = width >> 1;
var deltaY:int = Math.max(1, height / (MAX_MODULES << 3));
var deltaX:int = Math.max(1, width / (MAX_MODULES << 3));
var top:int = 0;
var bottom:int = height;
var left:int = 0;
var right:int = width;
var pointA:ResultPoint = findCornerFromCenter(halfWidth, 0, left, right,
halfHeight, -deltaY, top, bottom, halfWidth >> 1);
top = int(pointA.getY() - 1);
var pointB:ResultPoint = findCornerFromCenter(halfWidth, -deltaX, left, right,
halfHeight, 0, top, bottom, halfHeight >> 1);
left = int(pointB.getX() - 1);
var pointC:ResultPoint = findCornerFromCenter(halfWidth, deltaX, left, right,
halfHeight, 0, top, bottom, halfHeight >> 1);
right = int(pointC.getX() + 1);
var pointD:ResultPoint = findCornerFromCenter(halfWidth, 0, left, right,
halfHeight, deltaY, top, bottom, halfWidth >> 1);
bottom = int(pointD.getY() + 1);
// Go try to find point A again with better information -- might have been off at first.
pointA = findCornerFromCenter(halfWidth, 0, left, right,
halfHeight, -deltaY, top, bottom, halfWidth >> 2);
return [pointA, pointB, pointC, pointD ];
}
/**
* Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
* point which should be within the barcode.
*
* @param centerX center's x component (horizontal)
* @param deltaX same as deltaY but change in x per step instead
* @param left minimum value of x
* @param right maximum value of x
* @param centerY center's y component (vertical)
* @param deltaY change in y per step. If scanning up this is negative; down, positive;
* left or right, 0
* @param top minimum value of y to search through (meaningless when di == 0)
* @param bottom maximum value of y
* @param maxWhiteRun maximum run of white pixels that can still be considered to be within
* the barcode
* @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
* @throws com.google.zxing.ReaderException if such a point cannot be found
*/
private function findCornerFromCenter(centerX:int, deltaX:int, left:int, right:int,
centerY:int, deltaY:int, top:int, bottom:int, maxWhiteRun:int):ResultPoint
{
var lastRange:Array = null;
for (var y:int = centerY, x:int = centerX;
y < bottom && y >= top && x < right && x >= left;
y += deltaY, x += deltaX) {
var range:Array;
if (deltaX == 0) {
// horizontal slices, up and down
range = blackWhiteRange(y, maxWhiteRun, left, right, true);
} else {
// vertical slices, left and right
range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
}
if (range == null) {
if (lastRange == null) {
throw new ReaderException("MonochromeRectangleDetector : findCornerFromCenter : range and lastRange null");
}
// lastRange was found
if (deltaX == 0) {
var lastY:int = y - deltaY;
if (lastRange[0] < centerX) {
if (lastRange[1] > centerX) {
// straddle, choose one or the other based on direction
return new ResultPoint(deltaY > 0 ? lastRange[0] : lastRange[1], lastY);
}
return new ResultPoint(lastRange[0], lastY);
} else {
return new ResultPoint(lastRange[1], lastY);
}
} else {
var lastX:int = x - deltaX;
if (lastRange[0] < centerY) {
if (lastRange[1] > centerY) {
return new ResultPoint(lastX, deltaX < 0 ? lastRange[0] : lastRange[1]);
}
return new ResultPoint(lastX, lastRange[0]);
} else {
return new ResultPoint(lastX, lastRange[1]);
}
}
}
lastRange = range;
}
throw new ReaderException("MonochromeRectangleDetector : findCornerFromCenter :generic error");
}
/**
* Computes the start and end of a region of pixels, either horizontally or vertically, that could
* be part of a Data Matrix barcode.
*
* @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
* where we are scanning. If scanning vertically it's the colummn, the fixed horizontal location
* @param maxWhiteRun largest run of white pixels that can still be considered part of the
* barcode region
* @param minDim minimum pixel location, horizontally or vertically, to consider
* @param maxDim maximum pixel location, horizontally or vertically, to consider
* @param horizontal if true, we're scanning left-right, instead of up-down
* @return int[] with start and end of found range, or null if no such range is found
* (e.g. only white was found)
*/
private function blackWhiteRange(fixedDimension:int, maxWhiteRun:int, minDim:int, maxDim:int,
horizontal:Boolean):Array
{
var center:int = (minDim + maxDim) >> 1;
// Scan left/up first
var start:int = center;
var condition:Boolean;
var whiteRunStart:int;
while (start >= minDim) {
if (horizontal ? image._get(start, fixedDimension) : image._get(fixedDimension, start)) {
start--;
} else {
whiteRunStart = start;
do {
start--;
condition = (horizontal ? image._get(start, fixedDimension) : image._get(fixedDimension, start));
} while ((start >= minDim )&& !condition);
var whiteRunSize:int = whiteRunStart - start;
if (start < minDim || whiteRunSize > maxWhiteRun) {
start = whiteRunStart;
break;
}
}
}
start++;
// Then try right/down
var end:int = center;
while (end < maxDim) {
if (horizontal ? image._get(end, fixedDimension) : image._get(fixedDimension, end)) {
end++;
} else {
whiteRunStart = end;
do {
end++;
condition = horizontal ? image._get(end, fixedDimension) : image._get(fixedDimension, end);
} while (end < maxDim && !condition );
var whiteRunSize2:int = end - whiteRunStart;
if (end >= maxDim || whiteRunSize2 > maxWhiteRun) {
end = whiteRunStart;
break;
}
}
}
end--;
return end > start ? [start, end] : null;
}
}
}

View file

@ -0,0 +1,133 @@
package com.google.zxing.common.flexdatatypes
{
// these comparators should reside in the classes but that didn's work for some reason.
import com.google.zxing.datamatrix.detector.ResultPointsAndTransitionsComparator;
import com.google.zxing.qrcode.detector.CenterComparator;
public class ArrayList
{
//BAS : made public for debugging
public var _array:Array;
public function ArrayList(siz:int=0)
{
this._array = new Array(siz);
}
public function get Capacity():int
{
return this._array.length;
}
public function getObjectByIndex(index:int):Object
{
var obj:Object = this._array[index];
return obj;
}
public function setObjectByIndex(index:int,obj:Object):void
{
this._array[index] = obj;
}
public function Contains(o:Object):Boolean
{
if (this._array.indexOf(o) != -1)
{
return true;
}
else
{
return false;
}
}
public function set Capacity(cap:int):void
{
// not needed;
}
public function AddRange(itemsToAdd:Array):void
{
// add this number of items
var len:int = this._array.length;
for (var i:int=0;i<itemsToAdd.length;i++)
{
this._array.push(new Object());
}
}
public function indexOf(o:Object):int
{
return this._array.indexOf(o);
}
public function RemoveRange(newSize:int,itemsToRemove:int):void
{
// remove the items
var tmpAr:Object;
for (var i:int=0;i<itemsToRemove;i++)
{
// remove the item with this index
tmpAr = this._array.pop();
}
}
public function get Count():int
{
return this._array.length;
}
public function Add(item:Object):void
{
this._array.push(item);
}
public function addElement(item:Object):void
{
this.Add(item);
}
public function get length():int
{
return this._array.length;
}
public function sort_ResultPointsAndTransitionsComparator():void
{
this._array.sort(ResultPointsAndTransitionsComparator.compare);
//this._array.sort(args);
}
public function sort_CenterComparator():void
{
this._array.sort(CenterComparator.compare);
}
public function size():int
{
return this._array.length;
}
public function elementAt(index:int):Object
{
return this._array[index];
}
public function isEmpty():Boolean
{
if (this._array.length == 0)
{
return true;
}
else
{
return false;
}
}
}
}

View file

@ -0,0 +1,136 @@
package com.google.zxing.common.flexdatatypes
{
public class HashTable
{
// bas : made public for debugging
public var _arr:Array;
public function isEmpty():Boolean
{
return this.getSize()==0?true:false;
}
public function getSize():int
{
return this._arr.length;
}
public function getIndexOf(key:Object):int
{
for (var i:int=0;i<this._arr.length;i++)
{
if (this._arr[i][0] == key)
{
return i;
}
}
return undefined;
}
public function getValueByIndex(index:int):Object
{
return this._arr[index][1];
}
public function getKeyByIndex(index:int):Object
{
return this._arr[index][0];
}
public function HashTable(siz:int=0)
{
this._arr = new Array(siz);
}
public function Add(key:Object, value:Object):void
{
var ta:Array = new Array(2);
ta[0] = key;
ta[1] = value;
this._arr[this._arr.length] = ta;
}
public function _put(k:Object,v:Object):void
{
this.Add(k,v);
}
public function ContainsKey(key:Object):Boolean
{
for (var i:int=0;i<this._arr.length;i++)
{
if (this._arr[i][0] == key) { return true; }
}
return false;
}
public function getValuesByKey(key:Object):ArrayList
{
var al:ArrayList = new ArrayList();
for (var i:int=0;i<this._arr.length;i++)
{
if (this._arr[i][0] == key)
{
al.Add(this._arr[i][1]);
}
}
return al;
}
public function _get(key:Object):Object
{
return this.getValueByKey(key);
}
public function getValueByKey(key:Object):Object
{
var al:ArrayList = new ArrayList();
for (var i:int=0;i<this._arr.length;i++)
{
if (this._arr[i][0] == key)
{
return this._arr[i][1];
}
}
return al;
}
public function setValue(key:Object,value:Object):void
{
for (var i:int=0;i<this._arr.length;i++)
{
if (this._arr[i][0] == key)
{
this._arr[i][1] = value;
return;
}
}
}
public function getKeyByValue(value:Object):int
{
for (var i:int=0;i<this._arr.length;i++)
{
if (this._arr[i][1] == value)
{
return this._arr[i][0];
}
}
return -1;
}
public function containsKey(key:Object):Boolean
{
for (var i:int=0;i<this._arr.length;i++)
{
if (this._arr[i][0] == key)
{
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,11 @@
package com.google.zxing.common.flexdatatypes
{
public class IllegalArgumentException extends Error
{
public function IllegalArgumentException(message:String="")
{
super("IllegalArgumentException"+message);
}
}
}

View file

@ -0,0 +1,119 @@
package com.google.zxing.common.flexdatatypes
{
import com.google.zxing.ReaderException;
public class StringBuilder
{
public var _string:String = "";
public function StringBuilder(ignore:int=0)
{
}
public function charAt(index:int):String
{
return this._string.charAt(index);
}
public function setCharAt(index:int, char:String):void
{
var temp:Array = this._string.split("");
temp[index] = char.charAt(0);
this._string = temp.join("");
}
public function setLength(l:int):void
{
if (l == 0)
{
this._string = "";
}
else
{
throw new ReaderException("StringBuilder : setLength : only 0 supported");
}
}
public function Append(o:Object,startIndex:int=-1,count:int=-1):void
{
if (startIndex == -1)
{
if (o is Array)
{
this._string = this._string + (o as Array).join("");
}
else
{
this._string = this._string + o.toString();
}
}
else if (count == -1)
{
this._string = this._string + (o.toString()).substr(startIndex);
}
else
{
this._string = this._string + (o.toString()).substr(startIndex,count);
}
}
public function ToString():String
{
return this._string;
}
public function get length():int
{
return this._string.length;
}
public function set length(size:int):void
{
if (size==0) { this._string = "";}
else
{
throw new ReaderException("size can ony be set to 0");
}
}
public function Insert (pos:int,o:Object):void
{
if (pos == 0)
{
this._string = o.toString() + this._string;
}
else
{
throw new ReaderException('pos not supported yet');
}
}
public function Remove(startIndex:int,length:int):void
{
var leftPart:String = "";
var rightPart:String = "";
if (startIndex > 0) { leftPart = this._string.substring(0,startIndex); }
if ((startIndex+length) < this._string.length)
{ rightPart = this._string.substr(startIndex+length); }
this._string = leftPart + rightPart;
}
public function toString():String
{
return this._string;
}
public function deleteCharAt(index:int):void
{
var temp:Array = this._string.split("");
var result:String = "";
for(var i:int=0;i<temp.length;i++)
{
if (i!=index){result = result + (temp[i] as String); }
}
this._string = result;
}
}
}

View file

@ -0,0 +1,35 @@
package com.google.zxing.common.flexdatatypes
{
public class Utils
{
public function Utils()
{
}
public static function startsWith(text:String, subtext:String):Boolean
{
if (text.substr(0,subtext.length) == subtext) { return true; }
return false;
}
public static function endsWith(text:String, subtext:String):Boolean
{
if (text.substr(text.length-subtext.length) == subtext) { return true; }
return false;
}
public static function isDigit(s:String):Boolean
{
return !isNaN(Number(s));
}
public static function arraycopy(source:Array, sourceoffset:int, target:Array, targetoffset:int, length:int):void
{
for (var i:int=sourceoffset;i<(sourceoffset+length);i++)
{
target[targetoffset++] = source[i];
}
}
}
}

View file

@ -0,0 +1,162 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common.reedsolomon
{
/**
* <p>This class contains utility methods for performing mathematical operations over
* the Galois Field GF(256). Operations use a given primitive polynomial in calculations.</p>
*
* <p>Throughout this package, elements of GF(256) are represented as an <code>int</code>
* for convenience and speed (but at the cost of memory).
* Only the bottom 8 bits are really used.</p>
*
* @author Sean Owen
*/
public class GF256
{
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
public static var QR_CODE_FIELD:GF256 = new GF256(0x011D); // x^8 + x^4 + x^3 + x^2 + 1
public static var DATA_MATRIX_FIELD:GF256 = new GF256(0x012D); // x^8 + x^5 + x^3 + x^2 + 1
public var expTable:Array;
public var logTable:Array;
private var zero:GF256Poly;
private var one:GF256Poly;
/**
* Create a representation of GF(256) using the given primitive polynomial.
*
* @param primitive irreducible polynomial whose coefficients are represented by
* the bits of an int, where the least-significant bit represents the constant
* coefficient
*/
public function GF256(primitive:int) {
expTable = new Array(256);
logTable = new Array(256);
var x:int = 1;
for (var i:int = 0; i < 256; i++) {
expTable[i] = x;
x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
if (x >= 0x100) {
x ^= primitive;
}
}
for (var i2:int = 0; i2 < 255; i2++) {
logTable[expTable[i2]] = i2;
}
// logTable[0] == 0 but this should never be used
zero = new GF256Poly(this, [0]);
one = new GF256Poly(this, [1]);
}
public function getZero():GF256Poly {
return zero;
}
public function getOne():GF256Poly
{
return one;
}
/**
* @return the monomial representing coefficient * x^degree
*/
public function buildMonomial(degree:int, coefficient:int):GF256Poly
{
if (degree < 0) {
throw new IllegalArgumentException("common : reedsolomon : gf256 : buildnominal");
}
if (coefficient == 0) {
return zero;
}
var coefficients:Array = new Array(degree + 1);
coefficients[0] = coefficient;
return new GF256Poly(this, coefficients);
}
/**
* Implements both addition and subtraction -- they are the same in GF(256).
*
* @return sum/difference of a and b
*/
public static function addOrSubtract(a:int, b:int):int {
return a ^ b;
}
/**
* @return 2 to the power of a in GF(256)
*/
public function exp(a:int):int
{
return expTable[a];
}
/**
* @return base 2 log of a in GF(256)
*/
public function log(a:int):int
{
if (a == 0) {
throw new IllegalArgumentException("common : reedsolomon : gf256 : log : a == 0");
}
return logTable[a];
}
/**
* @return multiplicative inverse of a
*/
public function inverse(a:int):int
{
if (a == 0) {
throw new IllegalArgumentException("GF256:inverse: a cannot be 0");
}
return expTable[255 - logTable[a]];
}
/**
* @param a
* @param b
* @return product of a and b in GF(256)
*/
public function multiply(a:int, b:int):int
{
if (a == 0 || b == 0) {
return 0;
}
if (a == 1) {
return b;
}
if (b == 1) {
return a;
}
return expTable[(logTable[a] + logTable[b]) % 255];
}
public function Equals(other:GF256):Boolean
{
if (expTable != other.expTable) { return false; }
if (logTable != other.logTable) { return false; }
if (zero != other.getZero()) { return false; }
if (one != other.getOne()) { return false; }
return true;
}
}
}

View file

@ -0,0 +1,310 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common.reedsolomon
{
/**
* <p>Represents a polynomial whose coefficients are elements of GF(256).
* Instances of this class are immutable.</p>
*
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
* port of his C++ Reed-Solomon implementation.</p>
*
* @author Sean Owen
*/
public class GF256Poly
{
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private var field:GF256;
private var coefficients:Array ;
/**
* @param field the {@link GF256} instance representing the field to use
* to perform computations
* @param coefficients coefficients as ints representing elements of GF(256), arranged
* from most significant (highest-power term) coefficient to least significant
* @throws Error if argument is null or empty,
* or if leading coefficient is 0 and this is not a
* constant polynomial (that is, it is not the monomial "0")
*/
public function GF256Poly( field:GF256, coefficients:Array) {
if (coefficients == null || coefficients.length == 0) {
throw new IllegalArgumentException("common : reedsolomon : GFPoly : constructor input parameters invalid");
}
this.field = field;
var coefficientsLength:int = coefficients.length;
if (coefficientsLength > 1 && coefficients[0] == 0) {
// Leading term must be non-zero for anything except the constant polynomial "0"
var firstNonZero:int = 1;
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
firstNonZero++;
}
if (firstNonZero == coefficientsLength) {
this.coefficients = field.getZero().coefficients;
} else {
this.coefficients = new Array(coefficientsLength - firstNonZero);
//System.Array.Copy(coefficients,firstNonZero,this.coefficients,0,this.coefficients.length);
var ctr:int=0;
for (var i:int = firstNonZero;i<coefficients.length;i++)
{
this.coefficients[ctr] = coefficients[i];
ctr++;
}
}
} else {
this.coefficients = coefficients;
}
}
public function getCoefficients():Array
{
return coefficients;
}
/**
* @return degree of this polynomial
*/
public function getDegree():int
{
return coefficients.length - 1;
}
/**
* @return true iff this polynomial is the monomial "0"
*/
public function isZero():Boolean
{
return coefficients[0] == 0;
}
/**
* @return coefficient of x^degree term in this polynomial
*/
public function getCoefficient(degree:int):int
{
return coefficients[coefficients.length - 1 - degree];
}
/**
* @return evaluation of this polynomial at a given point
*/
public function evaluateAt(a:int):int
{
if (a == 0) {
// Just return the x^0 coefficient
return getCoefficient(0);
}
var size:int = coefficients.length;
var result:int = 0;
if (a == 1) {
// Just the sum of the coefficients
result = 0;
for (var i2:int = 0; i2 < size; i2++) {
result = GF256.addOrSubtract(result, coefficients[i2]);
}
return result;
}
result = coefficients[0];
for (var i:int = 1; i < size; i++) {
result = GF256.addOrSubtract(field.multiply(a, result), coefficients[i]);
}
return result;
}
public function addOrSubtract(other:GF256Poly):GF256Poly
{
if (field != other.field) {
throw new IllegalArgumentException("common : reedsolomon : GFPoly : GF256Polys do not have same GF256 field");
}
if (isZero()) {
return other;
}
if (other.isZero()) {
return this;
}
var smallerCoefficients:Array = this.coefficients;
var largerCoefficients:Array = other.coefficients;
if (smallerCoefficients.length > largerCoefficients.length) {
var temp:Array = smallerCoefficients;
smallerCoefficients = largerCoefficients;
largerCoefficients = temp;
}
var sumDiff:Array = new Array(largerCoefficients.length);
var lengthDiff:int = largerCoefficients.length - smallerCoefficients.length;
// Copy high-order terms only found in higher-degree polynomial's coefficients
//System.Array.Copy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
for (var ii:int=0;ii<lengthDiff;ii++)
{
sumDiff[ii] = largerCoefficients[ii];
}
for (var i:int = lengthDiff; i < largerCoefficients.length; i++) {
sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
}
return new GF256Poly(field, sumDiff);
}
public function multiply(other:Object):GF256Poly
{
if (other is GF256Poly) { return multiply_GF256Poly(other as GF256Poly);}
else if (other is int) { return multiply_int(other as int);}
else { new IllegalArgumentException('common : reedsolomon : GFPoly : GF256Poly : multiply : unknown type of other'); }
return null;
}
public function multiply_GF256Poly(other:GF256Poly ):GF256Poly
{
if (field != other.field) {
throw new IllegalArgumentException("common : reedsolomon : GFPoly : GF256Polys do not have same GF256 field");
}
if (isZero() || other.isZero()) {
return field.getZero();
}
var aCoefficients:Array = this.coefficients;
var aLength:int = aCoefficients.length;
var bCoefficients:Array = other.coefficients;
var bLength:int = bCoefficients.length;
var product:Array = new Array(aLength + bLength - 1);
for (var i:int = 0; i < aLength; i++) {
var aCoeff:int = aCoefficients[i];
for (var j:int = 0; j < bLength; j++) {
product[i + j] = GF256.addOrSubtract(product[i + j],
field.multiply(aCoeff, bCoefficients[j]));
}
}
return new GF256Poly(field, product);
}
public function multiply_int(scalar:int):GF256Poly
{
if (scalar == 0) {
return field.getZero();
}
if (scalar == 1) {
return this;
}
var size:int = coefficients.length;
var product:Array = new Array(size);
for (var i:int = 0; i < size; i++) {
product[i] = field.multiply(coefficients[i], scalar);
}
return new GF256Poly(field, product);
}
public function multiplyByMonomial(degree:int, coefficient:int):GF256Poly
{
if (degree < 0) {
throw new IllegalArgumentException("common : reedsolomon : GFPoly : degree less then 0");
}
if (coefficient == 0) {
return field.getZero();
}
var size:int = coefficients.length;
var product:Array = new Array(size + degree);
for (var i:int = 0; i < size; i++) {
product[i] = field.multiply(coefficients[i], coefficient);
}
return new GF256Poly(field, product);
}
public function divide(other:GF256Poly):Array
{
if (field != other.field) {
throw new IllegalArgumentException("common : reedsolomon : GFPoly : GF256Polys do not have same GF256 field");
}
if (other.isZero()) {
throw new IllegalArgumentException("common : reedsolomon : GFPoly : Divide by 0");
}
var quotient:GF256Poly = field.getZero();
var remainder:GF256Poly = this;
var denominatorLeadingTerm:int = other.getCoefficient(other.getDegree());
var inverseDenominatorLeadingTerm:int = field.inverse(denominatorLeadingTerm);
while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {
var degreeDifference:int = remainder.getDegree() - other.getDegree();
var scale:int = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);
var term:GF256Poly = other.multiplyByMonomial(degreeDifference, scale);
var iterationQuotient:GF256Poly = field.buildMonomial(degreeDifference, scale);
quotient = quotient.addOrSubtract(iterationQuotient);
remainder = remainder.addOrSubtract(term);
}
return [quotient, remainder];
}
public function toString():String {
var result:StringBuilder = new StringBuilder(8 * getDegree());
for (var degree:int = getDegree(); degree >= 0; degree--) {
var coefficient:int = getCoefficient(degree);
if (coefficient != 0) {
if (coefficient < 0) {
result.Append(" - ");
coefficient = -coefficient;
} else {
if (result.length > 0) {
result.Append(" + ");
}
}
if (degree == 0 || coefficient != 1) {
var alphaPower:int = field.log(coefficient);
if (alphaPower == 0) {
result.Append('1');
} else if (alphaPower == 1) {
result.Append('a');
} else {
result.Append("a^");
result.Append(alphaPower);
}
}
if (degree != 0) {
if (degree == 1) {
result.Append('x');
} else {
result.Append("x^");
result.Append(degree);
}
}
}
}
return result.ToString();
}
public function Equals(other:GF256Poly):Boolean
{
var result:Boolean = false;
if (this.field == other.field)
{
if (this.coefficients.Equals(other.coefficients))
{
result = true;
}
}
return result;
}
}
}

View file

@ -0,0 +1,190 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common.reedsolomon
{
/**
* <p>Implements Reed-Solomon decoding, as the name implies.</p>
*
* <p>The algorithm will not be explained here, but the following references were helpful
* in creating this implementation:</p>
*
* <ul>
* <li>Bruce Maggs.
* <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
* "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
* <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
* "Chapter 5. Generalized Reed-Solomon Codes"</a>
* (see discussion of Euclidean algorithm)</li>
* </ul>
*
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
* port of his C++ Reed-Solomon implementation.</p>
*
* @author Sean Owen
* @author William Rucklidge
* @author sanfordsquires
*/
public class ReedSolomonDecoder
{
private var field:GF256;
public function ReedSolomonDecoder(field:GF256) {
this.field = field;
}
/**
* <p>Decodes given set of received codewords, which include both data and error-correction
* codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
* in the input.</p>
*
* @param received data and error-correction codewords
* @param twoS number of error-correction codewords available
* @throws ReedSolomonException if decoding fails for any reason
*/
public function decode(received:Array, twoS:int):void {
var poly:GF256Poly = new GF256Poly(field, received);
var syndromeCoefficients:Array = new Array(twoS);
var dataMatrix:Boolean = (field == GF256.DATA_MATRIX_FIELD);
var noError:Boolean = true;
for (var i:int = 0; i < twoS; i++) {
// Thanks to sanfordsquires for this fix:
var eval:int = poly.evaluateAt(field.exp(dataMatrix ? i + 1 : i));
syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;
if (eval != 0) {
noError = false;
}
}
if (noError) {
return;
}
var syndrome:GF256Poly = new GF256Poly(field, syndromeCoefficients);
var sigmaOmega:Array = runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
var sigma:GF256Poly = sigmaOmega[0];
var omega:GF256Poly = sigmaOmega[1];
var errorLocations:Array = findErrorLocations(sigma);
var errorMagnitudes:Array = findErrorMagnitudes(omega, errorLocations, dataMatrix);
for (var j:int = 0; j < errorLocations.length; j++) {
var position:int = received.length - 1 - field.log(errorLocations[j]);
if (position < 0) {
throw new ReedSolomonException("Bad error location");
}
received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[j]);
}
}
private function runEuclideanAlgorithm(a:GF256Poly, b:GF256Poly, R:int ):Array
{
// Assume a's degree is >= b's
if (a.getDegree() < b.getDegree()) {
var temp:GF256Poly = a;
a = b;
b = temp;
}
var rLast:GF256Poly = a;
var r:GF256Poly = b;
var sLast:GF256Poly = field.getOne();
var s:GF256Poly = field.getZero();
var tLast:GF256Poly = field.getZero();
var t:GF256Poly = field.getOne();
// Run Euclidean algorithm until r's degree is less than R/2
while (r.getDegree() >= R / 2) {
var rLastLast:GF256Poly = rLast;
var sLastLast:GF256Poly = sLast;
var tLastLast:GF256Poly = tLast;
rLast = r;
sLast = s;
tLast = t;
// Divide rLastLast by rLast, with quotient in q and remainder in r
if (rLast.isZero()) {
// Oops, Euclidean algorithm already terminated?
throw new ReedSolomonException("r_{i-1} was zero");
}
r = rLastLast;
var q:GF256Poly = field.getZero();
var denominatorLeadingTerm:int = rLast.getCoefficient(rLast.getDegree());
var dltInverse:int = field.inverse(denominatorLeadingTerm);
while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
var degreeDiff:int = r.getDegree() - rLast.getDegree();
var scale:int = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);
q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));
r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
}
s = q.multiply(sLast).addOrSubtract(sLastLast);
t = q.multiply(tLast).addOrSubtract(tLastLast);
}
var sigmaTildeAtZero:int = t.getCoefficient(0);
if (sigmaTildeAtZero == 0) {
throw new ReedSolomonException("sigmaTilde(0) was zero");
}
var inverse:int = field.inverse(sigmaTildeAtZero);
var sigma:GF256Poly = t.multiply(inverse);
var omega:GF256Poly = r.multiply(inverse);
return [sigma, omega];
}
private function findErrorLocations(errorLocator:GF256Poly):Array{
// This is a direct application of Chien's search
var numErrors:int = errorLocator.getDegree();
if (numErrors == 1) { // shortcut
return [errorLocator.getCoefficient(1)];
}
var result:Array = new Array(numErrors);
var e:int = 0;
for (var i:int = 1; i < 256 && e < numErrors; i++) {
if (errorLocator.evaluateAt(i) == 0) {
result[e] = field.inverse(i);
e++;
}
}
if (e != numErrors) {
throw new ReedSolomonException("Error locator degree does not match number of roots");
}
return result;
}
private function findErrorMagnitudes(errorEvaluator:GF256Poly , errorLocations:Array, dataMatrix:Boolean):Array {
// This is directly applying Forney's Formula
var s:int = errorLocations.length;
var result:Array = new Array(s);
for (var i:int = 0; i < s; i++) {
var xiInverse:int = field.inverse(errorLocations[i]);
var denominator:int = 1;
for (var j:int = 0; j < s; j++) {
if (i != j) {
denominator = field.multiply(denominator,
GF256.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
}
}
result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),
field.inverse(denominator));
// Thanks to sanfordsquires for this fix:
if (dataMatrix) {
result[i] = field.multiply(result[i], xiInverse);
}
}
return result;
}
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common.reedsolomon
{
public class ReedSolomonEncoder
{
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private var Field:GF256 ;
private var cachedGenerators:ArrayList;
public function ReedSolomonEncoder(field:GF256 ) {
if (GF256.QR_CODE_FIELD != field) {
throw new IllegalArgumentException("Only QR Code is supported at this time");
}
this.Field = field;
this.cachedGenerators = new ArrayList();
cachedGenerators.Add(new GF256Poly(field, [ 1 ]));
}
private function buildGenerator(degree:int):GF256Poly {
if (degree >= cachedGenerators.Count) {
var lastGenerator:GF256Poly = cachedGenerators.getObjectByIndex(cachedGenerators.Count - 1) as GF256Poly;
for (var d:int = cachedGenerators.Count; d <= degree; d++)
{
var nextGenerator:GF256Poly = lastGenerator.multiply(new GF256Poly(Field, [ 1, Field.exp(d - 1)]));
cachedGenerators.Add(nextGenerator);
lastGenerator = nextGenerator;
}
}
return (cachedGenerators.getObjectByIndex(degree) as GF256Poly);
}
public function encode( toEncode:Array, ecBytes:int) :void{
if (ecBytes == 0) {
throw new IllegalArgumentException("No error correction bytes");
}
var dataBytes:int = toEncode.length - ecBytes;
if (dataBytes <= 0) {
throw new IllegalArgumentException("No data bytes provided");
}
var generator:GF256Poly = buildGenerator(ecBytes);
var infoCoefficients:Array = new Array(dataBytes);
//System.Array.Copy(toEncode, 0, infoCoefficients, 0, dataBytes);
for (var ii:int=0;ii<dataBytes;ii++)
{
infoCoefficients[ii] = toEncode[ii];
}
var info:GF256Poly = new GF256Poly(this.Field, infoCoefficients);
info = info.multiplyByMonomial(ecBytes, 1);
var remainder:GF256Poly = info.divide(generator)[1];
var coefficients:Array = remainder.getCoefficients();
var numZeroCoefficients:int = ecBytes - coefficients.length;
for (var i:int = 0; i < numZeroCoefficients; i++) {
toEncode[dataBytes + i] = 0;
}
//System.Array.Copy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);
for(var jj:int=0;jj < coefficients.length;jj++)
{
toEncode[dataBytes + numZeroCoefficients + jj] = coefficients[jj];
}
}
}
}

View file

@ -0,0 +1,11 @@
package com.google.zxing.common.reedsolomon
{
public class ReedSolomonException extends Error
{
public function ReedSolomonException(message:String="")
{
super(message);
}
}
}

View file

@ -0,0 +1,155 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.common
{
import mx.messaging.AbstractConsumer;
/**
* This class implements an array of unsigned bytes.
*
* @author dswitkin@google.com (Daniel Switkin)
*/ public class zxingByteArray
{
private static var INITIAL_SIZE:int = 32;
// BAS : made public for debugging
private var bytes:Array;
private var Size:int;
public function zxingByteArray(size:Object=null)
{
if (size == null)
{
bytes = null;
this.Size = 0;
}
else if (size is int)
{
bytes = new Array(int(size));
this.Size = int(size);
}
else if (size is Array)
{
bytes = (size as Array);
this.Size = size.length;
}
else
{
throw new Error("unknown type of size");
}
}
/**
* Access an unsigned byte at location index.
* @param index The index in the array to access.
* @return The unsigned value of the byte as an int.
*/
public function at(index:int):int
{
return bytes[index] & 0xff;
}
/*public function set(index:int, value:int):void
{
// Flex doesn't know bytes -> make it a byte
if (value > 127) { value = 256 - value);
bytes[index] = value;
}*/
public function setByte(index:int, value:int):void
{
// Flex doesn't know bytes -> make it a byte
if (value > 127) { value = (256 - value)*-1;}
bytes[index] = value;
}
public function getByte(index:int):int
{
return bytes[index];
}
public function size():int
{
return Size;
}
public function empty():Boolean
{
return (Size == 0);
}
public function appendByte(value:int):void
{
if (Size == 0 || Size >= bytes.length)
{
var newSize:int = Math.max(INITIAL_SIZE, Size << 1);
reserve(newSize);
}
// Flex doesn't know bytes -> make it a byte
if (value > 127) { value = (256 - value)*-1;}
bytes[Size] = value;
Size++;
}
public function reserve(capacity:int):void
{
if (bytes == null || bytes.length < capacity)
{
var newArray:Array = new Array(capacity);
if (bytes != null)
{
//System.Array.Copy(bytes, 0, newArray, 0, bytes.length);
for (var i:int=0;i<bytes.length;i++)
{
newArray[i] = bytes[i];
}
}
bytes = newArray;
}
}
// Copy count bytes from array source starting at offset.
public function _set(source:Array, offset:int, count:int):void
{
if (source == null)
{
this.bytes[offset] = count;
}
else
{
bytes = new Array(count);
Size = count;
for (var x:int = 0; x < count; x++)
{
// Flex doesn't know bytes -> make it a byte
if (source[offset + x] > 127)
{
bytes[x] = (256-source[offset + x])*-1;
}
else
{
bytes[x] = source[offset + x];
}
}
}
}
}
}

View file

@ -0,0 +1,161 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix
{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.Reader;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.DetectorResult;
import com.google.zxing.datamatrix.decoder.Decoder;
import com.google.zxing.datamatrix.detector.Detector;
import com.google.zxing.common.flexdatatypes.HashTable;
public class DataMatrixReader implements Reader
{
/**
* This implementation can detect and decode Data Matrix codes in an image.
*
* @author bbrown@google.com (Brian Brown)
*/
public function DataMatrixReader()
{}
private static var NO_POINTS:Array = new Array();
private var decoder:Decoder = new Decoder();
/**
* Locates and decodes a Data Matrix code in an image.
*
* @return a String representing the content encoded by the Data Matrix code
* @throws ReaderException if a Data Matrix code cannot be found, or cannot be decoded
*/
/* public function decode( image:MonochromeBitmapSource):Result {
return decode(image, null);
}
*/
public function decode( image:BinaryBitmap, hints:HashTable=null):Result
{
var decoderResult:DecoderResult ;
var points:Array;
if (hints != null && hints.ContainsKey(DecodeHintType.PURE_BARCODE))
{
var bits:BitMatrix = extractPureBits(image.getBlackMatrix());
decoderResult = decoder.decode(bits);
points = NO_POINTS;
}
else
{
var bm:BitMatrix = image.getBlackMatrix();
var detectorResult:DetectorResult = new Detector(bm).detect();
decoderResult = decoder.decode(detectorResult.getBits());
points = detectorResult.getPoints();
}
var result:Result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.DATAMATRIX);
if (decoderResult.getByteSegments() != null) {
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult.getByteSegments());
}
if (decoderResult.getECLevel() != null)
{
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.getECLevel().toString());
}
return result;
}
/**
* This method detects a Data Matrix code in a "pure" image -- that is, pure monochrome image
* which contains only an unrotated, unskewed, image of a Data Matrix code, with some white border
* around it. This is a specialized method that works exceptionally fast in this special
* case.
*/
private static function extractPureBits(image:BitMatrix ):BitMatrix {
// Now need to determine module size in pixels
var height:int = image.getHeight();
var width:int = image.getWidth();
var minDimension:int = Math.min(height, width);
// First, skip white border by tracking diagonally from the top left down and to the right:
var borderWidth:int = 0;
while (borderWidth < minDimension && !image._get(borderWidth, borderWidth)) {
borderWidth++;
}
if (borderWidth == minDimension) {
throw new ReaderException("DataMatrixReader : extractPureBits : borderWidth == minDimension");
}
// And then keep tracking across the top-left black module to determine module size
var moduleEnd:int = borderWidth + 1;
while (moduleEnd < width && image._get(moduleEnd, borderWidth)) {
moduleEnd++;
}
if (moduleEnd == width) {
throw new ReaderException("DataMatrixReader : extractPureBits : moduleEnd == width");
}
var moduleSize:int = moduleEnd - borderWidth;
// And now find where the bottommost black module on the first column ends
var columnEndOfSymbol:int = height - 1;
while (columnEndOfSymbol >= 0 && !image._get(borderWidth, columnEndOfSymbol)) {
columnEndOfSymbol--;
}
if (columnEndOfSymbol < 0) {
throw new ReaderException("DataMatrixReader : extractPureBits : columnEndOfSymbol < 0");
}
columnEndOfSymbol++;
// Make sure width of barcode is a multiple of module size
if ((columnEndOfSymbol - borderWidth) % moduleSize != 0) {
throw new ReaderException("DataMatrixReader : extractPureBits : barcode width is not a multiple of module size");
}
var dimension:int = (columnEndOfSymbol - borderWidth) / moduleSize;
// Push in the "border" by half the module width so that we start
// sampling in the middle of the module. Just in case the image is a
// little off, this will help recover.
borderWidth += moduleSize >> 1;
var sampleDimension:int = borderWidth + (dimension - 1) * moduleSize;
if (sampleDimension >= width || sampleDimension >= height) {
throw new ReaderException("DataMatrixReader : extractPureBits : sampleDimension ("+sampleDimension+") is large than width ("+width+") or height ("+height+")");
}
// Now just read off the bits
var bits:BitMatrix = new BitMatrix(dimension);
for (var i:int = 0; i < dimension; i++) {
var iOffset:int = borderWidth + i * moduleSize;
for (var j:int = 0; j < dimension; j++) {
if (image._get(borderWidth + j * moduleSize, iOffset)) {
bits._set(j, i);
}
}
}
return bits;
}
}
}

View file

@ -0,0 +1,458 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder
{
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.ReaderException;
/**
* @author bbrown@google.com (Brian Brown)
*/
public class BitMatrixParser
{
private var mappingBitMatrix:BitMatrix;
private var readMappingMatrix:BitMatrix;
private var version:Version;
/**
* @param bitMatrix {@link BitMatrix} to parse
* @throws ReaderException if dimension is < 10 or > 144 or not 0 mod 2
*/
public function BitMatrixParser(bitMatrix:BitMatrix) {
var dimension:int = bitMatrix.getDimension();
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) {
throw new ReaderException("BitMatrixParser : Dimension out of range :"+dimension+" range 11~143 or uneven number");
}
version = readVersion(bitMatrix);
this.mappingBitMatrix = extractDataRegion(bitMatrix);
// TODO(bbrown): Make this work for rectangular symbols
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getDimension());
}
/**
* <p>Creates the version object based on the dimension of the original bit matrix from
* the datamatrix code.</p>
*
* <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p>
*
* @param bitMatrix Original {@link BitMatrix} including alignment patterns
* @return {@link Version} encapsulating the Data Matrix Code's "version"
* @throws ReaderException if the dimensions of the mapping matrix are not valid
* Data Matrix dimensions.
*/
public function readVersion(bitMatrix:BitMatrix ):Version {
if (version != null) {
return version;
}
// TODO(bbrown): make this work for rectangular dimensions as well.
var numRows:int = bitMatrix.getDimension();
var numColumns:int = numRows;
return Version.getVersionForDimensions(numRows, numColumns);;
}
/**
* <p>Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns)
* in the correct order in order to reconstitute the codewords bytes contained within the
* Data Matrix Code.</p>
*
* @return bytes encoded within the Data Matrix Code
* @throws ReaderException if the exact number of bytes expected is not read
*/
public function readCodewords():Array {
var result:Array = new Array(version.getTotalCodewords());
var resultOffset:int = 0;
var row:int = 4;
var column:int = 0;
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
var numRows:int = mappingBitMatrix.getDimension();
var numColumns:int = numRows;
var corner1Read:Boolean = false;
var corner2Read:Boolean = false;
var corner3Read:Boolean = false;
var corner4Read:Boolean = false;
// Read all of the codewords
do {
// Check the four corner cases
if ((row == numRows) && (column == 0) && !corner1Read) {
result[resultOffset++] = int(readCorner1(numRows, numColumns));
row -= 2;
column +=2;
corner1Read = true;
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) {
result[resultOffset++] = int(readCorner2(numRows, numColumns));
row -= 2;
column +=2;
corner2Read = true;
} else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) {
result[resultOffset++] = int(readCorner3(numRows, numColumns));
row -= 2;
column +=2;
corner3Read = true;
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) {
result[resultOffset++] = int(readCorner4(numRows, numColumns));
row -= 2;
column +=2;
corner4Read = true;
} else {
// Sweep upward diagonally to the right
do {
if ((row < numRows) && (column >= 0) && !readMappingMatrix._get(column,row)) {
result[resultOffset++] = readUtah(row, column, numRows, numColumns);
}
row -= 2;
column +=2;
} while ((row >= 0) && (column < numColumns));
row += 1;
column +=3;
// Sweep downward diagonally to the left
do {
if ((row >= 0) && (column < numColumns) && !readMappingMatrix._get(column,row)) {
result[resultOffset++] = readUtah(row, column, numRows, numColumns);
}
row += 2;
column -=2;
} while ((row < numRows) && (column >= 0));
row += 3;
column +=1;
}
} while ((row < numRows) || (column < numColumns));
if (resultOffset != version.getTotalCodewords()) {
throw new ReaderException("BitMatrixParser : readCodewords : resultOffset != version.getTotalCodewords() : "+resultOffset +" - "+ version.getTotalCodewords());
}
// BAS : extra code for Flex : result should be a signed byte array (bit 7 = sign)
for (var jj:int=0;jj<result.length;jj++)
{
if ((result[jj] & 128) > 0 )
{
result[jj] = (result[jj] & 127) - 128
}
}
return result;
}
/**
* <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>
*
* @param row Row to read in the mapping matrix
* @param column Column to read in the mapping matrix
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return value of the given bit in the mapping matrix
*/
public function readModule(row:int , column:int , numRows:int , numColumns:int ):Boolean {
// Adjust the row and column indices based on boundary wrapping
if (row < 0) {
row += numRows;
column += 4 - ((numRows + 4) & 0x07);
}
if (column < 0) {
column += numColumns;
row += 4 - ((numColumns + 4) & 0x07);
}
readMappingMatrix._set(column,row);
return mappingBitMatrix._get(column,row);
}
/**
* <p>Reads the 8 bits of the standard utah shaped pattern.</p>
*
* <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
*
* @param row Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
* @param column Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the utah shape
*/
public function readUtah(row:int , column:int , numRows:int , numColumns:int ):int
{
var currentByte:int = 0;
if (readModule(row - 2, column - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 2, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Reads the 8 bits of the special corner condition 1.</p>
*
* <p>See ISO 16022:2006, Figure F.3</p>
*
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 1
*/
public function readCorner1(numRows:int, numColumns:int):int {
var currentByte:int = 0;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(2, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(3, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Reads the 8 bits of the special corner condition 2.</p>
*
* <p>See ISO 16022:2006, Figure F.4</p>
*
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 2
*/
public function readCorner2(numRows:int, numColumns:int):int {
var currentByte:int = 0;
if (readModule(numRows - 3, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 2, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 4, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Reads the 8 bits of the special corner condition 3.</p>
*
* <p>See ISO 16022:2006, Figure F.5</p>
*
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 3
*/
public function readCorner3(numRows:int, numColumns:int):int {
var currentByte:int = 0;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Reads the 8 bits of the special corner condition 4.</p>
*
* <p>See ISO 16022:2006, Figure F.6</p>
*
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 4
*/
public function readCorner4(numRows:int, numColumns:int):int {
var currentByte:int = 0;
if (readModule(numRows - 3, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 2, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(2, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(3, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Extracts the data region from a {@link BitMatrix} that contains
* alignment patterns.</p>
*
* @param bitMatrix Original {@link BitMatrix} with alignment patterns
* @return BitMatrix that has the alignment patterns removed
*/
public function extractDataRegion( bitMatrix:BitMatrix):BitMatrix {
var symbolSizeRows:int = version.getSymbolSizeRows();
var symbolSizeColumns:int = version.getSymbolSizeColumns();
// TODO(bbrown): Make this work with rectangular codes
if (bitMatrix.getDimension() != symbolSizeRows) {
throw new IllegalArgumentException("Dimension of bitMarix must match the version size");
}
var dataRegionSizeRows:int = version.getDataRegionSizeRows();
var dataRegionSizeColumns:int = version.getDataRegionSizeColumns();
var numDataRegionsRow:int = symbolSizeRows / dataRegionSizeRows;
var numDataRegionsColumn:int = symbolSizeColumns / dataRegionSizeColumns;
var sizeDataRegionRow:int = numDataRegionsRow * dataRegionSizeRows;
//int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
// TODO(bbrown): Make this work with rectangular codes
var bitMatrixWithoutAlignment:BitMatrix = new BitMatrix(sizeDataRegionRow);
for (var dataRegionRow:int = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
var dataRegionRowOffset:int = dataRegionRow * dataRegionSizeRows;
for (var dataRegionColumn:int = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
var dataRegionColumnOffset:int = dataRegionColumn * dataRegionSizeColumns;
for (var i:int = 0; i < dataRegionSizeRows; ++i) {
var readRowOffset:int = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
var writeRowOffset:int = dataRegionRowOffset + i;
for (var j:int = 0; j < dataRegionSizeColumns; ++j) {
var readColumnOffset:int = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
if (bitMatrix._get(readColumnOffset, readRowOffset)) {
var writeColumnOffset:int = dataRegionColumnOffset + j;
bitMatrixWithoutAlignment._set(writeColumnOffset, writeRowOffset);
}
}
}
}
}
return bitMatrixWithoutAlignment;
}
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder
{
/**
* <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
* is represented by an instance of this class.</p>
*
* @author bbrown@google.com (Brian Brown)
*/
public class DataBlock
{
import com.google.zxing.datamatrix.decoder.ECB;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private var numDataCodewords:int;
private var codewords:Array;
public function DataBlock(numDataCodewords:int, codewords:Array) {
this.numDataCodewords = numDataCodewords;
this.codewords = codewords;
}
/**
* <p>When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them.
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
* method will separate the data into original blocks.</p>
*
* @param rawCodewords bytes as read directly from the Data Matrix Code
* @param version version of the Data Matrix Code
* @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the
* Data Matrix Code
*/
public static function getDataBlocks(rawCodewords:Array,
version:Version):Array {
// Figure out the number and size of data blocks used by this version
var ecBlocks:ECBlocks = version.getECBlocks();
// First count the total number of data blocks
var totalBlocks:int = 0;
var ecBlockArray:Array = ecBlocks.getECBlocks();
for (var i:int = 0; i < ecBlockArray.length; i++) {
totalBlocks += ecBlockArray[i].getCount();
}
// Now establish DataBlocks of the appropriate size and number of data codewords
var result:Array = new Array(totalBlocks);
var numResultBlocks:int = 0;
for (var j:int = 0; j < ecBlockArray.length; j++) {
var ecBlock:ECB = ecBlockArray[j];
for (var i3:int = 0; i3 < ecBlock.getCount(); i3++) {
var numDataCodewords:int = ecBlock.getDataCodewords();
var numBlockCodewords:int = ecBlocks.getECCodewords() + numDataCodewords;
result[numResultBlocks++] = new DataBlock(numDataCodewords, new Array(numBlockCodewords));
}
}
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 less byte. Figure out where these start.
// TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144
var longerBlocksTotalCodewords:int = result[0].codewords.length;
//int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1;
var longerBlocksNumDataCodewords:int = longerBlocksTotalCodewords - ecBlocks.getECCodewords();
var shorterBlocksNumDataCodewords:int = longerBlocksNumDataCodewords - 1;
// The last elements of result may be 1 element shorter for 144 matrix
// first fill out as many elements as all of them have minus 1
var rawCodewordsOffset:int = 0;
for (var i2:int = 0; i2 < shorterBlocksNumDataCodewords; i2++) {
for (var j2:int = 0; j2 < numResultBlocks; j2++)
{
result[j2].codewords[i2] = rawCodewords[rawCodewordsOffset];
rawCodewordsOffset++;
}
}
// Fill out the last data block in the longer ones
var specialVersion:Boolean = version.getVersionNumber() == 24;
var numLongerBlocks:int = specialVersion ? 8 : numResultBlocks;
for (var j3:int = 0; j3 < numLongerBlocks; j3++) {
result[j3].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset];
rawCodewordsOffset++;
}
// Now add in error correction blocks
var max:int = result[0].codewords.length;
for (var i4:int = longerBlocksNumDataCodewords; i4 < max; i4++) {
for (var j4:int = 0; j4 < numResultBlocks; j4++) {
var iOffset:int = (specialVersion && j4 > 7) ? i4 - 1 : i4;
result[j4].codewords[iOffset] = rawCodewords[rawCodewordsOffset];
rawCodewordsOffset++;
}
}
if (rawCodewordsOffset != rawCodewords.length) {
throw new IllegalArgumentException("DataBlock : getDataBlocks : rawCodewordsOffset != rawCodewords.length : "+rawCodewordsOffset +" - "+rawCodewords.length);
}
return result;
}
public function getNumDataCodewords():int {
return numDataCodewords;
}
public function getCodewords():Array {
return codewords;
}
}
}

View file

@ -0,0 +1,464 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder
{
/**
* <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes
* in one Data Matrix Code. This class decodes the bits back into text.</p>
*
* <p>See ISO 16022:2006, 5.2.1 - 5.2.9.2</p>
*
* @author bbrown@google.com (Brian Brown)
* @author Sean Owen
*/
public class DecodedBitStreamParser
{
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.BitSource;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.zxingByteArray;
import com.google.zxing.ReaderException;
/**
* See ISO 16022:2006, Annex C Table C.1
* The C40 Basic Character Set (*'s used for placeholders for the shift values)
*/
private static var C40_BASIC_SET_CHARS:Array = [
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];
private static var C40_SHIFT2_SET_CHARS:Array = [
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'
];
/**
* See ISO 16022:2006, Annex C Table C.2
* The Text Basic Character Set (*'s used for placeholders for the shift values)
*/
private static var TEXT_BASIC_SET_CHARS:Array = [
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
];
private static var TEXT_SHIFT3_SET_CHARS:Array = [
'\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', String.fromCharCode(0x127)
];
public static const PAD_ENCODE:int = 0; // Not really an encoding
public static const ASCII_ENCODE:int = 1;
public static const C40_ENCODE:int = 2;
public static const TEXT_ENCODE:int = 3;
public static const ANSIX12_ENCODE:int = 4;
public static const EDIFACT_ENCODE:int = 5;
public static const BASE256_ENCODE:int = 6;
public function DecodedBitStreamParser() {
}
public static function decode(bytes:Array ):DecoderResult {
var bits:BitSource = new BitSource(bytes);
var result:StringBuilder = new StringBuilder();
var resultTrailer:StringBuilder = new StringBuilder(0);
var byteSegments:ArrayList = new ArrayList(1);
var mode:int = DecodedBitStreamParser.ASCII_ENCODE;
do {
if (mode == ASCII_ENCODE) {
mode = decodeAsciiSegment(bits, result, resultTrailer);
} else {
switch (mode) {
case C40_ENCODE:
decodeC40Segment(bits, result);
break;
case TEXT_ENCODE:
decodeTextSegment(bits, result);
break;
case ANSIX12_ENCODE:
decodeAnsiX12Segment(bits, result);
break;
case EDIFACT_ENCODE:
decodeEdifactSegment(bits, result);
break;
case BASE256_ENCODE:
decodeBase256Segment(bits, result, byteSegments);
break;
default:
throw new ReaderException("DecodedBitStreamParser : decode : unknown mode : "+mode);
}
mode = ASCII_ENCODE;
}
} while (mode != PAD_ENCODE && bits.available() > 0);
if (resultTrailer.length > 0) {
result.Append(resultTrailer);
}
return new DecoderResult(bytes, result.ToString(), (byteSegments.Count === 0) ? null : byteSegments, null);
}
/**
* See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
*/
private static function decodeAsciiSegment(bits:BitSource, result:StringBuilder, resultTrailer:StringBuilder):int
{
var upperShift:Boolean = false;
do {
var oneByte:int = bits.readBits(8);
if (oneByte == 0) {
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 0");
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
oneByte = upperShift ? (oneByte + 128) : oneByte;
upperShift = false;
result.Append(String.fromCharCode(oneByte - 1));
return ASCII_ENCODE;
} else if (oneByte == 129) { // Pad
return PAD_ENCODE;
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
var value:int = oneByte - 130;
if (value < 10) { // padd with '0' for single digit values
result.Append('0');
}
result.Append(value);
} else if (oneByte == 230) { // Latch to C40 encodation
return C40_ENCODE;
} else if (oneByte == 231) { // Latch to Base 256 encodation
return BASE256_ENCODE;
} else if (oneByte == 232) { // FNC1
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 232 ");
} else if (oneByte == 233) { // Structured Append
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 233");
} else if (oneByte == 234) { // Reader Programming
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 234");
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
upperShift = true;
} else if (oneByte == 236) { // 05 Macro
result.Append("[)>\u001E05\u001D");
resultTrailer.Insert(0, "\u001E\u0004");
} else if (oneByte == 237) { // 06 Macro
result.Append("[)>\u001E06\u001D");
resultTrailer.Insert(0, "\u001E\u0004");
} else if (oneByte == 238) { // Latch to ANSI X12 encodation
return ANSIX12_ENCODE;
} else if (oneByte == 239) { // Latch to Text encodation
return TEXT_ENCODE;
} else if (oneByte == 240) { // Latch to EDIFACT encodation
return EDIFACT_ENCODE;
} else if (oneByte == 241) { // ECI Character
// TODO(bbrown): I think we need to support ECI
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 241");
} else if (oneByte >= 242) { // Not to be used in ASCII encodation
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 242");
}
} while (bits.available() > 0);
return ASCII_ENCODE;
}
/**
* See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
*/
private static function decodeC40Segment(bits:BitSource , result:StringBuilder ):void {
// Three C40 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
var upperShift:Boolean = false;
var cValues:Array = new Array(3);
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8) {
return;
}
var firstByte:int = bits.readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
var shift:int = 0;
for (var i:int = 0; i < 3; i++) {
var cValue:int = cValues[i];
switch (shift) {
case 0:
if (cValue < 3) {
shift = cValue + 1;
} else {
if (upperShift) {
result.Append(C40_BASIC_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result.Append(C40_BASIC_SET_CHARS[cValue]);
}
}
break;
case 1:
if (upperShift) {
result.Append(cValue + 128);
upperShift = false;
} else {
result.Append(cValue);
}
shift = 0;
break;
case 2:
if (cValue < 27) {
if (upperShift) {
result.Append(C40_SHIFT2_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result.Append(C40_SHIFT2_SET_CHARS[cValue]);
}
} else if (cValue == 27) { // FNC1
throw new ReaderException("DecodedBitStreamParser : decodeC40Segment : cValue = 27");
} else if (cValue == 30) { // Upper Shift
upperShift = true;
} else {
throw new ReaderException("DecodedBitStreamParser : decodeC40Segment : cValue = no match:"+cValue);
}
shift = 0;
break;
case 3:
if (upperShift) {
result.Append(cValue + 224);
upperShift = false;
} else {
result.Append(cValue + 96);
}
shift = 0;
break;
default:
throw new ReaderException("DecodedBitStreamParser : decodeC40Segment : no match for shift:"+shift);
}
}
} while (bits.available() > 0);
}
/**
* See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
*/
private static function decodeTextSegment(bits:BitSource , result:StringBuilder ):void {
// Three Text values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
var upperShift:Boolean = false;
var cValues:Array = new Array(3);
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8) {
return;
}
var firstByte:int = bits.readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
var shift:int = 0;
for (var i:int = 0; i < 3; i++) {
var cValue:int = cValues[i];
switch (shift) {
case 0:
if (cValue < 3) {
shift = cValue + 1;
} else {
if (upperShift) {
result.Append(TEXT_BASIC_SET_CHARS[cValue]);
upperShift = false;
} else {
result.Append(TEXT_BASIC_SET_CHARS[cValue]);
}
}
break;
case 1:
if (upperShift) {
result.Append(cValue + 128);
upperShift = false;
} else {
result.Append(cValue);
}
shift = 0;
break;
case 2:
// Shift 2 for Text is the same encoding as C40
if (cValue < 27) {
if (upperShift) {
result.Append(C40_SHIFT2_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result.Append(C40_SHIFT2_SET_CHARS[cValue]);
}
} else if (cValue == 27) { // FNC1
throw new ReaderException( "DecodedBitStreamParser : decodeTextSegment : cValue = 27");
} else if (cValue == 30) { // Upper Shift
upperShift = true;
} else {
throw new ReaderException("DecodedBitStreamParser : decodeTextSegment : no match for cValue:"+cValue);
}
shift = 0;
break;
case 3:
if (upperShift) {
result.Append(TEXT_SHIFT3_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result.Append(TEXT_SHIFT3_SET_CHARS[cValue]);
}
shift = 0;
break;
default:
throw new ReaderException("DecodedBitStreamParser : decodeTextSegment : no match for shift"+shift);
}
}
} while (bits.available() > 0);
}
/**
* See ISO 16022:2006, 5.2.7
*/
private static function decodeAnsiX12Segment(bits:BitSource, result:StringBuilder):void {
// Three ANSI X12 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
var cValues:Array = new Array(3);
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8) {
return;
}
var firstByte:int = bits.readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
for (var i:int = 0; i < 3; i++) {
var cValue:int = cValues[i];
if (cValue == 0) { // X12 segment terminator <CR>
result.Append('\r');
} else if (cValue == 1) { // X12 segment separator *
result.Append('*');
} else if (cValue == 2) { // X12 sub-element separator >
result.Append('>');
} else if (cValue == 3) { // space
result.Append(' ');
} else if (cValue < 14) { // 0 - 9
result.Append(cValue + 44);
} else if (cValue < 40) { // A - Z
result.Append(cValue + 51);
} else {
throw new ReaderException("DecodedBitStreamParser : decodeTextSegment : no match for cValue : "+ cValue);
}
}
} while (bits.available() > 0);
}
private static function parseTwoBytes(firstByte:int, secondByte:int, result:Array):void {
var fullBitValue:int = (firstByte << 8) + secondByte - 1;
var temp:int = fullBitValue / 1600;
result[0] = temp;
fullBitValue -= temp * 1600;
temp = fullBitValue / 40;
result[1] = temp;
result[2] = fullBitValue - temp * 40;
}
/**
* See ISO 16022:2006, 5.2.8 and Annex C Table C.3
*/
private static function decodeEdifactSegment(bits:BitSource, result:StringBuilder):void {
var unlatch:Boolean = false;
do {
// If there is only two or less bytes left then it will be encoded as ASCII
if (bits.available() <= 16) {
return;
}
for (var i:int = 0; i < 4; i++) {
var edifactValue:int = bits.readBits(6);
// Check for the unlatch character
if (edifactValue == 0x2B67) { // 011111
unlatch = true;
// If we encounter the unlatch code then continue reading because the Codeword triple
// is padded with 0's
}
if (!unlatch) {
if ((edifactValue & 32) == 0) { // no 1 in the leading (6th) bit
edifactValue |= 64; // Add a leading 01 to the 6 bit binary value
}
result.Append(edifactValue);
}
}
} while (!unlatch && bits.available() > 0);
}
/**
* See ISO 16022:2006, 5.2.9 and Annex B, B.2
*/
private static function decodeBase256Segment(bits:BitSource, result:StringBuilder , byteSegments:ArrayList ):void {
// Figure out how long the Base 256 Segment is.
var d1:int = bits.readBits(8);
var count:int;
if (d1 == 0) { // Read the remainder of the symbol
count = bits.available() / 8;
} else if (d1 < 250) {
count = d1;
} else {
count = 250 * (d1 - 249) + bits.readBits(8);
}
var bytes:Array = new Array(count);
for (var i:int = 0; i < count; i++)
{
bytes[i] = unrandomize255State(bits.readBits(8), i);
}
byteSegments.Add(bytes);
try
{
//result.Append(System.Text.Encoding.GetEncoding("iso-8859-1").GetString(bytes));
var str:String = "";
for(var i2:int=0;i2<bytes.length;i2++)
{
str = str + String.fromCharCode(bytes[i2]);
}
result.Append(str);
} catch (uee:Error ) {
throw new Error("Platform does not support required encoding: " + uee);
}
}
/**
* See ISO 16022:2006, Annex B, B.2
*/
private static function unrandomize255State(randomizedBase256Codeword:int,
base256CodewordPosition:int ):int {
var pseudoRandomNumber:int = ((149 * base256CodewordPosition) % 255) + 1;
var tempVariable:int = randomizedBase256Codeword - pseudoRandomNumber;
var result:int = (tempVariable >= 0 ? tempVariable : (tempVariable + 256));
return result;
}
}
}

View file

@ -0,0 +1,141 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder
{
/**
* <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting
* the Data Matrix Code from an image.</p>
*
* @author bbrown@google.com (Brian Brown)
*/
public class Decoder
{
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
import com.google.zxing.common.reedsolomon.ReedSolomonException;
import com.google.zxing.common.reedsolomon.GF256;
import com.google.zxing.ReaderException;
private var rsDecoder:ReedSolomonDecoder;
public function Decoder() {
rsDecoder = new ReedSolomonDecoder(GF256.DATA_MATRIX_FIELD);
}
/**
* <p>Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans.
* "true" is taken to mean a black module.</p>
*
* @param image booleans representing white/black Data Matrix Code modules
* @return text and bytes encoded within the Data Matrix Code
* @throws ReaderException if the Data Matrix Code cannot be decoded
*/
public function decode (image:Object):DecoderResult
{
if (image is Array) { return decode_Array(image as Array); }
else if (image is BitMatrix) { return decode_BitMatrix(image as BitMatrix); }
else { throw new Error('Decoder : decode : unknown type of image'); }
}
public function decode_Array(image:Array):DecoderResult {
var dimension:int = image.length;
var bits:BitMatrix = new BitMatrix(dimension);
for (var i:int = 0; i < dimension; i++) {
for (var j:int = 0; j < dimension; j++) {
if (image[i][j]) {
bits._set(j, i);
}
}
}
return decode(bits);
}
/**
* <p>Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken
* to mean a black module.</p>
*
* @param bits booleans representing white/black Data Matrix Code modules
* @return text and bytes encoded within the Data Matrix Code
* @throws ReaderException if the Data Matrix Code cannot be decoded
*/
public function decode_BitMatrix(bits:BitMatrix ):DecoderResult {
// Construct a parser and read version, error-correction level
var parser:BitMatrixParser = new BitMatrixParser(bits);
var version:Version = parser.readVersion(bits);
// Read codewords
var codewords:Array = parser.readCodewords();
// Separate into data blocks
var dataBlocks:Array = DataBlock.getDataBlocks(codewords, version);
// Count total number of data bytes
var totalBytes:int = 0;
for (var i:int = 0; i < dataBlocks.length; i++) {
totalBytes += dataBlocks[i].getNumDataCodewords();
}
var resultBytes:Array = new Array(totalBytes);
var resultOffset:int = 0;
// Error-correct and copy data blocks together into a stream of bytes
for (var j:int = 0; j < dataBlocks.length; j++) {
var dataBlock:DataBlock = dataBlocks[j];
var codewordBytes:Array = dataBlock.getCodewords();
var numDataCodewords:int = dataBlock.getNumDataCodewords();
correctErrors(codewordBytes, numDataCodewords);
for (var ii:int = 0; ii < numDataCodewords; ii++) {
resultBytes[resultOffset++] = codewordBytes[ii];
}
}
// Decode the contents of that stream of bytes
return DecodedBitStreamParser.decode(resultBytes);
}
/**
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
* correct the errors in-place using Reed-Solomon error correction.</p>
*
* @param codewordBytes data and error correction codewords
* @param numDataCodewords number of codewords that are data bytes
* @throws ReaderException if error correction fails
*/
private function correctErrors( codewordBytes:Array, numDataCodewords:int ):void {
var numCodewords:int = codewordBytes.length;
// First read into an array of ints
var codewordsInts:Array = new Array(numCodewords);
for (var i:int = 0; i < numCodewords; i++) {
codewordsInts[i] = codewordBytes[i] & 0xFF;
}
var numECCodewords:int = codewordBytes.length - numDataCodewords;
try {
rsDecoder.decode(codewordsInts, numECCodewords);
} catch (rse:ReedSolomonException) {
throw new ReaderException("Decoder : correctErrors : could not decode codewords");
}
// Copy back into array of bytes -- only need to worry about the bytes that were data
// We don't care about errors in the error-correction codewords
for (var ii:int = 0; ii < numDataCodewords; ii++) {
codewordBytes[ii] = int( codewordsInts[ii]);
}
}
}
}

View file

@ -0,0 +1,27 @@
package com.google.zxing.datamatrix.decoder
{
/**
* <p>Encapsualtes the parameters for one error-correction block in one symbol version.
* This includes the number of data codewords, and the number of times a block with these
* parameters is used consecutively in the Data Matrix code version's format.</p>
*/
public class ECB {
private var count:int;
private var dataCodewords:int;
public function ECB(count:int, dataCodewords:int) {
this.count = count;
this.dataCodewords = dataCodewords;
}
public function getCount():int {
return count;
}
public function getDataCodewords():int {
return dataCodewords;
}
}
}

View file

@ -0,0 +1,39 @@
package com.google.zxing.datamatrix.decoder
{
/**
* <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
* use blocks of differing sizes within one version, so, this encapsulates the parameters for
* each set of blocks. It also holds the number of error-correction codewords per block since it
* will be the same across all blocks within one version.</p>
*/
public class ECBlocks
{
private var ecCodewords:int;
private var ecBlocks:Array;
public function ECBlocks(ecCodewords:int, ecBlocks:ECB, ecBlocks2:ECB=null)
{
this.ecCodewords = ecCodewords;
if (ecBlocks2 == null)
{
this.ecBlocks = [ecBlocks];
}
else
{
this.ecBlocks = [ecBlocks, ecBlocks2];
}
}
public function getECCodewords():int {
return ecCodewords;
}
public function getECBlocks():Array {
return ecBlocks;
}
}
}

View file

@ -0,0 +1,193 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder
{
import com.google.zxing.ReaderException;
/**
* The Version object encapsulates attributes about a particular
* size Data Matrix Code.
*
* @author bbrown@google.com (Brian Brown)
*/
public class Version
{
private static var VERSIONS:Array = buildVersions();
private var versionNumber:int;
private var symbolSizeRows:int;
private var symbolSizeColumns:int;
private var dataRegionSizeRows:int;
private var dataRegionSizeColumns:int;
private var ecBlocks:ECBlocks;
private var totalCodewords:int;
public function Version(versionNumber:int,
symbolSizeRows:int,
symbolSizeColumns:int,
dataRegionSizeRows:int,
dataRegionSizeColumns:int,
ecBlocks:ECBlocks) {
this.versionNumber = versionNumber;
this.symbolSizeRows = symbolSizeRows;
this.symbolSizeColumns = symbolSizeColumns;
this.dataRegionSizeRows = dataRegionSizeRows;
this.dataRegionSizeColumns = dataRegionSizeColumns;
this.ecBlocks = ecBlocks;
// Calculate the total number of codewords
var total:int = 0;
var ecCodewords:int = ecBlocks.getECCodewords();
var ecbArray:Array = ecBlocks.getECBlocks();
for (var i:int = 0; i < ecbArray.length; i++)
{
var ecBlock:ECB = ecbArray[i];
total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
}
this.totalCodewords = total;
}
public function getVersionNumber():int {
return versionNumber;
}
public function getSymbolSizeRows():int {
return symbolSizeRows;
}
public function getSymbolSizeColumns():int {
return symbolSizeColumns;
}
public function getDataRegionSizeRows():int {
return dataRegionSizeRows;
}
public function getDataRegionSizeColumns():int {
return dataRegionSizeColumns;
}
public function getTotalCodewords():int {
return totalCodewords;
}
public function getECBlocks():ECBlocks {
return ecBlocks;
}
/**
* <p>Deduces version information from Data Matrix dimensions.</p>
*
* @param numRows Number of rows in modules
* @param numColumns Number of columns in modules
* @return {@link Version} for a Data Matrix Code of those dimensions
* @throws ReaderException if dimensions do correspond to a valid Data Matrix size
*/
public static function getVersionForDimensions(numRows:int,numColumns:int):Version {
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
throw new ReaderException("Version : getVersionForDimensions : numColumns ("+numRows+") or numRows ("+numColumns+") uneven ");
}
// TODO(bbrown): This is doing a linear search through the array of versions.
// If we interleave the rectangular versions with the square versions we could
// do a binary search.
var numVersions:int = VERSIONS.length;
for (var i:int = 0; i < numVersions; ++i){
var version:Version = VERSIONS[i];
if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns) {
return version;
}
}
throw new ReaderException("Version : getVersionForDimensions : version could not be determined");
}
public function toString():String {
return versionNumber.toString();
}
/**
* See ISO 16022:2006 5.5.1 Table 7
*/
private static function buildVersions():Array {
return [
new Version(1, 10, 10, 8, 8,
new ECBlocks(5, new ECB(1, 3))),
new Version(2, 12, 12, 10, 10,
new ECBlocks(7, new ECB(1, 5))),
new Version(3, 14, 14, 12, 12,
new ECBlocks(10, new ECB(1, 8))),
new Version(4, 16, 16, 14, 14,
new ECBlocks(12, new ECB(1, 12))),
new Version(5, 18, 18, 16, 16,
new ECBlocks(14, new ECB(1, 18))),
new Version(6, 20, 20, 18, 18,
new ECBlocks(18, new ECB(1, 22))),
new Version(7, 22, 22, 20, 20,
new ECBlocks(20, new ECB(1, 30))),
new Version(8, 24, 24, 22, 22,
new ECBlocks(24, new ECB(1, 36))),
new Version(9, 26, 26, 24, 24,
new ECBlocks(28, new ECB(1, 44))),
new Version(10, 32, 32, 14, 14,
new ECBlocks(36, new ECB(1, 62))),
new Version(11, 36, 36, 16, 16,
new ECBlocks(42, new ECB(1, 86))),
new Version(12, 40, 40, 18, 18,
new ECBlocks(48, new ECB(1, 114))),
new Version(13, 44, 44, 20, 20,
new ECBlocks(56, new ECB(1, 144))),
new Version(14, 48, 48, 22, 22,
new ECBlocks(68, new ECB(1, 174))),
new Version(15, 52, 52, 24, 24,
new ECBlocks(42, new ECB(2, 102))),
new Version(16, 64, 64, 14, 14,
new ECBlocks(56, new ECB(2, 140))),
new Version(17, 72, 72, 16, 16,
new ECBlocks(36, new ECB(4, 92))),
new Version(18, 80, 80, 18, 18,
new ECBlocks(48, new ECB(4, 114))),
new Version(19, 88, 88, 20, 20,
new ECBlocks(56, new ECB(4, 144))),
new Version(20, 96, 96, 22, 22,
new ECBlocks(68, new ECB(4, 174))),
new Version(21, 104, 104, 24, 24,
new ECBlocks(56, new ECB(6, 136))),
new Version(22, 120, 120, 18, 18,
new ECBlocks(68, new ECB(6, 175))),
new Version(23, 132, 132, 20, 20,
new ECBlocks(62, new ECB(8, 163))),
new Version(24, 144, 144, 22, 22,
new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))),
new Version(25, 8, 18, 6, 16,
new ECBlocks(7, new ECB(1, 5))),
new Version(26, 8, 32, 6, 14,
new ECBlocks(11, new ECB(1, 10))),
new Version(27, 12, 26, 10, 24,
new ECBlocks(14, new ECB(1, 16))),
new Version(28, 12, 36, 10, 16,
new ECBlocks(18, new ECB(1, 22))),
new Version(29, 16, 36, 10, 16,
new ECBlocks(24, new ECB(1, 32))),
new Version(30, 16, 48, 14, 22,
new ECBlocks(28, new ECB(1, 49)))];
}
}
}

View file

@ -0,0 +1,289 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.detector
{
import com.google.zxing.common.detector.MonochromeRectangleDetector;
import com.google.zxing.common.GridSampler;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.DetectorResult;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.ReaderException;
/**
* <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code
* is rotated or skewed, or partially obscured.</p>
*
* @author Sean Owen
*/
public class Detector
{
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.DetectorResult;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.GridSampler;
import com.google.zxing.ResultPoint;
import com.google.zxing.datamatrix.detector.ResultPointsAndTransitionsComparator;
import com.google.zxing.common.detector.MonochromeRectangleDetector;
//private static var MAX_MODULES:int = 32;
// Trick to avoid creating new int objects below -- a sort of crude copy of
// the int.valueOf(int) optimization added in Java 5, not in J2ME
//private static var INTEGERS:Array = [0, 1, 2, 3, 4];
private var image:BitMatrix ;
private var rectangleDetector:MonochromeRectangleDetector
public function Detector(image:BitMatrix)
{
this.image = image;
rectangleDetector = new MonochromeRectangleDetector(image);
}
/**
* <p>Detects a Data Matrix Code in an image.</p>
*
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
* @throws ReaderException if no Data Matrix Code can be found
*/
public function detect():DetectorResult {
var cornerPoints:Array = rectangleDetector.detect();
var pointA:ResultPoint = cornerPoints[0]
var pointB:ResultPoint = cornerPoints[1]
var pointC:ResultPoint = cornerPoints[2]
var pointD:ResultPoint = cornerPoints[3]
// Point A and D are across the diagonal from one another,
// as are B and C. Figure out which are the solid black lines
// by counting transitions
var transitions:ArrayList = new ArrayList(4);
transitions.Add(transitionsBetween(pointA, pointB));
transitions.Add(transitionsBetween(pointA, pointC));
transitions.Add(transitionsBetween(pointB, pointD));
transitions.Add(transitionsBetween(pointC, pointD));
transitions.sort_ResultPointsAndTransitionsComparator();
//Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator());
// Sort by number of transitions. First two will be the two solid sides; last two
// will be the two alternating black/white sides
var lSideOne:ResultPointsAndTransitions = ResultPointsAndTransitions( transitions.getObjectByIndex(0));
var lSideTwo:ResultPointsAndTransitions = ResultPointsAndTransitions( transitions.getObjectByIndex(1));
// Figure out which point is their intersection by tallying up the number of times we see the
// endpoints in the four endpoints. One will show up twice.
var pointCount:HashTable = new HashTable();
increment(pointCount, lSideOne.getFrom());
increment(pointCount, lSideOne.getTo());
increment(pointCount, lSideTwo.getFrom());
increment(pointCount, lSideTwo.getTo());
var maybeTopLeft:ResultPoint = null;
var bottomLeft:ResultPoint = null;
var maybeBottomRight:ResultPoint = null;
var size:int = pointCount.getSize();
for (var ii:int=0;ii<size;ii++)
{
var point:ResultPoint = pointCount.getKeyByIndex(ii) as ResultPoint;// resultpoints are used as keys
var value:int = pointCount.getValueByIndex(ii) as int;
if (value == 2)
{
bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides
}
else
{
// Otherwise it's either top left or bottom right -- just assign the two arbitrarily now
if (maybeTopLeft == null)
{
maybeTopLeft = point;
}
else
{
maybeBottomRight = point;
}
}
}
if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null)
{
throw new ReaderException("Detector : detect : maybeTopLeft or bottomLeft or maybeBottomRight == null");
}
// Bottom left is correct but top left and bottom right might be switched
var corners:Array = [maybeTopLeft, bottomLeft, maybeBottomRight];
// Use the dot product trick to sort them out
ResultPoint.orderBestPatterns(corners);
// Now we know which is which:
var bottomRight:ResultPoint = corners[0];
bottomLeft = corners[1];
var topLeft:ResultPoint = corners[2];
// Which point didn't we find in relation to the "L" sides? that's the top right corner
var topRight:ResultPoint;
if (!pointCount.ContainsKey(pointA))
{
topRight = pointA;
}
else if (!pointCount.ContainsKey(pointB))
{
topRight = pointB;
}
else if (!pointCount.ContainsKey(pointC))
{
topRight = pointC;
}
else
{
topRight = pointD;
}
// Next determine the dimension by tracing along the top or right side and counting black/white
// transitions. Since we start inside a black module, we should see a number of transitions
// equal to 1 less than the code dimension. Well, actually 2 less, because we are going to
// end on a black module:
// The top right point is actually the corner of a module, which is one of the two black modules
// adjacent to the white module at the top right. Tracing to that corner from either the top left
// or bottom right should work here, but, one will be more reliable since it's traced straight
// up or across, rather than at a slight angle. We use dot products to figure out which is
// better to use:
var dimension:int = Math.min(transitionsBetween(topLeft, topRight).getTransitions(),
transitionsBetween(bottomRight, topRight).getTransitions());
if ((dimension & 0x01) == 1)
{
// it can't be odd, so, round... up?
dimension++;
}
dimension += 2;
var bits:BitMatrix = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension);
return new DetectorResult(bits, [pointA, pointB, pointC, pointD]);
}
/**
* Increments the int associated with a key by one.
*/
private static function increment(table:HashTable, key:ResultPoint):void
{
if (!table.ContainsKey(key))
{
table.Add(key,1);
}
else
{
var value:int = int(table.getValueByKey(key));
table.setValue(key,value+1);
}
}
private static function sampleGrid(image:BitMatrix ,
topLeft:ResultPoint,
bottomLeft:ResultPoint,
bottomRight:ResultPoint,
dimension:int):BitMatrix {
// We make up the top right point for now, based on the others.
// TODO: we actually found a fourth corner above and figured out which of two modules
// it was the corner of. We could use that here and adjust for perspective distortion.
var topRightX:Number = (bottomRight.getX() - bottomLeft.getX()) + topLeft.getX();
var topRightY:Number = (bottomRight.getY() - bottomLeft.getY()) + topLeft.getY();
// Note that unlike in the QR Code sampler, we didn't find the center of modules, but the
// very corners. So there is no 0.5f here; 0.0f is right.
var sampler:GridSampler;
sampler = GridSampler.getGridSamplerInstance();
return sampler.sampleGrid(
image,
dimension,
0,
0,
dimension,
0,
dimension,
dimension,
0,
dimension,
topLeft.getX(),
topLeft.getY(),
topRightX,
topRightY,
bottomRight.getX(),
bottomRight.getY(),
bottomLeft.getX(),
bottomLeft.getY());
}
/**
* Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
*/
private function transitionsBetween( from:ResultPoint, _to:ResultPoint):ResultPointsAndTransitions {
// See QR Code Detector, sizeOfBlackWhiteBlackRun()
var fromX:int = int( from.getX());
var fromY:int = int( from.getY());
var toX:int = int( _to.getX());
var toY:int = int( _to.getY());
var steep:Boolean = Math.abs(toY - fromY) > Math.abs(toX - fromX);
if (steep) {
var temp:int = fromX;
fromX = fromY;
fromY = temp;
temp = toX;
toX = toY;
toY = temp;
}
var dx:int = Math.abs(toX - fromX);
var dy:int = Math.abs(toY - fromY);
var error:int = -dx >> 1;
var ystep:int = fromY < toY ? 1 : -1;
var xstep:int = fromX < toX ? 1 : -1;
var transitions:int = 0;
var inBlack:Boolean = image._get(steep ? fromY : fromX, steep ? fromX : fromY);
for (var x:int = fromX, y:int = fromY; x != toX; x += xstep) {
var isBlack:Boolean = image._get(steep ? y : x, steep ? x : y);
if (isBlack == !inBlack) {
transitions++;
inBlack = isBlack;
}
error += dy;
if (error > 0) {
y += ystep;
error -= dx;
}
}
return new ResultPointsAndTransitions(from, _to, transitions);
}
}
}

View file

@ -0,0 +1,32 @@
package com.google.zxing.datamatrix.detector
{
import com.google.zxing.ResultPoint;
/**
* Simply encapsulates two points and a number of transitions between them.
*/
public class ResultPointsAndTransitions
{
private var from:ResultPoint;
private var to:ResultPoint;
private var transitions:int;
public function ResultPointsAndTransitions(from:ResultPoint, to:ResultPoint, transitions:int) {
this.from = from;
this.to = to;
this.transitions = transitions;
}
public function getFrom():ResultPoint {
return from;
}
public function getTo():ResultPoint {
return to;
}
public function getTransitions():int {
return transitions;
}
public function toString():String {
return from + "/" + to + '/' + transitions;
}
}
}

Some files were not shown because too many files have changed in this diff Show more