updated all actionscript files in accordance with the core library revision 1901

git-svn-id: https://zxing.googlecode.com/svn/trunk@2109 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
bas5winkel@gmail.com 2012-01-11 16:50:26 +00:00
parent 01c274cfd1
commit 9ee56c0b61
115 changed files with 7832 additions and 2764 deletions

View file

@ -20,10 +20,19 @@ package com.google.zxing
* *
* @author Sean Owen * @author Sean Owen
*/ */
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
public class BarcodeFormat public class BarcodeFormat
{ {
 // No, we can't use an enum here. J2ME doesn't support it.  // No, we can't use an enum here. J2ME doesn't support it.
private static var VALUES:HashTable = new HashTable();
/** Aztec 2D barcode format. */
public static var AZTEC:BarcodeFormat = new BarcodeFormat("AZTEC");
/** CODABAR 1D format. */
public static var CODABAR:BarcodeFormat = new BarcodeFormat("CODABAR");
/** QR Code 2D barcode format. */ /** QR Code 2D barcode format. */
public static var QR_CODE:BarcodeFormat = new BarcodeFormat("QR_CODE"); public static var QR_CODE:BarcodeFormat = new BarcodeFormat("QR_CODE");
/** DataMatrix 2D barcode format. */ /** DataMatrix 2D barcode format. */
@ -32,24 +41,35 @@ public class BarcodeFormat
public static var UPC_E:BarcodeFormat = new BarcodeFormat("UPC_E"); public static var UPC_E:BarcodeFormat = new BarcodeFormat("UPC_E");
/** UPC-A 1D format. */ /** UPC-A 1D format. */
public static var UPC_A:BarcodeFormat = new BarcodeFormat("UPC_A"); public static var UPC_A:BarcodeFormat = new BarcodeFormat("UPC_A");
/** UPC/EAN extension format. Not a stand-alone format. */
public static var UPC_EAN_EXTENSION:BarcodeFormat = new BarcodeFormat("UPC_EAN_EXTENSION");
/** EAN-8 1D format. */ /** EAN-8 1D format. */
public static var EAN_8:BarcodeFormat = new BarcodeFormat("EAN_8"); public static var EAN_8:BarcodeFormat = new BarcodeFormat("EAN_8");
/** EAN-13 1D format. */ /** EAN-13 1D format. */
public static var EAN_13:BarcodeFormat = new BarcodeFormat("EAN_13"); public static var EAN_13:BarcodeFormat = new BarcodeFormat("EAN_13");
/** Code 128 1D format. */ /** Code 128 1D format. */
public static var CODE_128:BarcodeFormat = new BarcodeFormat("CODE_128"); public static var CODE_128:BarcodeFormat = new BarcodeFormat("CODE_128");
/** Code 93 1D format. */
public static var CODE_93:BarcodeFormat = new BarcodeFormat("CODE_93");
/** Code 39 1D format. */ /** Code 39 1D format. */
public static var CODE_39:BarcodeFormat = new BarcodeFormat("CODE_39"); public static var CODE_39:BarcodeFormat = new BarcodeFormat("CODE_39");
/** ITF (Interleaved Two of Five) 1D format. */ /** ITF (Interleaved Two of Five) 1D format. */
public static var ITF:BarcodeFormat = new BarcodeFormat("ITF"); public static var ITF:BarcodeFormat = new BarcodeFormat("ITF");
/** PDF417 format. */ /** PDF417 format. */
public static var PDF417:BarcodeFormat = new BarcodeFormat("PDF417"); public static var PDF417:BarcodeFormat = new BarcodeFormat("PDF417");
/** RSS 14 */
public static var RSS_14:BarcodeFormat = new BarcodeFormat("RSS_14");
/** RSS EXPANDED */
public static var RSS_EXPANDED:BarcodeFormat = new BarcodeFormat("RSS_EXPANDED");
/** MAXICODE */
public static var MAXICODE:BarcodeFormat = new BarcodeFormat("MAXICODE");
private var _name:String; private var _name:String;
public function BarcodeFormat(name:String) public function BarcodeFormat(name:String)
{ {
this._name = name; this._name = name;
VALUES._put(name, this);
} }
public function toString():String public function toString():String
@ -57,25 +77,24 @@ public class BarcodeFormat
return this._name; return this._name;
} }
/*
public function get name():String public function get name():String
{ {
return this._name; return this._name;
} }
/* public static function valueOf(name:String):BarcodeFormat
public function Equals(other:BarcodeFormat):Boolean
{ {
if (this._name == other.name) if (name == null || name.length == 0) {
throw new IllegalArgumentException();
}
var format:BarcodeFormat = (VALUES._get(name) as BarcodeFormat);
if (format == null)
{ {
return true; throw new IllegalArgumentException();
} }
else return format;
{
return false;
} }
}
*/
} }
} }

View file

@ -123,7 +123,6 @@ public final class BufferedImageLuminanceSource extends LuminanceSource
public override function crop(left:int, top:int, width:int, height:int):LuminanceSource public override function crop(left:int, top:int, width:int, height:int):LuminanceSource
{ {
// BAS : todo
return new BufferedImageLuminanceSource(image, left, top, width, height); return new BufferedImageLuminanceSource(image, left, top, width, height);
} }
@ -133,7 +132,6 @@ public final class BufferedImageLuminanceSource extends LuminanceSource
public override function isRotateSupported():Boolean public override function isRotateSupported():Boolean
{ {
return false; return false;
//Bas : TOO
//return image.getType() != BufferedImage.TYPE_CUSTOM; //return image.getType() != BufferedImage.TYPE_CUSTOM;
} }
@ -144,7 +142,7 @@ public final class BufferedImageLuminanceSource extends LuminanceSource
{ {
throw new Error("Rotate not supported"); throw new Error("Rotate not supported");
} }
// Bas : todo // todo
return null; return null;
/* /*
var sourceWidth:int = image.getWidth(); var sourceWidth:int = image.getWidth();

View file

@ -0,0 +1,39 @@
/*
* 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
{
/**
* Thrown when a barcode was successfully detected and decoded, but
* was not returned because its checksum feature failed.
*
* @author Sean Owen
*/
public class ChecksumException extends ReaderException {
private static var instance:ChecksumException = new ChecksumException();
public function ChecksumException() {
// do nothing
}
public static function getChecksumInstance():ChecksumException {
return instance;
}
}
}

View file

@ -31,35 +31,53 @@ package com.google.zxing
/** /**
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}. * Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
*/ */
public static var OTHER:DecodeHintType = new DecodeHintType(); public static var OTHER:DecodeHintType = new DecodeHintType("OTHER");
/** /**
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to; * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
* use {@link Boolean#TRUE}. * use {@link Boolean#TRUE}.
*/ */
public static var PURE_BARCODE:DecodeHintType = new DecodeHintType(); public static var PURE_BARCODE:DecodeHintType = new DecodeHintType("PURE_BARCODE");
/** /**
* *
* Image is known to be of one of a few possible formats. * Image is known to be of one of a few possible formats.
* Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s. * Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s.
*/ */
public static var POSSIBLE_FORMATS:DecodeHintType = new DecodeHintType(); public static var POSSIBLE_FORMATS:DecodeHintType = new DecodeHintType("POSSIBLE_FORMATS");
/** /**
* Spend more time to try to find a barcode; optimize for accuracy, not speed. * 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}. * Doesn't matter what it maps to; use {@link Boolean#TRUE}.
*/ */
public static var TRY_HARDER:DecodeHintType = new DecodeHintType(); public static var TRY_HARDER:DecodeHintType = new DecodeHintType("TRY_HARDER");
/**
* Specifies what character encoding to use when decoding, where applicable (type String)
*/
public static var CHARACTER_SET:DecodeHintType = new DecodeHintType("CHARACTER_SET");
/** /**
* Allowed lengths of encoded data -- reject anything else. Maps to an int[]. * Allowed lengths of encoded data -- reject anything else. Maps to an int[].
*/ */
public static var ALLOWED_LENGTHS:DecodeHintType = new DecodeHintType(); public static var ALLOWED_LENGTHS:DecodeHintType = new DecodeHintType("ALLOWED_LENGTHS");
/**
* Assume Code 39 codes employ a check digit. Maps to {@link Boolean}.
*/
public static var ASSUME_CODE_39_CHECK_DIGIT:DecodeHintType = new DecodeHintType("ASSUME_CODE_39_CHECK_DIGIT");
public function DecodeHintType() { /**
* The caller needs to be notified via callback when a possible {@link ResultPoint}
* is found. Maps to a {@link ResultPointCallback}.
*/
public static var NEED_RESULT_POINT_CALLBACK:DecodeHintType = new DecodeHintType("NEED_RESULT_POINT_CALLBACK");
public var DHtype:String;
public function DecodeHintType(aType:String)
{
DHtype = aType;
} }
} }
} }

View file

@ -0,0 +1,40 @@
/*
* 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
{
/**
* Thrown when a barcode was successfully detected, but some aspect of
* the content did not conform to the barcode's format rules. This could have
* been due to a mis-detection.
*
* @author Sean Owen
*/
public class FormatException extends ReaderException {
private static var instance:FormatException = new FormatException();
public function FormatException() {
// do nothing
}
public static function getFormatInstance():FormatException {
return instance;
}
}
}

View file

@ -13,8 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.zxing package com.google.zxing
{ {
import com.google.zxing.common.flexdatatypes.StringBuilder;
public class LuminanceSource public class LuminanceSource
{ {
/** /**
@ -110,6 +115,32 @@ package com.google.zxing
throw new Error("This luminance source does not support rotation."); throw new Error("This luminance source does not support rotation.");
} }
public function toString():String
{
var row:Array = new Array(width);
var result:StringBuilder = new StringBuilder(height * (width + 1));
for (var y:int = 0; y < height; y++) {
row = getRow(y, row);
for (var x:int = 0; x < width; x++) {
var luminance:int = row[x] & 0xFF;
var c:String;
if (luminance < 0x40) {
c = '#';
} else if (luminance < 0x80) {
c = '+';
} else if (luminance < 0xC0) {
c = '.';
} else {
c = ' ';
}
result.Append(c);
}
result.Append('\n');
}
return result.ToString();
}
} }
} }

View file

@ -15,13 +15,16 @@
*/ */
package com.google.zxing package com.google.zxing
{ {
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.aztec.AztecReader;
import com.google.zxing.common.flexdatatypes.ArrayList; import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.datamatrix.DataMatrixReader;
import com.google.zxing.oned.MultiFormatOneDReader; import com.google.zxing.oned.MultiFormatOneDReader;
import com.google.zxing.pdf417.PDF417Reader; import com.google.zxing.pdf417.PDF417Reader;
import com.google.zxing.qrcode.QRCodeReader; import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.datamatrix.DataMatrixReader; import com.google.zxing.maxicode.MaxiCodeReader;
import com.google.zxing.Reader; import com.google.zxing.common.flexdatatypes.ArrayList;
/** /**
* MultiFormatReader is a convenience class and the main entry point into the library for most uses. * 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 * By default it attempts to decode all barcode formats that the library supports. Optionally, you
@ -86,10 +89,14 @@ package com.google.zxing
var addOneDReader:Boolean = var addOneDReader:Boolean =
(formats.indexOf(BarcodeFormat.UPC_A) != -1)|| (formats.indexOf(BarcodeFormat.UPC_A) != -1)||
(formats.indexOf(BarcodeFormat.UPC_E) != -1)|| (formats.indexOf(BarcodeFormat.UPC_E) != -1)||
(formats.indexOf(BarcodeFormat.CODABAR) != -1)||
(formats.indexOf(BarcodeFormat.ITF) != -1)|| (formats.indexOf(BarcodeFormat.ITF) != -1)||
(formats.indexOf(BarcodeFormat.EAN_13) != -1)|| (formats.indexOf(BarcodeFormat.EAN_13) != -1)||
(formats.indexOf(BarcodeFormat.EAN_8) != -1)|| (formats.indexOf(BarcodeFormat.EAN_8) != -1)||
(formats.indexOf(BarcodeFormat.RSS_14) != -1)||
(formats.indexOf(BarcodeFormat.RSS_EXPANDED) != -1)||
(formats.indexOf(BarcodeFormat.CODE_39) != -1)|| (formats.indexOf(BarcodeFormat.CODE_39) != -1)||
(formats.indexOf(BarcodeFormat.CODE_93) != -1)||
(formats.indexOf(BarcodeFormat.CODE_128) != -1); (formats.indexOf(BarcodeFormat.CODE_128) != -1);
// Put 1D readers upfront in "normal" mode // Put 1D readers upfront in "normal" mode
@ -105,11 +112,17 @@ package com.google.zxing
if (formats.indexOf(BarcodeFormat.PDF417) != -1) { if (formats.indexOf(BarcodeFormat.PDF417) != -1) {
readers.addElement(new PDF417Reader()); readers.addElement(new PDF417Reader());
} }
if (formats.indexOf(BarcodeFormat.AZTEC) != -1) {
readers.addElement(new AztecReader());
}
// TODO re-enable once Data Matrix is ready // TODO re-enable once Data Matrix is ready
if (formats.indexOf(BarcodeFormat.DATAMATRIX) != -1) { if (formats.indexOf(BarcodeFormat.DATAMATRIX) != -1) {
readers.Add(new DataMatrixReader()); readers.Add(new DataMatrixReader());
} }
if (formats.Contains(BarcodeFormat.MAXICODE)) {
readers.addElement(new MaxiCodeReader());
}
// At end in "try harder" mode // At end in "try harder" mode
if (addOneDReader && tryHarder) if (addOneDReader && tryHarder)
{ {
@ -125,11 +138,10 @@ package com.google.zxing
readers.Add(reader); readers.Add(reader);
} }
readers.Add(new QRCodeReader()); readers.Add(new QRCodeReader());
// TODO re-enable once Data Matrix is ready
readers.Add(new DataMatrixReader()); readers.Add(new DataMatrixReader());
readers.Add(new AztecReader());
// TODO: Enable once PDF417 has passed QA readers.Add(new PDF417Reader());
readers.addElement(new PDF417Reader()); readers.Add(new MaxiCodeReader());
if (tryHarder) if (tryHarder)
{ {
@ -138,6 +150,15 @@ package com.google.zxing
} }
} }
public function reset():void
{
var size:int = readers.size();
for (var i:int = 0; i < size; i++)
{
var reader:Reader = Reader(readers.elementAt(i));
reader.reset();
}
}
private function decodeInternal( image:BinaryBitmap):Result private function decodeInternal( image:BinaryBitmap):Result
{ {
@ -145,6 +166,7 @@ package com.google.zxing
for (var i:int = 0; i < size; i++) for (var i:int = 0; i < size; i++)
{ {
var reader:Reader = (readers.getObjectByIndex(i)) as Reader; var reader:Reader = (readers.getObjectByIndex(i)) as Reader;
try{
try try
{ {
var res:Result = reader.decode(image, hints); var res:Result = reader.decode(image, hints);
@ -153,9 +175,16 @@ package com.google.zxing
catch ( re:ReaderException) catch ( re:ReaderException)
{ {
// continue // continue
var a:int=0; var a:int =3;
} }
} }
catch (e:Error)
{
var b:int = 4;
}
}
// no decoder could decode the barcode // no decoder could decode the barcode
return null; return null;

View file

@ -21,20 +21,37 @@ package com.google.zxing
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException; import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.oned.Code128Writer;
import com.google.zxing.oned.Code39Writer;
import com.google.zxing.oned.EAN13Writer; import com.google.zxing.oned.EAN13Writer;
import com.google.zxing.oned.EAN8Writer; import com.google.zxing.oned.EAN8Writer;
import com.google.zxing.oned.ITFWriter;
import com.google.zxing.oned.UPCAWriter;
import com.google.zxing.pdf417.encoder.PDF417Writer;
public function encode(contents:String, format:BarcodeFormat=null,width:int=0,height:int=0, hints:HashTable=null):Object{ public function encode(contents:String, format:BarcodeFormat=null,width:int=0,height:int=0, hints:HashTable=null):Object{
var writer:Writer;
if (format == BarcodeFormat.EAN_8) { if (format == BarcodeFormat.EAN_8) {
return (new EAN8Writer()).encode(contents, format, width, height, hints); writer = new EAN8Writer();
} else if (format == BarcodeFormat.EAN_13) { } else if (format == BarcodeFormat.EAN_13) {
return (new EAN13Writer()).encode(contents, format, width, height, hints); writer = new EAN13Writer();
} else if (format == BarcodeFormat.UPC_A) {
writer = new UPCAWriter();
} else if (format == BarcodeFormat.CODE_39) {
writer = new Code39Writer();
} else if (format == BarcodeFormat.CODE_128) {
writer = new Code128Writer();
} else if (format == BarcodeFormat.ITF) {
writer = new ITFWriter();
} else if (format == BarcodeFormat.PDF417) {
writer = new PDF417Writer();
} else if (format == BarcodeFormat.QR_CODE) { } else if (format == BarcodeFormat.QR_CODE) {
return (new QRCodeWriter()).encode(contents, format, width, height, hints); writer = new QRCodeWriter();
} else { } else {
throw new IllegalArgumentException("No encoder available for format " + format); throw new IllegalArgumentException("No encoder available for format " + format);
} }
return writer.encode(contents, format, width, height, hints);
} }
} }

View file

@ -0,0 +1,39 @@
/*
* 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
{
/**
* Thrown when a barcode was not found in the image. It might have been
* partially detected but could not be confirmed.
*
* @author Sean Owen
*/
public class NotFoundException extends ReaderException {
private static var instance:NotFoundException = new NotFoundException();
public function NotFoundException() {
// do nothing
}
public static function getNotFoundInstance():NotFoundException {
return instance;
}
}
}

View file

@ -45,5 +45,6 @@ package com.google.zxing
* @throws ReaderException if the barcode cannot be located or decoded for any reason * @throws ReaderException if the barcode cannot be located or decoded for any reason
*/ */
function decode(image:BinaryBitmap, hints:HashTable=null):Result; function decode(image:BinaryBitmap, hints:HashTable=null):Result;
function reset():void;
} }
} }

View file

@ -24,27 +24,33 @@ package com.google.zxing
{ {
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException; import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.common.flexdatatypes.Enumeration;
//BAS : made public for debugging protected var text:String;
private var text:String; protected var rawBytes:Array;
private var rawBytes:Array; protected var resultPoints:Array;
private var resultPoints:Array; protected var format:BarcodeFormat;
private var format:BarcodeFormat; protected var resultMetadata:HashTable;
private var resultMetadata:HashTable; protected var timestamp:Number;
public function Result( text:String, public function Result( text:String,
rawBytes:Array, rawBytes:Array,
resultPoints:Array, resultPoints:Array,
format:BarcodeFormat) format:BarcodeFormat,
timestamp:Number = 0)
{ {
if (text == null && rawBytes == null) { if (text == null && rawBytes == null) {
throw new IllegalArgumentException("Result : Text and bytes are both null"); throw new IllegalArgumentException("Result : Text and bytes are both null");
} }
if (timestamp == 0) { timestamp = Math.round((new Date()).getTime()/1000); }
this.text = text; this.text = text;
this.rawBytes = rawBytes; this.rawBytes = rawBytes;
this.resultPoints = resultPoints; this.resultPoints = resultPoints;
this.format = format; this.format = format;
this.resultMetadata = null; this.resultMetadata = null;
this.timestamp = timestamp;
} }
/** /**
@ -100,6 +106,50 @@ package com.google.zxing
} }
} }
public function putAllMetadata(metadata:HashTable ):void
{
if (metadata != null) {
if (resultMetadata == null) {
resultMetadata = metadata;
} else {
var e:Enumeration = new Enumeration(metadata.keys());
while (e.hasMoreElement()) {
var key:ResultMetadataType = (e.nextElement() as ResultMetadataType);
var value:Object = metadata._get(key);
resultMetadata._put(key, value);
}
}
}
}
public function addResultPoints(newPoints:Array):void
{
if (resultPoints == null) {
resultPoints = newPoints;
} else if (newPoints != null && newPoints.length > 0) {
var allPoints:Array = new Array(resultPoints.length + newPoints.length);
//System.arraycopy(resultPoints, 0, allPoints, 0, resultPoints.length);
for (var i:int=0;i<resultPoints.length;i++)
{
allPoints[i] = resultPoints[i];
}
for (var j:int=0;j<newPoints.length;j++)
{
allPoints[i + resultPoints.length] = newPoints[i];
}
//System.arraycopy(newPoints, 0, allPoints, resultPoints.length, newPoints.length);
resultPoints = allPoints;
}
}
public function getTimestamp():Number
{
return timestamp;
}
} }
} }

View file

@ -1,5 +1,8 @@
package com.google.zxing package com.google.zxing
{ {
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.common.flexdatatypes.HashTable;
public class ResultMetadataType public class ResultMetadataType
{ {
// No, we can't use an enum here. J2ME doesn't support it. // No, we can't use an enum here. J2ME doesn't support it.
@ -7,7 +10,7 @@ package com.google.zxing
/** /**
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}. * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
*/ */
public static var OTHER:ResultMetadataType = new ResultMetadataType(); public static var OTHER:ResultMetadataType = new ResultMetadataType("OTHER");
/** /**
* Denotes the likely approximate orientation of the barcode in the image. This value * Denotes the likely approximate orientation of the barcode in the image. This value
@ -16,7 +19,7 @@ package com.google.zxing
* said to have orientation "90". This key maps to an {@link Integer} whose * said to have orientation "90". This key maps to an {@link Integer} whose
* value is in the range [0,360). * value is in the range [0,360).
*/ */
public static var ORIENTATION:ResultMetadataType = new ResultMetadataType(); public static var ORIENTATION:ResultMetadataType = new ResultMetadataType("ORIENTATION");
/** /**
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode' * <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
@ -27,19 +30,63 @@ package com.google.zxing
* <p>This maps to a {@link java.util.Vector} of byte arrays corresponding to the * <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> * raw bytes in the byte segments in the barcode, in order.</p>
*/ */
public static var BYTE_SEGMENTS:ResultMetadataType = new ResultMetadataType(); public static var BYTE_SEGMENTS:ResultMetadataType = new ResultMetadataType("BYTE SEGMENTS");
/** /**
* Error correction level used, if applicable. The value type depends on the * Error correction level used, if applicable. The value type depends on the
* format, but is typically a String. * format, but is typically a String.
*/ */
public static var ERROR_CORRECTION_LEVEL:ResultMetadataType = new ResultMetadataType(); public static var ERROR_CORRECTION_LEVEL:ResultMetadataType = new ResultMetadataType("ORIENTATION");
public function ResultMetadataType() { /**
* For some periodicals, indicates the issue number as an {@link Integer}.
*/
public static var ISSUE_NUMBER:ResultMetadataType = new ResultMetadataType("ISSUE_NUMBER");
/**
* For some products, indicates the suggested retail price in the barcode as a
* formatted {@link String}.
*/
public static var SUGGESTED_PRICE:ResultMetadataType = new ResultMetadataType("SUGGESTED_PRICE");
/**
* For some products, the possible country of manufacture as a {@link String} denoting the
* ISO country code. Some map to multiple possible countries, like "US/CA".
*/
public static var POSSIBLE_COUNTRY:ResultMetadataType = new ResultMetadataType("POSSIBLE_COUNTRY");
private var name:String ;
private static var VALUES:HashTable = new HashTable();
public function ResultMetadataType(name:String = "") {
this.name = name;
//VALUES.put(name, this);
} }
public function getName():String {
return name;
}
public function toString():String {
return name;
}
public static function valueOf(name:String):ResultMetadataType
{
if (name == null || name.length == 0)
{
throw new IllegalArgumentException();
}
var format:ResultMetadataType = ResultMetadataType(VALUES._get(name));
if (format == null)
{
throw new IllegalArgumentException();
}
return format;
}
} }
} }

View file

@ -26,9 +26,8 @@ package com.google.zxing
public class ResultPoint public class ResultPoint
{ {
protected var x:Number;
private var x:Number; protected var y:Number;
private var y:Number;
public function ResultPoint(x:Number, y:Number) public function ResultPoint(x:Number, y:Number)
{ {

View file

@ -0,0 +1,31 @@
/*
* 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
{
/**
* Callback which is invoked when a possible result point (significant
* point in the barcode image such as a corner) is found.
*
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
*/
public interface ResultPointCallback {
function foundPossibleResultPoint(point:ResultPoint ):void;
}
}

View file

@ -35,6 +35,7 @@ package com.google.zxing
* @param hints Additional parameters to supply to the encoder * @param hints Additional parameters to supply to the encoder
* @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white) * @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 function encode(contents:String, format:BarcodeFormat=null, width:int=0, height:int=0, hints:HashTable=null):Object;
} }
} }

View file

@ -0,0 +1,57 @@
/*
* Copyright 2010 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.aztec
{
import com.google.zxing.common.DetectorResult;
public final class AztecDetectorResult extends DetectorResult
{
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DetectorResult;
private var compact:Boolean;
private var nbDatablocks:int;
private var nbLayers:int;
public function AztecDetectorResult(bits:BitMatrix , points:Array, compact:Boolean, nbDatablocks:int, nbLayers:int)
{
super(bits, points);
this.compact = compact;
this.nbDatablocks = nbDatablocks;
this.nbLayers = nbLayers;
}
public function getNbLayers():int
{
return nbLayers;
}
public function getNbDatablocks():int
{
return nbDatablocks;
}
public function isCompact():Boolean
{
return compact;
}
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright 2010 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.aztec
{
import com.google.zxing.Reader;
public final class AztecReader implements Reader
{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.aztec.decoder.Decoder;
import com.google.zxing.aztec.detector.Detector;
import com.google.zxing.common.BitArray;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.HashTable;
/**
* This implementation can detect and decode Aztec codes in an image.
*
* @author David Olivier
*/
/**
* Locates and decodes a Data Matrix code in an image.
*
* @return a String representing the content encoded by the Data Matrix code
* @throws NotFoundException if a Data Matrix code cannot be found
* @throws FormatException if a Data Matrix code cannot be decoded
* @throws ChecksumException if error correction fails
*/
public function decode(image:BinaryBitmap, hints:HashTable=null ):Result
{
if (hints == null) {return decode(image, null);}
var detectorResult:AztecDetectorResult = new Detector(image.getBlackMatrix()).detect();
var points:Array = detectorResult.getPoints();
if ((hints != null) && (detectorResult.getPoints() != null))
{
var rpcb:ResultPointCallback = hints._get(DecodeHintType.NEED_RESULT_POINT_CALLBACK) as ResultPointCallback;
if (rpcb != null)
{
for (var i:int = 0; i < detectorResult.getPoints().length; i++)
{
rpcb.foundPossibleResultPoint(detectorResult.getPoints()[i]);
}
}
}
var decoderResult:DecoderResult = new Decoder().decode(detectorResult);
var result:Result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.AZTEC);
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;
}
public function reset():void
{
// do nothing
}
}
}

View file

@ -0,0 +1,19 @@
// ActionScript file
package com.google.zxing.aztec
{
import com.google.zxing.ResultPoint;
public class Point {
public var x:int;
public var y:int;
public function toResultPoint():ResultPoint {
return new ResultPoint(x, y);
}
public function Point( x:int, y:int) {
this.x = x;
this.y = y;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -45,6 +45,30 @@ package com.google.zxing.common
return Size; return Size;
} }
public function getSizeInBytes():int
{
return (this.Size + 7) >> 3;
}
private function ensureCapacity(size:int):void
{
if (size > bits.length << 5)
{
var newArray:Array = new Array(size);
if (bits != null)
{
//System.Array.Copy(bytes, 0, newArray, 0, bytes.length);
for (var i:int=0;i<bits.length;i++)
{
newArray[i] = bits[i];
}
}
this.bits = newArray;
}
}
/** /**
* @param i bit to get * @param i bit to get
* @return true iff bit i is set * @return true iff bit i is set
@ -132,6 +156,7 @@ package com.google.zxing.common
return true; return true;
} }
/** /**
* @return underlying array of ints. The first element holds the first 32 bits, and the least * @return underlying array of ints. The first element holds the first 32 bits, and the least
* significant bit is bit 0. * significant bit is bit 0.
@ -140,8 +165,7 @@ package com.google.zxing.common
return bits; return bits;
} }
// bas : for debugging purposes private function setBitArray(a:Array):void {
public function setBitArray(a:Array):void {
bits = a; bits = a;
} }
public function setSize(siz:int):void { public function setSize(siz:int):void {
@ -171,6 +195,7 @@ package com.google.zxing.common
} }
return new Array(arraySize); return new Array(arraySize);
} }
public function toString():String public function toString():String
{ {
var result:StringBuilder = new StringBuilder(this.Size); var result:StringBuilder = new StringBuilder(this.Size);
@ -184,5 +209,88 @@ package com.google.zxing.common
} }
return result.ToString(); return result.ToString();
} }
public function appendBit(bit:Boolean):void
{
this.ensureCapacity(this.Size + 1);
if (bit)
{
this.bits[this.Size >> 5] |= (1 << (this.Size & 0x1F));
}
this.Size++;
}
/**
* Appends the least-significant bits, from value, in order from most-significant to
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
* 0, 1, 1, 1, 1, 0 in that order.
*/
public function appendBits(value:int, numBits:int):void {
if (numBits < 0 || numBits > 32) {
throw new IllegalArgumentException("Num bits must be between 0 and 32");
}
this.ensureCapacity(this.Size + numBits);
for (var numBitsLeft:int = numBits; numBitsLeft > 0; numBitsLeft--) {
appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);
}
}
public function appendBitArray(other:BitArray):void {
var otherSize:int = other.getSize();
this.ensureCapacity(this.Size + otherSize);
for (var i:int = 0; i < otherSize; i++) {
appendBit(other._get(i));
}
}
public function xor(other:BitArray):void {
if (bits.length != other.bits.length) {
throw new IllegalArgumentException("Sizes don't match");
}
for (var i:int = 0; i < bits.length; i++) {
// The last byte could be incomplete (i.e. not have 8 bits in
// it) but there is no problem since 0 XOR 0 == 0.
bits[i] ^= other.bits[i];
}
}
/*
private static function makeArray(size:int):Array {
return new Array((size + 31) >> 5);
}
*
/*
public function toString():String {
var result:StringBuffer = new StringBuffer(size);
for (var i:int = 0; i < size; i++) {
if ((i & 0x07) == 0) {
result.append(' ');
}
result.append(get(i) ? 'X' : '.');
}
return result.toString();
}
*/
/**
*
* @param bitOffset first bit to start writing
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
* of the internal representation, which is exposed by {@link #getBitArray()}
* @param offset position in array to start writing
* @param numBytes how many bytes to write
*/
public function toBytes(bitOffset:int, array:Array, offset:int, numBytes:int):void
{
for (var i:int = 0; i < numBytes; i++) {
var theByte:int = 0;
for (var j:int = 0; j < 8; j++) {
if (_get(bitOffset)) {
theByte |= 1 << (7 - j);
}
bitOffset++;
}
array[offset + i] = theByte;
}
}
} }
} }

View file

@ -35,9 +35,9 @@ package com.google.zxing.common
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException; import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
private var width:int; public var width:int;
private var height:int; public var height:int;
private var rowSize:int; public var rowSize:int;
public var bits:Array; public var bits:Array;
@ -59,14 +59,9 @@ package com.google.zxing.common
} }
this.width = width; this.width = width;
this.height = height; this.height = height;
var rowSize:int = width >> 5; this.rowSize = (width+31) >> 5;
if ((width & 0x1f) != 0)
{
rowSize++;
}
this.rowSize = rowSize;
bits = new Array(rowSize * height); bits = new Array(rowSize * height);
// BAS : initialize the array // initialize the array
for (var i:int=0;i<bits.length;i++) { bits[i] = 0; } for (var i:int=0;i<bits.length;i++) { bits[i] = 0; }
} }
@ -93,6 +88,7 @@ package com.google.zxing.common
bits[offset] |= 1 << (x & 0x1f); bits[offset] |= 1 << (x & 0x1f);
} }
/** /**
* <p>Flips the given bit.</p> * <p>Flips the given bit.</p>
* *
@ -161,6 +157,25 @@ package com.google.zxing.common
return row; return row;
} }
public function getTopLeftOnBit():Array {
var bitsOffset:int = 0;
while (bitsOffset < bits.length && bits[bitsOffset] == 0) {
bitsOffset++;
}
if (bitsOffset == bits.length) {
return null;
}
var y:int = bitsOffset / rowSize;
var x:int = (bitsOffset % rowSize) << 5;
var theBits:int = bits[bitsOffset];
var bit:int = 0;
while ((theBits << (31-bit)) == 0) {
bit++;
}
x += bit;
return [x, y];
}
/** /**
* @return The width of the matrix * @return The width of the matrix
*/ */
@ -168,6 +183,31 @@ package com.google.zxing.common
return width; return width;
} }
public function getBottomRightOnBit():Array {
var bitsOffset:int = bits.length - 1;
while (bitsOffset >= 0 && bits[bitsOffset] == 0) {
bitsOffset--;
}
if (bitsOffset < 0) {
return null;
}
var y:int = bitsOffset / rowSize;
var x:int = (bitsOffset % rowSize) << 5;
var theBits:int = bits[bitsOffset];
var bit:int = 31;
while ((theBits >>> bit) == 0) {
bit--;
}
x += bit;
return [x, y];
}
/** /**
* @return The height of the matrix * @return The height of the matrix
*/ */
@ -175,17 +215,88 @@ package com.google.zxing.common
return height; return height;
} }
/**
* This method is for compatibility with older code. It's only logical to call if the matrix public function equals(o:Object):Boolean {
* is square, so I'm throwing if that's not the case. if (!(o is BitMatrix)) {
* return false;
* @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; var other:BitMatrix = BitMatrix(o);
if (width != other.width || height != other.height ||
rowSize != other.rowSize || bits.length != other.bits.length) {
return false;
}
for (var i:int = 0; i < bits.length; i++) {
if (bits[i] != other.bits[i]) {
return false;
}
}
return true;
}
public function hashCode():int {
var hash:int = width;
hash = 31 * hash + width;
hash = 31 * hash + height;
hash = 31 * hash + rowSize;
for (var i:int = 0; i < bits.length; i++) {
hash = 31 * hash + bits[i];
}
return hash;
}
/**
* This is useful in detecting the enclosing rectangle of a 'pure' barcode.
*
* @return {left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white
*/
public function getEnclosingRectangle():Array
{
var left:int = this.width;
var top:int = this.height;
var right:int = -1;
var bottom:int = -1;
var bit:int;
for (var y:int = 0; y < this.height; y++) {
for (var x32:int = 0; x32 < rowSize; x32++) {
var theBits:int = bits[y * rowSize + x32];
if (theBits != 0) {
if (y < top) {
top = y;
}
if (y > bottom) {
bottom = y;
}
if (x32 * 32 < left) {
bit = 0;
while ((theBits << (31 - bit)) == 0) {
bit++;
}
if ((x32 * 32 + bit) < left) {
left = x32 * 32 + bit;
}
}
if (x32 * 32 + 31 > right) {
bit = 31;
while ((theBits >>> bit) == 0) {
bit--;
}
if ((x32 * 32 + bit) > right) {
right = x32 * 32 + bit;
}
}
}
}
}
var width:int = right - left;
var height:int = bottom - top;
if (width < 0 || height < 0) {
return null;
}
return [left, top, width, height];
} }
public function toString():String { public function toString():String {
@ -198,5 +309,29 @@ package com.google.zxing.common
} }
return result.toString(); return result.toString();
} }
public function toString2():String {
var totalbits:int = 0;
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) ? "1" : "0");
if (_get(x, y)) { totalbits++;}
}
}
result.Append("\nsize:"+(this.width*this.height));
result.Append("\ntotalbits:"+totalbits);
return result.toString();
}
public function fromByteArray(width:int,height:int,arr:Array):void
{
this.bits = arr;
this.width = width;
this.height = height;
this.rowSize = (width+31) >> 5;
}
} }
} }

View file

@ -29,6 +29,13 @@ package com.google.zxing.common
this.bytes = bytes; this.bytes = bytes;
} }
/**
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
*/
public function getByteOffset():int {
return byteOffset;
}
/** /**
* @param numBits number of bits to read * @param numBits number of bits to read
* @return int representing the bits read. The bits will appear as the least-significant * @return int representing the bits read. The bits will appear as the least-significant

View file

@ -27,7 +27,6 @@ package com.google.zxing.common
public class ByteMatrix public class ByteMatrix
{ {
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
private var bytes:Array; private var bytes:Array;
private var Height:int; private var Height:int;
private var Width:int; private var Width:int;
@ -128,6 +127,30 @@ package com.google.zxing.common
} }
return result.ToString(); return result.ToString();
} }
public function toString2():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;
}
}
}
return result.ToString();
}
} }
} }

View file

@ -34,9 +34,9 @@ package com.google.zxing.common
private var rawBytes:Array; private var rawBytes:Array;
private var text:String ; private var text:String ;
private var byteSegments:ArrayList; private var byteSegments:ArrayList;
private var ecLevel:ErrorCorrectionLevel; private var ecLevel:String;
public function DecoderResult(rawBytes:Array, text:String, byteSegments:ArrayList, ecLevel:ErrorCorrectionLevel) public function DecoderResult(rawBytes:Array, text:String, byteSegments:ArrayList, ecLevel:String)
{ {
if (rawBytes == null && text == null) if (rawBytes == null && text == null)
{ {
@ -61,7 +61,7 @@ package com.google.zxing.common
return byteSegments; return byteSegments;
} }
public function getECLevel():ErrorCorrectionLevel public function getECLevel():String
{ {
return ecLevel; return ecLevel;
} }

View file

@ -0,0 +1,90 @@
/*
* 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.NotFoundException;
/**
* @author Sean Owen
*/
public class DefaultGridSampler extends GridSampler
{
public function sampleGrid2(image:BitMatrix,
dimensionX:int,
dimensionY: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
{
var transform:PerspectiveTransform = PerspectiveTransform.quadrilateralToQuadrilateral(
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
return sampleGrid(image, dimensionX, dimensionY, transform);
}
public override function sampleGrid(image:BitMatrix ,
dimensionX:int,
dimensionY:int,
transform:PerspectiveTransform):BitMatrix {
if (dimensionX <= 0 || dimensionY <= 0) {
throw NotFoundException.getNotFoundInstance();
}
var bits:BitMatrix = new BitMatrix(dimensionX, dimensionY);
var points:Array = new Array(dimensionX << 1);
for (var y:int = 0; y < dimensionY; 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(int(x >> 1) + 0.5);
points[x + 1] = iValue;
}
transform.transformPoints(points);
// Quick check to see if points transformed to something inside the image;
// sufficient to check the endpoints
checkAndNudgePoints(image, points);
try {
for (var x:int = 0; x < max; x += 2) {
if (image._get(int(points[x]), int( points[x + 1]))) {
// Black(-ish) pixel
bits._set(x >> 1, y);
}
}
} catch (aioobe:ArrayIndexOutOfBoundsException) {
// 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 NotFoundException.getNotFoundInstance();
}
}
return bits;
}
}
}

View file

@ -34,6 +34,7 @@ package com.google.zxing.common
public function DetectorResult( bits:BitMatrix, points:Array ) { public function DetectorResult( bits:BitMatrix, points:Array ) {
this.bits = bits; this.bits = bits;
this.points = points; this.points = points;
} }
public function getBits():BitMatrix { public function getBits():BitMatrix {

View file

@ -29,7 +29,7 @@ import com.google.zxing.ReaderException;
* @author dswitkin@google.com (Daniel Switkin) * @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen * @author Sean Owen
*/ */
public final class GlobalHistogramBinarizer extends Binarizer { public class GlobalHistogramBinarizer extends Binarizer {
private static var LUMINANCE_BITS:int = 5; private static var LUMINANCE_BITS:int = 5;
@ -45,6 +45,7 @@ public final class GlobalHistogramBinarizer extends Binarizer {
// Applies simple sharpening to the row data to improve performance of the 1D Readers. // Applies simple sharpening to the row data to improve performance of the 1D Readers.
public override function getBlackRow(y:int , row:BitArray ):BitArray { public override function getBlackRow(y:int , row:BitArray ):BitArray {
var source:LuminanceSource = getLuminanceSource(); var source:LuminanceSource = getLuminanceSource();
var width:int = source.getWidth(); var width:int = source.getWidth();
if (row == null || row.getSize() < width) { if (row == null || row.getSize() < width) {
@ -81,7 +82,8 @@ public final class GlobalHistogramBinarizer extends Binarizer {
} }
// Does not sharpen the data, as this call is intended to only be used by 2D Readers. // Does not sharpen the data, as this call is intended to only be used by 2D Readers.
public override function getBlackMatrix():BitMatrix { public override function getBlackMatrix():BitMatrix
{
var source:LuminanceSource = getLuminanceSource(); var source:LuminanceSource = getLuminanceSource();
var width:int = source.getWidth(); var width:int = source.getWidth();
var height:int = source.getHeight(); var height:int = source.getHeight();
@ -94,10 +96,10 @@ public final class GlobalHistogramBinarizer extends Binarizer {
var localBuckets:Array = buckets;//assign empty array var localBuckets:Array = buckets;//assign empty array
for (var y2:int = 1; y2 < 5; y2++) for (var y2:int = 1; y2 < 5; y2++)
{ {
var row:int = height * y2 / 5; var row:int = int(height * y2 / 5);
_localLuminances = source.getRow(row, luminances); _localLuminances = source.getRow(row, luminances);
var right:int = (width << 2) / 5; var right:int = int((width << 2) / 5);
for (var x:int = width / 5; x < right; x++) for (var x:int = int(width / 5); x < right; x++)
{ {
var pixel:int = _localLuminances[x] & 0xff; var pixel:int = _localLuminances[x] & 0xff;
var index:int = Math.floor(pixel >> LUMINANCE_SHIFT); var index:int = Math.floor(pixel >> LUMINANCE_SHIFT);

View file

@ -30,8 +30,9 @@ package com.google.zxing.common
* @author Sean Owen * @author Sean Owen
* *
*/ */
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.NotFoundException;
public class GridSampler public class GridSampler
{ {
@ -87,71 +88,65 @@ package com.google.zxing.common
* by the given points is invalid or results in sampling outside the image boundaries * by the given points is invalid or results in sampling outside the image boundaries
*/ */
public function sampleGrid(image:BitMatrix, public function sampleGrid(image:BitMatrix ,
dimension:int, dimensionX:int,
p1ToX:Number, dimensionY:int,
p1ToY:Number, transform:PerspectiveTransform):BitMatrix
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++)
{ {
// originally in DefaultGridSampler
if (dimensionX <= 0 || dimensionY <= 0) {
throw NotFoundException.getNotFoundInstance();
}
var bits:BitMatrix = new BitMatrix(dimensionX, dimensionY);
var points:Array = new Array(dimensionX << 1);
for (var y:int = 0; y < dimensionY; y++) {
var max:int = points.length; var max:int = points.length;
var iValue:Number = Number(y + 0.5); var iValue:Number = Number(y + 0.5);
for (var x:int = 0; x < max; x += 2) for (var x:int = 0; x < max; x += 2) {
{
points[x] = Number((x >> 1) + 0.5); points[x] = Number((x >> 1) + 0.5);
points[x + 1] = iValue; points[x + 1] = iValue;
} }
points = transform.transformPoints(points); transform.transformPoints(points);
// Quick check to see if points transformed to something inside the image; // Quick check to see if points transformed to something inside the image;
// sufficent to check the endpoints // sufficient to check the endpoints
checkAndNudgePoints(image, points); checkAndNudgePoints(image, points);
try try {
{ for (x = 0; x < max; x += 2) {
for (var x2:int = 0; x2 < max; x2 += 2) if (image._get(int(points[x]), int( points[x + 1]))) {
{
//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 // Black(-ish) pixel
bits._set(x2 >> 1,y ); bits._set(x >> 1, y);
} }
} }
} } catch (aioobe:RangeError) {
catch (aioobe:RangeError )
{
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting // 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 // 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 // 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. // 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. // This results in an ugly runtime exception despite our clever checks above -- can't have
// We could check each point's coordinates but that feels duplicative. We settle for // that. We could check each point's coordinates but that feels duplicative. We settle for
// catching and wrapping ArrayIndexOutOfBoundsException. // catching and wrapping ArrayIndexOutOfBoundsException.
throw new ReaderException("DefautGridSampler : sampleGrid : "+aioobe.message); throw NotFoundException.getNotFoundInstance();
} }
} }
return bits; return bits;
}
public function sampleGrid2(image:BitMatrix,
dimensionX:int,
dimensionY: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
{
var transform:PerspectiveTransform = PerspectiveTransform.quadrilateralToQuadrilateral(
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
return sampleGrid(image, dimensionX, dimensionY, transform);
} }
/** /**

View file

@ -0,0 +1,190 @@
/*
* 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 class HybridBinarizer extends GlobalHistogramBinarizer {
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
// So this is the smallest dimension in each axis we can accept.
private static var MINIMUM_DIMENSION:int = 40;
private var matrix:BitMatrix = null;
public function HybridBinarizer(source:LuminanceSource) {
super(source);
}
public override function getBlackMatrix():BitMatrix
{
binarizeEntireImage();
return matrix;
}
public override function createBinarizer(source:LuminanceSource):Binarizer
{
return new HybridBinarizer(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();
if (source.getWidth() >= MINIMUM_DIMENSION && source.getHeight() >= MINIMUM_DIMENSION)
{
var luminances:Array = source.getMatrix();
var width:int = source.getWidth();
var height:int = source.getHeight();
var subWidth:int = width >> 3;
if ((width & 0x07) != 0) {
subWidth++;
}
var subHeight:int = height >> 3;
if ((height & 0x07) != 0) {
subHeight++;
}
var blackPoints:Array = calculateBlackPoints(luminances, subWidth, subHeight, width, height);
matrix = new BitMatrix(width, height);
calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, matrix);
}
else
{
// If the image is too small, fall back to the global histogram approach.
matrix = super.getBlackMatrix();
}
}
}
// 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 ,
width:int, height:int , blackPoints:Array , matrix:BitMatrix ):void {
for (var y:int = 0; y < subHeight; y++) {
var yoffset:int = y << 3;
if ((yoffset + 8) >= height) {
yoffset = height - 8;
}
for (var x:int = 0; x < subWidth; x++) {
var xoffset:int = x << 3;
if ((xoffset + 8) >= width) {
xoffset = width - 8;
}
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++) {
var blackRow:Array = blackPoints[top + z];
sum += blackRow[left - 2];
sum += blackRow[left - 1];
sum += blackRow[left];
sum += blackRow[left + 1];
sum += blackRow[left + 2];
}
var average:int = int(sum / 25);
threshold8x8Block(luminances, xoffset, yoffset, average, width, 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,
width:int, height:int):Array {
var blackPoints:Array = new Array(subHeight);
for(var i:int=0;i<blackPoints.length;i++) { blackPoints[i]=new Array(subWidth);for(var j:int=0;j<subWidth;j++){blackPoints[i][j]=0; }}
for (var y:int = 0; y < subHeight; y++) {
var yoffset:int = y << 3;
if ((yoffset + 8) >= height) {
yoffset = height - 8;
}
for (var x:int = 0; x < subWidth; x++) {
var xoffset:int = x << 3;
if ((xoffset + 8) >= width) {
xoffset = width - 8;
}
var sum:int = 0;
var min:int = 255;
var max:int = 0;
for (var yy:int = 0; yy < 8; yy++) {
var offset:int = (yoffset + yy) * width + xoffset;
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;
if (max - min > 24) {
average = sum >> 6;
} else {
// When min == max == 0, let average be 1 so all is black
average = max == 0 ? 1 : min >> 1;
}
blackPoints[y][x] = average;
}
}
return blackPoints;
}
}
}

View file

@ -24,7 +24,6 @@ package com.google.zxing.common
*/ */
public class PerspectiveTransform public class PerspectiveTransform
{ {
private var a11:Number; private var a11:Number;
private var a12:Number; private var a12:Number;
private var a13:Number; private var a13:Number;
@ -108,12 +107,28 @@ package com.google.zxing.common
public function buildAdjoint():PerspectiveTransform public function buildAdjoint():PerspectiveTransform
{ {
// Adjoint is the transpose of the cofactor matrix: // 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); 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 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); 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,31 @@
/*
* 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
{
/**
* Callback which is invoked when a possible result point (significant
* point in the barcode image such as a corner) is found.
*
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
*/
public interface ResultPointCallback {
function foundPossibleResultPoint(point:ResultPoint ):void;
}
}

View file

@ -0,0 +1,354 @@
/*
* Copyright 2010 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.NotFoundException;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
/**
* <p>
* Detects a candidate barcode-like rectangular region within an image. It
* starts around the center of the image, increases the size of the candidate
* region until it finds a white rectangular region. By keeping track of the
* last black points it encountered, it determines the corners of the barcode.
* </p>
*
* @author David Olivier
*/
public final class WhiteRectangleDetector
{
private static var INIT_SIZE:int = 30;
private static var CORR:int = 1;
private var image:BitMatrix ;
private var height:int;
private var width:int;
private var leftInit:int;
private var rightInit:int;
private var downInit:int;
private var upInit:int;
/**
* @throws NotFoundException if image is too small
*/
public function WhiteRectangleDetector(image:BitMatrix , initSize:int=-1, x:int=-1, y:int=-1)
{
if ((initSize==-1)&&( x==-1)&&( y==-1))
{
this.image = image;
height = image.getHeight();
width = image.getWidth();
leftInit = (width - INIT_SIZE) >> 1;
rightInit = (width + INIT_SIZE) >> 1;
upInit = (height - INIT_SIZE) >> 1;
downInit = (height + INIT_SIZE) >> 1;
if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {
throw NotFoundException.getNotFoundInstance();
}
}
else
{
this.image = image;
height = image.getHeight();
width = image.getWidth();
var halfsize:int = initSize >> 1;
leftInit = x - halfsize;
rightInit = x + halfsize;
upInit = y - halfsize;
downInit = y + halfsize;
if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {
throw NotFoundException.getNotFoundInstance();
}
}
}
/**
* <p>
* Detects a candidate barcode-like rectangular region within an image. It
* starts around the center of the image, increases the size of the candidate
* region until it finds a white rectangular region.
* </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 NotFoundException if no Data Matrix Code can be found
*/
public function detect():Array {
var left:int = leftInit;
var right:int = rightInit;
var up:int = upInit;
var down:int = downInit;
var sizeExceeded:Boolean = false;
var aBlackPointFoundOnBorder:Boolean = true;
var atLeastOneBlackPointFoundOnBorder:Boolean = false;
while (aBlackPointFoundOnBorder) {
aBlackPointFoundOnBorder = false;
// .....
// . |
// .....
var rightBorderNotWhite:Boolean = true;
while (rightBorderNotWhite && right < width) {
rightBorderNotWhite = containsBlackPoint(up, down, right, false);
if (rightBorderNotWhite) {
right++;
aBlackPointFoundOnBorder = true;
}
}
if (right >= width) {
sizeExceeded = true;
break;
}
// .....
// . .
// .___.
var bottomBorderNotWhite:Boolean = true;
while (bottomBorderNotWhite && down < height) {
bottomBorderNotWhite = containsBlackPoint(left, right, down, true);
if (bottomBorderNotWhite) {
down++;
aBlackPointFoundOnBorder = true;
}
}
if (down >= height) {
sizeExceeded = true;
break;
}
// .....
// | .
// .....
var leftBorderNotWhite:Boolean = true;
while (leftBorderNotWhite && left >= 0) {
leftBorderNotWhite = containsBlackPoint(up, down, left, false);
if (leftBorderNotWhite) {
left--;
aBlackPointFoundOnBorder = true;
}
}
if (left < 0) {
sizeExceeded = true;
break;
}
// .___.
// . .
// .....
var topBorderNotWhite:Boolean = true;
while (topBorderNotWhite && up >= 0) {
topBorderNotWhite = containsBlackPoint(left, right, up, true);
if (topBorderNotWhite) {
up--;
aBlackPointFoundOnBorder = true;
}
}
if (up < 0) {
sizeExceeded = true;
break;
}
if (aBlackPointFoundOnBorder) {
atLeastOneBlackPointFoundOnBorder = true;
}
}
if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
var maxSize:int = right - left;
var z:ResultPoint = null;
for (var i:int = 1; i < maxSize; i++)
{
z = getBlackPointOnSegment(left, down - i, left + i, down);
if (z != null) {
break;
}
}
if (z == null) {
throw NotFoundException.getNotFoundInstance();
}
var t:ResultPoint = null;
//go down right
for (var i4:int = 1; i4 < maxSize; i4++) {
t = getBlackPointOnSegment(left, up + i4, left + i4, up);
if (t != null) {
break;
}
}
if (t == null) {
throw NotFoundException.getNotFoundInstance();
}
var x:ResultPoint = null;
//go down left
for (var i5:int = 1; i5 < maxSize; i5++) {
x = getBlackPointOnSegment(right, up + i5, right - i5, up);
if (x != null) {
break;
}
}
if (x == null) {
throw NotFoundException.getNotFoundInstance();
}
var y:ResultPoint = null;
//go up left
for (i = 1; i < maxSize; i++) {
y = getBlackPointOnSegment(right, down - i, right - i, down);
if (y != null) {
break;
}
}
if (y == null) {
throw NotFoundException.getNotFoundInstance();
}
return centerEdges(y, z, x, t);
} else {
throw NotFoundException.getNotFoundInstance();
}
}
/**
* Ends up being a bit faster than Math.round(). This merely rounds its
* argument to the nearest int, where x.5 rounds up.
*/
private static function round(d:Number):int {
return (int) (d + 0.5);
}
private function getBlackPointOnSegment(aX:Number, aY:Number, bX:Number, bY:Number):ResultPoint {
var dist:int = distanceL2(aX, aY, bX, bY);
var xStep:Number = (bX - aX) / dist;
var yStep:Number = (bY - aY) / dist;
for (var i:int = 0; i < dist; i++) {
var x:int = round(aX + i * xStep);
var y:int = round(aY + i * yStep);
if (image._get(x, y)) {
return new ResultPoint(x, y);
}
}
return null;
}
private static function distanceL2(aX:Number, aY:Number, bX:Number, bY:Number):int {
var xDiff:Number = aX - bX;
var yDiff:Number = aY - bY;
return round(Math.sqrt(xDiff * xDiff + yDiff * yDiff));
}
/**
* recenters the points of a constant distance towards the center
*
* @param y bottom most point
* @param z left most point
* @param x right most point
* @param t top most point
* @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
*/
private function centerEdges(y:ResultPoint , z:ResultPoint,
x:ResultPoint, t:ResultPoint):Array {
//
// t t
// z x
// x OR z
// y y
//
var yi:Number = y.getX();
var yj:Number = y.getY();
var zi:Number = z.getX();
var zj:Number = z.getY();
var xi:Number = x.getX();
var xj:Number = x.getY();
var ti:Number = t.getX();
var tj:Number = t.getY();
if (yi < width / 2) {
return [
new ResultPoint(ti - CORR, tj + CORR),
new ResultPoint(zi + CORR, zj + CORR),
new ResultPoint(xi - CORR, xj - CORR),
new ResultPoint(yi + CORR, yj - CORR)];
} else {
return [
new ResultPoint(ti + CORR, tj + CORR),
new ResultPoint(zi + CORR, zj - CORR),
new ResultPoint(xi - CORR, xj + CORR),
new ResultPoint(yi - CORR, yj - CORR)];
}
}
/**
* Determines whether a segment contains a black point
*
* @param a min value of the scanned coordinate
* @param b max value of the scanned coordinate
* @param fixed value of fixed coordinate
* @param horizontal set to true if scan must be horizontal, false if vertical
* @return true if a black point has been found, else false.
*/
private function containsBlackPoint(a:int, b:int, fixed:int, horizontal:Boolean):Boolean {
if (horizontal) {
for (var x:int = a; x <= b; x++) {
if (image._get(x, fixed)) {
return true;
}
}
} else {
for (var y:int = a; y <= b; y++) {
if (image._get(fixed, y)) {
return true;
}
}
}
return false;
}
}
}

View file

@ -3,12 +3,12 @@ package com.google.zxing.common.flexdatatypes
// these comparators should reside in the classes but that didn's work for some reason. // 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.datamatrix.detector.ResultPointsAndTransitionsComparator;
import com.google.zxing.qrcode.detector.CenterComparator; import com.google.zxing.qrcode.detector.CenterComparator;
import com.google.zxing.qrcode.detector.FurthestFromAverageComparator;
public class ArrayList public class ArrayList
{ {
//BAS : made public for debugging private var _array:Array;
public var _array:Array;
public function ArrayList(siz:int=0) public function ArrayList(siz:int=0)
{ {
this._array = new Array(siz); this._array = new Array(siz);
@ -63,6 +63,35 @@ package com.google.zxing.common.flexdatatypes
return this._array.indexOf(o); return this._array.indexOf(o);
} }
public function removeElementAt(index:int):void
{
var newArray:Array = new Array();
for(var i:int=0;i<this._array.length;i++)
{
if (i != index) { newArray.push(this._array[i]); }
}
this._array = newArray;
}
public function setElementAt(elem:Object, index:int):void
{
this._array[index] = elem;
}
// limit size of array
public function setSize(size:int):void
{
var newArray:Array = new Array();
if (this._array.length > size)
{
for (var i:int=0;i<size;i++)
{
newArray[i] = this._array[i]; // bas : fixed .push
}
this._array = newArray;
}
}
public function RemoveRange(newSize:int,itemsToRemove:int):void public function RemoveRange(newSize:int,itemsToRemove:int):void
{ {
// remove the items // remove the items
@ -101,11 +130,18 @@ package com.google.zxing.common.flexdatatypes
//this._array.sort(args); //this._array.sort(args);
} }
public function sort_CenterComparator():void public function sort_CenterComparator(average:Number):void
{ {
CenterComparator.setAverage(average);
this._array.sort(CenterComparator.compare); this._array.sort(CenterComparator.compare);
} }
public function sort_FurthestFromAverageComparator(average:Number):void
{
FurthestFromAverageComparator.setAverage(average);
this._array.sort(FurthestFromAverageComparator.compare);
}
public function size():int public function size():int
{ {
return this._array.length; return this._array.length;
@ -128,6 +164,21 @@ package com.google.zxing.common.flexdatatypes
} }
} }
public function clearAll():void
{
this._array = new Array();
}
public function elements():Array
{
return this._array;
}
public function lastElement():Object
{
return this._array[this._array.length-1]; // bas : fixed this
}
} }
} }

View file

@ -0,0 +1,34 @@
package com.google.zxing.common.flexdatatypes
{
public class Enumeration
{
private var _arr:Array;
public function isEmpty():Boolean
{
return this.getSize()==0?true:false;
}
public function getSize():int
{
return this._arr.length;
}
public function Enumeration(arr:Array)
{
this._arr = arr;
}
public function hasMoreElement():Boolean
{
return (!this.isEmpty());
}
public function nextElement():Object
{
return this._arr.shift();
}
}
}

View file

@ -2,8 +2,7 @@ package com.google.zxing.common.flexdatatypes
{ {
public class HashTable public class HashTable
{ {
// bas : made public for debugging private var _arr:Array;
public var _arr:Array;
public function isEmpty():Boolean public function isEmpty():Boolean
{ {
@ -57,6 +56,7 @@ package com.google.zxing.common.flexdatatypes
public function ContainsKey(key:Object):Boolean public function ContainsKey(key:Object):Boolean
{ {
//for (var i:int=0;i<this._arr.length;i++)
for (var i:String in this._arr) for (var i:String in this._arr)
{ {
if (this._arr[i][0] == key) { return true; } if (this._arr[i][0] == key) { return true; }
@ -67,6 +67,7 @@ package com.google.zxing.common.flexdatatypes
public function getValuesByKey(key:Object):ArrayList public function getValuesByKey(key:Object):ArrayList
{ {
var al:ArrayList = new ArrayList(); var al:ArrayList = new ArrayList();
//for (var i:int=0;i<this._arr.length;i++)
for (var i:String in this._arr) for (var i:String in this._arr)
{ {
if (this._arr[i][0] == key) if (this._arr[i][0] == key)
@ -85,6 +86,7 @@ package com.google.zxing.common.flexdatatypes
public function getValueByKey(key:Object):Object public function getValueByKey(key:Object):Object
{ {
var al:ArrayList = new ArrayList(); var al:ArrayList = new ArrayList();
//for (var i:int=0;i<this._arr.length;i++)
for (var i:String in this._arr) for (var i:String in this._arr)
{ {
if (this._arr[i][0] == key) if (this._arr[i][0] == key)
@ -92,11 +94,12 @@ package com.google.zxing.common.flexdatatypes
return this._arr[i][1]; return this._arr[i][1];
} }
} }
return al; return null;
} }
public function setValue(key:Object,value:Object):void public function setValue(key:Object,value:Object):void
{ {
//for (var i:int=0;i<this._arr.length;i++)
for (var i:String in this._arr) for (var i:String in this._arr)
{ {
if (this._arr[i][0] == key) if (this._arr[i][0] == key)
@ -109,6 +112,7 @@ package com.google.zxing.common.flexdatatypes
public function getKeyByValue(value:Object):int public function getKeyByValue(value:Object):int
{ {
//for (var i:int=0;i<this._arr.length;i++)
for (var i:String in this._arr) for (var i:String in this._arr)
{ {
if (this._arr[i][1] == value) if (this._arr[i][1] == value)
@ -121,16 +125,29 @@ package com.google.zxing.common.flexdatatypes
public function containsKey(key:Object):Boolean public function containsKey(key:Object):Boolean
{ {
//for (var i:int=0;i<this._arr.length;i++)
for (var i:String in this._arr) for (var i:String in this._arr)
{ {
if (this._arr[i][0] == key) if (this._arr[i][0] == key)
{ {
return true; return true;
} }
} }
return false; return false;
} }
public function keys():Array
{
var result:Array = new Array(this._arr.length);
//for (var i:int=0;i<this._arr.length;i++)
for (var i:String in this._arr)
{
result[i] = this._arr[i][0];
}
return result;
}
} }
} }

View file

@ -32,7 +32,7 @@ package com.google.zxing.common.flexdatatypes
} }
else else
{ {
throw new ReaderException("StringBuilder : setLength : only 0 supported"); this._string = this._string.substr(0,l);
} }
} }
@ -44,6 +44,10 @@ package com.google.zxing.common.flexdatatypes
{ {
this._string = this._string + (o as Array).join(""); this._string = this._string + (o as Array).join("");
} }
else if (o is String)
{
this._string = this._string + o;
}
else else
{ {
this._string = this._string + o.toString(); this._string = this._string + o.toString();
@ -105,6 +109,21 @@ package com.google.zxing.common.flexdatatypes
return this._string; return this._string;
} }
public function toHexString():String
{
var r:String="";
var e:int=this._string.length;
var c:int=0;
var h:String;
while(c<e){
h=this._string.charCodeAt(c++).toString(16);
while(h.length<3) h="0"+h;
r+=h;
}
return r;
}
public function deleteCharAt(index:int):void public function deleteCharAt(index:int):void
{ {
var temp:Array = this._string.split(""); var temp:Array = this._string.split("");

View file

@ -25,7 +25,6 @@
private static var INITIAL_SIZE:int = 32; private static var INITIAL_SIZE:int = 32;
// BAS : made public for debugging
private var bytes:Array; private var bytes:Array;
private var Size:int; private var Size:int;

View file

@ -29,10 +29,18 @@ package com.google.zxing.datamatrix
import com.google.zxing.datamatrix.decoder.Decoder; import com.google.zxing.datamatrix.decoder.Decoder;
import com.google.zxing.datamatrix.detector.Detector; import com.google.zxing.datamatrix.detector.Detector;
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.NotFoundException;
public class DataMatrixReader implements Reader public class DataMatrixReader implements Reader
{ {
public function reset():void {
// do nothing
}
/** /**
* This implementation can detect and decode Data Matrix codes in an image. * This implementation can detect and decode Data Matrix codes in an image.
* *
@ -91,71 +99,74 @@ package com.google.zxing.datamatrix
* around it. This is a specialized method that works exceptionally fast in this special * around it. This is a specialized method that works exceptionally fast in this special
* case. * case.
*/ */
private static function extractPureBits(image:BitMatrix ):BitMatrix { private static function extractPureBits(image:BitMatrix):BitMatrix
// Now need to determine module size in pixels {
var leftTopBlack:Array = image.getTopLeftOnBit();
var height:int = image.getHeight(); var rightBottomBlack:Array = image.getBottomRightOnBit();
var width:int = image.getWidth(); if (leftTopBlack == null || rightBottomBlack == null)
var minDimension:int = Math.min(height, width); {
throw NotFoundException.getNotFoundInstance();
// 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 moduleSize:int = moduleSize(leftTopBlack, image);
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; var top:int = leftTopBlack[1];
var bottom:int = rightBottomBlack[1];
var left:int = leftTopBlack[0];
var right:int = rightBottomBlack[0];
// And now find where the bottommost black module on the first column ends var matrixWidth:int = (right - left + 1) / moduleSize;
var columnEndOfSymbol:int = height - 1; var matrixHeight:int = (bottom - top + 1) / moduleSize;
while (columnEndOfSymbol >= 0 && !image._get(borderWidth, columnEndOfSymbol)) { if (matrixWidth <= 0 || matrixHeight <= 0)
columnEndOfSymbol--; {
throw NotFoundException.getNotFoundInstance();
} }
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 // 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 // sampling in the middle of the module. Just in case the image is a
// little off, this will help recover. // little off, this will help recover.
borderWidth += moduleSize >> 1; var nudge:int = moduleSize >> 1;
top += nudge;
var sampleDimension:int = borderWidth + (dimension - 1) * moduleSize; left += nudge;
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 // Now just read off the bits
var bits:BitMatrix = new BitMatrix(dimension); var bits:BitMatrix = new BitMatrix(matrixWidth, matrixHeight);
for (var i:int = 0; i < dimension; i++) { for (var y:int = 0; y < matrixHeight; y++)
var iOffset:int = borderWidth + i * moduleSize; {
for (var j:int = 0; j < dimension; j++) { var iOffset:int = top + y * moduleSize;
if (image._get(borderWidth + j * moduleSize, iOffset)) { for (var x:int = 0; x < matrixWidth; x++)
bits._set(j, i); {
if (image._get(left + x * moduleSize, iOffset))
{
bits._set(x, y);
} }
} }
} }
return bits; return bits;
} }
private static function moduleSize(leftTopBlack:Array, image:BitMatrix):int
{
var width:int = image.getWidth();
var x:int = leftTopBlack[0];
var y:int = leftTopBlack[1];
while (x < width && image._get(x, y))
{
x++;
}
if (x == width)
{
throw NotFoundException.getNotFoundInstance();
}
var moduleSize:int = x - leftTopBlack[0];
if (moduleSize == 0)
{
throw NotFoundException.getNotFoundInstance();
}
return moduleSize;
}
} }
} }

View file

@ -33,15 +33,14 @@ package com.google.zxing.datamatrix.decoder
* @throws ReaderException if dimension is < 10 or > 144 or not 0 mod 2 * @throws ReaderException if dimension is < 10 or > 144 or not 0 mod 2
*/ */
public function BitMatrixParser(bitMatrix:BitMatrix) { public function BitMatrixParser(bitMatrix:BitMatrix) {
var dimension:int = bitMatrix.getDimension(); var dimension:int = bitMatrix.getHeight();
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) { if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0) {
throw new ReaderException("BitMatrixParser : Dimension out of range :"+dimension+" range 11~143 or uneven number"); throw new ReaderException("BitMatrixParser : Dimension out of range :"+dimension+" range 11~143 or uneven number");
} }
version = readVersion(bitMatrix); version = readVersion(bitMatrix);
this.mappingBitMatrix = extractDataRegion(bitMatrix); this.mappingBitMatrix = extractDataRegion(bitMatrix);
// TODO(bbrown): Make this work for rectangular symbols this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getWidth(), this.mappingBitMatrix.getHeight());
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getDimension());
} }
/** /**
@ -55,16 +54,11 @@ package com.google.zxing.datamatrix.decoder
* @throws ReaderException if the dimensions of the mapping matrix are not valid * @throws ReaderException if the dimensions of the mapping matrix are not valid
* Data Matrix dimensions. * Data Matrix dimensions.
*/ */
public function readVersion(bitMatrix:BitMatrix ):Version { public function readVersion(bitMatrix:BitMatrix ):Version
{
if (version != null) { var numRows:int = bitMatrix.getHeight();
return version; var numColumns:int = bitMatrix.getWidth();
} return Version.getVersionForDimensions(numRows, numColumns);
// TODO(bbrown): make this work for rectangular dimensions as well.
var numRows:int = bitMatrix.getDimension();
var numColumns:int = numRows;
return Version.getVersionForDimensions(numRows, numColumns);;
} }
/** /**
@ -82,9 +76,9 @@ package com.google.zxing.datamatrix.decoder
var row:int = 4; var row:int = 4;
var column:int = 0; var column:int = 0;
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
var numRows:int = mappingBitMatrix.getDimension(); var numRows:int = mappingBitMatrix.getHeight();
var numColumns:int = numRows; var numColumns:int = mappingBitMatrix.getWidth();
var corner1Read:Boolean = false; var corner1Read:Boolean = false;
var corner2Read:Boolean = false; var corner2Read:Boolean = false;
@ -142,7 +136,8 @@ package com.google.zxing.datamatrix.decoder
if (resultOffset != version.getTotalCodewords()) { if (resultOffset != version.getTotalCodewords()) {
throw new ReaderException("BitMatrixParser : readCodewords : resultOffset != version.getTotalCodewords() : "+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)
// extra code for Flex : result should be a signed byte array (bit 7 = sign)
for (var jj:int=0;jj<result.length;jj++) for (var jj:int=0;jj<result.length;jj++)
{ {
if ((result[jj] & 128) > 0 ) if ((result[jj] & 128) > 0 )
@ -164,11 +159,13 @@ package com.google.zxing.datamatrix.decoder
*/ */
public function readModule(row:int , column:int , numRows:int , numColumns:int ):Boolean { public function readModule(row:int , column:int , numRows:int , numColumns:int ):Boolean {
// Adjust the row and column indices based on boundary wrapping // Adjust the row and column indices based on boundary wrapping
if (row < 0) { if (row < 0)
{
row += numRows; row += numRows;
column += 4 - ((numRows + 4) & 0x07); column += 4 - ((numRows + 4) & 0x07);
} }
if (column < 0) { if (column < 0)
{
column += numColumns; column += numColumns;
row += 4 - ((numColumns + 4) & 0x07); row += 4 - ((numColumns + 4) & 0x07);
} }
@ -416,8 +413,7 @@ package com.google.zxing.datamatrix.decoder
var symbolSizeRows:int = version.getSymbolSizeRows(); var symbolSizeRows:int = version.getSymbolSizeRows();
var symbolSizeColumns:int = version.getSymbolSizeColumns(); var symbolSizeColumns:int = version.getSymbolSizeColumns();
// TODO(bbrown): Make this work with rectangular codes if (bitMatrix.getHeight() != symbolSizeRows) {
if (bitMatrix.getDimension() != symbolSizeRows) {
throw new IllegalArgumentException("Dimension of bitMarix must match the version size"); throw new IllegalArgumentException("Dimension of bitMarix must match the version size");
} }
@ -428,10 +424,10 @@ package com.google.zxing.datamatrix.decoder
var numDataRegionsColumn:int = symbolSizeColumns / dataRegionSizeColumns; var numDataRegionsColumn:int = symbolSizeColumns / dataRegionSizeColumns;
var sizeDataRegionRow:int = numDataRegionsRow * dataRegionSizeRows; var sizeDataRegionRow:int = numDataRegionsRow * dataRegionSizeRows;
//int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; var sizeDataRegionColumn:int = numDataRegionsColumn * dataRegionSizeColumns;
// TODO(bbrown): Make this work with rectangular codes // TODO(bbrown): Make this work with rectangular codes
var bitMatrixWithoutAlignment:BitMatrix = new BitMatrix(sizeDataRegionRow); var bitMatrixWithoutAlignment:BitMatrix = new BitMatrix(sizeDataRegionColumn,sizeDataRegionRow);
for (var dataRegionRow:int = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { for (var dataRegionRow:int = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
var dataRegionRowOffset:int = dataRegionRow * dataRegionSizeRows; var dataRegionRowOffset:int = dataRegionRow * dataRegionSizeRows;
for (var dataRegionColumn:int = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) { for (var dataRegionColumn:int = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {

View file

@ -82,8 +82,7 @@ package com.google.zxing.datamatrix.decoder
for (var i2:int = 0; i2 < shorterBlocksNumDataCodewords; i2++) { for (var i2:int = 0; i2 < shorterBlocksNumDataCodewords; i2++) {
for (var j2:int = 0; j2 < numResultBlocks; j2++) for (var j2:int = 0; j2 < numResultBlocks; j2++)
{ {
result[j2].codewords[i2] = rawCodewords[rawCodewordsOffset]; result[j2].codewords[i2] = rawCodewords[rawCodewordsOffset++];
rawCodewordsOffset++;
} }
} }
@ -91,8 +90,7 @@ package com.google.zxing.datamatrix.decoder
var specialVersion:Boolean = version.getVersionNumber() == 24; var specialVersion:Boolean = version.getVersionNumber() == 24;
var numLongerBlocks:int = specialVersion ? 8 : numResultBlocks; var numLongerBlocks:int = specialVersion ? 8 : numResultBlocks;
for (var j3:int = 0; j3 < numLongerBlocks; j3++) { for (var j3:int = 0; j3 < numLongerBlocks; j3++) {
result[j3].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset]; result[j3].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++];
rawCodewordsOffset++;
} }
// Now add in error correction blocks // Now add in error correction blocks
@ -100,8 +98,7 @@ package com.google.zxing.datamatrix.decoder
for (var i4:int = longerBlocksNumDataCodewords; i4 < max; i4++) { for (var i4:int = longerBlocksNumDataCodewords; i4 < max; i4++) {
for (var j4:int = 0; j4 < numResultBlocks; j4++) { for (var j4:int = 0; j4 < numResultBlocks; j4++) {
var iOffset:int = (specialVersion && j4 > 7) ? i4 - 1 : i4; var iOffset:int = (specialVersion && j4 > 7) ? i4 - 1 : i4;
result[j4].codewords[iOffset] = rawCodewords[rawCodewordsOffset]; result[j4].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
rawCodewordsOffset++;
} }
} }

View file

@ -34,6 +34,7 @@ package com.google.zxing.datamatrix.decoder
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.zxingByteArray; import com.google.zxing.common.zxingByteArray;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.FormatException;
/** /**
* See ISO 16022:2006, Annex C Table C.1 * See ISO 16022:2006, Annex C Table C.1
* The C40 Basic Character Set (*'s used for placeholders for the shift values) * The C40 Basic Character Set (*'s used for placeholders for the shift values)
@ -77,10 +78,10 @@ package com.google.zxing.datamatrix.decoder
public static function decode(bytes:Array ):DecoderResult { public static function decode(bytes:Array ):DecoderResult {
var bits:BitSource = new BitSource(bytes); var bits:BitSource = new BitSource(bytes);
var result:StringBuilder = new StringBuilder(); var result:StringBuilder = new StringBuilder(100);
var resultTrailer:StringBuilder = new StringBuilder(0); var resultTrailer:StringBuilder = new StringBuilder(0);
var byteSegments:ArrayList = new ArrayList(1); var byteSegments:ArrayList = new ArrayList(1);
var mode:int = DecodedBitStreamParser.ASCII_ENCODE; var mode:int = ASCII_ENCODE;
do { do {
if (mode == ASCII_ENCODE) { if (mode == ASCII_ENCODE) {
mode = decodeAsciiSegment(bits, result, resultTrailer); mode = decodeAsciiSegment(bits, result, resultTrailer);
@ -140,12 +141,13 @@ package com.google.zxing.datamatrix.decoder
return C40_ENCODE; return C40_ENCODE;
} else if (oneByte == 231) { // Latch to Base 256 encodation } else if (oneByte == 231) { // Latch to Base 256 encodation
return BASE256_ENCODE; return BASE256_ENCODE;
} else if (oneByte == 232) { // FNC1 } else if (oneByte == 232) {
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 232 "); // FNC1
} else if (oneByte == 233) { // Structured Append String.fromCharCode(29);// translate as ASCII 29
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 233"); } else if ((oneByte == 233) || (oneByte == 234))
} else if (oneByte == 234) { // Reader Programming { // Structured Append, Reader Programming
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 234"); // Ignore these symbols for now
// throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 234");
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII) } else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
upperShift = true; upperShift = true;
} else if (oneByte == 236) { // 05 Macro } else if (oneByte == 236) { // 05 Macro
@ -162,9 +164,15 @@ package com.google.zxing.datamatrix.decoder
return EDIFACT_ENCODE; return EDIFACT_ENCODE;
} else if (oneByte == 241) { // ECI Character } else if (oneByte == 241) { // ECI Character
// TODO(bbrown): I think we need to support ECI // TODO(bbrown): I think we need to support ECI
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 241"); //throw ReaderException.getInstance();
// Ignore this symbol for now
} else if (oneByte >= 242) { // Not to be used in ASCII encodation } else if (oneByte >= 242) { // Not to be used in ASCII encodation
throw new ReaderException("DecodedBitStreamParser : decodeAsciiSegment : oneByte = 242"); // ... but work around encoders that end with 254, latch back to ASCII
if (oneByte == 254 && bits.available() == 0) {
// Ignore
} else {
throw FormatException.getFormatInstance();
}
} }
} while (bits.available() > 0); } while (bits.available() > 0);
return ASCII_ENCODE; return ASCII_ENCODE;
@ -226,7 +234,7 @@ package com.google.zxing.datamatrix.decoder
result.Append(C40_SHIFT2_SET_CHARS[cValue]); result.Append(C40_SHIFT2_SET_CHARS[cValue]);
} }
} else if (cValue == 27) { // FNC1 } else if (cValue == 27) { // FNC1
throw new ReaderException("DecodedBitStreamParser : decodeC40Segment : cValue = 27"); result.Append(String.fromCharCode(29)); // translate as ASCII 29
} else if (cValue == 30) { // Upper Shift } else if (cValue == 30) { // Upper Shift
upperShift = true; upperShift = true;
} else { } else {
@ -279,13 +287,16 @@ package com.google.zxing.datamatrix.decoder
case 0: case 0:
if (cValue < 3) { if (cValue < 3) {
shift = cValue + 1; shift = cValue + 1;
} else { } else if (cValue < TEXT_BASIC_SET_CHARS.length) {
var textChar:String = TEXT_BASIC_SET_CHARS[cValue];
if (upperShift) { if (upperShift) {
result.Append(TEXT_BASIC_SET_CHARS[cValue]); result.Append(String.fromCharCode(textChar.charCodeAt(0) + 128));
upperShift = false; upperShift = false;
} else { } else {
result.Append(TEXT_BASIC_SET_CHARS[cValue]); result.Append(textChar);
} }
} else {
throw FormatException.getFormatInstance();
} }
break; break;
case 1: case 1:
@ -299,15 +310,16 @@ package com.google.zxing.datamatrix.decoder
break; break;
case 2: case 2:
// Shift 2 for Text is the same encoding as C40 // Shift 2 for Text is the same encoding as C40
if (cValue < 27) { if (cValue < C40_SHIFT2_SET_CHARS.length) {
var c40char:String =C40_SHIFT2_SET_CHARS[cValue];
if (upperShift) { if (upperShift) {
result.Append(C40_SHIFT2_SET_CHARS[cValue] + 128); result.Append(c40char + 128);
upperShift = false; upperShift = false;
} else { } else {
result.Append(C40_SHIFT2_SET_CHARS[cValue]); result.Append(c40char);
} }
} else if (cValue == 27) { // FNC1 } else if (cValue == 27) { // FNC1
throw new ReaderException( "DecodedBitStreamParser : decodeTextSegment : cValue = 27"); result.Append(String.fromCharCode(29)); // translate as ASCII 29
} else if (cValue == 30) { // Upper Shift } else if (cValue == 30) { // Upper Shift
upperShift = true; upperShift = true;
} else { } else {
@ -316,13 +328,22 @@ package com.google.zxing.datamatrix.decoder
shift = 0; shift = 0;
break; break;
case 3: case 3:
if (cValue < TEXT_SHIFT3_SET_CHARS.length)
{
var textChar2:String = TEXT_SHIFT3_SET_CHARS[cValue];
if (upperShift) { if (upperShift) {
result.Append(TEXT_SHIFT3_SET_CHARS[cValue] + 128); result.Append(textChar2 + 128);
upperShift = false; upperShift = false;
} else { } else {
result.Append(TEXT_SHIFT3_SET_CHARS[cValue]); result.Append(textChar2);
} }
shift = 0; shift = 0;
}
else
{
throw new ReaderException();
}
break; break;
default: default:
throw new ReaderException("DecodedBitStreamParser : decodeTextSegment : no match for shift"+shift); throw new ReaderException("DecodedBitStreamParser : decodeTextSegment : no match for shift"+shift);
@ -404,8 +425,8 @@ package com.google.zxing.datamatrix.decoder
} }
if (!unlatch) { if (!unlatch) {
if ((edifactValue & 32) == 0) { // no 1 in the leading (6th) bit if ((edifactValue & 0x20) == 0) { // no 1 in the leading (6th) bit
edifactValue |= 64; // Add a leading 01 to the 6 bit binary value edifactValue |= 0x40; // Add a leading 01 to the 6 bit binary value
} }
result.Append(edifactValue); result.Append(edifactValue);
} }
@ -416,36 +437,54 @@ package com.google.zxing.datamatrix.decoder
/** /**
* See ISO 16022:2006, 5.2.9 and Annex B, B.2 * See ISO 16022:2006, 5.2.9 and Annex B, B.2
*/ */
private static function decodeBase256Segment(bits:BitSource, result:StringBuilder , byteSegments:ArrayList ):void { private static function decodeBase256Segment(bits:BitSource, result:StringBuilder , byteSegments:ArrayList ):void
{
// Figure out how long the Base 256 Segment is. // Figure out how long the Base 256 Segment is.
var d1:int = bits.readBits(8); // Figure out how long the Base 256 Segment is.
var codewordPosition:int = 1 + bits.getByteOffset(); // position is 1-indexed
var d1:int = unrandomize255State(bits.readBits(8), codewordPosition++);
var count:int; var count:int;
if (d1 == 0) { // Read the remainder of the symbol if (d1 == 0)
{ // Read the remainder of the symbol
count = bits.available() / 8; count = bits.available() / 8;
} else if (d1 < 250) {
count = d1;
} else {
count = 250 * (d1 - 249) + bits.readBits(8);
} }
else if (d1 < 250)
{
count = d1;
}
else
{
count = 250 * (d1 - 249) + unrandomize255State(bits.readBits(8), codewordPosition++);
}
// We're seeing NegativeArraySizeException errors from users.
if (count < 0)
{
throw FormatException.getFormatInstance();
}
var bytes:Array = new Array(count); var bytes:Array = new Array(count);
for (var i:int = 0; i < count; i++) for (var i:int = 0; i < count; i++)
{ {
bytes[i] = unrandomize255State(bits.readBits(8), i); // Have seen this particular error in the wild, such as at
} // http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2
byteSegments.Add(bytes); if (bits.available() < 8)
try
{ {
//result.Append(System.Text.Encoding.GetEncoding("iso-8859-1").GetString(bytes)); throw FormatException.getFormatInstance();
var str:String = "";
for(var i2:int=0;i2<bytes.length;i2++)
{
str = str + String.fromCharCode(bytes[i2]);
} }
result.Append(str); bytes[i] = unrandomize255State(bits.readBits(8), codewordPosition++);
}
byteSegments.addElement(bytes);
} catch (uee:Error ) { /*try
throw new Error("Platform does not support required encoding: " + uee); {
result.append(new String(bytes, "ISO8859_1"));
} catch (UnsupportedEncodingException uee)
{
throw new RuntimeException("Platform does not support required encoding: " + uee);
}*/
for (var k:int=0;k<bytes.length;k++)
{
result.Append(String.fromCharCode(bytes[k])); // BAS :Flex does not support encodings
} }
} }

View file

@ -29,13 +29,13 @@ package com.google.zxing.datamatrix.decoder
import com.google.zxing.common.DecoderResult; import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
import com.google.zxing.common.reedsolomon.ReedSolomonException; import com.google.zxing.common.reedsolomon.ReedSolomonException;
import com.google.zxing.common.reedsolomon.GF256; import com.google.zxing.common.reedsolomon.GenericGF;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
private var rsDecoder:ReedSolomonDecoder; private var rsDecoder:ReedSolomonDecoder;
public function Decoder() { public function Decoder() {
rsDecoder = new ReedSolomonDecoder(GF256.DATA_MATRIX_FIELD); rsDecoder = new ReedSolomonDecoder(GenericGF.DATA_MATRIX_FIELD_256);
} }
/** /**
@ -94,16 +94,18 @@ package com.google.zxing.datamatrix.decoder
var resultOffset:int = 0; var resultOffset:int = 0;
// Error-correct and copy data blocks together into a stream of bytes // Error-correct and copy data blocks together into a stream of bytes
for (var j:int = 0; j < dataBlocks.length; j++) { for (var j:int = 0; j < dataBlocks.length; j++)
{
var dataBlock:DataBlock = dataBlocks[j]; var dataBlock:DataBlock = dataBlocks[j];
var codewordBytes:Array = dataBlock.getCodewords(); var codewordBytes:Array = dataBlock.getCodewords();
var numDataCodewords:int = dataBlock.getNumDataCodewords(); var numDataCodewords:int = dataBlock.getNumDataCodewords();
correctErrors(codewordBytes, numDataCodewords); correctErrors(codewordBytes, numDataCodewords);
for (var ii:int = 0; ii < numDataCodewords; ii++) { for (var i5:int = 0; i5 < numDataCodewords; i5++)
resultBytes[resultOffset++] = codewordBytes[ii]; {
// De-interlace data blocks.
resultBytes[i5 * dataBlocks.length + j] = codewordBytes[i5];
} }
} }
// Decode the contents of that stream of bytes // Decode the contents of that stream of bytes
return DecodedBitStreamParser.decode(resultBytes); return DecodedBitStreamParser.decode(resultBytes);
} }
@ -131,8 +133,10 @@ package com.google.zxing.datamatrix.decoder
} }
// Copy back into array of bytes -- only need to worry about the bytes that were data // 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 // We don't care about errors in the error-correction codewords
for (var ii:int = 0; ii < numDataCodewords; ii++) { for (var ii:int = 0; ii < numDataCodewords; ii++)
codewordBytes[ii] = int( codewordsInts[ii]); {
codewordBytes[ii] = (codewordsInts[ii]>127)?codewordsInts[ii]-256:codewordsInts[ii];
} }
} }

View file

@ -25,7 +25,6 @@ package com.google.zxing.datamatrix.decoder
public class Version public class Version
{ {
private static var VERSIONS:Array = buildVersions(); private static var VERSIONS:Array = buildVersions();
private var versionNumber:int; private var versionNumber:int;
private var symbolSizeRows:int; private var symbolSizeRows:int;
@ -182,7 +181,7 @@ package com.google.zxing.datamatrix.decoder
new ECBlocks(14, new ECB(1, 16))), new ECBlocks(14, new ECB(1, 16))),
new Version(28, 12, 36, 10, 16, new Version(28, 12, 36, 10, 16,
new ECBlocks(18, new ECB(1, 22))), new ECBlocks(18, new ECB(1, 22))),
new Version(29, 16, 36, 10, 16, new Version(29, 16, 36, 14, 16,
new ECBlocks(24, new ECB(1, 32))), new ECBlocks(24, new ECB(1, 32))),
new Version(30, 16, 48, 14, 22, new Version(30, 16, 48, 14, 22,
new ECBlocks(28, new ECB(1, 49)))]; new ECBlocks(28, new ECB(1, 49)))];

View file

@ -15,17 +15,16 @@
*/ */
package com.google.zxing.datamatrix.detector package com.google.zxing.datamatrix.detector
{ {
import com.google.zxing.common.detector.MonochromeRectangleDetector; import com.google.zxing.ReaderException;
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.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.BitArray;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
import com.google.zxing.ReaderException; import com.google.zxing.common.DetectorResult;
import com.google.zxing.common.GridSampler;
import com.google.zxing.common.detector.MonochromeRectangleDetector;
import com.google.zxing.common.detector.WhiteRectangleDetector;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.flexdatatypes.HashTable;
/** /**
* <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code * <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code
@ -51,16 +50,16 @@ package com.google.zxing.datamatrix.detector
// Trick to avoid creating new int objects below -- a sort of crude copy of // 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 // the int.valueOf(int) optimization added in Java 5, not in J2ME
//private static var INTEGERS:Array = [0, 1, 2, 3, 4]; private static var INTEGERS:Array =[ 0,1,2,3,4 ];
private var image:BitMatrix ; private var image:BitMatrix ;
private var rectangleDetector:MonochromeRectangleDetector private var rectangleDetector:WhiteRectangleDetector;
public function Detector(image:BitMatrix) public function Detector(image:BitMatrix)
{ {
this.image = image; this.image = image;
rectangleDetector = new MonochromeRectangleDetector(image); rectangleDetector = new WhiteRectangleDetector(image);
} }
/** /**
@ -69,7 +68,8 @@ package com.google.zxing.datamatrix.detector
* @return {@link DetectorResult} encapsulating results of detecting a QR Code * @return {@link DetectorResult} encapsulating results of detecting a QR Code
* @throws ReaderException if no Data Matrix Code can be found * @throws ReaderException if no Data Matrix Code can be found
*/ */
public function detect():DetectorResult { public function detect():DetectorResult
{
var cornerPoints:Array = rectangleDetector.detect(); var cornerPoints:Array = rectangleDetector.detect();
@ -92,8 +92,8 @@ package com.google.zxing.datamatrix.detector
// Sort by number of transitions. First two will be the two solid sides; last two // Sort by number of transitions. First two will be the two solid sides; last two
// will be the two alternating black/white sides // will be the two alternating black/white sides
var lSideOne:ResultPointsAndTransitions = ResultPointsAndTransitions( transitions.getObjectByIndex(0)); var lSideOne:ResultPointsAndTransitions = ( transitions.getObjectByIndex(0) as ResultPointsAndTransitions);
var lSideTwo:ResultPointsAndTransitions = ResultPointsAndTransitions( transitions.getObjectByIndex(1)); var lSideTwo:ResultPointsAndTransitions = ( transitions.getObjectByIndex(1) as ResultPointsAndTransitions);
// Figure out which point is their intersection by tallying up the number of times we see the // 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. // endpoints in the four endpoints. One will show up twice.
@ -110,7 +110,7 @@ package com.google.zxing.datamatrix.detector
for (var ii:int=0;ii<size;ii++) for (var ii:int=0;ii<size;ii++)
{ {
var point:ResultPoint = pointCount.getKeyByIndex(ii) as ResultPoint;// resultpoints are used as keys var point:ResultPoint = pointCount.getKeyByIndex(ii) as ResultPoint;// resultpoints are used as keys
var value:int = pointCount.getValueByIndex(ii) as int; var value:int = Math.floor(pointCount.getValueByIndex(ii) as Number);
if (value == 2) if (value == 2)
{ {
bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides
@ -170,22 +170,185 @@ package com.google.zxing.datamatrix.detector
// The top right point is actually the corner of a module, which is one of the two black modules // 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 // 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 // or bottom right should work here.
// up or across, rather than at a slight angle. We use dot products to figure out which is
// better to use: var dimensionTop:int = transitionsBetween(topLeft, topRight).getTransitions();
var dimension:int = Math.min(transitionsBetween(topLeft, topRight).getTransitions(), var dimensionRight:int = transitionsBetween(bottomRight, topRight).getTransitions();
transitionsBetween(bottomRight, topRight).getTransitions());
if ((dimension & 0x01) == 1) if ((dimensionTop & 0x01) == 1) {
{
// it can't be odd, so, round... up? // it can't be odd, so, round... up?
dimension++; dimensionTop++;
}
dimensionTop += 2;
if ((dimensionRight & 0x01) == 1) {
// it can't be odd, so, round... up?
dimensionRight++;
}
dimensionRight += 2;
var bits:BitMatrix ;
var correctedTopRight:ResultPoint ;
// Rectanguar symbols are 6x16, 6x28, 10x24, 10x32, 14x32, or 14x44. If one dimension is more
// than twice the other, it's certainly rectangular, but to cut a bit more slack we accept it as
// rectangular if the bigger side is at least 7/4 times the other:
if (4 * dimensionTop >= 7 * dimensionRight || 4 * dimensionRight >= 7 * dimensionTop) {
// The matrix is rectangular
correctedTopRight =
correctTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight, dimensionTop, dimensionRight);
if (correctedTopRight == null){
correctedTopRight = topRight;
}
dimensionTop = transitionsBetween(topLeft, correctedTopRight).getTransitions();
dimensionRight = transitionsBetween(bottomRight, correctedTopRight).getTransitions();
if ((dimensionTop & 0x01) == 1) {
// it can't be odd, so, round... up?
dimensionTop++;
}
if ((dimensionRight & 0x01) == 1) {
// it can't be odd, so, round... up?
dimensionRight++;
}
bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, correctedTopRight, dimensionTop, dimensionRight);
} else {
// The matrix is square
var dimension:int = Math.min(dimensionRight, dimensionTop);
// correct top right point to match the white module
correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
if (correctedTopRight == null){
correctedTopRight = topRight;
}
// Redetermine the dimension using the corrected top right point
var dimensionCorrected:int = Math.max(transitionsBetween(topLeft, correctedTopRight).getTransitions(),
transitionsBetween(bottomRight, correctedTopRight).getTransitions());
dimensionCorrected++;
if ((dimensionCorrected & 0x01) == 1) {
dimensionCorrected++;
}
bits = sampleGrid(image,
topLeft,
bottomLeft,
bottomRight,
correctedTopRight,
dimensionCorrected,
dimensionCorrected);
}
return new DetectorResult(bits, [topLeft, bottomLeft, bottomRight, correctedTopRight]);
} }
dimension += 2;
var bits:BitMatrix = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension);
return new DetectorResult(bits, [pointA, pointB, pointC, pointD]);
/**
* Calculates the position of the white top right module using the output of the rectangle detector
* for a rectangular matrix
*/
private function correctTopRightRectangular(bottomLeft:ResultPoint ,
bottomRight:ResultPoint , topLeft:ResultPoint , topRight:ResultPoint ,
dimensionTop:int , dimensionRight:int ):ResultPoint {
var corr:Number = distance(bottomLeft, bottomRight) / dimensionTop;
var norm:int = distance(topLeft, topRight);
var cos:Number = (topRight.getX() - topLeft.getX()) / norm;
var sin:Number = (topRight.getY() - topLeft.getY()) / norm;
var c1:ResultPoint = new ResultPoint(topRight.getX()+corr*cos, topRight.getY()+corr*sin);
corr = distance(bottomLeft, topLeft) / dimensionRight;
norm = distance(bottomRight, topRight);
cos = (topRight.getX() - bottomRight.getX()) / norm;
sin = (topRight.getY() - bottomRight.getY()) / norm;
var c2:ResultPoint = new ResultPoint(topRight.getX()+corr*cos, topRight.getY()+corr*sin);
if (!isValid(c1)){
if (isValid(c2)){
return c2;
}
return null;
} else if (!isValid(c2)){
return c1;
}
var l1:int = Math.abs(dimensionTop - transitionsBetween(topLeft, c1).getTransitions()) +
Math.abs(dimensionRight - transitionsBetween(bottomRight, c1).getTransitions());
var l2:int = Math.abs(dimensionTop - transitionsBetween(topLeft, c2).getTransitions()) +
Math.abs(dimensionRight - transitionsBetween(bottomRight, c2).getTransitions());
if (l1 <= l2){
return c1;
}
return c2;
}
/**
* Calculates the position of the white top right module using the output of the rectangle detector
* for a square matrix
*/
private function correctTopRight(bottomLeft:ResultPoint ,
bottomRight:ResultPoint,
topLeft:ResultPoint ,
topRight:ResultPoint ,
dimension:int ):ResultPoint {
var corr:Number = distance(bottomLeft, bottomRight) / dimension;
var norm:int = distance(topLeft, topRight);
var cos:Number = (topRight.getX() - topLeft.getX()) / norm;
var sin:Number = (topRight.getY() - topLeft.getY()) / norm;
var c1:ResultPoint = new ResultPoint(topRight.getX() + corr * cos, topRight.getY() + corr * sin);
corr = distance(bottomLeft, bottomRight) / dimension;
norm = distance(bottomRight, topRight);
cos = (topRight.getX() - bottomRight.getX()) / norm;
sin = (topRight.getY() - bottomRight.getY()) / norm;
var c2:ResultPoint = new ResultPoint(topRight.getX() + corr * cos, topRight.getY() + corr * sin);
if (!isValid(c1)) {
if (isValid(c2)) {
return c2;
}
return null;
} else if (!isValid(c2)) {
return c1;
}
var l1:int = Math.abs(transitionsBetween(topLeft, c1).getTransitions() -
transitionsBetween(bottomRight, c1).getTransitions());
var l2:int = Math.abs(transitionsBetween(topLeft, c2).getTransitions() -
transitionsBetween(bottomRight, c2).getTransitions());
return l1 <= l2 ? c1 : c2;
}
private function isValid(p:ResultPoint):Boolean {
return p.getX() >= 0 && p.getX() < image.getWidth() && p.getY() > 0 && p.getY() < image.getHeight();
}
private static function round(d:Number):int
{
return Math.ceil(d);
}
// L2 distance
private static function distance(a:ResultPoint, b:ResultPoint):int {
return Detector.round( Math.sqrt((a.getX() - b.getX())
* (a.getX() - b.getX()) + (a.getY() - b.getY())
* (a.getY() - b.getY())));
} }
/** /**
@ -199,43 +362,35 @@ package com.google.zxing.datamatrix.detector
} }
else else
{ {
var value:int = int(table.getValueByKey(key)); table.setValue(key,Math.floor(table.getValueByKey(key) as Number)+1);
table.setValue(key,value+1);
} }
} }
private static function sampleGrid(image:BitMatrix , private static function sampleGrid(image:BitMatrix ,
topLeft:ResultPoint, topLeft:ResultPoint ,
bottomLeft:ResultPoint, bottomLeft:ResultPoint ,
bottomRight:ResultPoint, bottomRight:ResultPoint ,
dimension:int):BitMatrix { topRight:ResultPoint ,
dimensionX:int ,
dimensionY:int ):BitMatrix{
// We make up the top right point for now, based on the others. var sampler:GridSampler = GridSampler.getGridSamplerInstance();
// 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 return sampler.sampleGrid2(image,
// very corners. So there is no 0.5f here; 0.0f is right. dimensionX,
var sampler:GridSampler; dimensionY,
sampler = GridSampler.getGridSamplerInstance(); 0.5,
return sampler.sampleGrid( 0.5,
image, dimensionX - 0.5,
dimension, 0.5,
0, dimensionX - 0.5,
0, dimensionY - 0.5,
dimension, 0.5,
0, dimensionY - 0.5,
dimension,
dimension,
0,
dimension,
topLeft.getX(), topLeft.getX(),
topLeft.getY(), topLeft.getY(),
topRightX, topRight.getX(),
topRightY, topRight.getY(),
bottomRight.getX(), bottomRight.getX(),
bottomRight.getY(), bottomRight.getY(),
bottomLeft.getX(), bottomLeft.getX(),
@ -245,12 +400,13 @@ package com.google.zxing.datamatrix.detector
/** /**
* Counts the number of black/white transitions between two points, using something like Bresenham's algorithm. * Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
*/ */
private function transitionsBetween( from:ResultPoint, _to:ResultPoint):ResultPointsAndTransitions { private function transitionsBetween( from:ResultPoint, _to:ResultPoint):ResultPointsAndTransitions
{
// See QR Code Detector, sizeOfBlackWhiteBlackRun() // See QR Code Detector, sizeOfBlackWhiteBlackRun()
var fromX:int = int( from.getX()); var fromX:int = Math.floor(from.getX());
var fromY:int = int( from.getY()); var fromY:int = Math.floor( from.getY());
var toX:int = int( _to.getX()); var toX:int = Math.floor( _to.getX());
var toY:int = int( _to.getY()); var toY:int = Math.floor( _to.getY());
var steep:Boolean = Math.abs(toY - fromY) > Math.abs(toX - fromX); var steep:Boolean = Math.abs(toY - fromY) > Math.abs(toX - fromX);
if (steep) { if (steep) {
var temp:int = fromX; var temp:int = fromX;
@ -268,22 +424,28 @@ package com.google.zxing.datamatrix.detector
var xstep:int = fromX < toX ? 1 : -1; var xstep:int = fromX < toX ? 1 : -1;
var transitions:int = 0; var transitions:int = 0;
var inBlack:Boolean = image._get(steep ? fromY : fromX, steep ? fromX : fromY); var inBlack:Boolean = image._get(steep ? fromY : fromX, steep ? fromX : fromY);
for (var x:int = fromX, y:int = fromY; x != toX; x += xstep) { for (var x:int = fromX, y:int = fromY; x != toX; x += xstep)
{
var isBlack:Boolean = image._get(steep ? y : x, steep ? x : y); var isBlack:Boolean = image._get(steep ? y : x, steep ? x : y);
if (isBlack == !inBlack) {
if (isBlack == !inBlack)
{
transitions++; transitions++;
inBlack = isBlack; inBlack = isBlack;
} }
error += dy; error += dy;
if (error > 0) { if (error > 0)
{
if (y == toY)
{
break;
}
y += ystep; y += ystep;
error -= dx; error -= dx;
} }
} }
return new ResultPointsAndTransitions(from, _to, transitions); return new ResultPointsAndTransitions(from, _to, transitions);
} }
} }
} }

View file

@ -9,7 +9,7 @@ package com.google.zxing.datamatrix.detector
public static function compare(o1:Object, o2:Object):int public static function compare(o1:Object, o2:Object):int
{ {
var result:int = (ResultPointsAndTransitions( o1)).getTransitions() - (ResultPointsAndTransitions( o2)).getTransitions(); var result:int = ((o1 as ResultPointsAndTransitions)).getTransitions() - ((o2 as ResultPointsAndTransitions)).getTransitions();
if (result > 0) { return 1; } if (result > 0) { return 1; }
else if (result < 0) { return -1; } else if (result < 0) { return -1; }
else {return 0;} else {return 0;}

View file

@ -0,0 +1,129 @@
/*
* Copyright 2011 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.maxicode
{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.maxicode.decoder.Decoder;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.NotFoundException;
/**
* This implementation can detect and decode a MaxiCode in an image.
*/
public class MaxiCodeReader implements Reader
{
private static var NO_POINTS:Array = new Array(0);
private static var MATRIX_WIDTH:int = 30;
private static var MATRIX_HEIGHT:int = 33;
private var decoder:Decoder = new Decoder();
public function getDecoder():Decoder
{
return decoder;
}
/**
* Locates and decodes a MaxiCode in an image.
*
* @return a String representing the content encoded by the MaxiCode
* @throws NotFoundException if a MaxiCode cannot be found
* @throws FormatException if a MaxiCode cannot be decoded
* @throws ChecksumException if error correction fails
*/
public function decode(image:BinaryBitmap, hints:HashTable=null):Result
{
var decoderResult:DecoderResult ;
if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE))
{
var bits:BitMatrix = extractPureBits(image.getBlackMatrix());
decoderResult = decoder.decode(bits, hints);
}
else
{
throw NotFoundException.getNotFoundInstance();
}
var points:Array = NO_POINTS;
var result:Result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.MAXICODE);
if (decoderResult.getECLevel() != null)
{
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.getECLevel());
}
return result;
}
public function reset():void
{
// do nothing
}
/**
* This method detects a code in a "pure" image -- that is, pure monochrome image
* which contains only an unrotated, unskewed, image of a code, with some white border
* around it. This is a specialized method that works exceptionally fast in this special
* case.
*
* @see com.google.zxing.pdf417.PDF417Reader#extractPureBits(BitMatrix)
* @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
* @see com.google.zxing.qrcode.QRCodeReader#extractPureBits(BitMatrix)
*/
private static function extractPureBits(image:BitMatrix):BitMatrix
{
var enclosingRectangle:Array = image.getEnclosingRectangle();
if (enclosingRectangle == null)
{
throw NotFoundException.getNotFoundInstance();
}
var left:int = enclosingRectangle[0];
var top:int = enclosingRectangle[1];
var width:int = enclosingRectangle[2];
var height:int = enclosingRectangle[3];
// Now just read off the bits
var bits:BitMatrix = new BitMatrix(MATRIX_WIDTH, MATRIX_HEIGHT);
for (var y:int = 0; y < MATRIX_HEIGHT; y++) {
var iy:int = top + (y * height + height / 2) / MATRIX_HEIGHT;
for (var x:int = 0; x < MATRIX_WIDTH; x++) {
var ix:int = left + (x * width + width / 2 + (y & 0x01) * width / 2) / MATRIX_WIDTH;
if (image._get(ix, iy)) {
bits._set(x, y);
}
}
}
return bits;
}
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright 2011 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.maxicode.decoder
{
import com.google.zxing.FormatException;
import com.google.zxing.common.BitMatrix;
/**
* @author mike32767
* @author Manuel Kasten
*/
public class BitMatrixParser {
private static var BITNR:Array = [
[121,120,127,126,133,132,139,138,145,144,151,150,157,156,163,162,169,168,175,174,181,180,187,186,193,192,199,198, -2, -2],
[123,122,129,128,135,134,141,140,147,146,153,152,159,158,165,164,171,170,177,176,183,182,189,188,195,194,201,200,816, -3],
[125,124,131,130,137,136,143,142,149,148,155,154,161,160,167,166,173,172,179,178,185,184,191,190,197,196,203,202,818,817],
[283,282,277,276,271,270,265,264,259,258,253,252,247,246,241,240,235,234,229,228,223,222,217,216,211,210,205,204,819, -3],
[285,284,279,278,273,272,267,266,261,260,255,254,249,248,243,242,237,236,231,230,225,224,219,218,213,212,207,206,821,820],
[287,286,281,280,275,274,269,268,263,262,257,256,251,250,245,244,239,238,233,232,227,226,221,220,215,214,209,208,822, -3],
[289,288,295,294,301,300,307,306,313,312,319,318,325,324,331,330,337,336,343,342,349,348,355,354,361,360,367,366,824,823],
[291,290,297,296,303,302,309,308,315,314,321,320,327,326,333,332,339,338,345,344,351,350,357,356,363,362,369,368,825, -3],
[293,292,299,298,305,304,311,310,317,316,323,322,329,328,335,334,341,340,347,346,353,352,359,358,365,364,371,370,827,826],
[409,408,403,402,397,396,391,390, 79, 78, -2, -2, 13, 12, 37, 36, 2, -1, 44, 43,109,108,385,384,379,378,373,372,828, -3],
[411,410,405,404,399,398,393,392, 81, 80, 40, -2, 15, 14, 39, 38, 3, -1, -1, 45,111,110,387,386,381,380,375,374,830,829],
[413,412,407,406,401,400,395,394, 83, 82, 41, -3, -3, -3, -3, -3, 5, 4, 47, 46,113,112,389,388,383,382,377,376,831, -3],
[415,414,421,420,427,426,103,102, 55, 54, 16, -3, -3, -3, -3, -3, -3, -3, 20, 19, 85, 84,433,432,439,438,445,444,833,832],
[417,416,423,422,429,428,105,104, 57, 56, -3, -3, -3, -3, -3, -3, -3, -3, 22, 21, 87, 86,435,434,441,440,447,446,834, -3],
[419,418,425,424,431,430,107,106, 59, 58, -3, -3, -3, -3, -3, -3, -3, -3, -3, 23, 89, 88,437,436,443,442,449,448,836,835],
[481,480,475,474,469,468, 48, -2, 30, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 0, 53, 52,463,462,457,456,451,450,837, -3],
[483,482,477,476,471,470, 49, -1, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -1,465,464,459,458,453,452,839,838],
[485,484,479,478,473,472, 51, 50, 31, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 1, -2, 42,467,466,461,460,455,454,840, -3],
[487,486,493,492,499,498, 97, 96, 61, 60, -3, -3, -3, -3, -3, -3, -3, -3, -3, 26, 91, 90,505,504,511,510,517,516,842,841],
[489,488,495,494,501,500, 99, 98, 63, 62, -3, -3, -3, -3, -3, -3, -3, -3, 28, 27, 93, 92,507,506,513,512,519,518,843, -3],
[491,490,497,496,503,502,101,100, 65, 64, 17, -3, -3, -3, -3, -3, -3, -3, 18, 29, 95, 94,509,508,515,514,521,520,845,844],
[559,558,553,552,547,546,541,540, 73, 72, 32, -3, -3, -3, -3, -3, -3, 10, 67, 66,115,114,535,534,529,528,523,522,846, -3],
[561,560,555,554,549,548,543,542, 75, 74, -2, -1, 7, 6, 35, 34, 11, -2, 69, 68,117,116,537,536,531,530,525,524,848,847],
[563,562,557,556,551,550,545,544, 77, 76, -2, 33, 9, 8, 25, 24, -1, -2, 71, 70,119,118,539,538,533,532,527,526,849, -3],
[565,564,571,570,577,576,583,582,589,588,595,594,601,600,607,606,613,612,619,618,625,624,631,630,637,636,643,642,851,850],
[567,566,573,572,579,578,585,584,591,590,597,596,603,602,609,608,615,614,621,620,627,626,633,632,639,638,645,644,852, -3],
[569,568,575,574,581,580,587,586,593,592,599,598,605,604,611,610,617,616,623,622,629,628,635,634,641,640,647,646,854,853],
[727,726,721,720,715,714,709,708,703,702,697,696,691,690,685,684,679,678,673,672,667,666,661,660,655,654,649,648,855, -3],
[729,728,723,722,717,716,711,710,705,704,699,698,693,692,687,686,681,680,675,674,669,668,663,662,657,656,651,650,857,856],
[731,730,725,724,719,718,713,712,707,706,701,700,695,694,689,688,683,682,677,676,671,670,665,664,659,658,653,652,858, -3],
[733,732,739,738,745,744,751,750,757,756,763,762,769,768,775,774,781,780,787,786,793,792,799,798,805,804,811,810,860,859],
[735,734,741,740,747,746,753,752,759,758,765,764,771,770,777,776,783,782,789,788,795,794,801,800,807,806,813,812,861, -3],
[737,736,743,742,749,748,755,754,761,760,767,766,773,772,779,778,785,784,791,790,797,796,803,802,809,808,815,814,863,862]
];
private var bitMatrix:BitMatrix;
/**
* @param bitMatrix {@link BitMatrix} to parse
* @throws FormatException if height is not 33 or width is not 30
*/
public function BitMatrixParser(bitMatrix:BitMatrix)
{
this.bitMatrix = bitMatrix;
}
public function readCodewords():Array
{
var result:Array = new Array(144);
var height:int = bitMatrix.getHeight();
var width:int = bitMatrix.getWidth();
for (var y:int = 0; y < height; y++) {
var bitnrRow:Array = BITNR[y];
for (var x:int = 0; x < width; x++) {
var bit:int = bitnrRow[x];
if (bit >= 0 && bitMatrix._get(x, y)) {
result[bit / 6] |= int((1 << (5 - (bit % 6))) & 255) ;
}
}
}
return result;
}
}
}

View file

@ -0,0 +1,207 @@
/*
* Copyright 2011 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.maxicode.decoder
{
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.flexdatatypes.StringBuilder;
/**
* <p>MaxiCodes can encode text or structured information as bits in one of several modes,
* with multiple character sets in one code. This class decodes the bits back into text.</p>
*
* @author mike32767
* @author Manuel Kasten
*/
public class DecodedBitStreamParser
{
private static var SHIFTA:String = '\uFFF0';
private static var SHIFTB:String = '\uFFF1';
private static var SHIFTC:String = '\uFFF2';
private static var SHIFTD:String = '\uFFF3';
private static var SHIFTE:String = '\uFFF4';
private static var TWOSHIFTA:String = '\uFFF5';
private static var THREESHIFTA:String = '\uFFF6';
private static var LATCHA:String = '\uFFF7';
private static var LATCHB:String = '\uFFF8';
private static var LOCK:String = '\uFFF9';
private static var ECI:String = '\uFFFA';
private static var NS:String = '\uFFFB';
private static var PAD:String = '\uFFFC';
private static var FS:String = '\u001C';
private static var GS:String = '\u001D';
private static var RS:String = '\u001E';
private static var NINE_DIGITS:String = "000000000";
private static var THREE_DIGITS:String = "000";
private static var SETS:Array = [
"\nABCDEFGHIJKLMNOPQRSTUVWXYZ"+ECI+FS+GS+RS+NS+' '+PAD+"\"#$%&'()*+,-./0123456789:"+SHIFTB+SHIFTC+SHIFTD+SHIFTE+LATCHB,
"`abcdefghijklmnopqrstuvwxyz"+ECI+FS+GS+RS+NS+'{'+PAD+"}~\u007F;<=>?[\\]^_ ,./:@!|"+PAD+TWOSHIFTA+THREESHIFTA+PAD+SHIFTA+SHIFTC+SHIFTD+SHIFTE+LATCHA,
"\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA"+ECI+FS+GS+RS+"\u00DB\u00DC\u00DD\u00DE\u00DF\u00AA\u00AC\u00B1\u00B2\u00B3\u00B5\u00B9\u00BA\u00BC\u00BD\u00BE\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089"+LATCHA+' '+LOCK+SHIFTD+SHIFTE+LATCHB,
"\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA"+ECI+FS+GS+RS+NS+"\u00FB\u00FC\u00FD\u00FE\u00FF\u00A1\u00A8\u00AB\u00AF\u00B0\u00B4\u00B7\u00B8\u00BB\u00BF\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094"+LATCHA+' '+SHIFTC+LOCK+SHIFTE+LATCHB,
"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\n\u000B\u000C\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A"+ECI+PAD+PAD+'\u001B'+NS+FS+GS+RS+"\u001F\u009F\u00A0\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A9\u00AD\u00AE\u00B6\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E"+LATCHA+' '+SHIFTC+SHIFTD+LOCK+LATCHB,
"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\n\u000B\u000C\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u0020\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B\u003C\u003D\u003E\u003F"
];
public static function decode(bytes:Array, mode:int):DecoderResult
{
var result:StringBuilder = new StringBuilder(144);
switch (mode)
{
case 2:
case 3:
var postcode:String;
if (mode == 2)
{
postcode = NINE_DIGITS.substring(0,(getPostCode2(bytes).toString()).length) + getPostCode2(bytes).toString();
}
else
{
postcode = getPostCode3(bytes);
}
var country:String = THREE_DIGITS.substring(0,(getCountry(bytes).toString()).length) + getCountry(bytes).toString();
var service:String = THREE_DIGITS.substring(0,(getServiceClass(bytes).toString()).length) + getServiceClass(bytes).toString();
result.Append(getMessage(bytes, 10, 84));
var matchstring:String = "[)>"+RS+"01"+GS;
if (result.toString().substr(0,matchstring.length) == matchstring)
{
result.Insert(9, postcode + GS + country + GS + service + GS);
}
else
{
result.Insert(0, postcode + GS + country + GS + service + GS);
}
break;
case 4:
result.Append(getMessage(bytes, 1, 93));
break;
case 5:
result.Append(getMessage(bytes, 1, 77));
break;
}
return new DecoderResult(bytes, result.toString(), null, mode.toString());
}
private static function getBit(bit:int, bytes:Array):int
{
bit--;
return (bytes[bit / 6] & (1 << (5 - (bit % 6)))) == 0 ? 0 : 1;
}
private static function getInt(bytes:Array, x:Array):int
{
var val:int = 0;
for (var i:int = 0; i < x.length; i++)
{
val += getBit(x[i], bytes) << (x.length - i - 1);
}
return val;
}
private static function getCountry(bytes:Array):int
{
return getInt(bytes, [53, 54, 43, 44, 45, 46, 47, 48, 37, 38]);
}
private static function getServiceClass(bytes:Array):int
{
return getInt(bytes, [55, 56, 57, 58, 59, 60, 49, 50, 51, 52]);
}
private static function getPostCode2Length(bytes:Array):int
{
return getInt(bytes, [39, 40, 41, 42, 31, 32]);
}
private static function getPostCode2(bytes:Array):int
{
return getInt(bytes, [33, 34, 35, 36, 25, 26, 27, 28, 29, 30, 19,
20, 21, 22, 23, 24, 13, 14, 15, 16, 17, 18, 7, 8, 9, 10, 11, 12, 1, 2]);
}
private static function getPostCode3(bytes:Array):String {
return (SETS[0] as String).charAt(getInt(bytes, [39, 40, 41, 42, 31, 32])) +
(SETS[0] as String).charAt(getInt(bytes, [33, 34, 35, 36, 25, 26])) +
(SETS[0] as String).charAt(getInt(bytes, [27, 28, 29, 30, 19, 20])) +
(SETS[0] as String).charAt(getInt(bytes, [21, 22, 23, 24, 13, 14])) +
(SETS[0] as String).charAt(getInt(bytes, [15, 16, 17, 18, 7, 8])) +
(SETS[0] as String).charAt(getInt(bytes, [ 9, 10, 11, 12, 1, 2]));
}
private static function getMessage(bytes:Array, start:int, len:int):String
{
var sb:StringBuilder = new StringBuilder();
var shift:int = -1;
var curset:int = 0;
var lastset:int = 0;
for (var i:int = start; i < start + len; i++) {
var c:String = SETS[curset].charAt(bytes[i]);
switch (c) {
case LATCHA:
curset = 0;
shift = -1;
break;
case LATCHB:
curset = 1;
shift = -1;
break;
case SHIFTA:
case SHIFTB:
case SHIFTC:
case SHIFTD:
case SHIFTE:
lastset = curset;
curset = c.charCodeAt(0) - SHIFTA.charCodeAt(0);
shift = 1;
break;
case TWOSHIFTA:
lastset = curset;
curset = 0;
shift = 2;
break;
case THREESHIFTA:
lastset = curset;
curset = 0;
shift = 3;
break;
case NS:
var nsval:int = (bytes[++i] << 24) + (bytes[++i] << 18) + (bytes[++i] << 12) + (bytes[++i] << 6) + bytes[++i];
sb.Append(NINE_DIGITS.substring(0,NINE_DIGITS.length - (nsval.toString()).length) + nsval.toString());
break;
case LOCK:
shift = -1;
break;
default:
sb.Append(c);
}
if (shift-- == 0) {
curset = lastset;
}
}
while (sb.length > 0 && sb.charAt(sb.length - 1) == PAD) {
sb.setLength(sb.length - 1);
}
return sb.toString();
}
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright 2011 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.maxicode.decoder
{
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.reedsolomon.GenericGFPoly;
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
import com.google.zxing.common.reedsolomon.ReedSolomonException;
import com.google.zxing.common.flexdatatypes.HashTable;
/**
* <p>The main class which implements MaxiCode decoding -- as opposed to locating and extracting
* the MaxiCode from an image.</p>
*
* @author Manuel Kasten
*/
public class Decoder {
private static var ALL:int = 0;
private static var EVEN:int = 1;
private static var ODD:int = 2;
private var rsDecoder:ReedSolomonDecoder;
public function Decoder()
{
//rsDecoder = new ReedSolomonDecoder(GF256.MAXICODE_FIELD_64);
}
public function decode(bits:BitMatrix, hints:HashTable=null):DecoderResult {
var parser:BitMatrixParser = new BitMatrixParser(bits);
var codewords:Array = parser.readCodewords();
correctErrors(codewords, 0, 10, 10, ALL);
var mode:int = codewords[0] & 0x0F;
var datawords:Array;
switch (mode) {
case 2:
case 3:
case 4:
correctErrors(codewords, 20, 84, 40, EVEN);
correctErrors(codewords, 20, 84, 40, ODD);
datawords = new Array(94);
break;
case 5:
correctErrors(codewords, 20, 68, 56, EVEN);
correctErrors(codewords, 20, 68, 56, ODD);
datawords = new Array(78);
break;
default:
throw FormatException.getFormatInstance();
}
for (var i:int = 0; i < 10; i++)
{
datawords[i] = codewords[i];
}
for (i = 20; i < datawords.length + 10; i++)
{
datawords[i - 10] = codewords[i];
}
return DecodedBitStreamParser.decode(datawords, mode);
}
private function correctErrors(codewordBytes:Array,
start:int,
dataCodewords:int,
ecCodewords:int,
mode:int):void
{
var codewords:int = dataCodewords + ecCodewords;
// in EVEN or ODD mode only half the codewords
var divisor:int = mode == ALL ? 1 : 2;
// First read into an array of ints
var codewordsInts:Array = new Array(codewords / divisor);
for (var i:int = 0; i < codewords; i++)
{
if ((mode == ALL) || (i % 2 == (mode - 1))) {
codewordsInts[i / divisor] = codewordBytes[i + start] & 0xFF;
}
}
try
{
rsDecoder.decode(codewordsInts, ecCodewords / divisor);
}
catch (rse:ReedSolomonException)
{
throw ChecksumException.getChecksumInstance();
}
// 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 (i = 0; i < dataCodewords; i++)
{
if ((mode == ALL) || (i % 2 == (mode - 1)))
{
codewordBytes[i + start] = (int(codewordsInts[i / divisor]) & 255);
}
}
}
}
}

View file

@ -76,12 +76,15 @@ public final class ByQuadrantReader implements Reader {
// continue // continue
} }
var quarterWidth:int = halfWidth / 2; var quarterWidth:int = Math.floor(halfWidth / 2);
var quarterHeight:int = halfHeight / 2; var quarterHeight:int = Math.floor(halfHeight / 2);
var center:BinaryBitmap = image.crop(quarterWidth, quarterHeight, width - quarterWidth, var center:BinaryBitmap = image.crop(quarterWidth, quarterHeight, halfWidth, halfHeight);
height - quarterHeight);
return delegate.decode(center, hints); return delegate.decode(center, hints);
} }
public function reset():void {
delegate.reset();
}
} }
} }

View file

@ -1,265 +0,0 @@
/*
* 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.oned
{
import com.google.zxing.common.BitArray;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.Reader;
public class AbstractOneDReader implements Reader
{
private static var INTEGER_MATH_SHIFT:int = 8;
public static var PATTERN_MATCH_RESULT_SCALE_FACTOR:int = (1 << INTEGER_MATH_SHIFT);
//public function decode(image:MonochromeBitmapSource):Result
//{
// return decode(image, null);
//}
public function decode(image:BinaryBitmap,hints:HashTable=null):Result
{
try
{
return doDecode(image, hints);
}
catch (re:ReaderException)
{
var tryHarder:Boolean = (hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER));
if (tryHarder && image.isRotateSupported())
{
var rotatedImage:BinaryBitmap = image.rotateCounterClockwise();
var result:Result = doDecode(rotatedImage, hints);
// Record that we found it rotated 90 degrees CCW / 270 degrees CW
var metadata:HashTable = result.getResultMetadata();
var orientation:int = 270;
if (metadata != null && metadata.ContainsKey(ResultMetadataType.ORIENTATION))
{
// But if we found it reversed in doDecode(), add in that result here:
orientation = (orientation + (int(metadata[ResultMetadataType.ORIENTATION]) )) % 360;
}
result.putMetadata(ResultMetadataType.ORIENTATION, orientation);
return result;
}
else
{
throw re;
}
}
return null;
}
/**
* We're going to examine rows from the middle outward, searching alternately above and below the
* middle, and farther out each time. rowStep is the number of rows between each successive
* attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then
* middle + rowStep, then middle - (2 * rowStep), etc.
* rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily
* decided that moving up and down by about 1/16 of the image is pretty good; we try more of the
* image if "trying harder".
*
* @param image The image to decode
* @param hints Any hints that were requested
* @return The contents of the decoded barcode
* @throws ReaderException Any spontaneous errors which occur
*/
private function doDecode(image:BinaryBitmap, hints:HashTable):Result
{
var width:int = image.getWidth();
var height:int = image.getHeight();
var row:BitArray = new BitArray(width);
var middle:int = height >> 1;
var tryHarder:Boolean = (hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER));
var rowStep:int = Math.max(1, height >> (tryHarder ? 7 : 4));
var MaxLines:int;
if (tryHarder)
{
MaxLines = height; // Look at the whole image, not just the center
} else {
MaxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image
}
for (var x:int = 0; x < MaxLines; x++) {
// Scanning from the middle out. Determine which row we're looking at next:
var rowStepsAboveOrBelow:int = (x + 1) >> 1;
var isAbove:Boolean = (x & 0x01) == 0; // i.e. is x even?
var rowNumber:int = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
if (rowNumber < 0 || rowNumber >= height) {
// Oops, if we run off the top or bottom, stop
break;
}
// Estimate black point for this row and load it:
try {
row = image.getBlackRow(rowNumber,row);
}
catch (re:ReaderException)
{
continue;
}
// While we have the image data in a BitArray, it's fairly cheap to reverse it in place to
// handle decoding upside down barcodes.
for (var attempt:int = 0; attempt < 2; attempt++) {
if (attempt == 1) { // trying again?
row.reverse(); // reverse the row and continue
}
try {
// Look for a barcode
var result:Result = decodeRow(rowNumber, row, hints);
// We found our barcode (else exception thrown)
if (attempt == 1)
{
// But it was upside down, so note that
result.putMetadata(ResultMetadataType.ORIENTATION, 180);
// And remember to flip the result points horizontally.
var points:Array = result.getResultPoints();
points[0] = ResultPoint(new ResultPoint(width - points[0].getX() - 1, points[0].getY()));
points[1] = ResultPoint(new ResultPoint(width - points[1].getX() - 1, points[1].getY()));
}
return result;
} catch (re:ReaderException) {
// continue -- just couldn't decode this row
var a:int=0;
}
}
}
throw new ReaderException("AbstractOneDReader : doDecode : could not decode image");
}
/**
* Records the size of successive runs of white and black pixels in a row, starting at a given point.
* The values are recorded in the given array, and the number of runs recorded is equal to the size
* of the array. If the row starts on a white pixel at the given start point, then the first count
* recorded is the run of white pixels starting from that point; likewise it is the count of a run
* of black pixels if the row begin on a black pixels at that point.
*
* @param row row to count from
* @param start offset into row to start at
* @param counters array into which to record counts
* @throws ReaderException if counters cannot be filled entirely from row before running out of pixels
*/
public static function recordPattern(row:BitArray, start:int, counters:Array):void
{
var numCounters:int = counters.length;
for (var i:int=0;i<counters.length;i++) { counters[i] = 0;}
var end:int = row.getSize();
if (start >= end) {
throw new ReaderException("AbstractOneDReader : recordPattern : start after end ("+start+" > "+end);
}
var isWhite:Boolean = !row._get(start);
var counterPosition:int = 0;
var k:int = start;
while (k < end)
{
var pixel:Boolean = row._get(k);
if (pixel != isWhite)
{
counters[counterPosition] = counters[counterPosition] + 1;
}
else
{
counterPosition++;
if (counterPosition == numCounters)
{
break;
}
else
{
counters[counterPosition] = 1;
isWhite = !isWhite;//isWhite = !isWhite; Is this too clever? shorter byte code, no conditional
}
}
k++;
}
// If we read fully the last section of pixels and filled up our counters -- or filled
// the last counter but ran off the side of the image, OK. Otherwise, a problem.
if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && k == end)))
{
throw new ReaderException("AbstractOneDReader : recordPattern : barcode seems to extend outside of the image");
}
}
/**
* Determines how closely a set of observed counts of runs of black/white values matches a given
* target pattern. This is reported as the ratio of the total variance from the expected pattern
* proportions across all pattern elements, to the length of the pattern.
*
* @param counters observed counters
* @param pattern expected pattern
* @param MaxIndividualVariance The most any counter can differ before we give up
* @return ratio of total variance between counters and pattern compared to total pattern size,
* where the ratio has been multiplied by 256. So, 0 means no variance (perfect match); 256 means
* the total variance between counters and patterns equals the pattern length, higher values mean
* even more variance
*/
public static function patternMatchVariance(counters:Array,pattern:Array, MaxIndividualVariance:int):int
{
var numCounters:int = counters.length;
var total:int = 0;
var patternLength:int = 0;
for (var i:int = 0; i < numCounters; i++)
{
total += counters[i];
patternLength += pattern[i];
}
if (total < patternLength)
{
// If we don't even have one pixel per unit of bar width, assume this is too small
// to reliably match, so fail:
return int.MAX_VALUE;
}
// We're going to fake floating-point math in integers. We just need to use more bits.
// Scale up patternLength so that intermediate values below like scaledCounter will have
// more "significant digits"
var unitBarWidth:int = (total << INTEGER_MATH_SHIFT) / patternLength;
MaxIndividualVariance = (MaxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
var totalVariance:int = 0;
for (var x:int = 0; x < numCounters; x++) {
var counter:int = (counters[x]) << INTEGER_MATH_SHIFT;
var scaledPattern:int = pattern[x] * unitBarWidth;
var variance:int = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
if (variance > MaxIndividualVariance)
{
return int.MAX_VALUE
}
totalVariance += variance;
}
return totalVariance / total;
}
// This declaration should not be necessary, since this class is
// abstract and so does not have to provide an implementation for every
// method of an interface it implements, but it is causing NoSuchMethodError
// issues on some Nokia JVMs. So we add this superfluous declaration:
public function decodeRow(rowNumber:int, row:BitArray, hints:Object):Result{return null;};
}
}

View file

@ -1,337 +0,0 @@
/*
* 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.oned
{
public class AbstractUPCEANReader extends AbstractOneDReader
{
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.BitArray;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
private static var MAX_AVG_VARIANCE:int = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42);
private static var MAX_INDIVIDUAL_VARIANCE:int = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7);
/**
* Start/end guard pattern.
*/
public static var START_END_PATTERN:Array = [1, 1, 1];
/**
* Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
*/
public static var MIDDLE_PATTERN:Array = [1, 1, 1, 1, 1];
/**
* "Odd", or "L" patterns used to encode UPC/EAN digits.
*/
public static var L_PATTERNS:Array = [
[3, 2, 1, 1], // 0
[2, 2, 2, 1], // 1
[2, 1, 2, 2], // 2
[1, 4, 1, 1], // 3
[1, 1, 3, 2], // 4
[1, 2, 3, 1], // 5
[1, 1, 1, 4], // 6
[1, 3, 1, 2], // 7
[1, 2, 1, 3], // 8
[3, 1, 1, 2]]; // 9
/**
* As above but also including the "even", or "G" patterns used to encode UPC/EAN digits.
*/
//public static var L_AND_G_PATTERNS:Array = new Array(20);
public static var L_AND_G_PATTERNS:Array = [
[3, 2, 1, 1], // 0
[2, 2, 2, 1], // 1
[2, 1, 2, 2], // 2
[1, 4, 1, 1], // 3
[1, 1, 3, 2], // 4
[1, 2, 3, 1], // 5
[1, 1, 1, 4], // 6
[1, 3, 1, 2], // 7
[1, 2, 1, 3], // 8
[3, 1, 1, 2], // 9
[1, 1, 2, 3], //R0
[1, 2, 2, 2], //R1
[2, 2, 1, 2], //R2
[1, 1, 4, 1], //R3
[2, 3, 1, 1], //R4
[1, 3, 2, 1], //R5
[4, 1, 1, 1], //R6
[2, 1, 3, 1], //R7
[3, 1, 2, 1], //R8
[2, 1, 1, 3], //R9
];
// create the inverse of the L patterns
/* static {
L_AND_G_PATTERNS = new int[20][];
for (var i:int = 0; i < 10; i++)
{
L_AND_G_PATTERNS[i] = L_PATTERNS[i];
}
for (int i = 10; i < 20; i++)
{
var widths:Array = L_PATTERNS[i - 10];
var reversedWidths:Array = new Array(widths.length);
for (int j = 0; j < widths.length; j++)
{
reversedWidths[j] = widths[widths.length - j - 1];
}
L_AND_G_PATTERNS[i] = reversedWidths;
}
}
*/
private var decodeRowStringBuffer:StringBuilder;
public function AbstractUPCEANReader()
{
decodeRowStringBuffer = new StringBuilder(20);
}
public static function findStartGuardPattern(row:BitArray):Array
{
var foundStart:Boolean = false;
var startRange:Array = null;
var nextStart:int = 0;
while (!foundStart)
{
startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN);
var start:int = startRange[0];
nextStart = startRange[1];
// Make sure there is a quiet zone at least as big as the start pattern before the barcode. If
// this check would run off the left edge of the image, do not accept this barcode, as it is
// very likely to be a false positive.
var quietStart:int = start - (nextStart - start);
if (quietStart >= 0)
{
foundStart = row.isRange(quietStart, start, false);
}
}
return startRange;
}
public override function decodeRow(rowNumber:int, row:BitArray, o:Object):Result
{
if (o is HashTable){ return decodeRow_HashTable(rowNumber,row,o as HashTable); }
else if (o is Array) { return decodeRow_Array(rowNumber,row,o as Array); }
else {throw new Error('AbstractUPCEANReader : decodeRow : unknow type of object');}
}
public function decodeRow_HashTable(rowNumber:int, row:BitArray, hints:HashTable):Result
{
return decodeRow(rowNumber, row, findStartGuardPattern(row));
}
public function decodeRow_Array(rowNumber:int, row:BitArray, startGuardRange:Array):Result
{
var result:StringBuilder = decodeRowStringBuffer;// empty stringbuilder
result.length = 0;
var endStart:int = decodeMiddle(row, startGuardRange, result);
var endRange:Array = decodeEnd(row, endStart);
// Make sure there is a quiet zone at least as big as the end pattern after the barcode. The
// spec might want more whitespace, but in practice this is the maximum we can count on.
var end:int = endRange[1];
var quietEnd:int = end + (end - endRange[0]);
if (quietEnd >= row.getSize() || !row.isRange(end, quietEnd, false))
{
throw new ReaderException("AbstractUPCEANReader : decodeRow_Array : ending white space is missing");
}
var resultString:String = result.toString();
if (!checkChecksum(resultString)) {
throw new ReaderException("AbstractUPCEANReader : decodeRow_Array : checkChecksum failed");
}
var left:Number = (Number) (startGuardRange[1] + startGuardRange[0]) / 2;
var right:Number = (Number) (endRange[1] + endRange[0]) / 2;
return new Result(resultString,
null, // no natural byte representation for these barcodes
[new ResultPoint(left, Number(rowNumber)),new ResultPoint(right, Number(rowNumber))],
getBarcodeFormat());
}
public function getBarcodeFormat():BarcodeFormat
{
return null;
}
/**
* @return {@link #checkStandardUPCEANChecksum(String)}
*/
public function checkChecksum(s:String):Boolean {
return checkStandardUPCEANChecksum(s);
}
/**
* Computes the UPC/EAN checksum on a string of digits, and reports
* whether the checksum is correct or not.
*
* @param s string of digits to check
* @return true iff string of digits passes the UPC/EAN checksum algorithm
* @throws ReaderException if the string does not contain only digits
*/
public static function checkStandardUPCEANChecksum(s:String):Boolean {
var length:int = s.length;
if (length == 0)
{
return false;
}
var sum:int = 0;
for (var i:int = length - 2; i >= 0; i -= 2) {
var digit:int = s.charCodeAt(i) - ('0').charCodeAt(0);
if (digit < 0 || digit > 9) {
throw new ReaderException("AbstractUPCEANReader : checkStandardUPCEANChecksum : digit out of range ("+digit+")");
}
sum += digit;
}
sum *= 3;
for (var i3:int = length - 1; i3 >= 0; i3 -= 2) {
var digit2:int = s.charCodeAt(i3) - ('0').charCodeAt(0);
if (digit2 < 0 || digit2 > 9) {
throw new ReaderException("AbstractUPCEANReader : checkStandardUPCEANChecksum : digit2 out of range ("+digit2+")");
}
sum += digit2;
}
return sum % 10 == 0;
}
/**
* Subclasses override this to decode the portion of a barcode between the start and end guard patterns.
*
* @param row row of black/white values to search
* @param startRange start/end offset of start guard pattern
* @param resultString {@link StringBuffer} to append decoded chars to
* @return horizontal offset of first pixel after the "middle" that was decoded
* @throws ReaderException if decoding could not complete successfully
*/
protected function decodeMiddle(row:BitArray, startRange:Array, resultString:StringBuilder):int{return -1;};
public function decodeEnd(row:BitArray, endStart:int):Array
{
return findGuardPattern(row, endStart, false, START_END_PATTERN);
}
/**
* @param row row of black/white values to search
* @param rowOffset position to start search
* @param whiteFirst if true, indicates that the pattern specifies white/black/white/...
* pixel counts, otherwise, it is interpreted as black/white/black/...
* @param pattern pattern of counts of number of black and white pixels that are being
* searched for as a pattern
* @return start/end horizontal offset of guard pattern, as an array of two ints
* @throws ReaderException if pattern is not found
*/
public static function findGuardPattern( row:BitArray, rowOffset:int, whiteFirst:Boolean, pattern:Array):Array
{
var patternLength:int = pattern.length;
var counters:Array = new Array(patternLength);
for (var i:int=0;i<patternLength;i++) { counters[i] = 0; }
var width:int = row.getSize();
var isWhite:Boolean = false;
while (rowOffset < width) {
isWhite = !row._get(rowOffset);
if (whiteFirst == isWhite) {
break;
}
rowOffset++;
}
var counterPosition:int = 0;
var patternStart:int = rowOffset;
for (var x:int = rowOffset; x < width; x++)
{
var pixel:Boolean = row._get(x);
if (pixel != isWhite)
{
counters[counterPosition] = counters[counterPosition] + 1;
}
else
{
if (counterPosition == patternLength - 1)
{
if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE)
{
return [patternStart, x];
}
patternStart = patternStart + counters[0] + counters[1];
for (var y:int = 2; y < patternLength; y++)
{
counters[y - 2] = counters[y];
}
counters[patternLength - 2] = 0;
counters[patternLength - 1] = 0;
counterPosition--;
}
else
{
counterPosition++;
}
counters[counterPosition] = 1;
isWhite = !isWhite;
}
}
throw new ReaderException("AbstractUPCEANReader : findGuardPattern : pattern not found)");
}
/**
* Attempts to decode a single UPC/EAN-encoded digit.
*
* @param row row of black/white values to decode
* @param counters the counts of runs of observed black/white/black/... values
* @param rowOffset horizontal offset to start decoding from
* @param patterns the set of patterns to use to decode -- sometimes different encodings
* for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should
* be used
* @return horizontal offset of first pixel beyond the decoded digit
* @throws ReaderException if digit cannot be decoded
*/
public static function decodeDigit(row:BitArray,counters:Array, rowOffset:int, patterns:Array):int
{
recordPattern(row, rowOffset, counters);
var bestVariance:int = MAX_AVG_VARIANCE; // worst variance we'll accept
var bestMatch:int = -1;
var max:int = patterns.length;
for (var i:int = 0; i < max; i++)
{
var pattern:Array = patterns[i];
var variance:int = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance) {
bestVariance = variance;
bestMatch = i;
}
}
if (bestMatch >= 0) {
return bestMatch;
} else {
throw new ReaderException("AbstractUPCEANReader : decodeDigit : not bestMatch found");
}
}
}
}

View file

@ -1,127 +0,0 @@
/*
* 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.oned
{
import com.google.zxing.common.ByteMatrix;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.BarcodeFormat;
/**
* <p>Encapsulates functionality and implementation that is common to UPC and EAN families
* of one-dimensional barcodes.</p>
*
* @author aripollak@gmail.com (Ari Pollak)
*/
public class AbstractUPCEANWriter implements UPCEANWriter
{
public function encode(contents:String, format:BarcodeFormat=null, width:int=0, height:int=0, hints:HashTable=null):Object
{
if (contents == null || contents.length == 0) {
throw new IllegalArgumentException("oned : AbstractUPCEANWriter : encode : Found empty contents");
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("oned : AbstractUPCEANWriter : encode : Requested dimensions are too small: "+ width + 'x' + height);
}
var code:Array = encode(contents) as Array;
return renderResult(code, width, height);
}
/** @return a byte array of horizontal pixels (0 = white, 1 = black) */
protected static function renderResult(code:Array, width:int, height:int):ByteMatrix {
var inputWidth:int = code.length;
// Add quiet zone on both sides
var fullWidth:int = inputWidth + (AbstractUPCEANReader.START_END_PATTERN.length << 1);
var outputWidth:int = Math.max(width, fullWidth);
var outputHeight:int = Math.max(1, height);
var multiple:int = outputWidth / fullWidth;
var leftPadding:int = (outputWidth - (inputWidth * multiple)) / 2;
var output:ByteMatrix = new ByteMatrix(outputWidth, outputHeight);
var outputArray:Array = output.getArray();
var row:Array = new Array(outputWidth);
// a. Write the white pixels at the left of each row
for (var x:int = 0; x < leftPadding; x++) {
row[x] = 255;
}
// b. Write the contents of this row of the barcode
var offset:int = leftPadding;
for (var x2:int = 0; x2 < inputWidth; x2++) {
var value:int = (code[x2] == 1) ? 0 : 255;
for (var z2:int = 0; z2 < multiple; z2++) {
row[offset + z2] = value;
}
offset += multiple;
}
// c. Write the white pixels at the right of each row
offset = leftPadding + (inputWidth * multiple);
for (var x3:int = offset; x3 < outputWidth; x3++) {
row[x3] = 255;
}
// d. Write the completed row multiple times
for (var z:int = 0; z < outputHeight; z++)
{
//BAS : to convert
//System.arraycopy(row, 0, outputArray[z], 0, outputWidth);
var ctr:int=0;
for (var i:int = 0;i<outputWidth;i++)
{
outputArray[z][ctr] = row[i];
ctr++;
}
}
return output;
}
/**
* Appends the given pattern to the target array starting at pos.
*
* @param startColor
* starting color - 0 for white, 1 for black
* @return the number of elements added to target.
*/
protected static function appendPattern(target:Array, pos:int, pattern:Array, startColor:int):int {
if (startColor != 0 && startColor != 1) {
throw new Error("oned : AbstractUPCEANWriter : appendPattern : startColor must be either 0 or 1, but got: " + startColor);
}
var color:int = startColor;
var numAdded:int = 0;
for (var i:int = 0; i < pattern.length; i++) {
for (var j:int = 0; j < pattern[i]; j++) {
target[pos] = color;
pos += 1;
numAdded += 1;
}
color ^= 1; // flip color after each segment
}
return numAdded;
}
}
}

View file

@ -0,0 +1,264 @@
/*
* 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.oned
{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.common.flexdatatypes.StringBuilder;
/**
* <p>Decodes Codabar barcodes.</p>
*
* @author Bas Vijfwinkel
*/
public final class CodaBarReader extends OneDReader {
public static var ALPHABET_STRING:String = "0123456789-$:/.+ABCDTN";
public static var ALPHABET:Array = CodaBarReader.ALPHABET_STRING.split("");
/**
* These represent the encodings of characters, as patterns of wide and narrow bars. The 7 least-significant bits of
* each int correspond to the pattern of wide and narrow, with 1s representing "wide" and 0s representing narrow. NOTE
* : c is equal to the * pattern NOTE : d is equal to the e pattern
*/
private static var CHARACTER_ENCODINGS:Array = [
0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, // 0-9
0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E, // -$:/.+ABCD
0x01A, 0x029 //TN
];
// minimal number of characters that should be present (inclusing start and stop characters)
// this check has been added to reduce the number of false positive on other formats
// until the cause for this behaviour has been determined
// under normal circumstances this should be set to 3
private static var minCharacterLength:int = 6;
// multiple start/end patterns
// official start and end patterns
private static var STARTEND_ENCODING:Array = ['E', '*', 'A', 'B', 'C', 'D', 'T', 'N'];
// some codabar generator allow the codabar string to be closed by every character
//private static final char[] STARTEND_ENCODING = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '$', ':', '/', '.', '+', 'A', 'B', 'C', 'D', 'T', 'N'};
// some industries use a checksum standard but this is not part of the original codabar standard
// for more information see : http://www.mecsw.com/specs/codabar.html
public override function decodeRow(rowNumber:Object, row:BitArray, o:Object):Result
{
var start:Array = findAsteriskPattern(row);
start[1] = 0; // settings this to 0 improves the recognition rate somehow?
var nextStart:int = start[1];
var end:int = row.getSize();
// Read off white space
while (nextStart < end && !row._get(nextStart)) {
nextStart++;
}
var result:StringBuilder = new StringBuilder();
//int[] counters = new int[7];
var counters:Array;
var lastStart:int;
do {
counters = [0, 0, 0, 0, 0, 0, 0]; // reset counters
recordPattern(row, nextStart, counters);
var decodedChar:String = toNarrowWidePattern(counters);
if (decodedChar == '!') {
throw NotFoundException.getNotFoundInstance();
}
result.Append(decodedChar);
lastStart = nextStart;
for (i = 0; i < counters.length; i++) {
nextStart += counters[i];
}
// Read off white space
while (nextStart < end && !row._get(nextStart)) {
nextStart++;
}
} while (nextStart < end); // no fixed end pattern so keep on reading while data is available
// Look for whitespace after pattern:
var lastPatternSize:int = 0;
for (var i:int = 0; i < counters.length; i++) {
lastPatternSize += counters[i];
}
var whiteSpaceAfterEnd:int = nextStart - lastStart - lastPatternSize;
// If 50% of last pattern size, following last pattern, is not whitespace, fail
// (but if it's whitespace to the very end of the image, that's OK)
if (nextStart != end && (whiteSpaceAfterEnd / 2 < lastPatternSize)) {
throw NotFoundException.getNotFoundInstance();
}
// valid result?
if (result.length < 2)
{
throw NotFoundException.getNotFoundInstance();
}
var startchar:String = result.charAt(0);
if (!arrayContains(STARTEND_ENCODING,startchar))
{
//invalid start character
throw NotFoundException.getNotFoundInstance();
}
// find stop character
for (var k:int = 1;k < result.length ;k++)
{
if (result.charAt(k) == startchar)
{
// found stop character -> discard rest of the string
if ((k+1) != result.length)
{
result.Remove(k+1,result.length-1);
k = result.length;// break out of loop
}
}
}
// remove stop/start characters character and check if a string longer than 5 characters is contained
if (result.length > minCharacterLength)
{
result.deleteCharAt(result.length-1);
result.deleteCharAt(0);
}
else
{
// Almost surely a false positive ( start + stop + at least 1 character)
throw NotFoundException.getNotFoundInstance();
}
var left:Number = (start[1] + start[0]) / 2.0;
var right:Number = (nextStart + lastStart) / 2.0;
return new Result(
result.toString(),
null,
[new ResultPoint(left, Math.floor(rowNumber as Number)),new ResultPoint(right, Math.floor(rowNumber as Number))],
BarcodeFormat.CODABAR);
}
private static function findAsteriskPattern(row:BitArray):Array {
var width:int = row.getSize();
var rowOffset:int = 0;
while (rowOffset < width) {
if (row._get(rowOffset)) {
break;
}
rowOffset++;
}
var counterPosition:int = 0;
var counters:Array = new Array(7);
for (var k:int = 0;k<counters.length;k++){counters[k]=0;}
var patternStart:int = rowOffset;
var isWhite:Boolean = false;
var patternLength:int = counters.length;
for (var i:int = rowOffset; i < width; i++) {
var pixel:Boolean = row._get(i);
if (pixel != isWhite) {
counters[counterPosition]++;
} else {
if (counterPosition == patternLength - 1) {
try {
if (arrayContains(STARTEND_ENCODING, toNarrowWidePattern(counters))) {
// Look for whitespace before start pattern, >= 50% of width of start pattern
if (row.isRange(Math.max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {
return [patternStart, i];
}
}
} catch (re:IllegalArgumentException ) {
// no match, continue
}
patternStart += counters[0] + counters[1];
for (var y:int = 2; y < patternLength; y++) {
counters[y - 2] = counters[y];
}
counters[patternLength - 2] = 0;
counters[patternLength - 1] = 0;
counterPosition--;
} else {
counterPosition++;
}
counters[counterPosition] = 1;
if (isWhite) {isWhite = false; } else { isWhite = true;} // isWhite = !isWhite;
}
}
throw NotFoundException.getNotFoundInstance();
}
private static function arrayContains(array:Array, key:String):Boolean {
if (array != null) {
for (var i:int = 0; i < array.length; i++) {
if (array[i] == key) {
return true;
}
}
}
return false;
}
private static function toNarrowWidePattern(counters:Array):String {
// BAS : I have changed the following part because some codabar images would fail with the original routine
// I took from the Code39Reader.java file
// ----------- change start
var numCounters:int = counters.length;
var maxNarrowCounter:int = 0;
var minCounter:int = int.MAX_VALUE;
for (var i:int = 0; i < numCounters; i++) {
if (counters[i] < minCounter) {
minCounter = counters[i];
}
if (counters[i] > maxNarrowCounter) {
maxNarrowCounter = counters[i];
}
}
// ---------- change end
do {
var wideCounters:int = 0;
var pattern:int = 0;
for (i = 0; i < numCounters; i++) {
if (counters[i] > maxNarrowCounter) {
pattern |= 1 << (numCounters - 1 - i);
wideCounters++;
}
}
if ((wideCounters == 2) || (wideCounters == 3)) {
for (i = 0; i < CHARACTER_ENCODINGS.length; i++) {
if (CHARACTER_ENCODINGS[i] == pattern) {
return ALPHABET[i];
}
}
}
maxNarrowCounter--;
} while (maxNarrowCounter > minCounter);
return '!';
}
}
}

View file

@ -15,8 +15,10 @@
*/ */
package com.google.zxing.oned package com.google.zxing.oned
{ {
import com.google.zxing.ResultMetadataType;
public class Code128Reader extends AbstractOneDReader
public class Code128Reader extends OneDReader
{ {
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
@ -25,9 +27,10 @@ package com.google.zxing.oned
import com.google.zxing.Result; import com.google.zxing.Result;
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.BinaryBitmap;
private static var CODE_PATTERNS:Array = [ public static var CODE_PATTERNS:Array = [
[2, 1, 2, 2, 2, 2], // 0 [2, 1, 2, 2, 2, 2], // 0
[2, 2, 2, 1, 2, 2], [2, 2, 2, 1, 2, 2],
[2, 2, 2, 2, 2, 1], [2, 2, 2, 2, 2, 1],
@ -239,7 +242,7 @@ package com.google.zxing.oned
} }
} }
public override function decodeRow(rowNumber:int, row:BitArray, hints:Object):Result { public override function decodeRow(rowNumber:Object, row:BitArray, hints:Object):Result {
var startPatternInfo:Array = findStartPattern(row); var startPatternInfo:Array = findStartPattern(row);
var startCode:int = startPatternInfo[2]; var startCode:int = startPatternInfo[2];
@ -405,14 +408,15 @@ package com.google.zxing.oned
if (unshift) { if (unshift) {
switch (codeSet) { switch (codeSet) {
case CODE_CODE_A: case CODE_CODE_A:
codeSet = CODE_CODE_C; codeSet = CODE_CODE_B;
// codeSet = CODE_CODE_C;
break; break;
case CODE_CODE_B: case CODE_CODE_B:
codeSet = CODE_CODE_A; codeSet = CODE_CODE_A;
break; break;
case CODE_CODE_C: // case CODE_CODE_C:
codeSet = CODE_CODE_B; // codeSet = CODE_CODE_B;
break; // break;
} }
} }
@ -464,6 +468,12 @@ package com.google.zxing.oned
} }
/* function decode(image:BinaryBitmap, hints:HashTable=null):Result
{
return super.decode(image,hints);
}*/
} }

View file

@ -0,0 +1,203 @@
/*
* Copyright 2010 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.oned
{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.common.flexdatatypes.ArrayList;
/**
* This object renders a CODE128 code as a {@link BitMatrix}.
*
* @author erik.barbara@gmail.com (Erik Barbara)
*/
public final class Code128Writer extends UPCEANWriter {
private static var CODE_START_B:int = 104;
private static var CODE_START_C:int = 105;
private static var CODE_CODE_B:int = 100;
private static var CODE_CODE_C:int = 99;
private static var CODE_STOP:int = 106;
// Dummy characters used to specify control characters in input
private static var ESCAPE_FNC_1:String = '\u00f1';
private static var ESCAPE_FNC_2:String = '\u00f2';
private static var ESCAPE_FNC_3:String = '\u00f3';
private static var ESCAPE_FNC_4:String = '\u00f4';
private static var CODE_FNC_1:int = 102; // Code A, Code B, Code C
private static var CODE_FNC_2:int = 97; // Code A, Code B
private static var CODE_FNC_3:int = 96; // Code A, Code B
private static var CODE_FNC_4_B:int = 100; // Code B
public override function encode(contents:String ,
format:BarcodeFormat = null,
width:int = 0,
height:int = 0,
hints:HashTable = null):Object // either BitMatrix or String
{
if (format != null)
{
if (format != BarcodeFormat.CODE_128) {
throw new IllegalArgumentException("Can only encode CODE_128, but got " + format);
}
//returns a BitMatrix
return super.encode(contents, format, width, height, hints);
}
// this part will return a string
var length:int = contents.length;
// Check length
if (length < 1 || length > 80) {throw new IllegalArgumentException("Contents length should be between 1 and 80 characters, but got " + length);
}
// Check content
for (var i:int = 0; i < length; i++) {
var c:String = contents.charAt(i);
if (c < ' ' || c > '~') {
switch (c) {
case ESCAPE_FNC_1:
case ESCAPE_FNC_2:
case ESCAPE_FNC_3:
case ESCAPE_FNC_4:
break;
default:
throw new IllegalArgumentException("Bad character in input: " + c);
}
}
}
var patterns:ArrayList = new ArrayList(); // temporary storage for patterns
var checkSum:int = 0;
var checkWeight:int = 1;
var codeSet:int = 0; // selected code (CODE_CODE_B or CODE_CODE_C)
var position:int = 0; // position in contents
while (position < length) {
//Select code to use
var requiredDigitCount:int = codeSet == CODE_CODE_C ? 2 : 4;
var newCodeSet:int;
if (isDigits(contents, position, requiredDigitCount)) {
newCodeSet = CODE_CODE_C;
} else {
newCodeSet = CODE_CODE_B;
}
//Get the pattern index
var patternIndex:int;
if (newCodeSet == codeSet) {
// Encode the current character
if (codeSet == CODE_CODE_B) {
patternIndex = contents.charCodeAt(position) - (' ').charCodeAt(0);
position += 1;
} else { // CODE_CODE_C
switch (contents.charAt(position)) {
case ESCAPE_FNC_1:
patternIndex = CODE_FNC_1;
position++;
break;
case ESCAPE_FNC_2:
patternIndex = CODE_FNC_2;
position++;
break;
case ESCAPE_FNC_3:
patternIndex = CODE_FNC_3;
position++;
break;
case ESCAPE_FNC_4:
patternIndex = CODE_FNC_4_B; // FIXME if this ever outputs Code A
position++;
break;
default:
patternIndex = parseInt(contents.substring(position, position + 2));
position += 2;
break;
}
}
} else {
// Should we change the current code?
// Do we have a code set?
if (codeSet == 0) {
// No, we don't have a code set
if (newCodeSet == CODE_CODE_B) {
patternIndex = CODE_START_B;
} else {
// CODE_CODE_C
patternIndex = CODE_START_C;
}
} else {
// Yes, we have a code set
patternIndex = newCodeSet;
}
codeSet = newCodeSet;
}
// Get the pattern
patterns.addElement(Code128Reader.CODE_PATTERNS[patternIndex]);
// Compute checksum
checkSum += patternIndex * checkWeight;
if (position != 0) {
checkWeight++;
}
}
// Compute and append checksum
checkSum %= 103;
patterns.addElement(Code128Reader.CODE_PATTERNS[checkSum]);
// Append stop code
patterns.addElement(Code128Reader.CODE_PATTERNS[CODE_STOP]);
// Compute code width
var codeWidth:int = 0;
for (var j:int=0;j<patterns.length;j++)
{
var pattern:Array = patterns.getObjectByIndex(j) as Array;
for (i = 0; i < pattern.length; i++)
{
codeWidth += pattern[i];
}
}
// Compute result
var result:Array = new Array(codeWidth);
var pos:int = 0;
for (var jk:int=0;jk<patterns.length;jk++)
{
pos += appendPattern(result, pos, patterns.getObjectByIndex(jk) as Array, 1);
}
return result;
}
private static function isDigits(value:String, start:int, length:int):Boolean {
var end:int = start + length;
var last:int = value.length;
for (var i:int = start; i < end && i < last; i++) {
var c:String = value.charAt(i);
if (c < '0' || c > '9') {
if (c != ESCAPE_FNC_1) {
return false;
}
end++; // ignore FNC_1
}
}
return end <= last; // end > last if we've run out of string
}
}
}

View file

@ -15,7 +15,7 @@
*/ */
package com.google.zxing.oned package com.google.zxing.oned
{ {
public class Code39Reader extends AbstractOneDReader public class Code39Reader extends OneDReader
{ {
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
@ -25,16 +25,17 @@ package com.google.zxing.oned
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.BinaryBitmap;
private static var ALPHABET_STRING:String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"; public static var ALPHABET_STRING:String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";
private static var ALPHABET:Array = ALPHABET_STRING.split(""); public static var ALPHABET:Array = ALPHABET_STRING.split("");
/** /**
* These represent the encodings of characters, as patterns of wide and narrow bars. * These represent the encodings of characters, as patterns of wide and narrow bars.
* The 9 least-significant bits of each int correspond to the pattern of wide and narrow, * The 9 least-significant bits of each int correspond to the pattern of wide and narrow,
* with 1s representing "wide" and 0s representing narrow. * with 1s representing "wide" and 0s representing narrow.
*/ */
private static var CHARACTER_ENCODINGS:Array = [ public static var CHARACTER_ENCODINGS:Array = [
0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9 0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9
0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J
0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T
@ -42,7 +43,7 @@ package com.google.zxing.oned
0x0A8, 0x0A2, 0x08A, 0x02A // $-% 0x0A8, 0x0A2, 0x08A, 0x02A // $-%
]; ];
private static var ASTERISK_ENCODING:int = CHARACTER_ENCODINGS[39]; public static var ASTERISK_ENCODING:int = CHARACTER_ENCODINGS[39];
private var usingCheckDigit:Boolean; private var usingCheckDigit:Boolean;
private var extendedMode:Boolean; private var extendedMode:Boolean;
@ -60,6 +61,9 @@ package com.google.zxing.oned
* data, and verify that the checksum passes. * data, and verify that the checksum passes.
*/ */
// function decode(image:BinaryBitmap, hints:HashTable=null):Result { return null; }
/** /**
* Creates a reader that can be configured to check the last character as a check digit, * Creates a reader that can be configured to check the last character as a check digit,
* or optionally attempt to decode "extended Code 39" sequences that are used to encode * or optionally attempt to decode "extended Code 39" sequences that are used to encode
@ -91,7 +95,7 @@ package com.google.zxing.oned
} }
} }
public override function decodeRow(rowNumber:int, row:BitArray, hints:Object):Result { public override function decodeRow(rowNumber:Object, row:BitArray, hints:Object):Result {
var start:Array = findAsteriskPattern(row); var start:Array = findAsteriskPattern(row);
var nextStart:int = start[1]; var nextStart:int = start[1];
@ -139,11 +143,11 @@ package com.google.zxing.oned
var max:int = result.length - 1; var max:int = result.length - 1;
var total:int = 0; var total:int = 0;
for (var i3:int = 0; i3 < max; i3++) { for (var i3:int = 0; i3 < max; i3++) {
total += ALPHABET_STRING.indexOf(result[i3]); total += ALPHABET_STRING.indexOf(result.charAt(i3));
} }
if (total % 43 != ALPHABET_STRING.indexOf(result[max])) if (result.charAt(max) != ALPHABET[total % 43])
{ {
throw new ReaderException("Code39Reader : decodeRow : total % 43 != ALPHABET_STRING.indexOf(result[max])"); throw new ReaderException("Code39Reader : decodeRow : checkDigit incorrect)");
} }
result.Remove(max,1); result.Remove(max,1);
} }

View file

@ -0,0 +1,85 @@
/*
* Copyright 2010 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.oned{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.oned.Code39Reader
/**
* This object renders a CODE39 code as a {@link BitMatrix}.
*
* @author erik.barbara@gmail.com (Erik Barbara)
*/
public class Code39Writer extends UPCEANWriter {
public override function encode(contents:String ,
format:BarcodeFormat = null,
width:int = 0,
height:int = 0,
hints:HashTable = null):Object
{
if (format != null)
{
if (format != BarcodeFormat.CODE_39) {
throw new IllegalArgumentException("Can only encode CODE_39, but got " + format);
}
// returns a bitmatrix
return super.encode(contents, format, width, height, hints);
}
// this part returns an array
var length:int = contents.length;
if (length > 80) {
throw new IllegalArgumentException(
"Requested contents should be less than 80 digits long, but got " + length);
}
var widths:Array = new Array(9);
var codeWidth:int = 24 + 1 + length;
for (var i:int = 0; i < length; i++) {
var indexInString:int = Code39Reader.ALPHABET_STRING.indexOf(contents.charAt(i));
toIntArray(Code39Reader.CHARACTER_ENCODINGS[indexInString], widths);
for(var j:int = 0; j < widths.length; j++) {
codeWidth += widths[j];
}
}
var result:Array = new Array(codeWidth);
toIntArray(Code39Reader.CHARACTER_ENCODINGS[39], widths);
var pos:int = appendPattern(result, 0, widths, 1);
var narrowWhite:Array = [1];
pos += appendPattern(result, pos, narrowWhite, 0);
//append next character to bytematrix
for(i = length-1; i >= 0; i--) {
indexInString = Code39Reader.ALPHABET_STRING.indexOf(contents.charAt(i));
toIntArray(Code39Reader.CHARACTER_ENCODINGS[indexInString], widths);
pos += appendPattern(result, pos, widths, 1);
pos += appendPattern(result, pos, narrowWhite, 0);
}
toIntArray(Code39Reader.CHARACTER_ENCODINGS[39], widths);
pos += appendPattern(result, pos, widths, 1);
return result;
}
private static function toIntArray(a:int, toReturn:Array):void {
for (var i:int = 0; i < 9; i++) {
var temp:int = a & (1 << i);
toReturn[i] = temp == 0 ? 1 : 2;
}
}
}
}

View file

@ -0,0 +1,276 @@
/*
* Copyright 2010 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.oned
{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.flexdatatypes.StringBuilder;
/**
* <p>Decodes Code 93 barcodes.</p>
*
* @author Sean Owen
* @see Code39Reader
*/
public final class Code93Reader extends OneDReader {
// Note that 'abcd' are dummy characters in place of control characters.
private static var ALPHABET_STRING:String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*";
private static var ALPHABET:Array = ALPHABET_STRING.split("");
/**
* These represent the encodings of characters, as patterns of wide and narrow bars.
* The 9 least-significant bits of each int correspond to the pattern of wide and narrow.
*/
private static var CHARACTER_ENCODINGS:Array = [
0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A, // 0-9
0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134, // A-J
0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6, // K-T
0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, // U-Z
0x12E, 0x1D4, 0x1D2, 0x1CA, 0x16E, 0x176, 0x1AE, // - - %
0x126, 0x1DA, 0x1D6, 0x132, 0x15E, // Control chars? $-*
];
private static var ASTERISK_ENCODING:int = CHARACTER_ENCODINGS[47];
public override function decodeRow(rowNumber:Object, row:BitArray, hints:Object):Result
{
var start:Array = findAsteriskPattern(row);
var nextStart:int = start[1];
var end:int = row.getSize();
// Read off white space
while (nextStart < end && !row._get(nextStart)) {
nextStart++;
}
var result:StringBuilder = new StringBuilder(20);
var counters:Array = new Array(6);
var decodedChar:String;
var lastStart:int;
do {
recordPattern(row, nextStart, counters);
var pattern:int = toPattern(counters);
if (pattern < 0) {
throw NotFoundException.getNotFoundInstance();
}
decodedChar = patternToChar(pattern);
result.Append(decodedChar);
lastStart = nextStart;
for (var i:int = 0; i < counters.length; i++) {
nextStart += counters[i];
}
// Read off white space
while (nextStart < end && !row._get(nextStart)) {
nextStart++;
}
} while (decodedChar != '*');
result.deleteCharAt(result.length - 1); // remove asterisk
// Should be at least one more black module
if (nextStart == end || !row._get(nextStart)) {
throw NotFoundException.getNotFoundInstance();
}
if (result.length < 4) {
// Almost surely a false positive
throw NotFoundException.getNotFoundInstance();
}
checkChecksums(result);
// Remove checksum digits
result.setLength(result.length - 2);
var resultString:String = decodeExtended(result);
var left:Number = (start[1] + start[0]) / 2.0;
var right:Number = (nextStart + lastStart) / 2.0;
return new Result(
resultString,
null,
[
new ResultPoint(left, Math.floor(rowNumber as Number)),
new ResultPoint(right, Math.floor(rowNumber as Number))],
BarcodeFormat.CODE_93);
}
private static function findAsteriskPattern(row:BitArray ):Array {
var width:int = row.getSize();
var rowOffset:int = 0;
while (rowOffset < width) {
if (row._get(rowOffset)) {
break;
}
rowOffset++;
}
var counterPosition:int = 0;
var counters:Array= new Array(6);
for(var k:int=0;k<counters.length;k++){ counters[k] = 0; }
var patternStart:int = rowOffset;
var isWhite:Boolean = false;
var patternLength:int = counters.length;
for (var i:int = rowOffset; i < width; i++) {
var pixel:Boolean = row._get(i);
if (pixel != isWhite) {
counters[counterPosition]++;
} else {
if (counterPosition == patternLength - 1) {
if (toPattern(counters) == ASTERISK_ENCODING) {
return [patternStart, i];
}
patternStart += counters[0] + counters[1];
for (var y:int = 2; y < patternLength; y++) {
counters[y - 2] = counters[y];
}
counters[patternLength - 2] = 0;
counters[patternLength - 1] = 0;
counterPosition--;
} else {
counterPosition++;
}
counters[counterPosition] = 1;
if (isWhite) { isWhite = false; } else { isWhite = true;} //isWhite = !isWhite;
}
}
throw NotFoundException.getNotFoundInstance();
}
private static function toPattern(counters:Array):int {
var max:int = counters.length;
var sum:int = 0;
for (var i:int = 0; i < max; i++) {
sum += counters[i];
}
var pattern:int = 0;
for (i = 0; i < max; i++) {
var scaledShifted:int = (counters[i] << INTEGER_MATH_SHIFT) * 9 / sum;
var scaledUnshifted:int = scaledShifted >> INTEGER_MATH_SHIFT;
if ((scaledShifted & 0xFF) > 0x7F) {
scaledUnshifted++;
}
if (scaledUnshifted < 1 || scaledUnshifted > 4) {
return -1;
}
if ((i & 0x01) == 0) {
for (var j:int = 0; j < scaledUnshifted; j++) {
pattern = (pattern << 1) | 0x01;
}
} else {
pattern <<= scaledUnshifted;
}
}
return pattern;
}
private static function patternToChar(pattern:int):String {
for (var i:int = 0; i < CHARACTER_ENCODINGS.length; i++) {
if (CHARACTER_ENCODINGS[i] == pattern) {
return ALPHABET[i];
}
}
throw NotFoundException.getNotFoundInstance();
}
private static function decodeExtended(encoded:StringBuilder):String {
var length:int = encoded.length;
var decoded:StringBuilder = new StringBuilder(length);
for (var i:int = 0; i < length; i++) {
var c:String = encoded.charAt(i);
if (c >= 'a' && c <= 'd') {
var next:String = encoded.charAt(i + 1);
var decodedChar:String = '\0';
switch (c) {
case 'd':
// +A to +Z map to a to z
if (next >= 'A' && next <= 'Z') {
decodedChar = String.fromCharCode(next.charCodeAt(0) + 32);
} else {
throw FormatException.getFormatInstance();
}
break;
case 'a':
// $A to $Z map to control codes SH to SB
if (next >= 'A' && next <= 'Z') {
decodedChar = String.fromCharCode(next.charCodeAt(0) - 64);
} else {
throw FormatException.getFormatInstance();
}
break;
case 'b':
// %A to %E map to control codes ESC to US
if (next >= 'A' && next <= 'E') {
decodedChar = String.fromCharCode(next.charCodeAt(0) - 38);
} else if (next >= 'F' && next <= 'W') {
decodedChar = String.fromCharCode(next.charCodeAt(0) - 11);
} else {
throw FormatException.getFormatInstance();
}
break;
case 'c':
// /A to /O map to ! to , and /Z maps to :
if (next >= 'A' && next <= 'O') {
decodedChar = String.fromCharCode(next.charCodeAt(0) - 32);
} else if (next == 'Z') {
decodedChar = ':';
} else {
throw FormatException.getFormatInstance();
}
break;
}
decoded.Append(decodedChar);
// bump up i again since we read two characters
i++;
} else {
decoded.Append(c);
}
}
return decoded.toString();
}
private static function checkChecksums(result:StringBuilder):void {
var length:int = result.length;
checkOneChecksum(result, length - 2, 20);
checkOneChecksum(result, length - 1, 15);
}
private static function checkOneChecksum(result:StringBuilder, checkPosition:int, weightMax:int):void
{
var weight:int = 1;
var total:int = 0;
for (var i:int = checkPosition - 1; i >= 0; i--) {
total += weight * ALPHABET_STRING.indexOf(result.charAt(i));
if (++weight > weightMax) {
weight = 1;
}
}
if (result.charAt(checkPosition) != ALPHABET[total % 47]) {
throw ChecksumException.getChecksumInstance();
}
}
}
}

View file

@ -17,7 +17,7 @@
package com.google.zxing.oned package com.google.zxing.oned
{ {
public class EAN13Reader extends AbstractUPCEANReader public class EAN13Reader extends UPCEANReader
{ {
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;

View file

@ -24,7 +24,7 @@ package com.google.zxing.oned
* *
* @author aripollak@gmail.com (Ari Pollak) * @author aripollak@gmail.com (Ari Pollak)
*/ */
public final class EAN13Writer extends AbstractUPCEANWriter { public final class EAN13Writer extends UPCEANWriter {
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException; import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
@ -47,7 +47,7 @@ public final class EAN13Writer extends AbstractUPCEANWriter {
} }
else else
{ {
return (this.encode_extended(contents,format,width,height,hints) as ByteMatrix); return (this.encode_extended(contents,format,width,height,hints) as BitMatrix);
} }
} }
@ -71,7 +71,7 @@ public final class EAN13Writer extends AbstractUPCEANWriter {
var result:Array = new Array(codeWidth); var result:Array = new Array(codeWidth);
var pos:int = 0; var pos:int = 0;
pos += appendPattern(result, pos, AbstractUPCEANReader.START_END_PATTERN, 1); pos += appendPattern(result, pos,UPCEANReader.START_END_PATTERN, 1);
// See {@link #EAN13Reader} for a description of how the first digit & left bars are encoded // See {@link #EAN13Reader} for a description of how the first digit & left bars are encoded
for (var i:int = 1; i <= 6; i++) { for (var i:int = 1; i <= 6; i++) {
@ -79,16 +79,16 @@ public final class EAN13Writer extends AbstractUPCEANWriter {
if ((parities >> (6 - i) & 1) == 1) { if ((parities >> (6 - i) & 1) == 1) {
digit += 10; digit += 10;
} }
pos += appendPattern(result, pos, AbstractUPCEANReader.L_AND_G_PATTERNS[digit], 0); pos += appendPattern(result, pos, UPCEANReader.L_AND_G_PATTERNS[digit], 0);
} }
pos += appendPattern(result, pos, AbstractUPCEANReader.MIDDLE_PATTERN, 0); pos += appendPattern(result, pos, UPCEANReader.MIDDLE_PATTERN, 0);
for (var i2:int = 7; i2 <= 12; i2++) { for (var i2:int = 7; i2 <= 12; i2++) {
var digit2:int = parseInt(contents.substring(i2, i2 + 1)); var digit2:int = parseInt(contents.substring(i2, i2 + 1));
pos += appendPattern(result, pos, AbstractUPCEANReader.L_PATTERNS[digit2], 1); pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit2], 1);
} }
pos += appendPattern(result, pos, AbstractUPCEANReader.START_END_PATTERN, 1); pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, 1);
return result; return result;
} }

View file

@ -19,7 +19,7 @@ package com.google.zxing.oned
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
public class EAN8Reader extends AbstractUPCEANReader public class EAN8Reader extends UPCEANReader
{ {
private var decodeMiddleCounters:Array; private var decodeMiddleCounters:Array;
@ -28,7 +28,8 @@ package com.google.zxing.oned
decodeMiddleCounters = new Array(4); decodeMiddleCounters = new Array(4);
} }
protected override function decodeMiddle(row:BitArray, startRange:Array, result:StringBuilder):int { protected override function decodeMiddle(row:BitArray, startRange:Array, result:StringBuilder):int
{
var counters:Array = decodeMiddleCounters; var counters:Array = decodeMiddleCounters;
counters[0] = 0; counters[0] = 0;
counters[1] = 0; counters[1] = 0;

View file

@ -21,13 +21,15 @@ package com.google.zxing.oned
* *
* @author aripollak@gmail.com (Ari Pollak) * @author aripollak@gmail.com (Ari Pollak)
*/ */
public final class EAN8Writer extends AbstractUPCEANWriter { public final class EAN8Writer extends UPCEANWriter {
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException; import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException; import com.google.zxing.WriterException;
import com.google.zxing.common.ByteMatrix;
private static var codeWidth:int = 3 + // start guard private static var codeWidth:int = 3 + // start guard
@ -44,19 +46,19 @@ import com.google.zxing.common.ByteMatrix;
} }
else else
{ {
return (this.encode_extended(contents,format,width,height,hints) as ByteMatrix); return (this.encode_extended(contents,format,width,height,hints) as BitMatrix);
} }
} }
public function encode_extended(contents:String, format:BarcodeFormat, width:int , height:int, hints:HashTable):ByteMatrix public function encode_extended(contents:String, format:BarcodeFormat, width:int , height:int, hints:HashTable):BitMatrix
{ {
if (format != BarcodeFormat.EAN_8) { if (format != BarcodeFormat.EAN_8) {
throw new IllegalArgumentException("Can only encode EAN_8, but got " + format); throw new IllegalArgumentException("Can only encode EAN_8, but got " + format);
} }
return (super.encode(contents, format, width, height, hints) as ByteMatrix); return (super.encode(contents, format, width, height, hints) as BitMatrix);
} }
/** @return a byte array of horizontal pixels (0 = white, 1 = black) */ /** @return a byte array of horizontal pixels (0 = white, 1 = black) */
@ -69,20 +71,20 @@ import com.google.zxing.common.ByteMatrix;
var result:Array = new Array(codeWidth); var result:Array = new Array(codeWidth);
var pos:int = 0; var pos:int = 0;
pos += appendPattern(result, pos, AbstractUPCEANReader.START_END_PATTERN, 1); pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, 1);
for (var i:int = 0; i <= 3; i++) { for (var i:int = 0; i <= 3; i++) {
var digit:int = parseInt(contents.substring(i, i + 1)); var digit:int = parseInt(contents.substring(i, i + 1));
pos += appendPattern(result, pos, AbstractUPCEANReader.L_PATTERNS[digit], 0); pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit], 0);
} }
pos += appendPattern(result, pos, AbstractUPCEANReader.MIDDLE_PATTERN, 0); pos += appendPattern(result, pos, UPCEANReader.MIDDLE_PATTERN, 0);
for (var i2:int = 4; i2 <= 7; i2++) { for (var i2:int = 4; i2 <= 7; i2++) {
var digit2:int = parseInt(contents.substring(i2, i2 + 1)); var digit2:int = parseInt(contents.substring(i2, i2 + 1));
pos += appendPattern(result, pos, AbstractUPCEANReader.L_PATTERNS[digit2], 1); pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit2], 1);
} }
pos += appendPattern(result, pos, AbstractUPCEANReader.START_END_PATTERN, 1); pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, 1);
return result; return result;
} }

View file

@ -0,0 +1,172 @@
/*
* Copyright (C) 2010 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.oned
{
import com.google.zxing.common.flexdatatypes.ArrayList;
/**
* Records EAN prefix to GS1 Member Organization, where the member organization
* correlates strongly with a country. This is an imperfect means of identifying
* a country of origin by EAN-13 barcode value. See
* <a href="http://en.wikipedia.org/wiki/List_of_GS1_country_codes">
* http://en.wikipedia.org/wiki/List_of_GS1_country_codes</a>.
*
* @author Sean Owen
*/
public class EANManufacturerOrgSupport {
private var ranges:ArrayList = new ArrayList();
private var countryIdentifiers:ArrayList = new ArrayList();
public function lookupCountryIdentifier(productCode:String):String {
initIfNeeded();
var prefix:int = int(productCode.substring(0, 3));
var max:int = ranges.size();
for (var i:int = 0; i < max; i++) {
var range:Array = ranges.elementAt(i) as Array;
var start:int = range[0];
if (prefix < start) {
return null;
}
var end:int = range.length == 1 ? start : range[1];
if (prefix <= end) {
return (countryIdentifiers.elementAt(i) as String);
}
}
return null;
}
private function add(range:Array, id:String):void {
ranges.addElement(range);
countryIdentifiers.addElement(id);
}
private function initIfNeeded():void {
if (!ranges.isEmpty()) {
return;
}
add([0,19], "US/CA");
add([30,39], "US");
add([60,139], "US/CA");
add([300,379],"FR");
add([380],"BG");
add([383],"SI");
add([385],"HR");
add([387],"BA");
add([400,440],"DE");
add([450,459],"JP");
add([460,469],"RU");
add([471],"TW");
add([474],"EE");
add([475],"LV");
add([476],"AZ");
add([477],"LT");
add([478],"UZ");
add([479],"LK");
add([480],"PH");
add([481],"BY");
add([482],"UA");
add([484],"MD");
add([485],"AM");
add([486],"GE");
add([487],"KZ");
add([489],"HK");
add([490,499],"JP");
add([500,509],"GB");
add([520],"GR");
add([528],"LB");
add([529],"CY");
add([531],"MK");
add([535],"MT");
add([539],"IE");
add([540,549],"BE/LU");
add([560],"PT");
add([569],"IS");
add([570,579],"DK");
add([590],"PL");
add([594],"RO");
add([599],"HU");
add([600,601],"ZA");
add([603],"GH");
add([608],"BH");
add([609],"MU");
add([611],"MA");
add([613],"DZ");
add([616],"KE");
add([618],"CI");
add([619],"TN");
add([621],"SY");
add([622],"EG");
add([624],"LY");
add([625],"JO");
add([626],"IR");
add([627],"KW");
add([628],"SA");
add([629],"AE");
add([640,649],"FI");
add([690,695],"CN");
add([700,709],"NO");
add([729],"IL");
add([730,739],"SE");
add([740],"GT");
add([741],"SV");
add([742],"HN");
add([743],"NI");
add([744],"CR");
add([745],"PA");
add([746],"DO");
add([750],"MX");
add([754,755],"CA");
add([759],"VE");
add([760,769],"CH");
add([770],"CO");
add([773],"UY");
add([775],"PE");
add([777],"BO");
add([779],"AR");
add([780],"CL");
add([784],"PY");
add([785],"PE");
add([786],"EC");
add([789,790],"BR");
add([800,839],"IT");
add([840,849],"ES");
add([850],"CU");
add([858],"SK");
add([859],"CZ");
add([860],"YU");
add([865],"MN");
add([867],"KP");
add([868,869],"TR");
add([870,879],"NL");
add([880],"KR");
add([885],"TH");
add([888],"SG");
add([890],"IN");
add([893],"VN");
add([896],"PK");
add([899],"ID");
add([900,919],"AT");
add([930,939],"AU");
add([940,949],"AZ");
add([955],"MY");
add([958],"MO");
}
}
}

View file

@ -16,25 +16,27 @@
package com.google.zxing.oned package com.google.zxing.oned
{ {
public class ITFReader extends AbstractOneDReader public class ITFReader extends OneDReader
{ {
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.google.zxing.DecodeHintType; import com.google.zxing.DecodeHintType;
import com.google.zxing.BinaryBitmap;
private static var MAX_AVG_VARIANCE:int = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42); public static var MAX_AVG_VARIANCE:int = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42);
private static var MAX_INDIVIDUAL_VARIANCE:int = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8); public static var MAX_INDIVIDUAL_VARIANCE:int = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8);
private static var W:int = 3; // Pixel width of a wide line public static var W:int = 3; // Pixel width of a wide line
private static var N:int = 1; // Pixed width of a narrow line public static var N:int = 1; // Pixed width of a narrow line
// Stores the actual narrow line width of the image being decoded. // Stores the actual narrow line width of the image being decoded.
private var narrowLineWidth:int = -1; public var narrowLineWidth:int = -1;
/** /**
* Start/end guard pattern. * Start/end guard pattern.
@ -42,14 +44,14 @@ package com.google.zxing.oned
* Note: The end pattern is reversed because the row is reversed before * Note: The end pattern is reversed because the row is reversed before
* searching for the END_PATTERN * searching for the END_PATTERN
*/ */
private static var START_PATTERN:Array = [N, N, N, N]; public static var START_PATTERN:Array = [N, N, N, N];
private static var END_PATTERN_REVERSED:Array = [N, N, W]; public static var END_PATTERN_REVERSED:Array = [N, N, W];
private static var DEFAULT_ALLOWED_LENGTHS:Array = [ 6, 10, 14 ]; public static var DEFAULT_ALLOWED_LENGTHS:Array = [ 6, 10, 14 ];
/** /**
* Patterns of Wide / Narrow lines to indicate each digit * Patterns of Wide / Narrow lines to indicate each digit
*/ */
private static var PATTERNS:Array = [ public static var PATTERNS:Array = [
[N, N, W, W, N], // 0 [N, N, W, W, N], // 0
[W, N, N, N, W], // 1 [W, N, N, N, W], // 1
[N, W, N, N, W], // 2 [N, W, N, N, W], // 2
@ -62,7 +64,9 @@ package com.google.zxing.oned
[N, W, N, W, N] // 9 [N, W, N, W, N] // 9
]; ];
public override function decodeRow(rowNumber:int, row:BitArray, hints:Object):Result //function decode(image:BinaryBitmap, hints:HashTable=null):Result { return null; }
public override function decodeRow(rowNumber:Object, row:BitArray, hints:Object):Result
{ {
// Find out where the Middle section (payload) starts & ends // Find out where the Middle section (payload) starts & ends
var startRange:Array = decodeStart(row); var startRange:Array = decodeStart(row);

View file

@ -0,0 +1,74 @@
/*
* Copyright 2010 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.oned
{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
/**
* This object renders a ITF code as a {@link BitMatrix}.
*
* @author erik.barbara@gmail.com (Erik Barbara)
*/
public class ITFWriter extends UPCEANWriter
{
public override function encode(contents:String ,
format:BarcodeFormat=null ,
width:int=0,
height:int=0,
hints:HashTable=null ) : Object
{
if (format != null)
{
if (format != BarcodeFormat.ITF) {
throw new IllegalArgumentException("Can only encode ITF, but got " + format);
}
return super.encode(contents, format, width, height, hints);
}
//public byte[] encode(String contents) {
var length:int = contents.length;
if (length > 80) {
throw new IllegalArgumentException(
"Requested contents should be less than 80 digits long, but got " + length);
}
var result:Array = new Array(9 + 9 * length);
var start:Array = [1, 1, 1, 1];
var pos:int = appendPattern(result, 0, start, 1);
for (var i:int = 0; i < length; i += 2) {
var one:int = contents.charCodeAt(i) - 48 ;
var two:int = contents.charCodeAt(i+1) - 48;
var encoding:Array = new Array(18);
for (var j:int = 0; j < 5; j++) {
encoding[(j << 1)] = ITFReader.PATTERNS[one][j];
encoding[(j << 1) + 1] = ITFReader.PATTERNS[two][j];
}
pos += appendPattern(result, pos, encoding, 1);
}
var end:Array = [3, 1, 1];
pos += appendPattern(result, pos, end, 1);
return result;
}
}
}

View file

@ -15,7 +15,7 @@
*/ */
package com.google.zxing.oned package com.google.zxing.oned
{ {
public class MultiFormatOneDReader extends AbstractOneDReader public class MultiFormatOneDReader extends OneDReader
{ {
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.common.flexdatatypes.ArrayList; import com.google.zxing.common.flexdatatypes.ArrayList;
@ -24,55 +24,79 @@ package com.google.zxing.oned
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
import com.google.zxing.oned.rss.RSS14Reader;
import com.google.zxing.oned.rss.expanded.RSSExpandedReader;
private var readers:ArrayList; private var readers:ArrayList;
public function MultiFormatOneDReader(hints:HashTable) public function MultiFormatOneDReader(hints:HashTable)
{ {
var possibleFormats:ArrayList = (hints == null ? null : hints.getValuesByKey(DecodeHintType.POSSIBLE_FORMATS)); var possibleFormats:ArrayList = (hints == null ? null : hints.getValuesByKey(DecodeHintType.POSSIBLE_FORMATS));
var useCode39CheckDigit:Boolean = hints != null && hints._get(DecodeHintType.ASSUME_CODE_39_CHECK_DIGIT) != null;
readers = new ArrayList(); readers = new ArrayList();
if (possibleFormats != null) {
if (possibleFormats != null)
{
if (possibleFormats.Contains(BarcodeFormat.EAN_13) || if (possibleFormats.Contains(BarcodeFormat.EAN_13) ||
possibleFormats.Contains(BarcodeFormat.UPC_A) || possibleFormats.Contains(BarcodeFormat.UPC_A) ||
possibleFormats.Contains(BarcodeFormat.EAN_8) || possibleFormats.Contains(BarcodeFormat.EAN_8) ||
possibleFormats.Contains(BarcodeFormat.UPC_E)) possibleFormats.Contains(BarcodeFormat.UPC_E))
{ {
readers.Add(new MultiFormatUPCEANReader(hints)); readers.addElement(new MultiFormatUPCEANReader(hints));
} }
if (possibleFormats.Contains(BarcodeFormat.CODE_39)) if (possibleFormats.Contains(BarcodeFormat.CODE_39))
{ {
readers.Add(new Code39Reader()); readers.addElement(new Code39Reader(useCode39CheckDigit));
}
if (possibleFormats.Contains(BarcodeFormat.CODE_93))
{
readers.addElement(new Code93Reader());
} }
if (possibleFormats.Contains(BarcodeFormat.CODE_128)) if (possibleFormats.Contains(BarcodeFormat.CODE_128))
{ {
readers.Add(new Code128Reader()); readers.addElement(new Code128Reader());
} }
if (possibleFormats.Contains(BarcodeFormat.ITF)) if (possibleFormats.Contains(BarcodeFormat.ITF))
{ {
readers.Add(new ITFReader()); readers.addElement(new ITFReader());
} }
} if (possibleFormats.Contains(BarcodeFormat.CODABAR))
if (readers.Count==0)
{ {
readers.Add(new MultiFormatUPCEANReader(hints)); readers.addElement(new CodaBarReader());
readers.Add(new Code39Reader());
readers.Add(new Code128Reader());
readers.Add(new ITFReader());
} }
if (possibleFormats.Contains(BarcodeFormat.RSS_14))
{
readers.addElement(new RSS14Reader());
}
if (possibleFormats.Contains(BarcodeFormat.RSS_EXPANDED))
{
readers.addElement(new RSSExpandedReader());
}
}
if (readers.isEmpty())
{
readers.addElement(new MultiFormatUPCEANReader(hints));
readers.addElement(new Code39Reader());
readers.addElement(new CodaBarReader());
readers.addElement(new Code93Reader());
readers.addElement(new Code128Reader());
readers.addElement(new ITFReader());
readers.addElement(new RSS14Reader());
readers.addElement(new RSSExpandedReader());
}
} }
public override function decodeRow(rowNumber:int, row:BitArray, hints:Object):Result public override function decodeRow(rowNumber:Object, row:BitArray, hints:Object):Result
{ {
var size:int = readers.Count; var size:int = readers.Count;
for (var i:int = 0; i < size; i++) { for (var i:int = 0; i < size; i++) {
var reader:Object = readers.getObjectByIndex(i); var reader:Object = readers.getObjectByIndex(i);
try { try {
var res:Result = reader.decodeRow(rowNumber, row, hints); var res:Result = reader.decodeRow(rowNumber as Number, row, hints);
return res; return res;
} catch (re:ReaderException) { } catch (re:Error) {
// continue // continue
var a:int=0;//BAS :needed for debugging var a:int=0;//BAS :needed for debugging
} }

View file

@ -15,7 +15,7 @@
*/ */
package com.google.zxing.oned package com.google.zxing.oned
{ {
public class MultiFormatUPCEANReader extends AbstractOneDReader public class MultiFormatUPCEANReader extends OneDReader
{ {
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.common.flexdatatypes.ArrayList; import com.google.zxing.common.flexdatatypes.ArrayList;
@ -52,11 +52,11 @@ package com.google.zxing.oned
} }
} }
public override function decodeRow(rowNumber:int, row:BitArray,hints:Object):Result public override function decodeRow(rowNumber:Object, row:BitArray,hints:Object):Result
{ {
// Compute this location once and reuse it on multiple implementations // Compute this location once and reuse it on multiple implementations
var startGuardPattern:Array = AbstractUPCEANReader.findStartGuardPattern(row); var startGuardPattern:Array = UPCEANReader.findStartGuardPattern(row);
var size:int = readers.Count; var size:int = readers.Count;
for (var i:int = 0; i < size; i++) { for (var i:int = 0; i < size; i++) {

View file

@ -13,17 +13,291 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.zxing.oned package com.google.zxing.oned
{ {
import com.google.zxing.Reader; import com.google.zxing.BinaryBitmap;
import com.google.zxing.common.BitArray; import com.google.zxing.DecodeHintType;
import com.google.zxing.Reader; import com.google.zxing.NotFoundException;
import com.google.zxing.ReaderException; import com.google.zxing.Reader;
import com.google.zxing.Result; import com.google.zxing.ReaderException;
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.flexdatatypes.Enumeration;
import com.google.zxing.common.flexdatatypes.HashTable;
public interface OneDReader extends Reader
/**
* Encapsulates functionality and implementation that is common to all families
* of one-dimensional barcodes.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
public class OneDReader implements Reader
{
protected static var INTEGER_MATH_SHIFT:int = 8;
protected static var PATTERN_MATCH_RESULT_SCALE_FACTOR:int = (1 << INTEGER_MATH_SHIFT);
// Note that we don't try rotation without the try harder flag, even if rotation was supported.
public function decode(image:BinaryBitmap , hints:HashTable=null):Result {
try {
return doDecode(image, hints);
}
catch (nfe:NotFoundException ) {
var tryHarder:Boolean = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
if (tryHarder && image.isRotateSupported()) {
var rotatedImage:BinaryBitmap = image.rotateCounterClockwise();
var result:Result = doDecode(rotatedImage, hints);
// Record that we found it rotated 90 degrees CCW / 270 degrees CW
var metadata:HashTable = result.getResultMetadata();
var orientation:int = 270;
if (metadata != null && metadata.containsKey(ResultMetadataType.ORIENTATION)) {
// But if we found it reversed in doDecode(), add in that result here:
orientation = (orientation +
(metadata._get(ResultMetadataType.ORIENTATION) as int)) % 360;
}
result.putMetadata(ResultMetadataType.ORIENTATION, orientation);
// Update result points
var points:Array = result.getResultPoints();
var height:int = rotatedImage.getHeight();
for (var i:int = 0; i < points.length; i++) {
points[i] = new ResultPoint(height - points[i].getY() - 1, points[i].getX());
}
return result;
} else {
throw nfe;
}
}
return null;
}
public function reset():void {
// do nothing
}
/**
* We're going to examine rows from the middle outward, searching alternately above and below the
* middle, and farther out each time. rowStep is the number of rows between each successive
* attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then
* middle + rowStep, then middle - (2 * rowStep), etc.
* rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily
* decided that moving up and down by about 1/16 of the image is pretty good; we try more of the
* image if "trying harder".
*
* @param image The image to decode
* @param hints Any hints that were requested
* @return The contents of the decoded barcode
* @throws NotFoundException Any spontaneous errors which occur
*/
private function doDecode(image:BinaryBitmap , hints:HashTable ):Result {
try
{ {
var width:int = image.getWidth();
var height:int = image.getHeight();
var row:BitArray = new BitArray(width);
var middle:int = height >> 1;
var tryHarder:Boolean = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
var rowStep:int = Math.max(1, height >> (tryHarder ? 8 : 5));
var maxLines:int;
if (tryHarder) {
maxLines = height; // Look at the whole image, not just the center
} else {
maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image
}
for (var x:int = 0; x < maxLines; x++)
{
// Scanning from the middle out. Determine which row we're looking at next:
var rowStepsAboveOrBelow:int = (x + 1) >> 1;
var isAbove:Boolean = (x & 0x01) == 0; // i.e. is x even?
var rowNumber:int = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
if (rowNumber < 0 || rowNumber >= height) {
// Oops, if we run off the top or bottom, stop
break;
}
// Estimate black point for this row and load it:
try
{
row = image.getBlackRow(rowNumber, row);
}
catch (nfe:Error )
{
continue;
}
// While we have the image data in a BitArray, it's fairly cheap to reverse it in place to
// handle decoding upside down barcodes.
for (var attempt:int = 0; attempt < 2; attempt++)
{
if (attempt == 1)
{ // trying again?
row.reverse(); // reverse the row and continue
// This means we will only ever draw result points *once* in the life of this method
// since we want to avoid drawing the wrong points after flipping the row, and,
// don't want to clutter with noise from every single row scan -- just the scans
// that start on the center line.
if (hints != null && hints.containsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) {
var newHints:HashTable = new HashTable(); // Can't use clone() in J2ME
var hintEnum:Enumeration = new Enumeration( hints.keys());
while (hintEnum.hasMoreElement()) {
var key:Object = hintEnum.nextElement();
if (key != DecodeHintType.NEED_RESULT_POINT_CALLBACK)
{
newHints._put(key, hints._get(key));
}
}
hints = newHints;
}
}
try
{
// Look for a barcode
var result:Result = decodeRow(rowNumber, row, hints);
// We found our barcode
if (attempt == 1) {
// But it was upside down, so note that
//result.putMetadata(ResultMetadataType.ORIENTATION, new Array(180));
result.putMetadata(ResultMetadataType.ORIENTATION,new Array(180));
// And remember to flip the result points horizontally.
var points:Array = result.getResultPoints();
points[0] = new ResultPoint(width - points[0].getX() - 1, points[0].getY());
points[1] = new ResultPoint(width - points[1].getX() - 1, points[1].getY());
}
return result;
}
catch (e:Error)
{
var g:int=5;
}
}
}
}
catch (e:Error)
{
var a:int=0;
}
throw NotFoundException.getNotFoundInstance();
}
/**
* Records the size of successive runs of white and black pixels in a row, starting at a given point.
* The values are recorded in the given array, and the number of runs recorded is equal to the size
* of the array. If the row starts on a white pixel at the given start point, then the first count
* recorded is the run of white pixels starting from that point; likewise it is the count of a run
* of black pixels if the row begin on a black pixels at that point.
*
* @param row row to count from
* @param start offset into row to start at
* @param counters array into which to record counts
* @throws NotFoundException if counters cannot be filled entirely from row before running out
* of pixels
*/
protected static function recordPattern(row:BitArray , start:int , counters:Array):void {
var numCounters:int = counters.length;
for (var i:int = 0; i < numCounters; i++) {
counters[i] = 0;
}
var end:int = row.getSize();
if (start >= end) {
throw NotFoundException.getNotFoundInstance();
}
var isWhite:Boolean = !row._get(start);
var counterPosition:int = 0;
i= start;
while (i < end) {
var pixel:Boolean = row._get(i);
if ((pixel && !isWhite) || (!pixel && isWhite))
{ // that is, exactly one is true
counters[counterPosition]++;
} else {
counterPosition++;
if (counterPosition == numCounters) {
break;
} else {
counters[counterPosition] = 1;
isWhite = !isWhite;
}
}
i++;
}
// If we read fully the last section of pixels and filled up our counters -- or filled
// the last counter but ran off the side of the image, OK. Otherwise, a problem.
if (!(counterPosition == numCounters || ((counterPosition == numCounters - 1) &&( i == end)))) {
throw NotFoundException.getNotFoundInstance();
}
}
protected static function recordPatternInReverse(row:BitArray , start:int, counters:Array):void {
// This could be more efficient I guess
var numTransitionsLeft:int = counters.length;
var last:Boolean = row._get(start);
while (start > 0 && numTransitionsLeft >= 0) {
if (row._get(--start) != last) {
numTransitionsLeft--;
last = !last;
}
}
if (numTransitionsLeft >= 0) {
throw NotFoundException.getNotFoundInstance();
}
recordPattern(row, start + 1, counters);
}
/**
* Determines how closely a set of observed counts of runs of black/white values matches a given
* target pattern. This is reported as the ratio of the total variance from the expected pattern
* proportions across all pattern elements, to the length of the pattern.
*
* @param counters observed counters
* @param pattern expected pattern
* @param maxIndividualVariance The most any counter can differ before we give up
* @return ratio of total variance between counters and pattern compared to total pattern size,
* where the ratio has been multiplied by 256. So, 0 means no variance (perfect match); 256 means
* the total variance between counters and patterns equals the pattern length, higher values mean
* even more variance
*/
protected static function patternMatchVariance(counters:Array, pattern:Array, maxIndividualVariance:int ):int {
var numCounters:int = counters.length;
var total:int = 0;
var patternLength:int = 0;
for (var i:int = 0; i < numCounters; i++) {
total += counters[i];
patternLength += pattern[i];
}
if (total < patternLength) {
// If we don't even have one pixel per unit of bar width, assume this is too small
// to reliably match, so fail:
return int.MAX_VALUE;
}
// We're going to fake floating-point math in integers. We just need to use more bits.
// Scale up patternLength so that intermediate values below like scaledCounter will have
// more "significant digits"
var unitBarWidth:int = (total << INTEGER_MATH_SHIFT) / patternLength;
maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
var totalVariance:int = 0;
for (var x:int = 0; x < numCounters; x++) {
var counter:int = counters[x] << INTEGER_MATH_SHIFT;
var scaledPattern:int = pattern[x] * unitBarWidth;
var variance:int = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
if (variance > maxIndividualVariance) {
return int.MAX_VALUE;
}
totalVariance += variance;
}
return totalVariance / total;
}
/** /**
* <p>Attempts to decode a one-dimensional barcode format given a single row of * <p>Attempts to decode a one-dimensional barcode format given a single row of
* an image.</p> * an image.</p>
@ -32,13 +306,9 @@ package com.google.zxing.oned
* @param row the black/white pixel data of the row * @param row the black/white pixel data of the row
* @param hints decode hints * @param hints decode hints
* @return {@link Result} containing encoded string and start/end of barcode * @return {@link Result} containing encoded string and start/end of barcode
* @throws ReaderException if an error occurs or barcode cannot be found * @throws NotFoundException if an error occurs or barcode cannot be found
*/ */;
/* public function decodeRow_Hashtable(rowNumber:int, row:BitArray, hints:Object):Result public function decodeRow(rowNumber:Object , row:BitArray , o:Object):Result { return null;}
{
return null;
};*/
}
} }
}

View file

@ -28,7 +28,7 @@ package com.google.zxing.oned
private var ean13Reader:EAN13Reader = new EAN13Reader(); private var ean13Reader:EAN13Reader = new EAN13Reader();
public function decodeRow(rowNumber:Object, row:BitArray, o:Object):Result public override function decodeRow(rowNumber:Object, row:BitArray, o:Object):Result
{ {
if (rowNumber is int) if (rowNumber is int)
{ {

View file

@ -0,0 +1,71 @@
/*
* Copyright 2010 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.oned
{
import com.google.zxing.Writer;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
import com.google.zxing.BarcodeFormat;
/**
* This object renders a UPC-A code as a {@link BitMatrix}.
*
* @author qwandor@google.com (Andrew Walbran)
*/
public class UPCAWriter implements Writer
{
private var subWriter:EAN13Writer = new EAN13Writer();
public function encode(contents:String, format:BarcodeFormat=null, width:int=0, height:int=0, hints:HashTable=null):Object
{
if (format != BarcodeFormat.UPC_A)
{
throw new IllegalArgumentException("Can only encode UPC-A, but got " + format);
}
return subWriter.encode(preencode(contents), BarcodeFormat.EAN_13, width, height, hints);
}
/**
* Transform a UPC-A code into the equivalent EAN-13 code, and add a check digit if it is not
* already present.
*/
private static function preencode(contents:String):String
{
var length:int = contents.length;
if (length == 11)
{
// No check digit present, calculate it and add it
var sum:int = 0;
for (var i:int = 0; i < 11; ++i)
{
sum += (contents.charCodeAt(i) - ('0' as String).charCodeAt(0)) * (i % 2 == 0 ? 3 : 1);
}
contents += (1000 - sum) % 10;
}
else if (length != 12)
{
throw new IllegalArgumentException(
"Requested contents should be 11 or 12 digits long, but got " + contents.length);
}
return '0' + contents;
}
}
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (C) 2010 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.oned
{
import com.google.zxing.BarcodeFormat;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.StringBuilder;
public class UPCEANExtensionSupport {
private var EXTENSION_START_PATTERN:Array = [1,1,2];
private static var CHECK_DIGIT_ENCODINGS:Array = [
0x18, 0x14, 0x12, 0x11, 0x0C, 0x06, 0x03, 0x0A, 0x09, 0x05
];
private var decodeMiddleCounters:Array = new Array(4);
private var decodeRowStringBuffer:StringBuilder = new StringBuilder();
public function decodeRow(rowNumber:int, row:BitArray, rowOffset:int):Result {
var extensionStartRange:Array = UPCEANReader.findGuardPattern(row, rowOffset, false, EXTENSION_START_PATTERN);
var result:StringBuilder = decodeRowStringBuffer;
result.length = 0;
var end:int = decodeMiddle(row, extensionStartRange, result);
var resultString:String = result.toString();
var extensionData:HashTable = parseExtensionString(resultString);
var extensionResult:Result =
new Result(resultString,
null,
[
new ResultPoint((extensionStartRange[0] + extensionStartRange[1]) / 2.0, rowNumber),
new ResultPoint( end, rowNumber),
],
BarcodeFormat.UPC_EAN_EXTENSION);
if (extensionData != null) {
extensionResult.putAllMetadata(extensionData);
}
return extensionResult;
}
public function decodeMiddle(row:BitArray, startRange:Array, resultString:StringBuilder):int {
var counters:Array = decodeMiddleCounters;
counters[0] = 0;
counters[1] = 0;
counters[2] = 0;
counters[3] = 0;
var end:int = row.getSize();
var rowOffset:int = startRange[1];
var lgPatternFound:int = 0;
for (var x:int = 0; x < 5 && rowOffset < end; x++) {
var bestMatch:int = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS);
resultString.Append((('0' as String).charCodeAt(0) + bestMatch % 10));
for (var i:int = 0; i < counters.length; i++) {
rowOffset += counters[i];
}
if (bestMatch >= 10) {
lgPatternFound |= 1 << (4 - x);
}
if (x != 4) {
// Read off separator if not last
while (rowOffset < end && !row._get(rowOffset)) {
rowOffset++;
}
while (rowOffset < end && row._get(rowOffset)) {
rowOffset++;
}
}
}
if (resultString.length != 5) {
throw NotFoundException.getNotFoundInstance();
}
var checkDigit:int = determineCheckDigit(lgPatternFound);
if (extensionChecksum(resultString.toString()) != checkDigit) {
throw NotFoundException.getNotFoundInstance();
}
return rowOffset;
}
private static function extensionChecksum(s:String):int {
var length:int = s.length;
var sum:int = 0;
for (var i:int = length - 2; i >= 0; i -= 2) {
sum += s.charAt(i).charCodeAt(0) - ('0' as String).charCodeAt(0);
}
sum *= 3;
for (i = length - 1; i >= 0; i -= 2) {
sum += s.charAt(i).charCodeAt(0) - ('0' as String).charCodeAt(0);
}
sum *= 3;
return sum % 10;
}
private static function determineCheckDigit(lgPatternFound:int):int {
for (var d:int = 0; d < 10; d++) {
if (lgPatternFound == UPCEANExtensionSupport.CHECK_DIGIT_ENCODINGS[d]) {
return d;
}
}
throw NotFoundException.getNotFoundInstance();
}
/**
* @param raw raw content of extension
* @return formatted interpretation of raw content as a {@link Hashtable} mapping
* one {@link ResultMetadataType} to appropriate value, or <code>null</code> if not known
*/
private static function parseExtensionString(raw:String):HashTable {
var type:ResultMetadataType;
var value:Object ;
switch (raw.length) {
case 2:
type = ResultMetadataType.ISSUE_NUMBER;
value = parseExtension2String(raw);
break;
case 5:
type = ResultMetadataType.SUGGESTED_PRICE;
value = parseExtension5String(raw);
break;
default:
return null;
}
if (value == null) {
return null;
}
var result:HashTable = new HashTable(1);
result._put(type, value);
return result;
}
private static function parseExtension2String(raw:String):int {
return int(raw);
}
private static function parseExtension5String(raw:String):String {
var currency:String;
switch (raw.charAt(0)) {
case '0':
currency = "£";
break;
case '5':
currency = "$";
break;
case '9':
// Reference: http://www.jollytech.com
if (raw == "90000") {
// No suggested retail price
return null;
} else if (raw == "99991") {
// Complementary
return "0.00";
} else if (raw == "99990") {
return "Used";
}
// Otherwise... unknown currency?
currency = "";
break;
default:
currency = "";
break;
}
var rawAmount:int = int(raw.substring(1));
var unitsString:String = (Number(rawAmount / 100)).toString();
var hundredths:int = rawAmount % 100;
var hundredthsString:String = hundredths < 10 ? String.fromCharCode(("0" as String).charCodeAt(0)+ hundredths) : String(hundredths);
return currency + unitsString + '.' + hundredthsString;
}
}
}

View file

@ -1,35 +1,363 @@
/*
* 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.oned package com.google.zxing.oned
{ {
import com.google.zxing.common.BitArray;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.Result;
/** public class UPCEANReader extends OneDReader
* <p>This interfaces captures addtional functionality that readers of
* UPC/EAN family of barcodes should expose.</p>
*
* @author Sean Owen
*/
public class UPCEANReader implements OneDReader
{ {
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.BitArray;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.ResultMetadataType;
public static var MAX_AVG_VARIANCE:int = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42);
public static var MAX_INDIVIDUAL_VARIANCE:int = int(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7);
/** /**
* <p>Like {@link #decodeRow(int, BitArray, java.util.Hashtable)}, but * Start/end guard pattern.
* allows caller to inform method about where the UPC/EAN start pattern is
* found. This allows this to be computed once and reused across many implementations.</p>
*/ */
public static var START_END_PATTERN:Array = [1, 1, 1];
/**
* Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
*/
public static var MIDDLE_PATTERN:Array = [1, 1, 1, 1, 1];
/**
* "Odd", or "L" patterns used to encode UPC/EAN digits.
*/
public static var L_PATTERNS:Array = [
[3, 2, 1, 1], // 0
[2, 2, 2, 1], // 1
[2, 1, 2, 2], // 2
[1, 4, 1, 1], // 3
[1, 1, 3, 2], // 4
[1, 2, 3, 1], // 5
[1, 1, 1, 4], // 6
[1, 3, 1, 2], // 7
[1, 2, 1, 3], // 8
[3, 1, 1, 2]]; // 9
/**
* As above but also including the "even", or "G" patterns used to encode UPC/EAN digits.
*/
//public static var L_AND_G_PATTERNS:Array = new Array(20);
public static var L_AND_G_PATTERNS:Array = [
[3, 2, 1, 1], // 0
[2, 2, 2, 1], // 1
[2, 1, 2, 2], // 2
[1, 4, 1, 1], // 3
[1, 1, 3, 2], // 4
[1, 2, 3, 1], // 5
[1, 1, 1, 4], // 6
[1, 3, 1, 2], // 7
[1, 2, 1, 3], // 8
[3, 1, 1, 2], // 9
[1, 1, 2, 3], //R0
[1, 2, 2, 2], //R1
[2, 2, 1, 2], //R2
[1, 1, 4, 1], //R3
[2, 3, 1, 1], //R4
[1, 3, 2, 1], //R5
[4, 1, 1, 1], //R6
[2, 1, 3, 1], //R7
[3, 1, 2, 1], //R8
[2, 1, 1, 3], //R9
];
// create the inverse of the L patterns
/* static {
L_AND_G_PATTERNS = new int[20][];
for (var i:int = 0; i < 10; i++)
{
L_AND_G_PATTERNS[i] = L_PATTERNS[i];
}
for (int i = 10; i < 20; i++)
{
var widths:Array = L_PATTERNS[i - 10];
var reversedWidths:Array = new Array(widths.length);
for (int j = 0; j < widths.length; j++)
{
reversedWidths[j] = widths[widths.length - j - 1];
}
L_AND_G_PATTERNS[i] = reversedWidths;
}
}
*/
private var decodeRowStringBuffer:StringBuilder;
private var extensionReader:UPCEANExtensionSupport;
private var eanManSupport:EANManufacturerOrgSupport;
public function UPCEANReader():void
{
decodeRowStringBuffer = new StringBuilder(20);
extensionReader = new UPCEANExtensionSupport();
eanManSupport = new EANManufacturerOrgSupport();
}
public static function findStartGuardPattern(row:BitArray):Array
{
var foundStart:Boolean = false;
var startRange:Array = null;
var nextStart:int = 0;
while (!foundStart)
{
startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN);
var start:int = startRange[0];
nextStart = startRange[1];
// Make sure there is a quiet zone at least as big as the start pattern before the barcode. If
// this check would run off the left edge of the image, do not accept this barcode, as it is
// very likely to be a false positive.
var quietStart:int = start - (nextStart - start);
if (quietStart >= 0)
{
foundStart = row.isRange(quietStart, start, false);
}
}
return startRange;
}
public override function decodeRow(rowNumber:Object, row:BitArray, o:Object):Result
{
if (o is HashTable){ return decodeRow_HashTable(rowNumber as int,row,o as HashTable); }
else if (o is Array) { return decodeRow_Array(rowNumber as int,row,o as Array); }
else {throw new Error('AbstractUPCEANReader : decodeRow : unknow type of object');}
}
public function decodeRow_HashTable(rowNumber:int, row:BitArray, hints:HashTable):Result
{
return decodeRow(rowNumber, row, findStartGuardPattern(row));
}
public function decodeRow_Array(rowNumber:int, row:BitArray, startGuardRange:Array):Result public function decodeRow_Array(rowNumber:int, row:BitArray, startGuardRange:Array):Result
{ {
return null; var result:StringBuilder = decodeRowStringBuffer;// empty stringbuilder
}; result.length = 0;
var endStart:int = decodeMiddle(row, startGuardRange, result);
var endRange:Array = decodeEnd(row, endStart);
public function decode(image:BinaryBitmap, hints:HashTable=null):Result // Make sure there is a quiet zone at least as big as the end pattern after the barcode. The
// spec might want more whitespace, but in practice this is the maximum we can count on.
var end:int = endRange[1];
var quietEnd:int = end + (end - endRange[0]);
if (quietEnd >= row.getSize() || !row.isRange(end, quietEnd, false))
{
throw new ReaderException("AbstractUPCEANReader : decodeRow_Array : ending white space is missing");
}
var resultString:String = result.toString();
if (!checkChecksum(resultString)) {
throw new ReaderException("AbstractUPCEANReader : decodeRow_Array : checkChecksum failed");
}
var left:Number = (Number) (startGuardRange[1] + startGuardRange[0]) / 2;
var right:Number = (Number) (endRange[1] + endRange[0]) / 2;
var format:BarcodeFormat = getBarcodeFormat();
var decodeResult:Result = new Result(resultString,
null, // no natural byte representation for these barcodes
[
new ResultPoint(left, rowNumber),
new ResultPoint(right, rowNumber)],
format);
try {
var extensionResult:Result = extensionReader.decodeRow(rowNumber, row, endRange[1]);
decodeResult.putAllMetadata(extensionResult.getResultMetadata());
decodeResult.addResultPoints(extensionResult.getResultPoints());
} catch (re:ReaderException) {
// continue
}
if ((format == BarcodeFormat.EAN_13) || (format == BarcodeFormat.UPC_A)) {
var countryID:String = eanManSupport.lookupCountryIdentifier(resultString);
if (countryID != null) {
decodeResult.putMetadata(ResultMetadataType.POSSIBLE_COUNTRY, countryID);
}
}
return decodeResult;
}
public function getBarcodeFormat():BarcodeFormat
{ {
return null; return null;
} }
/**
* @return {@link #checkStandardUPCEANChecksum(String)}
*/
public function checkChecksum(s:String):Boolean {
return checkStandardUPCEANChecksum(s);
} }
/**
* Computes the UPC/EAN checksum on a string of digits, and reports
* whether the checksum is correct or not.
*
* @param s string of digits to check
* @return true iff string of digits passes the UPC/EAN checksum algorithm
* @throws ReaderException if the string does not contain only digits
*/
public static function checkStandardUPCEANChecksum(s:String):Boolean {
var length:int = s.length;
if (length == 0)
{
return false;
}
var sum:int = 0;
for (var i:int = length - 2; i >= 0; i -= 2) {
var digit:int = s.charCodeAt(i) - ('0').charCodeAt(0);
if (digit < 0 || digit > 9) {
throw new ReaderException("AbstractUPCEANReader : checkStandardUPCEANChecksum : digit out of range ("+digit+")");
}
sum += digit;
}
sum *= 3;
for (var i3:int = length - 1; i3 >= 0; i3 -= 2) {
var digit2:int = s.charCodeAt(i3) - ('0').charCodeAt(0);
if (digit2 < 0 || digit2 > 9) {
throw new ReaderException("AbstractUPCEANReader : checkStandardUPCEANChecksum : digit2 out of range ("+digit2+")");
}
sum += digit2;
}
return sum % 10 == 0;
}
/**
* Subclasses override this to decode the portion of a barcode between the start and end guard patterns.
*
* @param row row of black/white values to search
* @param startRange start/end offset of start guard pattern
* @param resultString {@link StringBuffer} to append decoded chars to
* @return horizontal offset of first pixel after the "middle" that was decoded
* @throws ReaderException if decoding could not complete successfully
*/
protected function decodeMiddle(row:BitArray, startRange:Array, resultString:StringBuilder):int{return -1;};
public function decodeEnd(row:BitArray, endStart:int):Array
{
return findGuardPattern(row, endStart, false, START_END_PATTERN);
}
/**
* @param row row of black/white values to search
* @param rowOffset position to start search
* @param whiteFirst if true, indicates that the pattern specifies white/black/white/...
* pixel counts, otherwise, it is interpreted as black/white/black/...
* @param pattern pattern of counts of number of black and white pixels that are being
* searched for as a pattern
* @return start/end horizontal offset of guard pattern, as an array of two ints
* @throws ReaderException if pattern is not found
*/
public static function findGuardPattern( row:BitArray, rowOffset:int, whiteFirst:Boolean, pattern:Array):Array
{
var patternLength:int = pattern.length;
var counters:Array = new Array(patternLength);
for (var i:int=0;i<patternLength;i++) { counters[i] = 0; }
var width:int = row.getSize();
var isWhite:Boolean = false;
while (rowOffset < width) {
isWhite = !row._get(rowOffset);
if (whiteFirst == isWhite) {
break;
}
rowOffset++;
}
var counterPosition:int = 0;
var patternStart:int = rowOffset;
for (var x:int = rowOffset; x < width; x++)
{
var pixel:Boolean = row._get(x);
if (pixel != isWhite)
{
counters[counterPosition] = counters[counterPosition] + 1;
}
else
{
if (counterPosition == patternLength - 1)
{
if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE)
{
return [patternStart, x];
}
patternStart = patternStart + counters[0] + counters[1];
for (var y:int = 2; y < patternLength; y++)
{
counters[y - 2] = counters[y];
}
counters[patternLength - 2] = 0;
counters[patternLength - 1] = 0;
counterPosition--;
}
else
{
counterPosition++;
}
counters[counterPosition] = 1;
isWhite = !isWhite;
}
}
throw new ReaderException("AbstractUPCEANReader : findGuardPattern : pattern not found)");
}
/**
* Attempts to decode a single UPC/EAN-encoded digit.
*
* @param row row of black/white values to decode
* @param counters the counts of runs of observed black/white/black/... values
* @param rowOffset horizontal offset to start decoding from
* @param patterns the set of patterns to use to decode -- sometimes different encodings
* for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should
* be used
* @return horizontal offset of first pixel beyond the decoded digit
* @throws ReaderException if digit cannot be decoded
*/
public static function decodeDigit(row:BitArray,counters:Array, rowOffset:int, patterns:Array):int
{
recordPattern(row, rowOffset, counters);
var bestVariance:int = MAX_AVG_VARIANCE; // worst variance we'll accept
var bestMatch:int = -1;
var max:int = patterns.length;
for (var i:int = 0; i < max; i++)
{
var pattern:Array = patterns[i];
var variance:int = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance) {
bestVariance = variance;
bestMatch = i;
}
}
if (bestMatch >= 0) {
return bestMatch;
} else {
throw new ReaderException("AbstractUPCEANReader : decodeDigit : not bestMatch found");
}
}
}
} }

View file

@ -14,20 +14,90 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.zxing.oned package com.google.zxing.oned{
{
import com.google.zxing.common.ByteMatrix; import com.google.zxing.BarcodeFormat;
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.Writer;
import com.google.zxing.Writer; import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.IllegalArgumentException;
/** /**
* @author Ari Pollak * <p>Encapsulates functionality and implementation that is common to UPC and EAN families
* of one-dimensional barcodes.</p>
*
* @author aripollak@gmail.com (Ari Pollak)
*/ */
public interface UPCEANWriter extends Writer public class UPCEANWriter implements Writer
{ {
public function encode(contents:String , format:BarcodeFormat=null, width:int=0, height:int=0, hints:HashTable = null):Object
{
if (contents == null || contents.length == 0) {
throw new IllegalArgumentException("Found empty contents");
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Requested dimensions are too small: "
+ width + 'x' + height);
}
var code:Array = encode(contents) as Array;
return renderResult(code, width, height);
}
/** @return a byte array of horizontal pixels (0 = white, 1 = black) */ /** @return a byte array of horizontal pixels (0 = white, 1 = black) */
//function encode(contents:String, format:BarcodeFormat, width:int, height:int, hints:HashTable=undefined):ByteMatrix; private static function renderResult(code:Array, width:int , height:int ):BitMatrix {
var inputWidth:int = code.length;
// Add quiet zone on both sides
var fullWidth:int = inputWidth + (UPCEANReader.START_END_PATTERN.length << 1);
var outputWidth:int = Math.max(width, fullWidth);
var outputHeight:int = Math.max(1, height);
var multiple:int = int(outputWidth / fullWidth);
var leftPadding:int = int((outputWidth - (inputWidth * multiple)) / 2);
var output:BitMatrix = new BitMatrix(outputWidth, outputHeight);
var outputX:int = leftPadding;
for (var inputX:int = 0; inputX < inputWidth; inputX++, outputX += multiple) {
if (code[inputX] == 1)
{
output.setRegion(outputX, 0, multiple, outputHeight);
}
}
return output;
}
/**
* Appends the given pattern to the target array starting at pos.
*
* @param startColor
* starting color - 0 for white, 1 for black
* @return the number of elements added to target.
*/
protected static function appendPattern(target:Array, pos:int, pattern:Array, startColor:int):int
{
if (startColor != 0 && startColor != 1)
{
throw new IllegalArgumentException(
"startColor must be either 0 or 1, but got: " + startColor);
}
var color:int = startColor;
var numAdded:int = 0;
for (var i:int = 0; i < pattern.length; i++) {
for (var j:int = 0; j < pattern[i]; j++) {
target[pos] = color;
pos += 1;
numAdded += 1;
}
color ^= 1; // flip color after each segment
}
return numAdded;
}
} }
} }

View file

@ -26,13 +26,17 @@ package com.google.zxing.oned
*/ */
public class UPCEReader extends AbstractUPCEANReader public class UPCEReader extends UPCEANReader
{ {
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.NotFoundException;
/** /**
@ -53,6 +57,8 @@ package com.google.zxing.oned
private var decodeMiddleCounters:Array; private var decodeMiddleCounters:Array;
// function decode(image:BinaryBitmap, hints:HashTable=null):Result { return null; }
public function UPCEReader() { public function UPCEReader() {
decodeMiddleCounters = new Array(4); decodeMiddleCounters = new Array(4);
} }
@ -151,6 +157,40 @@ package com.google.zxing.oned
} }
/**
* Attempts to decode a single UPC/EAN-encoded digit.
*
* @param row row of black/white values to decode
* @param counters the counts of runs of observed black/white/black/... values
* @param rowOffset horizontal offset to start decoding from
* @param patterns the set of patterns to use to decode -- sometimes different encodings
* for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should
* be used
* @return horizontal offset of first pixel beyond the decoded digit
* @throws NotFoundException if digit cannot be decoded
*/
public static function decodeDigit(row:BitArray , counters:Array, rowOffset:int,patterns:Array):int {
recordPattern(row, rowOffset, counters);
var bestVariance:int = MAX_AVG_VARIANCE; // worst variance we'll accept
var bestMatch:int = -1;
var max:int = patterns.length;
for (var i:int = 0; i < max; i++) {
var pattern:Array = patterns[i];
var variance:int = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);
if (variance < bestVariance) {
bestVariance = variance;
bestMatch = i;
}
}
if (bestMatch >= 0) {
return bestMatch;
} else {
throw NotFoundException.getNotFoundInstance();
}
}
} }

View file

@ -36,6 +36,7 @@ import com.google.zxing.common.DetectorResult;
import com.google.zxing.pdf417.decoder.Decoder; import com.google.zxing.pdf417.decoder.Decoder;
import com.google.zxing.pdf417.detector.Detector; import com.google.zxing.pdf417.detector.Detector;
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.NotFoundException;
public final class PDF417Reader implements Reader { public final class PDF417Reader implements Reader {
@ -56,12 +57,16 @@ public final class PDF417Reader implements Reader {
} }
*/ */
public function reset():void {
// do nothing
}
public function decode(image:BinaryBitmap , hints:HashTable=null):Result public function decode(image:BinaryBitmap , hints:HashTable=null):Result
{ {
var decoderResult:DecoderResult ; var decoderResult:DecoderResult ;
var points:Array ; var points:Array ;
if (hints != null && hints.ContainsKey(DecodeHintType.PURE_BARCODE)) { if (hints != null && hints.ContainsKey(DecodeHintType.PURE_BARCODE)) {
var bits:BitMatrix = extractPureBits(image); var bits:BitMatrix = extractPureBits(image.getBlackMatrix());
decoderResult = decoder.decode(bits); decoderResult = decoder.decode(bits);
points = NO_POINTS; points = NO_POINTS;
} else { } else {
@ -69,80 +74,123 @@ public final class PDF417Reader implements Reader {
decoderResult = decoder.decode(detectorResult.getBits()); decoderResult = decoder.decode(detectorResult.getBits());
points = detectorResult.getPoints(); points = detectorResult.getPoints();
} }
return new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, return new Result(decoderResult.getText(), decoderResult.getRawBytes(), points,BarcodeFormat.PDF417,0);
BarcodeFormat.PDF417);
} }
/** /**
* This method detects a barcode in a "pure" image -- that is, pure monochrome image * This method detects a barcode in a "pure" image -- that is, pure monochrome image
* which contains only an unrotated, unskewed, image of a barcode, with some white border * which contains only an unrotated, unskewed, image of a barcode, with some white border
* around it. This is a specialized method that works exceptionally fast in this special * around it. This is a specialized method that works exceptionally fast in this special
* case. * case.
*/ */
private static function extractPureBits(image:BinaryBitmap ):BitMatrix { private static function extractPureBits(image:BitMatrix):BitMatrix {
// Now need to determine module size in pixels
var matrix:BitMatrix = image.getBlackMatrix();
var height:int = matrix.getHeight();
var width:int = matrix.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 leftTopBlack:Array = image.getTopLeftOnBit();
var borderWidth:int = 0; var rightBottomBlack:Array = image.getBottomRightOnBit();
while (borderWidth < minDimension && !matrix._get(borderWidth, borderWidth)) { if (leftTopBlack == null || rightBottomBlack == null) {
borderWidth++; throw NotFoundException.getNotFoundInstance();
}
if (borderWidth == minDimension) {
throw new ReaderException("PDF417Reader : extractPureBits");
} }
// And then keep tracking across the top-left black module to determine module size var moduleSize:int = moduleSize(leftTopBlack, image);
var moduleEnd:int = borderWidth;
while (moduleEnd < minDimension && matrix._get(moduleEnd, moduleEnd)) {
moduleEnd++;
}
if (moduleEnd == minDimension) {
throw new ReaderException("PDF417Reader : extractPureBits");
}
var moduleSize:int = moduleEnd - borderWidth; var top:int = leftTopBlack[1];
var bottom:int = rightBottomBlack[1];
var left:int = findPatternStart(leftTopBlack[0], top, image);
var right:int = findPatternEnd(leftTopBlack[0], top, image);
// And now find where the rightmost black module on the first row ends var matrixWidth:int = (right - left + 1) / moduleSize;
var rowEndOfSymbol:int = width - 1; var matrixHeight:int = (bottom - top + 1) / moduleSize;
while (rowEndOfSymbol >= 0 && !matrix._get(rowEndOfSymbol, borderWidth)) { if (matrixWidth <= 0 || matrixHeight <= 0) {
rowEndOfSymbol--; throw NotFoundException.getNotFoundInstance();
} }
if (rowEndOfSymbol < 0) {
throw new ReaderException("PDF417Reader : extractPureBits");
}
rowEndOfSymbol++;
// Make sure width of barcode is a multiple of module size
if ((rowEndOfSymbol - borderWidth) % moduleSize != 0) {
throw new ReaderException("PDF417Reader : extractPureBits");
}
var dimension:int = (rowEndOfSymbol - borderWidth) / moduleSize;
// Push in the "border" by half the module width so that we start // 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 // sampling in the middle of the module. Just in case the image is a
// little off, this will help recover. // little off, this will help recover.
borderWidth += moduleSize >> 1; var nudge:int = moduleSize >> 1;
top += nudge;
var sampleDimension:int = borderWidth + (dimension - 1) * moduleSize; left += nudge;
if (sampleDimension >= width || sampleDimension >= height) {
throw new ReaderException("PDF417Reader : extractPureBits");
}
// Now just read off the bits // Now just read off the bits
var bits:BitMatrix = new BitMatrix(dimension); var bits:BitMatrix = new BitMatrix(matrixWidth, matrixHeight);
for (var y:int = 0; y < dimension; y++) { for (var y:int = 0; y < matrixHeight; y++) {
var iOffset:int = borderWidth + y * moduleSize; var iOffset:int = top + y * moduleSize;
for (var x:int = 0; x < dimension; x++) { for (var x:int = 0; x < matrixWidth; x++) {
if (matrix._get(borderWidth + x * moduleSize, iOffset)) { if (image._get(left + x * moduleSize, iOffset)) {
bits._set(x, y); bits._set(x, y);
} }
} }
} }
return bits; return bits;
} }
private static function moduleSize(leftTopBlack:Array , image:BitMatrix ):int
{
var x:int = leftTopBlack[0];
var y:int = leftTopBlack[1];
var width:int = image.getWidth();
while (x < width && image._get(x, y)) {
x++;
}
if (x == width) {
throw NotFoundException.getNotFoundInstance();
}
var moduleSize:int = (x - leftTopBlack[0]) >>> 3; // We've crossed left first bar, which is 8x
if (moduleSize == 0) {
throw NotFoundException.getNotFoundInstance();
}
return moduleSize;
}
private static function findPatternStart(x:int, y:int, image:BitMatrix):int {
var width:int = image.getWidth();
var start:int = x;
// start should be on black
var transitions:int = 0;
var black:Boolean = true;
while (start < width - 1 && transitions < 8) {
start++;
var newBlack:Boolean = image._get(start, y);
if (black != newBlack) {
transitions++;
}
black = newBlack;
}
if (start == width - 1) {
throw NotFoundException.getNotFoundInstance();
}
return start;
}
private static function findPatternEnd(x:int, y:int, image:BitMatrix):int {
var width:int = image.getWidth();
var end:int = width - 1;
// end should be on black
while (end > x && !image._get(end, y)) {
end--;
}
var transitions:int = 0;
var black:Boolean = true;
while (end > x && transitions < 9) {
end--;
var newBlack:Boolean = image._get(end, y);
if (black != newBlack) {
transitions++;
}
black = newBlack;
}
if (end == x) {
throw NotFoundException.getNotFoundInstance();
}
return end;
}
} }
} }

View file

@ -62,28 +62,21 @@ import com.google.zxing.common.BitMatrix;
*/ */
public function readCodewords():Array public function readCodewords():Array
{ {
var width:int = bitMatrix.getDimension(); var width:int = bitMatrix.getWidth();
// TODO should be a rectangular matrix var height:int = bitMatrix.getHeight();
var height:int = width;
erasures = new Array(MAX_CW_CAPACITY); erasures = new Array(MAX_CW_CAPACITY);for (var k:int=0;k<erasures.length;k++) { erasures[k]=0; }
for (var k:int=0;k<erasures.length;k++) { erasures[k]=0; }
// Get the number of pixels in a module across the X dimension // Get the number of pixels in a module across the X dimension
//float moduleWidth = bitMatrix.getModuleWidth(); //float moduleWidth = bitMatrix.getModuleWidth();
var moduleWidth:Number = 1.0; // Image has been sampled and reduced var moduleWidth:Number = 1.0; // Image has been sampled and reduced
var rowCounters:Array = new Array(width);for (var n:int=0;n<rowCounters.length;n++) { rowCounters[n] = 0;}
var rowCounters:Array = new Array(width); var codewords:Array = new Array(MAX_CW_CAPACITY);for(var m:int=0;m<codewords.length;m++){codewords[m]=0;}
for (var n:int=0;n<rowCounters.length;n++) { rowCounters[n] = 0;}
var codewords:Array = new Array(MAX_CW_CAPACITY);
for(var m:int=0;m<codewords.length;m++){codewords[m]=0;}
var next:int = 0; var next:int = 0;
var matchingConsecutiveScans:int = 0; var matchingConsecutiveScans:int = 0;
var rowInProgress:Boolean = false; var rowInProgress:Boolean = false;
var rowNumber:int = 0; var rowNumber:int = 0;
var rowHeight:int = 0; var rowHeight:int = 0;
for (var i:int = 1; i < height; i++) { for (var i:int = 1; i < height; i++) {
if (rowNumber >= MAX_ROWS) { if (rowNumber >= MAX_ROWS) {
// Something is wrong, since we have exceeded // Something is wrong, since we have exceeded
@ -193,7 +186,7 @@ import com.google.zxing.common.BitMatrix;
* this row. * this row.
*/ */
public function processRow(rowCounters:Array, rowNumber:int, rowHeight:int, codewords:Array, next:int):int { public function processRow(rowCounters:Array, rowNumber:int, rowHeight:int, codewords:Array, next:int):int {
var width:int = bitMatrix.getDimension(); var width:int = bitMatrix.getWidth();
var columnNumber:int = 0; var columnNumber:int = 0;
var symbol:Number = 0; var symbol:Number = 0;
for (var i:int = 0; i < width; i += MODULES_IN_SYMBOL) for (var i:int = 0; i < width; i += MODULES_IN_SYMBOL)
@ -263,7 +256,7 @@ import com.google.zxing.common.BitMatrix;
if (rightColumnECData == leftColumnECData if (rightColumnECData == leftColumnECData
&& leftColumnECData != 0) && leftColumnECData != 0)
{ {
ecLevel = ((rightColumnECData % 30) - rows % 3) / 3; ecLevel = int(((rightColumnECData % 30) - rows % 3) / 3);
} }
break; break;
} }
@ -386,158 +379,7 @@ import com.google.zxing.common.BitMatrix;
return ecLevel; return ecLevel;
} }
/**
* Convert the symbols in the row to codewords.
* Each PDF417 symbol character consists of four bar elements and four space
* elements, each of which can be one to six modules wide. The four bar and
* four space elements shall measure 17 modules in total.
*
* @param rowCounters an array containing the counts of black pixels for each column
* in the row.
* @param rowNumber the current row number of codewords.
* @param rowHeight the height of this row in pixels.
* @param moduleWidth the size of a module in pixels.
* @param codewords the codeword array to save codewords into.
* @param next the next available index into the codewords array.
* @return the next available index into the codeword array after processing
* this row.
* @throws ReaderException
*/
/*
int processRow1(int[] rowCounters, int rowNumber, int rowHeight,
float moduleWidth, int[] codewords, int next) {
int width = bitMatrix.getDimension();
int firstBlack = 0;
for (firstBlack = 0; firstBlack < width; firstBlack++) {
// Step forward until we find the first black pixels
if (rowCounters[firstBlack] >= rowHeight >>> 1) {
break;
}
}
int[] counters = new int[8];
int state = 0; // In black pixels, looking for white, first or second time
long symbol = 0;
int columnNumber = 0;
for (int i = firstBlack; i < width; i++) {
if (state == 1 || state == 3 || state == 5 || state == 7) { // In white
// pixels,
// looking
// for
// black
// If more than half the column is black
if (rowCounters[i] >= rowHeight >>> 1 || i == width - 1) {
if (i == width - 1) {
counters[state]++;
}
// In black pixels or the end of a row
state++;
if (state < 8) {
// Don't count the last one
counters[state]++;
}
} else {
counters[state]++;
}
} else {
if (rowCounters[i] < rowHeight >>> 1) {
// Found white pixels
state++;
if (state == 7 && i == width - 1) {
// Its found white pixels at the end of the row,
// give it a chance to exit gracefully
i--;
} else {
// Found white pixels
counters[state]++;
}
} else {
if (state < 8) {
// Still in black pixels
counters[state]++;
}
}
}
if (state == 8) { // Found black, white, black, white, black, white,
// black, white and stumbled back onto black; done
if (columnNumber >= MAX_COLUMNS) {
// Something is wrong, since we have exceeded
// the maximum columns in the specification.
// TODO Maybe return error code
return -1;
}
if (columnNumber > 0) {
symbol = getSymbol(counters, moduleWidth);
int cw = getCodeword(symbol);
// if (debug) System.out.println(" " +
// Long.toBinaryString(symbol) + " cw=" +cw + " ColumnNumber="
// +columnNumber + "i=" +i);
if (cw < 0) {
erasures[eraseCount] = next;
next++;
eraseCount++;
} else {
codewords[next++] = cw;
}
} else {
// Left row indicator column
symbol = getSymbol(counters, moduleWidth);
int cw = getCodeword(symbol);
if (ecLevel < 0) {
switch (rowNumber % 3) {
case 0:
break;
case 1:
leftColumnECData = cw;
break;
case 2:
break;
}
}
}
// Step back so that this pixel can be examined during the next
// pass.
i--;
counters = new int[8];
columns = columnNumber;
columnNumber++;
// Introduce some errors if (rowNumber == 0 && columnNumber == 4)
// { codewords[next-1] = 0; erasures[eraseCount] = next-1;
// eraseCount++; } if (rowNumber == 0 && columnNumber == 6) {
// codewords[next-1] = 10; erasures[eraseCount] = next-1;
// eraseCount++; } if (rowNumber == 0 && columnNumber == 8) {
// codewords[next-1] = 10; erasures[eraseCount] = next-1;
// eraseCount++; }
state = 0;
symbol = 0;
}
}
if (columnNumber > 1) {
// Right row indicator column is in codeword[next]
columns--;
// Overwrite the last codeword i.e. Right Row Indicator
--next;
if (ecLevel < 0) {
switch (rowNumber % 3) {
case 0:
break;
case 1:
break;
case 2:
rightColumnECData = codewords[next];
if (rightColumnECData == leftColumnECData
&& leftColumnECData != 0) {
ecLevel = ((rightColumnECData % 30) - rows % 3) / 3;
}
break;
}
}
codewords[next] = 0;
}
return next;
}
*/
/** /**
* The sorted table of all possible symbols. Extracted from the PDF417 * The sorted table of all possible symbols. Extracted from the PDF417

View file

@ -23,7 +23,7 @@ package com.google.zxing.pdf417.decoder
* *
* @author SITA Lab (kevin.osullivan@sita.aero) * @author SITA Lab (kevin.osullivan@sita.aero)
*/ */
public final class DecodedBitStreamParser { public class DecodedBitStreamParser {
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.common.DecoderResult; import com.google.zxing.common.DecoderResult;
@ -43,7 +43,9 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
private static var LOWER:int = 1; private static var LOWER:int = 1;
private static var MIXED:int = 2; private static var MIXED:int = 2;
private static var PUNCT:int = 3; private static var PUNCT:int = 3;
private static var PUNCT_SHIFT:int = 4; private static var ALPHA_SHIFT:int = 4;
private static var PUNCT_SHIFT:int = 5;
private static var PL:int = 25; private static var PL:int = 25;
private static var LL:int = 27; private static var LL:int = 27;
@ -55,7 +57,7 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
private static var PUNCT_CHARS:Array = [';', '<', '>', '@', '[', String.fromCharCode(92), '}', '_', String.fromCharCode(96), '~', '!', private static var PUNCT_CHARS:Array = [';', '<', '>', '@', '[', String.fromCharCode(92), '}', '_', String.fromCharCode(96), '~', '!',
String.fromCharCode(13), String.fromCharCode(9), ',', ':', String.fromCharCode(10), '-', '.', '$', '/', String.fromCharCode(34), '|', '*', String.fromCharCode(13), String.fromCharCode(9), ',', ':', String.fromCharCode(10), '-', '.', '$', '/', String.fromCharCode(34), '|', '*',
'(', ')', '?', '{', '}', 39]; '(', ')', '?', '{', '}', String.fromCharCode(39)];
private static var MIXED_CHARS:Array = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '&', private static var MIXED_CHARS:Array = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '&',
String.fromCharCode(13), String.fromCharCode(9), ',', ':', '#', '-', '.', '$', '/', '+', '%', '*', String.fromCharCode(13), String.fromCharCode(9), ',', ':', '#', '-', '.', '$', '/', '+', '%', '*',
@ -150,14 +152,13 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
for (var k:int=0;k<textCompactionData.length;k++) { textCompactionData[k]=0;byteCompactionData[k]=0;} for (var k:int=0;k<textCompactionData.length;k++) { textCompactionData[k]=0;byteCompactionData[k]=0;}
var index:int = 0; var index:int = 0;
var code:int = 0;
var end:Boolean = false; var end:Boolean = false;
while ((codeIndex < codewords[0]) && !end) while ((codeIndex < codewords[0]) && !end)
{ {
code = codewords[codeIndex++]; var code:int = codewords[codeIndex++];
if (code < TEXT_COMPACTION_MODE_LATCH) if (code < TEXT_COMPACTION_MODE_LATCH)
{ {
textCompactionData[index] = Math.floor(code / 30); textCompactionData[index] = int(code / 30);
textCompactionData[index + 1] = code % 30; textCompactionData[index + 1] = code % 30;
index += 2; index += 2;
} }
@ -188,6 +189,7 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
// of the Text Compaction mode. Codeword 913 is only available // of the Text Compaction mode. Codeword 913 is only available
// in Text Compaction mode; its use is described in 5.4.2.4. // in Text Compaction mode; its use is described in 5.4.2.4.
textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE; textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE;
code = codewords[codeIndex++];
byteCompactionData[index] = code; //Integer.toHexString(code); byteCompactionData[index] = code; //Integer.toHexString(code);
index++; index++;
break; break;
@ -264,8 +266,9 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
} else { } else {
if (subModeCh == 26) { if (subModeCh == 26) {
ch = ' '; ch = ' ';
} else if (subModeCh == AL) { } else if (subModeCh == AS) {
subMode = ALPHA; priorToShiftMode = subMode;
subMode = ALPHA_SHIFT;
} else if (subModeCh == ML) { } else if (subModeCh == ML) {
subMode = MIXED; subMode = MIXED;
} else if (subModeCh == PS) { } else if (subModeCh == PS) {
@ -287,8 +290,8 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
subMode = PUNCT; subMode = PUNCT;
} else if (subModeCh == 26) { } else if (subModeCh == 26) {
ch = ' '; ch = ' ';
} else if (subModeCh == AS) { } else if (subModeCh == LL) {
//mode_change = true; subMode = LOWER;
} else if (subModeCh == AL) { } else if (subModeCh == AL) {
subMode = ALPHA; subMode = ALPHA;
} else if (subModeCh == PS) { } else if (subModeCh == PS) {
@ -303,7 +306,7 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
case PUNCT: case PUNCT:
// Punctuation // Punctuation
if (subModeCh < PS) { if (subModeCh < PAL) {
ch = PUNCT_CHARS[subModeCh]; ch = PUNCT_CHARS[subModeCh];
} else { } else {
if (subModeCh == PAL) { if (subModeCh == PAL) {
@ -312,12 +315,26 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
result.Append(byteCompactionData[i]); result.Append(byteCompactionData[i]);
} }
} }
break;
case ALPHA_SHIFT:
// Restore sub-mode
subMode = priorToShiftMode;
if (subModeCh < 26) {
ch = String.fromCharCode(String('A').charCodeAt(0) + subModeCh);
} else {
if (subModeCh == 26) {
ch = ' ';
} else {
// is this even possible?
}
}
break; break;
case PUNCT_SHIFT: case PUNCT_SHIFT:
// Restore sub-mode // Restore sub-mode
subMode = priorToShiftMode; subMode = priorToShiftMode;
if (subModeCh < PS) { if (subModeCh < PAL) {
ch = PUNCT_CHARS[subModeCh]; ch = PUNCT_CHARS[subModeCh];
} else { } else {
if (subModeCh == PAL) { if (subModeCh == PAL) {
@ -326,6 +343,7 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
} }
break; break;
} }
if ((ch).charCodeAt(0) != 0) { if ((ch).charCodeAt(0) != 0) {
// Append decoded character to result // Append decoded character to result
result.Append(ch); result.Append(ch);
@ -349,7 +367,6 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
{ {
var count:int; var count:int;
var value:Number; var value:Number;
var code:int;
var end:Boolean; var end:Boolean;
if (mode == BYTE_COMPACTION_MODE_LATCH) { if (mode == BYTE_COMPACTION_MODE_LATCH) {
@ -358,20 +375,17 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
count = 0; count = 0;
value = 0; value = 0;
var decodedData:Array = new Array(6); var decodedData:Array = new Array(6);
var byteCompactedCodewords:Array = new Array(6); var byteCompactedCodewords:Array = new Array(6);for (var k:int=0;k<6;k++) { decodedData[k]=0;byteCompactedCodewords[k]=0;}
for (var k:int=0;k<6;k++) { decodedData[k]=0;byteCompactedCodewords[k]=0;}
code = 0;
end = false; end = false;
while ((codeIndex < codewords[0]) && !end) while ((codeIndex < codewords[0]) && !end)
{ {
code = codewords[codeIndex++]; var code:int = codewords[codeIndex++];
if (code < TEXT_COMPACTION_MODE_LATCH) if (code < TEXT_COMPACTION_MODE_LATCH)
{ {
byteCompactedCodewords[count] = String.fromCharCode(code); byteCompactedCodewords[count] = String.fromCharCode(code);
count++; count++;
// Base 900 // Base 900
value = value * 900; value = value * 900 + code;
value = value + code;
} else { } else {
if ((code == TEXT_COMPACTION_MODE_LATCH) || if ((code == TEXT_COMPACTION_MODE_LATCH) ||
(code == BYTE_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH) ||
@ -401,7 +415,7 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
// If Byte Compaction mode is invoked with codeword 901, // If Byte Compaction mode is invoked with codeword 901,
// the group of codewords is interpreted directly // the group of codewords is interpreted directly
// as one byte per codeword, without compaction. // as one byte per codeword, without compaction.
for (var i:int = (Math.floor(count / 5) * 5); i < count; i++) for (var i:int = (int(count / 5) * 5); i < count; i++)
{ {
result.Append( byteCompactedCodewords[i]); result.Append( byteCompactedCodewords[i]);
} }
@ -421,8 +435,7 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
if (code < TEXT_COMPACTION_MODE_LATCH) { if (code < TEXT_COMPACTION_MODE_LATCH) {
count += 1; count += 1;
// Base 900 // Base 900
value *= 900; value = value * 900 + code;
value += code;
} else { } else {
if ((code == TEXT_COMPACTION_MODE_LATCH) || if ((code == TEXT_COMPACTION_MODE_LATCH) ||
(code == BYTE_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH) ||
@ -439,12 +452,11 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
{ {
// Decode every 5 codewords // Decode every 5 codewords
// Convert to Base 256 // Convert to Base 256
var decodedData2:Array = new Array(6); var decodedData2:Array = new Array(6); for (var kk:int=0;kk<decodedData2.length;kk++) { decodedData2[kk] = 0; }
for (var kk:int=0;kk<decodedData2.length;kk++) { decodedData2[kk] = 0; }
for (var j2:int = 0; j2 < 6; ++j2) { for (var j2:int = 0; j2 < 6; ++j2) {
var chcode2:int = value - Math.floor(value / 256) * 256; // BAS : Actionscript can't handle modulo for values > int.MAX_VALUE var chcode2:int = value - Math.floor(value / 256) * 256; // BAS : Actionscript can't handle modulo for values > int.MAX_VALUE
decodedData2[5 - j2] = String.fromCharCode(chcode2);// BAS : Actionscript can't handle modulo for values > int.MAX_VALUE decodedData2[5 - j2] = String.fromCharCode(chcode2);// BAS : Actionscript can't handle modulo for values > int.MAX_VALUE
value = Math.floor(value / 256); value >>= 8;
} }
result.Append(decodedData2); result.Append(decodedData2);
} }
@ -465,8 +477,7 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
var count:int = 0; var count:int = 0;
var end:Boolean = false; var end:Boolean = false;
var numericCodewords:Array = new Array(MAX_NUMERIC_CODEWORDS); var numericCodewords:Array = new Array(MAX_NUMERIC_CODEWORDS);for (var kk:int=0;kk<numericCodewords.length;kk++) { numericCodewords[kk] = 0; }
for (var kk:int=0;kk<numericCodewords.length;kk++) { numericCodewords[kk] = 0; }
while ((codeIndex < codewords.length) && !end) { while ((codeIndex < codewords.length) && !end) {
var code:int = codewords[codeIndex++]; var code:int = codewords[codeIndex++];
@ -587,9 +598,9 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
// Put zeros into the result. // Put zeros into the result.
result.Append('0'); result.Append('0');
} }
var hundreds:int = value2 / 100; var hundreds:int = int(value2 / 100);
var tens:int = (value2 / 10) % 10; var tens:int = int(int(value2 / 10) % 10);
var ones:int = value2 % 10; var ones:int = int(value2 % 10);
// Multiply by ones // Multiply by ones
for (var j:int = 0; j < ones; j++) { for (var j:int = 0; j < ones; j++) {
result = add(result.toString(), value1); result = add(result.toString(), value1);
@ -636,31 +647,16 @@ import com.google.zxing.common.flexdatatypes.StringBuilder;
var intValue1:int = parseInt(temp1.toString()); var intValue1:int = parseInt(temp1.toString());
var intValue2:int = parseInt(temp2.toString()); var intValue2:int = parseInt(temp2.toString());
var sumval:int = (intValue1 + intValue2 + carry) % 1000; var sumval:int = int((intValue1 + intValue2 + carry) % 1000);
carry = (intValue1 + intValue2 + carry) / 1000; carry = int((intValue1 + intValue2 + carry) / 1000);
result.setCharAt(i + 2, String.fromCharCode( ((sumval % 10) + ('0').charCodeAt(0)))); result.setCharAt(i + 2, String.fromCharCode( (int(sumval % 10) + 0x30)));
result.setCharAt(i + 1, String.fromCharCode( (((sumval / 10) % 10) + ('0').charCodeAt(0)))); result.setCharAt(i + 1, String.fromCharCode( (int(int(sumval / 10) % 10) + 0x30)));
result.setCharAt(i, String.fromCharCode( ((sumval / 100) + ('0').charCodeAt(0)))); result.setCharAt(i, String.fromCharCode( (int(sumval / 100) + 0x30)));
} }
return result; return result;
} }
/*
private static String decodeBase900toBase10(int codewords[], int count) {
BigInteger accum = BigInteger.valueOf(0);
BigInteger value = null;
for (int i = 0; i < count; i++) {
value = BigInteger.valueOf(900).pow(count - i - 1);
value = value.multiply(BigInteger.valueOf(codewords[i]));
accum = accum.add(value);
}
if (debug) System.out.println("Big Integer " + accum);
String result = accum.toString().substring(1);
return result;
}
*/
} }

View file

@ -93,7 +93,11 @@ import com.google.zxing.common.flexdatatypes.HashTable;
correctCodeWordVertices(vertices, false); correctCodeWordVertices(vertices, false);
} }
if (vertices != null) { if (vertices == null)
{
throw new ReaderException("pdf417 : Detector : detect : no vertices");
}
var moduleWidth:Number = computeModuleWidth(vertices); var moduleWidth:Number = computeModuleWidth(vertices);
if (moduleWidth < 1) { if (moduleWidth < 1) {
throw new ReaderException("pdf417 : Detector : detect : module width < 1"); throw new ReaderException("pdf417 : Detector : detect : module width < 1");
@ -104,9 +108,7 @@ import com.google.zxing.common.flexdatatypes.HashTable;
// Deskew and sample image. // Deskew and sample image.
var bits:BitMatrix = sampleGrid(matrix, vertices[4], vertices[5],vertices[6], vertices[7], dimension); var bits:BitMatrix = sampleGrid(matrix, vertices[4], vertices[5],vertices[6], vertices[7], dimension);
return new DetectorResult(bits, [vertices[4],vertices[5], vertices[6], vertices[7]]); return new DetectorResult(bits, [vertices[4],vertices[5], vertices[6], vertices[7]]);
} else {
throw new ReaderException("pdf417 : Detector : detect : no vertices");
}
} }
/** /**
@ -132,13 +134,13 @@ import com.google.zxing.common.flexdatatypes.HashTable;
var width:int = matrix.getWidth(); var width:int = matrix.getWidth();
var halfWidth:int = width >> 1; var halfWidth:int = width >> 1;
var result:Array = new Array(8); var result:Array = new Array(8); for(var kk:int=0;kk<result.length;kk++){result[kk]=0;}
var found:Boolean = false; var found:Boolean = false;
var loc:Array = null; var loc:Array = null;
// Top Left // Top Left
for (var i:int = 0; i < height; i++) { for (var i:int = 0; i < height; i++) {
loc = findGuardPattern(matrix, 0, i, halfWidth, false, START_PATTERN); loc = findGuardPattern(matrix, 0, i, width, false, START_PATTERN);
if (loc != null) { if (loc != null) {
result[0] = new ResultPoint(loc[0], i); result[0] = new ResultPoint(loc[0], i);
result[4] = new ResultPoint(loc[1], i); result[4] = new ResultPoint(loc[1], i);
@ -150,7 +152,7 @@ import com.google.zxing.common.flexdatatypes.HashTable;
if (found) { // Found the Top Left vertex if (found) { // Found the Top Left vertex
found = false; found = false;
for (var i2:int = height - 1; i2 > 0; i2--) { for (var i2:int = height - 1; i2 > 0; i2--) {
loc = findGuardPattern(matrix, 0, i2, halfWidth, false, START_PATTERN); loc = findGuardPattern(matrix, 0, i2, width, false, START_PATTERN);
if (loc != null) { if (loc != null) {
result[1] = new ResultPoint(loc[0], i2); result[1] = new ResultPoint(loc[0], i2);
result[5] = new ResultPoint(loc[1], i2); result[5] = new ResultPoint(loc[1], i2);
@ -163,7 +165,7 @@ import com.google.zxing.common.flexdatatypes.HashTable;
if (found) { // Found the Bottom Left vertex if (found) { // Found the Bottom Left vertex
found = false; found = false;
for (var i3:int = 0; i3 < height; i3++) { for (var i3:int = 0; i3 < height; i3++) {
loc = findGuardPattern(matrix, halfWidth, i3, halfWidth, false, STOP_PATTERN); loc = findGuardPattern(matrix, 0, i3, width, false, STOP_PATTERN);
if (loc != null) { if (loc != null) {
result[2] = new ResultPoint(loc[1], i3); result[2] = new ResultPoint(loc[1], i3);
result[6] = new ResultPoint(loc[0], i3); result[6] = new ResultPoint(loc[0], i3);
@ -176,7 +178,7 @@ import com.google.zxing.common.flexdatatypes.HashTable;
if (found) { // Found the Top right vertex if (found) { // Found the Top right vertex
found = false; found = false;
for (var i4:int = height - 1; i4 > 0; i4--) { for (var i4:int = height - 1; i4 > 0; i4--) {
loc = findGuardPattern(matrix, halfWidth, i4, halfWidth, false, STOP_PATTERN); loc = findGuardPattern(matrix, 0, i4, width, false, STOP_PATTERN);
if (loc != null) { if (loc != null) {
result[3] = new ResultPoint(loc[1], i4); result[3] = new ResultPoint(loc[1], i4);
result[7] = new ResultPoint(loc[0], i4); result[7] = new ResultPoint(loc[0], i4);
@ -212,7 +214,7 @@ import com.google.zxing.common.flexdatatypes.HashTable;
var width:int = matrix.getWidth(); var width:int = matrix.getWidth();
var halfWidth:int = width >> 1; var halfWidth:int = width >> 1;
var result:Array = new Array(8); var result:Array = new Array(8);for(var kk:int=0;kk<result.length;kk++){result[kk]=0;}
var found:Boolean = false; var found:Boolean = false;
var loc:Array = null; var loc:Array = null;
@ -361,9 +363,9 @@ import com.google.zxing.common.flexdatatypes.HashTable;
*/ */
private static function computeDimension(topLeft:ResultPoint, topRight:ResultPoint,bottomLeft:ResultPoint, bottomRight:ResultPoint, moduleWidth:Number):int private static function computeDimension(topLeft:ResultPoint, topRight:ResultPoint,bottomLeft:ResultPoint, bottomRight:ResultPoint, moduleWidth:Number):int
{ {
var topRowDimension:int = Math.floor((ResultPoint.distance(topLeft, topRight) / moduleWidth)); var topRowDimension:int = Math.round((ResultPoint.distance(topLeft, topRight) / moduleWidth));
var bottomRowDimension:int = Math.floor(ResultPoint.distance(bottomLeft, bottomRight) / moduleWidth); var bottomRowDimension:int = Math.round(ResultPoint.distance(bottomLeft, bottomRight) / moduleWidth);
return Math.floor((((topRowDimension + bottomRowDimension) >> 1) + 8) / 17) * 17; return int((((topRowDimension + bottomRowDimension) >> 1) + 8) / 17) * 17;
/* /*
* int topRowDimension = round(ResultPoint.distance(topLeft, * int topRowDimension = round(ResultPoint.distance(topLeft,
* topRight)); //moduleWidth); int bottomRowDimension = * topRight)); //moduleWidth); int bottomRowDimension =
@ -383,7 +385,11 @@ import com.google.zxing.common.flexdatatypes.HashTable;
// very corners. So there is no 0.5f here; 0.0f is right. // very corners. So there is no 0.5f here; 0.0f is right.
var sampler:GridSampler = GridSampler.getGridSamplerInstance(); var sampler:GridSampler = GridSampler.getGridSamplerInstance();
return sampler.sampleGrid(matrix, dimension, 0, // p1ToX return sampler.sampleGrid2(
matrix,
dimension,
dimension,
0, // p1ToX
0, // p1ToY 0, // p1ToY
dimension, // p2ToX dimension, // p2ToX
0, // p2ToY 0, // p2ToY
@ -423,8 +429,7 @@ import com.google.zxing.common.flexdatatypes.HashTable;
var patternLength:int = pattern.length; var patternLength:int = pattern.length;
// TODO: Find a way to cache this array, as this method is called hundreds of times // TODO: Find a way to cache this array, as this method is called hundreds of times
// per image, and we want to allocate as seldom as possible. // per image, and we want to allocate as seldom as possible.
var counters:Array = new Array(patternLength); var counters:Array = new Array(patternLength);for (var k:int=0;k<counters.length;k++){counters[k] = 0; }
for (var k:int=0;k<counters.length;k++){counters[k] = 0; }
var isWhite:Boolean = whiteFirst; var isWhite:Boolean = whiteFirst;
var counterPosition:int = 0; var counterPosition:int = 0;
@ -488,7 +493,7 @@ import com.google.zxing.common.flexdatatypes.HashTable;
// We're going to fake floating-point math in integers. We just need to use more bits. // We're going to fake floating-point math in integers. We just need to use more bits.
// Scale up patternLength so that intermediate values below like scaledCounter will have // Scale up patternLength so that intermediate values below like scaledCounter will have
// more "significant digits". // more "significant digits".
var unitBarWidth:int = (total << 8) / patternLength; var unitBarWidth:int = int((total << 8) / patternLength);
maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> 8; maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> 8;
var totalVariance:int = 0; var totalVariance:int = 0;
@ -501,7 +506,7 @@ import com.google.zxing.common.flexdatatypes.HashTable;
} }
totalVariance += variance; totalVariance += variance;
} }
return totalVariance / total; return int(totalVariance / total);
} }
} }

File diff suppressed because one or more lines are too long

View file

@ -26,6 +26,7 @@ package com.google.zxing.qrcode
import com.google.zxing.EncodeHintType; import com.google.zxing.EncodeHintType;
import com.google.zxing.Writer; import com.google.zxing.Writer;
import com.google.zxing.WriterException; import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
public class QRCodeWriter implements Writer public class QRCodeWriter implements Writer
{ {
@ -65,7 +66,7 @@ package com.google.zxing.qrcode
// Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
// 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap). // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
private static function renderResult( code:QRCode, width:int, height:int):ByteMatrix { private static function renderResult( code:QRCode, width:int, height:int):BitMatrix {
var input:ByteMatrix = code.getMatrix(); var input:ByteMatrix = code.getMatrix();
var inputWidth:int = input.width(); var inputWidth:int = input.width();
var inputHeight:int = input.height(); var inputHeight:int = input.height();
@ -82,60 +83,15 @@ package com.google.zxing.qrcode
var leftPadding:int = (outputWidth - (inputWidth * multiple)) / 2; var leftPadding:int = (outputWidth - (inputWidth * multiple)) / 2;
var topPadding:int = (outputHeight - (inputHeight * multiple)) / 2; var topPadding:int = (outputHeight - (inputHeight * multiple)) / 2;
var output:ByteMatrix = new ByteMatrix(outputHeight, outputWidth); var output:BitMatrix = new BitMatrix(outputHeight, outputWidth);
var outputArray:Array = output.getArray(); //sbyte[][]
// We could be tricky and use the first row in each set of multiple as the temporary storage,
// instead of allocating this separate array.
var row:Array = new Array(outputWidth);
// 1. Write the white lines at the top
for (var y:int = 0; y < topPadding; y++) {
setRowColor(outputArray[y], 255);
}
// 2. Expand the QR image to the multiple
var inputArray:Array = input.getArray();
for (var y2:int = 0; y2 < inputHeight; y2++) {
// a. Write the white pixels at the left of each row
for (var x2:int = 0; x2 < leftPadding; x2++) {
row[x2] = 255;
}
// b. Write the contents of this row of the barcode
var offset:int = leftPadding;
for (var x3:int = 0; x3 < inputWidth; x3++) {
var value:int = (inputArray[y2][x3] == 1) ? 0 : 255;
for (var z:int = 0; z < multiple; z++) {
row[offset + z] = value;
}
offset += multiple;
}
// c. Write the white pixels at the right of each row
offset = leftPadding + (inputWidth * multiple);
for (var x4:int = offset; x4 < outputWidth; x4++) {
row[x4] = 255;
}
// d. Write the completed row multiple times
offset = topPadding + (y2 * multiple);
for (var z2:int = 0; z2 < multiple; z2++)
{
//System.Array.Copy(row, 0, outputArray[offset + z], 0, outputWidth);
for (var ii:int=0;ii<outputWidth;ii++)
{
outputArray[offset + z2][ii] = row[ii];
}
for (var inputY:int = 0, outputY:int = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
// Write the contents of this row of the barcode
for (var inputX:int = 0, outputX:int = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (input._get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
} }
} }
// 3. Write the white lines at the bottom
var offset2:int = topPadding + (inputHeight * multiple);
for (var y3:int = offset2; y3 < outputHeight; y3++)
{
setRowColor(outputArray[y3], 255);
} }
return output; return output;
} }

View file

@ -32,10 +32,10 @@ package com.google.zxing.qrcode.decoder
*/ */
public function BitMatrixParser(bitMatrix:BitMatrix ) public function BitMatrixParser(bitMatrix:BitMatrix )
{ {
var dimension:int = bitMatrix.getDimension(); var dimension:int = bitMatrix.getHeight();
if ((dimension < 21) || ((dimension & 0x03) != 1)) if ((dimension < 21) || ((dimension & 0x03) != 1))
{ {
throw new ReaderException("BitMatrixParser : BitMatrixParser : dimension ("+dimension+" less tahn 21 or not a power of 3)"); throw new ReaderException("BitMatrixParser : BitMatrixParser : dimension ("+dimension+" less than 21 or not a power of 3)");
} }
this.bitMatrix = bitMatrix; this.bitMatrix = bitMatrix;
} }
@ -56,41 +56,34 @@ package com.google.zxing.qrcode.decoder
} }
// Read top-left format info bits // Read top-left format info bits
var formatInfoBits:int = 0; var formatInfoBits1:int = 0;
for (var j:int = 0; j < 6; j++) for (var j:int = 0; j < 6; j++)
{ {
formatInfoBits = copyBit(8, j, formatInfoBits); formatInfoBits1 = copyBit(j, 8, formatInfoBits1);
} }
// .. and skip a bit in the timing pattern ... // .. and skip a bit in the timing pattern ...
formatInfoBits = copyBit(8, 7, formatInfoBits); formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
formatInfoBits = copyBit(8, 8, formatInfoBits); formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
formatInfoBits = copyBit(7, 8, formatInfoBits); formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
// .. and skip a bit in the timing pattern ... // .. and skip a bit in the timing pattern ...
for (var i:int = 5; i >= 0; i--) for (var i:int = 5; i >= 0; i--)
{ {
formatInfoBits = copyBit(i, 8, formatInfoBits); formatInfoBits1 = copyBit(8, i, formatInfoBits1);
} }
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); var dimension:int = bitMatrix.getHeight();
if (parsedFormatInfo != null) var formatInfoBits2:int = 0;
var jMin:int = dimension - 7;
for (var j1:int= dimension - 1; j1 >= jMin; j1--)
{ {
return parsedFormatInfo; formatInfoBits2 = copyBit(8, j1, formatInfoBits2);
}
for (var i1:int = dimension - 8; i1 < dimension; i1++)
{
formatInfoBits2 = copyBit(i1, 8, formatInfoBits2);
} }
// Hmm, failed. Try the top-right/bottom-left pattern parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1,formatInfoBits2);
var dimension:int = bitMatrix.getDimension();
formatInfoBits = 0;
var iMin:int = dimension - 8;
for (i = dimension - 1; i >= iMin; i--)
{
formatInfoBits = copyBit(i, 8, formatInfoBits);
}
for (j = dimension - 7; j < dimension; j++)
{
formatInfoBits = copyBit(8, j, formatInfoBits);
}
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
if (parsedFormatInfo != null) if (parsedFormatInfo != null)
{ {
return parsedFormatInfo; return parsedFormatInfo;
@ -113,7 +106,7 @@ package com.google.zxing.qrcode.decoder
return parsedVersion; return parsedVersion;
} }
var dimension:int = bitMatrix.getDimension(); var dimension:int = bitMatrix.getHeight();
var provisionalVersion:int = (dimension - 17) >> 2; var provisionalVersion:int = (dimension - 17) >> 2;
if (provisionalVersion <= 6) if (provisionalVersion <= 6)
@ -123,34 +116,34 @@ package com.google.zxing.qrcode.decoder
// Read top-right version info: 3 wide by 6 tall // Read top-right version info: 3 wide by 6 tall
var versionBits:int = 0; var versionBits:int = 0;
var jMin:int = dimension - 11;
for (var i:int = 5; i >= 0; i--) for (var i:int = 5; i >= 0; i--)
{ {
var jMin:int = dimension - 11;
for (var j2:int = dimension - 9; j2 >= jMin; j2--) for (var j2:int = dimension - 9; j2 >= jMin; j2--)
{ {
versionBits = copyBit(i, j2, versionBits); versionBits = copyBit(j2, i, versionBits);
} }
} }
parsedVersion = Version.decodeVersionInformation(versionBits); parsedVersion = Version.decodeVersionInformation(versionBits);
if (parsedVersion != null) if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension)
{ {
return parsedVersion; return parsedVersion;
} }
// Hmm, failed. Try bottom left: 6 wide by 3 tall // Hmm, failed. Try bottom left: 6 wide by 3 tall
versionBits = 0; versionBits = 0;
var iMin:int = dimension - 11;
for (var j:int = 5; j >= 0; j--) for (var j:int = 5; j >= 0; j--)
{ {
var iMin:int = dimension - 11; for (var i2:int = dimension - 9; i2 >= iMin; i2--)
for (var i2:int = dimension - 11; i2 >= iMin; i2--)
{ {
versionBits = copyBit(i2, j, versionBits); versionBits = copyBit(j, i2, versionBits);
} }
} }
parsedVersion = Version.decodeVersionInformation(versionBits); parsedVersion = Version.decodeVersionInformation(versionBits);
if (parsedVersion != null) if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension)
{ {
return parsedVersion; return parsedVersion;
} }
@ -159,7 +152,7 @@ package com.google.zxing.qrcode.decoder
private function copyBit(i:int, j:int, versionBits:int):int private function copyBit(i:int, j:int, versionBits:int):int
{ {
return bitMatrix._get(j, i) ? (versionBits << 1) | 0x1 : versionBits << 1; return bitMatrix._get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
} }
/** /**
@ -181,7 +174,7 @@ package com.google.zxing.qrcode.decoder
var dataMask:DataMaskBase; var dataMask:DataMaskBase;
var ref:int = formatInfo.getDataMask(); var ref:int = formatInfo.getDataMask();
dataMask = DataMask.forReference(ref); dataMask = DataMask.forReference(ref);
var dimension:int = bitMatrix.getDimension(); var dimension:int = bitMatrix.getHeight();
dataMask.unmaskBitMatrix(bitMatrix, dimension); dataMask.unmaskBitMatrix(bitMatrix, dimension);
var functionPattern:BitMatrix = version.buildFunctionPattern(); var functionPattern:BitMatrix = version.buildFunctionPattern();

View file

@ -8,7 +8,7 @@ package com.google.zxing.qrcode.decoder
public class DataMask011 extends DataMaskBase public class DataMask011 extends DataMaskBase
{ {
public override function isMasked(i:int, j:int):Boolean { public override function isMasked(i:int, j:int):Boolean {
return (i + j) % 3 == 0; return int((i + j) % 3) == 0;
} }
} }

View file

@ -8,7 +8,7 @@ package com.google.zxing.qrcode.decoder
public class DataMask100 extends DataMaskBase { public class DataMask100 extends DataMaskBase {
public override function isMasked(i:int, j:int):Boolean { public override function isMasked(i:int, j:int):Boolean {
return (((i >>> 1) + (j /3)) & 0x01) == 0; return (((i >>> 1) + (int(j /3))) & 0x01) == 0;
} }
} }

View file

@ -11,7 +11,7 @@ package com.google.zxing.qrcode.decoder
public override function isMasked(i:int, j:int):Boolean public override function isMasked(i:int, j:int):Boolean
{ {
var temp:int = i * j; var temp:int = i * j;
return (temp & 0x01) + (temp % 3) == 0; return (temp & 0x01) + (int(temp % 3)) == 0;
} }
} }

View file

@ -11,7 +11,7 @@ package com.google.zxing.qrcode.decoder
public override function isMasked(i:int, j:int):Boolean public override function isMasked(i:int, j:int):Boolean
{ {
var temp:int = i * j; var temp:int = i * j;
return (((temp & 0x01) + (temp % 3)) & 0x01) == 0; return (((temp & 0x01) + (int(temp % 3))) & 0x01) == 0;
} }
} }

View file

@ -9,7 +9,7 @@ package com.google.zxing.qrcode.decoder
{ {
public override function isMasked(i:int, j:int):Boolean { public override function isMasked(i:int, j:int):Boolean {
return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0; return ((((i + j) & 0x01) + (int((i * j) % 3))) & 0x01) == 0;
} }
} }

View file

@ -15,12 +15,12 @@
*/ */
package com.google.zxing.qrcode.decoder package com.google.zxing.qrcode.decoder
{ {
import com.google.zxing.common.zxingByteArray; import com.google.zxing.FormatException;
import flash.utils.ByteArray;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.BitSource; import com.google.zxing.common.BitSource;
import com.google.zxing.common.CharacterSetECI; import com.google.zxing.common.CharacterSetECI;
import com.google.zxing.common.DecoderResult; import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.flexdatatypes.ArrayList;
import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.flexdatatypes.StringBuilder; import com.google.zxing.common.flexdatatypes.StringBuilder;
@ -52,242 +52,220 @@ package com.google.zxing.qrcode.decoder
* @author Sean Owen * @author Sean Owen
*/ */
private static var ALPHANUMERIC_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 ALPHANUMERIC_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 const SHIFT_JIS:String = "Shift_JIS"; private static const GB2312_SUBSET:int = 1;
private static var ASSUME_SHIFT_JIS:Boolean=true;
private static const EUC_JP:String = "EUC_JP";
private static const UTF8:String = "UTF8";
private static const ISO88591:String = "ISO8859_1";
public function DecodedBitStreamParser() public function DecodedBitStreamParser()
{ {
// var platformDefault:String = System.getProperty("file.encoding");
//ASSUME_SHIFT_JIS = SHIFT_JIS.EqualsIgnoreCase(platformDefault) || EUC_JP.EqualsIgnoreCase(platformDefault);
ASSUME_SHIFT_JIS = true;// BAS : actionscript has no build in method to check the encoding
} }
public static function decode(bytes:Array, version:Version, ecLevel:ErrorCorrectionLevel):DecoderResult
public static function decode(bytes:Array, version:Version, ecLevel:ErrorCorrectionLevel, hints:HashTable):DecoderResult
{ {
var bits:BitSource = new BitSource(bytes); var bits:BitSource = new BitSource(bytes);
var result:StringBuilder = new StringBuilder(); var result:StringBuilder = new StringBuilder(50);
var currentCharacterSetECI:CharacterSetECI = null; var currentCharacterSetECI:CharacterSetECI = null;
var fc1InEffect:Boolean = false; var fc1InEffect:Boolean = false;
var byteSegments:ArrayList = new ArrayList(1); var byteSegments:ArrayList = new ArrayList(1);
var mode:Mode; var mode:Mode;
do do {
{
// While still another segment to read... // While still another segment to read...
if (bits.available() < 4) if (bits.available() < 4) {
{
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
mode = Mode.TERMINATOR; mode = Mode.TERMINATOR;
} else } else {
{ try {
try
{
mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
} } catch (iae:IllegalArgumentException) {
catch (e:IllegalArgumentException) throw FormatException.getFormatInstance();
{
throw new IllegalArgumentException();
} }
} }
if (!mode.Equals(Mode.TERMINATOR)) if (!mode.Equals(Mode.TERMINATOR)) {
{ if (mode.Equals(Mode.FNC1_FIRST_POSITION) || mode.Equals(Mode.FNC1_SECOND_POSITION)) {
if (mode.Equals(Mode.FNC1_FIRST_POSITION) || mode.Equals(Mode.FNC1_SECOND_POSITION))
{
// We do little with FNC1 except alter the parsed result a bit according to the spec // We do little with FNC1 except alter the parsed result a bit according to the spec
fc1InEffect = true; fc1InEffect = true;
} } else if (mode.Equals(Mode.STRUCTURED_APPEND)) {
else if (mode.Equals(Mode.STRUCTURED_APPEND))
{
// not really supported; all we do is ignore it // not really supported; all we do is ignore it
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
bits.readBits(16); bits.readBits(16);
} } else if (mode.Equals(Mode.ECI)) {
else if (mode.Equals(Mode.ECI))
{
// Count doesn't apply to ECI // Count doesn't apply to ECI
var value:int = parseECIValue(bits); var value:int = parseECIValue(bits);
currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value); currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);
if (currentCharacterSetECI == null) if (currentCharacterSetECI == null) {
{ throw FormatException.getFormatInstance();
throw new ReaderException();
} }
} else {
// First handle Hanzi mode which does not start with character count
if (mode.Equals(Mode.HANZI)) {
//chinese mode contains a sub set indicator right after mode indicator
var subset:int = bits.readBits(4);
var countHanzi:int = bits.readBits(mode.getCharacterCountBits(version));
if (subset == GB2312_SUBSET) {
decodeHanziSegment(bits, result, countHanzi);
} }
else } else {
{ // "Normal" QR code modes:
// How many characters will follow, encoded in this mode? // How many characters will follow, encoded in this mode?
var count:int = bits.readBits(mode.getCharacterCountBits(version)); var count:int = bits.readBits(mode.getCharacterCountBits(version));
if (mode.Equals(Mode.NUMERIC)) if (mode.Equals(Mode.NUMERIC)) {
{
decodeNumericSegment(bits, result, count); decodeNumericSegment(bits, result, count);
} } else if (mode.Equals(Mode.ALPHANUMERIC)) {
else if (mode.Equals(Mode.ALPHANUMERIC))
{
decodeAlphanumericSegment(bits, result, count, fc1InEffect); decodeAlphanumericSegment(bits, result, count, fc1InEffect);
} } else if (mode.Equals(Mode.BYTE)) {
else if (mode.Equals(Mode.BYTE)) decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints);
{ } else if (mode.Equals(Mode.KANJI)) {
decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments);
}
else if (mode.Equals(Mode.KANJI))
{
decodeKanjiSegment(bits, result, count); decodeKanjiSegment(bits, result, count);
} else {
throw FormatException.getFormatInstance();
} }
else
{
throw new ReaderException();
} }
} }
} }
} while (!mode.Equals(Mode.TERMINATOR)); } while (!mode.Equals(Mode.TERMINATOR));
return new DecoderResult(bytes, result.toString(), byteSegments.isEmpty() ? null : byteSegments, ecLevel); return new DecoderResult(bytes,
result.toString(),
byteSegments.isEmpty() ? null : byteSegments,
ecLevel == null ? null : ecLevel.toString());
}
/**
* See specification GBT 18284-2000
*/
private static function decodeHanziSegment(bits:BitSource,
result:StringBuilder,
count:int):void {
// Don't crash trying to read more bits than we have available.
if (count * 13 > bits.available()) {
throw FormatException.getFormatInstance();
}
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as GB2312 afterwards
var buffer:Array = new Array(2 * count);
var offset:int = 0;
while (count > 0) {
// Each 13 bits encodes a 2-byte character
var twoBytes:int = bits.readBits(13);
var assembledTwoBytes:int = (int(twoBytes / 0x060) << 8) | int(twoBytes % 0x060);
if (assembledTwoBytes < 0x003BF) {
// In the 0xA1A1 to 0xAAFE range
assembledTwoBytes += 0x0A1A1;
} else {
// In the 0xB0A1 to 0xFAFE range
assembledTwoBytes += 0x0A6A1;
}
buffer[offset] = int(((assembledTwoBytes >> 8) & 0xFF));
buffer[offset + 1] = int(assembledTwoBytes & 0xFF);
offset += 2;
count--;
}
//try {
//result.Append(new String(buffer, StringUtils.GB2312));
result.Append(buffer);
//} catch (uee:UnsupportedEncodingException) {
// throw FormatException.getFormatInstance();
// }
}
private static function decodeKanjiSegment(bits:BitSource,
result:StringBuilder,
count:int):void {
// Don't crash trying to read more bits than we have available.
if (count * 13 > bits.available()) {
throw FormatException.getFormatInstance();
} }
private static function decodeKanjiSegment(bits:BitSource, result:StringBuilder, count:int):void
{
// Each character will require 2 bytes. Read the characters as 2-byte pairs // Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as Shift_JIS afterwards // and decode as Shift_JIS afterwards
var buffer:Array = new Array(2 * count); var buffer:Array = new Array(2 * count);
var offset:int = 0; var offset:int = 0;
while (count > 0) while (count > 0) {
{
// Each 13 bits encodes a 2-byte character // Each 13 bits encodes a 2-byte character
var twoBytes:int = bits.readBits(13); var twoBytes:int = bits.readBits(13);
var assembledTwoBytes:int = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); var assembledTwoBytes:int = (int(twoBytes / 0x0C0) << 8) | int(twoBytes % 0x0C0);
if (assembledTwoBytes < 0x01F00) if (assembledTwoBytes < 0x01F00) {
{
// In the 0x8140 to 0x9FFC range // In the 0x8140 to 0x9FFC range
assembledTwoBytes += 0x08140; assembledTwoBytes += 0x08140;
} } else {
else
{
// In the 0xE040 to 0xEBBF range // In the 0xE040 to 0xEBBF range
assembledTwoBytes += 0x0C140; assembledTwoBytes += 0x0C140;
} }
buffer[offset] = assembledTwoBytes >> 8; buffer[offset] = int(assembledTwoBytes >> 8);
buffer[offset + 1] = assembledTwoBytes; buffer[offset + 1] = int( assembledTwoBytes);
offset += 2; offset += 2;
count--; count--;
} }
// Shift_JIS may not be supported in some environments: // Shift_JIS may not be supported in some environments:
try //try {
{ ///result.Append(new String(buffer, StringUtils.SHIFT_JIS));
// var bytes:Array = SupportClass.ToByteArray(buffer); result.Append(buffer);
//var bytes:Array = new Array(buffer.length); //} catch (uee:UnsupportedEncodingException) {
//for (var i:int=0;i<buffer.length;i++) { bytes[i] = buffer[i]; } // throw FormatException.getFormatInstance();
//UPGRADE_TODO: The differences in the Format of parameters for constructor 'java.lang.String.String' may cause compilation errors. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1092'" //}
//result.Append(System.Text.Encoding.GetEncoding("Shift_JIS").GetString(bytes, 0, bytes.length)); }
var ba:ByteArray = new ByteArray();
for (var k:int=0;k<buffer.length;k++) { ba.writeByte(buffer[k]); } private static function decodeByteSegment(bits:BitSource,
ba.position = 0; result:StringBuilder,
result.Append(ba.readMultiByte(ba.length,"shift-jis")); count:int,
currentCharacterSetECI:CharacterSetECI,
} byteSegments:ArrayList,
catch (uee:Error) hints:HashTable):void {
{ // Don't crash trying to read more bits than we have available.
throw new ReaderException("SHIFT_JIS encoding is not supported on this device"); if (count << 3 > bits.available()) {
} throw FormatException.getFormatInstance();
} }
private static function decodeByteSegment(bits:BitSource ,
result:StringBuilder ,
count:int ,
currentCharacterSetECI:CharacterSetECI ,
byteSegments:ArrayList) :void
{
var readBytes:Array = new Array(count); var readBytes:Array = new Array(count);
if (count << 3 > bits.available()) for (var i:int = 0; i < count; i++) {
{ readBytes[i] = String.fromCharCode(bits.readBits(8));
throw new ReaderException("Count too large: " + count);
} }
for (var i:int = 0; i < count; i++)
{
readBytes[i] = bits.readBits(8);
}
// The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
var encoding:String; var encoding:String;
if (currentCharacterSetECI == null) //if (currentCharacterSetECI == null) {
{
// The spec isn't clear on this mode; see // The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming // section 6.4.5: t does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as // upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to // Shift_JIS -- without anything like an ECI designator to
// give a hint. // give a hint.
encoding = guessEncoding(readBytes); //encoding = StringUtils.guessEncoding(readBytes, hints);
} //} else {
else // encoding = currentCharacterSetECI.getEncodingName();
{ // }
encoding = currentCharacterSetECI.getEncodingName(); //try {
}
try
{
//result.Append(new String(readBytes, encoding)); //result.Append(new String(readBytes, encoding));
// BAS : todo : encoding not supported in AS
// convert bytes to string
//private static const SHIFT_JIS:String = "Shift_JIS";
//private static var ASSUME_SHIFT_JIS:Boolean=true;
//private static const EUC_JP:String = "EUC_JP";
//private static const UTF8:String = "UTF8";
//private static const ISO88591:String = "ISO8859_1";
if ((encoding == DecodedBitStreamParser.SHIFT_JIS) || result.Append(readBytes);
(encoding == DecodedBitStreamParser.EUC_JP) || //} catch (uce:UnsupportedEncodingException) {
(encoding == DecodedBitStreamParser.UTF8) || // throw FormatException.getFormatInstance();
(encoding == DecodedBitStreamParser.ISO88591)) //}
{
var ba:ByteArray = new ByteArray();
for (var k:int=0;k<readBytes.length;k++) { ba.writeByte(readBytes[k]); }
ba.position = 0;
if (encoding == DecodedBitStreamParser.SHIFT_JIS) { result.Append(ba.readMultiByte(ba.length,"shift-jis")); }
else if (encoding == DecodedBitStreamParser.EUC_JP) { result.Append(ba.readMultiByte(ba.length," euc-jp")); }
else if (encoding == DecodedBitStreamParser.UTF8) { result.Append(ba.readMultiByte(ba.length,"utf-8")); }
else if (encoding == DecodedBitStreamParser.ISO88591) { result.Append(ba.readMultiByte(ba.length,"iso-8859-1")); }
}
else
{
var text:String ="";
for (var i2:int=0;i2<readBytes.length;i2++)
{
text = text + String.fromCharCode(readBytes[i2]);
}
result.Append(text);
}
}
catch (e:Error)
{
throw new ReaderException();
}
byteSegments.addElement(readBytes); byteSegments.addElement(readBytes);
} }
private static function decodeAlphanumericSegment(bits:BitSource , result:StringBuilder, count:int, fc1InEffect:Boolean):void private static function toAlphaNumericChar(value:int):String {
{ if (value >= ALPHANUMERIC_CHARS.length) {
var start:int = result.length; throw FormatException.getFormatInstance();
}
return ALPHANUMERIC_CHARS[value];
}
private static function decodeAlphanumericSegment(bits:BitSource,
result:StringBuilder,
count:int,
fc1InEffect:Boolean):void {
// Read two characters at a time // Read two characters at a time
while (count > 1) var start:int = result.length;
{ while (count > 1) {
var nextTwoCharsBits:int = bits.readBits(11); var nextTwoCharsBits:int = bits.readBits(11);
result.Append(ALPHANUMERIC_CHARS[int(nextTwoCharsBits / 45)]); result.Append(toAlphaNumericChar(int(nextTwoCharsBits / 45)));
result.Append(ALPHANUMERIC_CHARS[int(nextTwoCharsBits % 45)]); result.Append(toAlphaNumericChar(int(nextTwoCharsBits % 45)));
count -= 2; count -= 2;
} }
if (count == 1) if (count == 1) {
{
// special case: one character left // special case: one character left
result.Append(ALPHANUMERIC_CHARS[bits.readBits(6)]); result.Append(toAlphaNumericChar(bits.readBits(6)));
} }
// See section 6.4.8.1, 6.4.8.2 // See section 6.4.8.1, 6.4.8.2
if (fc1InEffect) { if (fc1InEffect) {
@ -306,153 +284,65 @@ package com.google.zxing.qrcode.decoder
} }
} }
private static function decodeNumericSegment(bits:BitSource , result:StringBuilder , count:int ):void private static function decodeNumericSegment(bits:BitSource,
{ result:StringBuilder,
count:int):void {
// Read three digits at a time // Read three digits at a time
while (count >= 3) while (count >= 3) {
{
// Each 10 bits encodes three digits // Each 10 bits encodes three digits
var threeDigitsBits:int = bits.readBits(10); if (bits.available() < 10) {
if (threeDigitsBits >= 1000) throw FormatException.getFormatInstance();
{
throw new ReaderException("Illegal value for 3-digit unit: " + threeDigitsBits);
} }
result.Append(ALPHANUMERIC_CHARS[int(threeDigitsBits / 100)]); var threeDigitsBits:int = bits.readBits(10);
result.Append(ALPHANUMERIC_CHARS[int((threeDigitsBits / 10) % 10)]); if (threeDigitsBits >= 1000) {
result.Append(ALPHANUMERIC_CHARS[int(threeDigitsBits % 10)]); throw FormatException.getFormatInstance();
}
result.Append(toAlphaNumericChar(int(threeDigitsBits / 100)));
result.Append(toAlphaNumericChar(int(int(threeDigitsBits / 10) % 10)));
result.Append(toAlphaNumericChar(int(threeDigitsBits % 10)));
count -= 3; count -= 3;
} }
if (count == 2) if (count == 2) {
{
// Two digits left over to read, encoded in 7 bits // Two digits left over to read, encoded in 7 bits
if (bits.available() < 7) {
throw FormatException.getFormatInstance();
}
var twoDigitsBits:int = bits.readBits(7); var twoDigitsBits:int = bits.readBits(7);
if (twoDigitsBits >= 100) if (twoDigitsBits >= 100) {
{ throw FormatException.getFormatInstance();
throw new ReaderException("Illegal value for 2-digit unit: " + twoDigitsBits);
} }
result.Append(ALPHANUMERIC_CHARS[int(twoDigitsBits / 10)]); result.Append(toAlphaNumericChar(int(twoDigitsBits / 10)));
result.Append(ALPHANUMERIC_CHARS[int(twoDigitsBits % 10)]); result.Append(toAlphaNumericChar(int(twoDigitsBits % 10)));
} } else if (count == 1) {
else if (count == 1)
{
// One digit left over to read // One digit left over to read
if (bits.available() < 4) {
throw FormatException.getFormatInstance();
}
var digitBits:int = bits.readBits(4); var digitBits:int = bits.readBits(4);
if (digitBits >= 10) if (digitBits >= 10) {
{ throw FormatException.getFormatInstance();
throw new ReaderException("Illegal value for digit unit: " + digitBits);
} }
result.Append(ALPHANUMERIC_CHARS[digitBits]); result.Append(toAlphaNumericChar(digitBits));
} }
} }
private static function guessEncoding(bytes:Array):String
{
if (ASSUME_SHIFT_JIS) {
return SHIFT_JIS;
}
// Does it start with the UTF-8 byte order mark? then guess it's UTF-8
if (bytes.length > 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
return UTF8;
}
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
// which should be by far the most common encodings. ISO-8859-1
// should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
// uses this as a first byte of a two-byte character. If we see this
// followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
// If we see something else in that second byte, we'll make the risky guess
// that it's UTF-8.
var length:int = bytes.length;
var canBeISO88591:Boolean = true;
var canBeShiftJIS:Boolean = true;
var maybeDoubleByteCount:int = 0;
var maybeSingleByteKatakanaCount:int = 0;
var sawLatin1Supplement:Boolean = false;
var lastWasPossibleDoubleByteStart:Boolean = false;
for (var i:int = 0; i < length && (canBeISO88591 || canBeShiftJIS); i++) {
var value:int = bytes[i] & 0xFF;
if ((value == 0xC2 || value == 0xC3) && i < length - 1) {
// This is really a poor hack. The slightly more exotic characters people might want to put in
// a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings
// that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF].
var nextValue:int = bytes[i + 1] & 0xFF;
if (nextValue <= 0xBF && ((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) {
sawLatin1Supplement = true;
}
}
if (value >= 0x7F && value <= 0x9F) {
canBeISO88591 = false;
}
if (value >= 0xA1 && value <= 0xDF) {
// count the number of characters that might be a Shift_JIS single-byte Katakana character
if (!lastWasPossibleDoubleByteStart) {
maybeSingleByteKatakanaCount++;
}
}
if (!lastWasPossibleDoubleByteStart && ((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) {
canBeShiftJIS = false;
}
if (((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF))) {
// These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid
// second byte.
if (lastWasPossibleDoubleByteStart) {
// If we just checked this and the last byte for being a valid double-byte
// char, don't check starting on this byte. If this and the last byte
// formed a valid pair, then this shouldn't be checked to see if it starts
// a double byte pair of course.
lastWasPossibleDoubleByteStart = false;
} else {
// ... otherwise do check to see if this plus the next byte form a valid
// double byte pair encoding a character.
lastWasPossibleDoubleByteStart = true;
if (i >= bytes.length - 1) {
canBeShiftJIS = false;
} else {
var nextValue2:int = bytes[i + 1] & 0xFF;
if (nextValue2 < 0x40 || nextValue2 > 0xFC) {
canBeShiftJIS = false;
} else {
maybeDoubleByteCount++;
}
// There is some conflicting information out there about which bytes can follow which in
// double-byte Shift_JIS characters. The rule above seems to be the one that matches practice.
}
}
} else {
lastWasPossibleDoubleByteStart = false;
}
}
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is:
// - If we saw
// - at least three byte that starts a double-byte value (bytes that are rare in ISO-8859-1), or
// - over 5% of bytes that could be single-byte Katakana (also rare in ISO-8859-1),
// - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS
if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) {
return SHIFT_JIS;
}
// Otherwise, we default to ISO-8859-1 unless we know it can't be
if (!sawLatin1Supplement && canBeISO88591) {
return ISO88591;
}
// Otherwise, we take a wild guess with UTF-8
return UTF8;
}
private static function parseECIValue(bits:BitSource):int { private static function parseECIValue(bits:BitSource):int {
var firstByte:int = bits.readBits(8); var firstByte:int = bits.readBits(8);
if ((firstByte & 0x80) == 0) { if ((firstByte & 0x80) == 0) {
// just one byte // just one byte
return firstByte & 0x7F; return firstByte & 0x7F;
} else if ((firstByte & 0xC0) == 0x80) { }
if ((firstByte & 0xC0) == 0x80) {
// two bytes // two bytes
var secondByte:int = bits.readBits(8); var secondByte:int = bits.readBits(8);
return ((firstByte & 0x3F) << 8) | secondByte; return ((firstByte & 0x3F) << 8) | secondByte;
} else if ((firstByte & 0xE0) == 0xC0) { }
if ((firstByte & 0xE0) == 0xC0) {
// three bytes // three bytes
var secondThirdBytes:int = bits.readBits(16); var secondThirdBytes:int = bits.readBits(16);
return ((firstByte & 0x1F) << 16) | secondThirdBytes; return ((firstByte & 0x1F) << 16) | secondThirdBytes;
} }
throw new Error("Bad ECI bits starting with byte " + firstByte); throw new IllegalArgumentException("Bad ECI bits starting with byte " + firstByte);
} }
} }

View file

@ -15,6 +15,8 @@
*/ */
package com.google.zxing.qrcode.decoder package com.google.zxing.qrcode.decoder
{ {
import com.google.zxing.common.flexdatatypes.HashTable;
public class Decoder public class Decoder
{ {
@ -22,7 +24,7 @@ package com.google.zxing.qrcode.decoder
import com.google.zxing.common.DecoderResult; import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
import com.google.zxing.common.reedsolomon.ReedSolomonException; import com.google.zxing.common.reedsolomon.ReedSolomonException;
import com.google.zxing.common.reedsolomon.GF256; import com.google.zxing.common.reedsolomon.GenericGF;
import com.google.zxing.common.zxingByteArray; import com.google.zxing.common.zxingByteArray;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
@ -36,7 +38,7 @@ package com.google.zxing.qrcode.decoder
private var rsDecoder:ReedSolomonDecoder; private var rsDecoder:ReedSolomonDecoder;
public function Decoder() { public function Decoder() {
rsDecoder = new ReedSolomonDecoder(GF256.QR_CODE_FIELD); rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256);
} }
/** /**
@ -48,14 +50,14 @@ package com.google.zxing.qrcode.decoder
* @throws ReaderException if the QR Code cannot be decoded * @throws ReaderException if the QR Code cannot be decoded
*/ */
public function decode(image:Object):DecoderResult public function decode(image:Object,hints:HashTable=null):DecoderResult
{ {
if (image is Array) { return decode_Array(image as Array);} if (image is Array) { return decode_Array(image as Array,hints);}
else if (image is BitMatrix) { return decode_BitMatrix(image as BitMatrix);} else if (image is BitMatrix) { return decode_BitMatrix(image as BitMatrix,hints);}
else { throw new Error('Decoder : decode : unknown type of image');} else { throw new Error('Decoder : decode : unknown type of image');}
} }
public function decode_Array(image:Array):DecoderResult { public function decode_Array(image:Array, hints:HashTable):DecoderResult {
var dimension:int = image.length; var dimension:int = image.length;
var bits:BitMatrix = new BitMatrix(dimension); var bits:BitMatrix = new BitMatrix(dimension);
for (var i:int = 0; i < dimension; i++) { for (var i:int = 0; i < dimension; i++) {
@ -65,7 +67,7 @@ package com.google.zxing.qrcode.decoder
} }
} }
} }
return decode(bits); return decode(bits,hints);
} }
/** /**
@ -75,7 +77,7 @@ package com.google.zxing.qrcode.decoder
* @return text and bytes encoded within the QR Code * @return text and bytes encoded within the QR Code
* @throws ReaderException if the QR Code cannot be decoded * @throws ReaderException if the QR Code cannot be decoded
*/ */
public function decode_BitMatrix(bits:BitMatrix ):DecoderResult{ public function decode_BitMatrix(bits:BitMatrix, hints:HashTable ):DecoderResult{
try{ try{
// Construct a parser and read version, error-correction level // Construct a parser and read version, error-correction level
var parser:BitMatrixParser = new BitMatrixParser(bits); var parser:BitMatrixParser = new BitMatrixParser(bits);
@ -109,7 +111,7 @@ package com.google.zxing.qrcode.decoder
} }
// Decode the contents of that stream of bytes // Decode the contents of that stream of bytes
return DecodedBitStreamParser.decode(resultBytes, version,ecLevel); return DecodedBitStreamParser.decode(resultBytes, version,ecLevel,hints);
}catch(e:ReedSolomonException){ }catch(e:ReedSolomonException){
throw new ReaderException(e.message); throw new ReaderException(e.message);
} }
@ -146,9 +148,9 @@ package com.google.zxing.qrcode.decoder
// We don't care about errors in the error-correction codewords // We don't care about errors in the error-correction codewords
for (var i3:int = 0; i3 < numDataCodewords; i3++) for (var i3:int = 0; i3 < numDataCodewords; i3++)
{ {
codewordBytes[i3] = int(codewordsInts[i3]); codewordBytes[i3] = Math.floor(codewordsInts[i3]);
// Flex : make bytes // Flex : make bytes
if (codewordBytes[i3] > 127) { codewordBytes[i3] = (256-codewordBytes[i3])*-1; } if (codewordBytes[i3] > 127) { codewordBytes[i3] = (256-(Math.floor(codewordBytes[i3])))*-1; }
} }
} }
} }

View file

@ -7,9 +7,8 @@ package com.google.zxing.qrcode.decoder
*/ */
public class ECB public class ECB
{ {
// bas : made public for debugging protected var count:int;
public var count:int; protected var dataCodewords:int;
public var dataCodewords:int;
public function ECB(count:int, dataCodewords:int) public function ECB(count:int, dataCodewords:int)
{ {

View file

@ -3,9 +3,8 @@ package com.google.zxing.qrcode.decoder
public class ECBlocks public class ECBlocks
{ {
// bas : made public for debugging protected var ecCodewordsPerBlock:int;
public var ecCodewordsPerBlock:int; protected var ecBlocks:Array;
public var ecBlocks:Array;
//public function ECBlocks(ecCodewordsPerBlock, ECB ecBlocks) //public function ECBlocks(ecCodewordsPerBlock, ECB ecBlocks)
//{ //{

View file

@ -38,10 +38,9 @@ package com.google.zxing.qrcode.decoder
public static var H:ErrorCorrectionLevel = new ErrorCorrectionLevel(3, 0x02, "H"); public static var H:ErrorCorrectionLevel = new ErrorCorrectionLevel(3, 0x02, "H");
private static var FOR_BITS:Array = [M, L, H, Q]; private static var FOR_BITS:Array = [M, L, H, Q];
protected var Ordinal:int;
private var Ordinal:int; protected var bits:int;
private var bits:int; protected var name:String;
private var name:String;
public function ErrorCorrectionLevel(ordinal:int, bits:int, name:String) { public function ErrorCorrectionLevel(ordinal:int, bits:int, name:String) {
this.Ordinal = ordinal; this.Ordinal = ordinal;

View file

@ -96,38 +96,47 @@ package com.google.zxing.qrcode.decoder
* @param rawFormatInfo * @param rawFormatInfo
* @return * @return
*/ */
public static function decodeFormatInformation(rawFormatInfo:int):FormatInformation public static function decodeFormatInformation(maskedFormatInfo1:int, maskedFormatInfo2:int):FormatInformation
{
var formatInfo:FormatInformation = doDecodeFormatInformation(rawFormatInfo);
if (formatInfo != null)
{ {
var formatInfo:FormatInformation = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
if (formatInfo != null) {
return formatInfo; return formatInfo;
} }
// Should return null, but, some QR codes apparently // Should return null, but, some QR codes apparently
// do not mask this info. Try again, first masking the raw bits so // do not mask this info. Try again by actually masking the pattern
// the function will unmask // first
return doDecodeFormatInformation(rawFormatInfo ^ FORMAT_INFO_MASK_QR); return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
} }
private static function doDecodeFormatInformation(rawFormatInfo:int):FormatInformation { private static function doDecodeFormatInformation(maskedFormatInfo1:int, maskedFormatInfo2:int):FormatInformation
// Unmask: {
var unmaskedFormatInfo:int = rawFormatInfo ^ FORMAT_INFO_MASK_QR;
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
var bestDifference:int = int.MAX_VALUE; var bestDifference:int = int.MAX_VALUE;
var bestFormatInfo:int = 0; var bestFormatInfo:int = 0;
for (var i:int = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) { for (var i:int = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
var decodeInfo:Array = FORMAT_INFO_DECODE_LOOKUP[i]; var decodeInfo:Array = FORMAT_INFO_DECODE_LOOKUP[i];
var targetInfo:int = decodeInfo[0]; var targetInfo:int = decodeInfo[0];
if (targetInfo == unmaskedFormatInfo) { if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
// Found an exact match // Found an exact match
return new FormatInformation(decodeInfo[1]); return new FormatInformation(decodeInfo[1]);
} }
var bitsDifference:int = numBitsDiffering(unmaskedFormatInfo, targetInfo); var bitsDifference:int = numBitsDiffering(maskedFormatInfo1, targetInfo);
if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1];
bestDifference = bitsDifference;
}
if (maskedFormatInfo1 != maskedFormatInfo2) {
// also try the other option
bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
if (bitsDifference < bestDifference) { if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1]; bestFormatInfo = decodeInfo[1];
bestDifference = bitsDifference; bestDifference = bitsDifference;
} }
} }
}
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
// differing means we found a match
if (bestDifference <= 3) { if (bestDifference <= 3) {
return new FormatInformation(bestFormatInfo); return new FormatInformation(bestFormatInfo);
} }

View file

@ -37,10 +37,12 @@ package com.google.zxing.qrcode.decoder
public static var KANJI:Mode = new Mode([8, 10, 12], 0x08, "KANJI"); public static var KANJI:Mode = new Mode([8, 10, 12], 0x08, "KANJI");
public static var FNC1_FIRST_POSITION:Mode = new Mode(null, 0x05, "FNC1_FIRST_POSITION"); public static var FNC1_FIRST_POSITION:Mode = new Mode(null, 0x05, "FNC1_FIRST_POSITION");
public static var FNC1_SECOND_POSITION:Mode = new Mode(null, 0x09, "FNC1_SECOND_POSITION"); public static var FNC1_SECOND_POSITION:Mode = new Mode(null, 0x09, "FNC1_SECOND_POSITION");
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
public static var HANZI:Mode = new Mode([8, 10, 12], 0x0D, "HANZI");
private var characterCountBitsForVersions:Array; protected var characterCountBitsForVersions:Array;
private var bits:int; protected var bits:int;
private var name:String; protected var name:String;
public function Mode(characterCountBitsForVersions:Array, bits:int, name:String) public function Mode(characterCountBitsForVersions:Array, bits:int, name:String)
{ {
@ -74,6 +76,9 @@ package com.google.zxing.qrcode.decoder
return KANJI; return KANJI;
case 0x9: case 0x9:
return FNC1_SECOND_POSITION; return FNC1_SECOND_POSITION;
case 0xD:
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
return HANZI;
default: default:
throw new IllegalArgumentException("Mode : forBits : bits does not match any format : "+bits); throw new IllegalArgumentException("Mode : forBits : bits does not match any format : "+bits);
} }

View file

@ -43,11 +43,10 @@ package com.google.zxing.qrcode.decoder
]; ];
private static var VERSIONS:Array = buildVersions(); private static var VERSIONS:Array = buildVersions();
protected var versionNumber:int;
private var versionNumber:int; protected var alignmentPatternCenters:Array;
private var alignmentPatternCenters:Array; protected var ecBlocks:Array;
private var ecBlocks:Array; protected var totalCodewords:int;
private var totalCodewords:int;
public function Version(versionNumber:int, public function Version(versionNumber:int,
alignmentPatternCenters:Array, alignmentPatternCenters:Array,
@ -92,9 +91,7 @@ package com.google.zxing.qrcode.decoder
public function getECBlocksForLevel(ecLevel:ErrorCorrectionLevel):ECBlocks public function getECBlocksForLevel(ecLevel:ErrorCorrectionLevel):ECBlocks
{ {
var ordinal:int = ecLevel.ordinal(); return ecBlocks[ecLevel.ordinal()];
var result:ECBlocks = ecBlocks[ordinal]
return result;
} }
/** /**
@ -163,9 +160,9 @@ package com.google.zxing.qrcode.decoder
// Top left finder pattern + separator + format // Top left finder pattern + separator + format
bitMatrix.setRegion(0, 0, 9, 9); bitMatrix.setRegion(0, 0, 9, 9);
// Top right finder pattern + separator + format // Top right finder pattern + separator + format
bitMatrix.setRegion(0, dimension - 8, 9, 8);
// Bottom left finder pattern + separator + format
bitMatrix.setRegion(dimension - 8, 0, 8, 9); bitMatrix.setRegion(dimension - 8, 0, 8, 9);
// Bottom left finder pattern + separator + format
bitMatrix.setRegion(0, dimension - 8, 9, 8); // bas : hier zat een fout
// Alignment patterns // Alignment patterns
var max:int = alignmentPatternCenters.length; var max:int = alignmentPatternCenters.length;
@ -178,20 +175,20 @@ package com.google.zxing.qrcode.decoder
// No alignment patterns near the three finder paterns // No alignment patterns near the three finder paterns
continue; continue;
} }
bitMatrix.setRegion(i, alignmentPatternCenters[y] - 2, 5, 5); bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5);
} }
} }
// Vertical timing pattern // Vertical timing pattern
bitMatrix.setRegion(9, 6, dimension - 17, 1); bitMatrix.setRegion(6, 9, 1,dimension - 17);
// Horizontal timing pattern // Horizontal timing pattern
bitMatrix.setRegion(6, 9, 1, dimension - 17); bitMatrix.setRegion(9, 6, dimension - 17,1);
if (versionNumber > 6) { if (versionNumber > 6) {
// Version info, top right // Version info, top right
bitMatrix.setRegion(0, dimension - 11, 6, 3); bitMatrix.setRegion(dimension - 11, 0,3,6);
// Version info, bottom left // Version info, bottom left
bitMatrix.setRegion(dimension - 11, 0, 3, 6); bitMatrix.setRegion(0, dimension - 11, 6,3);
} }
return bitMatrix; return bitMatrix;

View file

@ -17,12 +17,29 @@ package com.google.zxing.qrcode.detector
* <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated * <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
* position and size -- meaning, it is at nearly the same center with nearly the same size.</p> * position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
*/ */
public function aboutEquals(moduleSize:Number, i:Number, j:Number):Boolean { public function aboutEquals(moduleSize:Number, i:Number, j:Number):Boolean
return Math.abs(i - getY()) <= moduleSize && {
Math.abs(j - getX()) <= moduleSize && if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize)
(Math.abs(moduleSize - estimatedModuleSize) <= 1 || {
Math.abs(moduleSize - estimatedModuleSize) / estimatedModuleSize <= 0.1); var moduleSizeDiff:Number = Math.abs(moduleSize - estimatedModuleSize);
return moduleSizeDiff <= 1 || moduleSizeDiff <= estimatedModuleSize;
} }
return false;
}
/**
* Combines this object's current estimate of a finder pattern position and module size
* with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
*/
public function combineEstimate(i:Number, j:Number, newModuleSize:Number ):AlignmentPattern {
var combinedX:Number = (getX() + j) / 2;
var combinedY:Number = (getY() + i) / 2;
var combinedModuleSize:Number = (estimatedModuleSize + newModuleSize) / 2;
return new AlignmentPattern(combinedX, combinedY, combinedModuleSize);
}
} }

View file

@ -7,6 +7,8 @@ package com.google.zxing.qrcode.detector
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.ResultPoint;
private var image:BitMatrix; private var image:BitMatrix;
@ -17,6 +19,7 @@ package com.google.zxing.qrcode.detector
private var height:int; private var height:int;
private var moduleSize:Number; private var moduleSize:Number;
private var crossCheckStateCount:Array; private var crossCheckStateCount:Array;
private var resultPointCallback:ResultPointCallback;
/** /**
@ -34,15 +37,17 @@ package com.google.zxing.qrcode.detector
startY:int, startY:int,
width:int, width:int,
height:int, height:int,
moduleSize:Number) { moduleSize:Number,
resultPointCallback:ResultPointCallback) {
this.image = image; this.image = image;
this.possibleCenters = new ArrayList(5); this.possibleCenters = new ArrayList(); // no fixed length : was 5
this.startX = startX; this.startX = startX;
this.startY = startY; this.startY = startY;
this.width = width; this.width = width;
this.height = height; this.height = height;
this.moduleSize = moduleSize; this.moduleSize = moduleSize;
this.crossCheckStateCount = new Array(3); this.crossCheckStateCount = new Array(3);
this.resultPointCallback = resultPointCallback;
} }
/** /**
@ -60,7 +65,9 @@ package com.google.zxing.qrcode.detector
// We are looking for black/white/black modules in 1:1:1 ratio; // We are looking for black/white/black modules in 1:1:1 ratio;
// this tracks the number of black/white/black modules seen so far // this tracks the number of black/white/black modules seen so far
var stateCount:Array = new Array(3); var stateCount:Array = new Array(3);
for (var iGen:int = 0; iGen < height; iGen++) { for (var iGen:int = 0; iGen < height; iGen++)
{
// Search from middle outwards // Search from middle outwards
var i:int = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1)); var i:int = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));
stateCount[0] = 0; stateCount[0] = 0;
@ -76,6 +83,12 @@ package com.google.zxing.qrcode.detector
var currentState:int = 0; var currentState:int = 0;
while (j < maxJ) while (j < maxJ)
{ {
/*
if (i==140 && j==210)
{
var wop:int=0;
}
*/
if (image._get(j,i)) if (image._get(j,i))
{ {
// Black pixel // Black pixel
@ -103,8 +116,7 @@ package com.google.zxing.qrcode.detector
} }
else else
{ {
currentState++; stateCount[++currentState]++;
stateCount[currentState] = stateCount[currentState] + 1;
} }
} }
} }
@ -112,7 +124,7 @@ package com.google.zxing.qrcode.detector
if (currentState == 1) { // Counting black pixels if (currentState == 1) { // Counting black pixels
currentState++; currentState++;
} }
stateCount[currentState] = stateCount[currentState] + 1; stateCount[currentState]++;
} }
j++; j++;
} }
@ -138,8 +150,9 @@ package com.google.zxing.qrcode.detector
* Given a count of black/white/black pixels just seen and an end position, * Given a count of black/white/black pixels just seen and an end position,
* figures the location of the center of this black/white/black run. * figures the location of the center of this black/white/black run.
*/ */
private static function centerFromEnd(stateCount:Array, end:int):Number { private static function centerFromEnd(stateCount:Array, end:int):Number
return Number( (end - stateCount[2]) - stateCount[1] / 2); {
return (end - stateCount[2]) - stateCount[1] / 2;
} }
/** /**
@ -181,7 +194,7 @@ package com.google.zxing.qrcode.detector
// Start counting up from center // Start counting up from center
var i:int = startI; var i:int = startI;
while (i >= 0 && image._get(centerJ, i) && stateCount[1] <= maxCount) { while (i >= 0 && image._get(centerJ, i) && stateCount[1] <= maxCount) {
stateCount[1] = stateCount[1] + 1; stateCount[1]++;
i--; i--;
} }
// If already too many modules in this state or ran off the edge: // If already too many modules in this state or ran off the edge:
@ -199,14 +212,14 @@ package com.google.zxing.qrcode.detector
// Now also count down from center // Now also count down from center
i = startI + 1; i = startI + 1;
while (i < maxI && image._get(centerJ, i) && stateCount[1] <= maxCount) { while (i < maxI && image._get(centerJ, i) && stateCount[1] <= maxCount) {
stateCount[1] = stateCount[1] + 1; stateCount[1]++;
i++; i++;
} }
if (i == maxI || stateCount[1] > maxCount) { if (i == maxI || stateCount[1] > maxCount) {
return Number.NaN; return Number.NaN;
} }
while (i < maxI && !image._get(centerJ, i) && stateCount[2] <= maxCount) { while (i < maxI && !image._get(centerJ, i) && stateCount[2] <= maxCount) {
stateCount[2] = stateCount[2] + 1; stateCount[2]++;
i++; i++;
} }
if (stateCount[2] > maxCount) { if (stateCount[2] > maxCount) {
@ -214,11 +227,11 @@ package com.google.zxing.qrcode.detector
} }
var stateCountTotal:int = stateCount[0] + stateCount[1] + stateCount[2]; var stateCountTotal:int = stateCount[0] + stateCount[1] + stateCount[2];
if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2*originalStateCountTotal) {
return Number.NaN; return Number.NaN;
} }
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NaN; return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Number.NaN;
} }
/** /**
@ -232,7 +245,8 @@ package com.google.zxing.qrcode.detector
* @param j end of possible alignment pattern in row * @param j end of possible alignment pattern in row
* @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
*/ */
private function handlePossibleCenter(stateCount:Array, i:int, j:int):AlignmentPattern { private function handlePossibleCenter(stateCount:Array, i:int, j:int):AlignmentPattern
{
var stateCountTotal:int = stateCount[0] + stateCount[1] + stateCount[2]; var stateCountTotal:int = stateCount[0] + stateCount[1] + stateCount[2];
var centerJ:Number = centerFromEnd(stateCount, j); var centerJ:Number = centerFromEnd(stateCount, j);
var centerI:Number = crossCheckVertical(i, int(centerJ), 2 * stateCount[1], stateCountTotal); var centerI:Number = crossCheckVertical(i, int(centerJ), 2 * stateCount[1], stateCountTotal);
@ -242,21 +256,23 @@ package com.google.zxing.qrcode.detector
var max:int = possibleCenters.Count; var max:int = possibleCenters.Count;
for (var index:int = 0; index < max; index++) for (var index:int = 0; index < max; index++)
{ {
var tmp:Object = possibleCenters.getObjectByIndex(index); var center:AlignmentPattern = (possibleCenters.getObjectByIndex(index) as AlignmentPattern);
if (tmp != null)
{
var center:AlignmentPattern = AlignmentPattern( tmp);
// Look for about the same center and module size: // Look for about the same center and module size:
if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) { if ((center != null) && (center.aboutEquals(estimatedModuleSize, centerI, centerJ))) {
return new AlignmentPattern(centerJ, centerI, estimatedModuleSize); return center.combineEstimate(centerI, centerJ, estimatedModuleSize);
}
} }
} }
// Hadn't found this before; save it // Hadn't found this before; save it
possibleCenters.Add(new AlignmentPattern(centerJ, centerI, estimatedModuleSize)); var point:ResultPoint = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
possibleCenters.addElement(point);
if (resultPointCallback != null)
{
resultPointCallback.foundPossibleResultPoint(point);
} }
return null;s
} }
return null;
}
} }

View file

@ -6,6 +6,8 @@ package com.google.zxing.qrcode.detector
*/ */
public class CenterComparator public class CenterComparator
{ {
public static var average:Number = 0;
/*
public static function compare(center1:Object, center2:Object):int public static function compare(center1:Object, center2:Object):int
{ {
var res:int = FinderPattern(center2).getCount() - (FinderPattern( center1)).getCount(); var res:int = FinderPattern(center2).getCount() - (FinderPattern( center1)).getCount();
@ -22,5 +24,40 @@ package com.google.zxing.qrcode.detector
return 0; return 0;
} }
} }
*/
public static function compare(center1:Object, center2:Object):int
{
if (((center2 as FinderPattern).getCount()) == ((center1 as FinderPattern).getCount()))
{
var dA:Number = Math.abs((center2 as FinderPattern).getEstimatedModuleSize() - CenterComparator.average);
var dB:Number = Math.abs((center1 as FinderPattern).getEstimatedModuleSize() - CenterComparator.average);
return dA < dB ? 1 : dA == dB ? 0 : -1;
}
else
{
return ((center2 as FinderPattern).getCount()) - ((center1 as FinderPattern).getCount());
}
}
public static function setAverage(average:Number):void
{
CenterComparator.average = average;
}
/**
* <p>Orders by furthest from average</p>
*/
/*
private static class FurthestFromAverageComparator implements Comparator {
private final float average;
private FurthestFromAverageComparator(float f) {
average = f;
}
public int compare(Object center1, Object center2) {
float dA = Math.abs(((FinderPattern) center2).getEstimatedModuleSize() - average);
float dB = Math.abs(((FinderPattern) center1).getEstimatedModuleSize() - average);
return dA < dB ? -1 : dA == dB ? 0 : 1;
}
}*/
} }
} }

View file

@ -6,13 +6,19 @@ package com.google.zxing.qrcode.detector
import com.google.zxing.common.flexdatatypes.HashTable; import com.google.zxing.common.flexdatatypes.HashTable;
import com.google.zxing.common.DetectorResult; import com.google.zxing.common.DetectorResult;
import com.google.zxing.common.GridSampler; import com.google.zxing.common.GridSampler;
import com.google.zxing.common.PerspectiveTransform;
import com.google.zxing.qrcode.decoder.Version; import com.google.zxing.qrcode.decoder.Version;
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.DecodeHintType;
import com.google.zxing.NotFoundException;
import com.google.zxing.ResultPointCallback;
private var image:BitMatrix ; private var image:BitMatrix ;
private var resultPointCallback:ResultPointCallback ;
protected function getImage():BitMatrix protected function getImage():BitMatrix
{ {
@ -25,6 +31,11 @@ package com.google.zxing.qrcode.detector
this.image = image; this.image = image;
} }
public function getResultPointCallback():ResultPointCallback
{
return resultPointCallback;
}
/** /**
* <p>Detects a QR Code in an image, simply.</p> * <p>Detects a QR Code in an image, simply.</p>
* *
@ -46,69 +57,18 @@ package com.google.zxing.qrcode.detector
* @return {@link DetectorResult} encapsulating results of detecting a QR Code * @return {@link DetectorResult} encapsulating results of detecting a QR Code
* @throws ReaderException if no QR Code can be found * @throws ReaderException if no QR Code can be found
*/ */
public function detect(hints:HashTable=null):DetectorResult { public function detect(hints:HashTable=null):DetectorResult
{
var resultPointCallback:ResultPointCallback = hints == null ? null : (hints._get(DecodeHintType.NEED_RESULT_POINT_CALLBACK) as ResultPointCallback);
var finder:FinderPatternFinder = new FinderPatternFinder(image,resultPointCallback);
var finder:FinderPatternFinder = new FinderPatternFinder(image);
var info:FinderPatternInfo = finder.find(hints); var info:FinderPatternInfo = finder.find(hints);
var topLeft:FinderPattern = info.getTopLeft(); var result:DetectorResult = processFinderPatternInfo(info);
var topRight:FinderPattern = info.getTopRight(); return result;
var bottomLeft:FinderPattern = info.getBottomLeft();
var moduleSize:Number = calculateModuleSize(topLeft, topRight, bottomLeft);
if (moduleSize < 1) {
throw new ReaderException("Detector : detect : moduleSize < 1");
}
var dimension:int = computeDimension(topLeft, topRight, bottomLeft, moduleSize);
var provisionalVersion:Version = Version.getProvisionalVersionForDimension(dimension);
var modulesBetweenFPCenters:int = provisionalVersion.getDimensionForVersion() - 7;
var alignmentPattern:AlignmentPattern = null;
// Anything above version 1 has an alignment pattern
if (provisionalVersion.getAlignmentPatternCenters().length > 0) {
// Guess where a "bottom right" finder pattern would have been
var bottomRightX:Number = topRight.getX() - topLeft.getX() + bottomLeft.getX();
var bottomRightY:Number = topRight.getY() - topLeft.getY() + bottomLeft.getY();
// Estimate that alignment pattern is closer by 3 modules
// from "bottom right" to known top left location
var correctionToTopLeft:Number = 1 - 3 / Number( modulesBetweenFPCenters);
var estAlignmentX:int = int( (topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX())));
var estAlignmentY:int = int( (topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY())));
// Kind of arbitrary -- expand search radius before giving up
for (var i:int = 4; i <= 16; i <<= 1) {
try {
alignmentPattern = findAlignmentInRegion(moduleSize,
estAlignmentX,
estAlignmentY,
Number(i));
break;
} catch (re:ReaderException) {
// try next round
}
}
if (alignmentPattern == null) {
throw new ReaderException("Detector : detect : alignmentPattern == null");
} }
} public function processFinderPatternInfo(info:FinderPatternInfo ):DetectorResult
var bits:BitMatrix = sampleGrid(image, topLeft, topRight, bottomLeft, alignmentPattern, dimension);
var points:Array;
if (alignmentPattern == null) {
points = [bottomLeft, topLeft, topRight];
} else {
points = [bottomLeft, topLeft, topRight, alignmentPattern];
}
return new DetectorResult(bits, points);
}
protected function processFinderPatternInfo(info:FinderPatternInfo ):DetectorResult
{ {
var topLeft:FinderPattern = info.getTopLeft(); var topLeft:FinderPattern = info.getTopLeft();
@ -145,14 +105,19 @@ package com.google.zxing.qrcode.detector
estAlignmentY, estAlignmentY,
i); i);
break; break;
} catch (re:ReaderException) { }
catch (re:ReaderException)
{
// try next round // try next round
} }
} }
// If we didn't find alignment pattern... well try anyway without it // If we didn't find alignment pattern... well try anyway without it
} }
var bits:BitMatrix = sampleGrid(image, topLeft, topRight, bottomLeft, alignmentPattern, dimension); var transform:PerspectiveTransform =
createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
var bits:BitMatrix = sampleGrid(image, transform, dimension);
var points:Array; var points:Array;
if (alignmentPattern == null) { if (alignmentPattern == null) {
@ -164,49 +129,12 @@ package com.google.zxing.qrcode.detector
} }
private static function sampleGrid(image:BitMatrix ,
private static function sampleGrid(image:BitMatrix, transform:PerspectiveTransform ,
topLeft:ResultPoint, dimension:int ):BitMatrix {
topRight:ResultPoint,
bottomLeft:ResultPoint,
alignmentPattern:ResultPoint,
dimension:int):BitMatrix {
var dimMinusThree:Number = Number( dimension - 3.5);
var bottomRightX:Number;
var bottomRightY:Number;
var sourceBottomRightX:Number;
var sourceBottomRightY:Number;
if (alignmentPattern != null) {
bottomRightX = alignmentPattern.getX();
bottomRightY = alignmentPattern.getY();
sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3;
} else {
// Don't have an alignment pattern, just make up the bottom-right point
bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX();
bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY();
sourceBottomRightX = sourceBottomRightY = dimMinusThree;
}
var sampler:GridSampler = GridSampler.getGridSamplerInstance(); var sampler:GridSampler = GridSampler.getGridSamplerInstance();
return sampler.sampleGrid( return sampler.sampleGrid(image, dimension, dimension, transform);
image,
dimension,
3.5,
3.5,
dimMinusThree,
3.5,
sourceBottomRightX,
sourceBottomRightY,
3.5,
dimMinusThree,
topLeft.getX(),
topLeft.getY(),
topRight.getX(),
topRight.getY(),
bottomRightX,
bottomRightY,
bottomLeft.getX(),
bottomLeft.getY());
} }
/** /**
@ -241,8 +169,10 @@ package com.google.zxing.qrcode.detector
*/ */
private function calculateModuleSize(topLeft:ResultPoint , topRight:ResultPoint , bottomLeft:ResultPoint ):Number { private function calculateModuleSize(topLeft:ResultPoint , topRight:ResultPoint , bottomLeft:ResultPoint ):Number {
// Take the average // Take the average
return (calculateModuleSizeOneWay(topLeft, topRight) + var num1:Number = calculateModuleSizeOneWay(topLeft, topRight);
calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2; var num2:Number = calculateModuleSizeOneWay(topLeft, bottomLeft);
var res:Number = (num1+num2) / 2;
return res;
} }
/** /**
@ -261,11 +191,11 @@ package com.google.zxing.qrcode.detector
int( pattern.getY())); int( pattern.getY()));
if (isNaN(moduleSizeEst1)) { if (isNaN(moduleSizeEst1)) {
return moduleSizeEst2; return moduleSizeEst2 / 7;
} }
if (isNaN(moduleSizeEst2)) if (isNaN(moduleSizeEst2))
{ {
return moduleSizeEst1; return moduleSizeEst1 / 7;
} }
// Average them, and divide by 7 since we've counted the width of 3 black modules, // Average them, and divide by 7 since we've counted the width of 3 black modules,
// and 1 white and 1 black module on either side. Ergo, divide sum by 14. // and 1 white and 1 black module on either side. Ergo, divide sum by 14.
@ -282,20 +212,29 @@ package com.google.zxing.qrcode.detector
var result:Number = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); var result:Number = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
// Now count other way -- don't run off image though of course // Now count other way -- don't run off image though of course
var scale:Number = 1;
var otherToX:int = fromX - (toX - fromX); var otherToX:int = fromX - (toX - fromX);
if (otherToX < 0) { if (otherToX < 0) {
// "to" should the be the first value not included, so, the first value off // "to" should the be the first value not included, so, the first value off
// the edge is -1 // the edge is -1
otherToX = -1; scale = fromX / (fromX - otherToX);
otherToX = 0;
} else if (otherToX >= image.getWidth()) { } else if (otherToX >= image.getWidth()) {
otherToX = image.getWidth(); scale = (image.getWidth() - 1 - fromX) / (otherToX - fromX);
otherToX = image.getWidth() - 1;
} }
var otherToY:int = fromY - (toY - fromY); var otherToY:int = int(fromY - (toY - fromY) * scale);
scale = 1;
if (otherToY < 0) { if (otherToY < 0) {
otherToY = -1; scale = fromY / (fromY - otherToY);
otherToY = 0;
} else if (otherToY >= image.getHeight()) { } else if (otherToY >= image.getHeight()) {
otherToY = image.getHeight(); scale = (image.getHeight() - 1 - fromY) / (otherToY - fromY);
otherToY = image.getHeight() - 1;
} }
otherToX = int(fromX + (otherToX - fromX) * scale);
result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
return result - 1; // -1 because we counted the middle pixel twice return result - 1; // -1 because we counted the middle pixel twice
} }
@ -326,39 +265,51 @@ package com.google.zxing.qrcode.detector
var error:int = -dx >> 1; var error:int = -dx >> 1;
var ystep:int = fromY < toY ? 1 : -1; var ystep:int = fromY < toY ? 1 : -1;
var xstep:int = fromX < toX ? 1 : -1; var xstep:int = fromX < toX ? 1 : -1;
var state:int = 0; // In black pixels, looking for white, first or second time // In black pixels, looking for white, first or second time.
var diffX:int =0; var state:int = 0;
var diffY:int =0; // Loop up until x == toX, but not beyond
var xLimit:int = toX + xstep;
for (var x:int = fromX, y:int = fromY; x != toX; x += xstep) { for (var x:int = fromX, y:int= fromY; x != xLimit; x += xstep)
{
var realX:int = steep ? y : x; var realX:int = steep ? y : x;
var realY:int = steep ? x : y; var realY:int = steep ? x : y;
if (state == 1) { // In white pixels, looking for black
if (image._get(realX, realY)) { // Does current pixel mean we have moved white to black or vice versa?
state++; var pixl:Boolean = image._get(realX, realY);
if (((state == 0) && !pixl) ||
((state == 1) && pixl) ||
((state == 2) && !pixl))
{
if (state == 2)
{
var diffX:int = x - fromX;
var diffY:int = y - fromY;
return Math.sqrt(diffX * diffX + diffY * diffY);
} }
} else {
if (!image._get(realX, realY)) {
state++; state++;
} }
}
if (state == 3) { // Found black, white, black, and stumbled back onto white; done
diffX = x - fromX;
diffY = y - fromY;
return Number( Math.sqrt(diffX * diffX + diffY * diffY));
}
error += dy; error += dy;
if (error > 0) { if (error > 0)
{
if (y == toY)
{
break;
}
y += ystep; y += ystep;
error -= dx; error -= dx;
} }
} }
// Found black-white-black; give the benefit of the doubt that the next pixel outside the image
diffX = toX - fromX; // is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a
diffY = toY - fromY; // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
return Number( Math.sqrt(diffX * diffX + diffY * diffY)); if (state == 2) {
var diffX1:int = toX + xstep - fromX;
var diffY1:int = toY - fromY;
return Math.sqrt(diffX1 * diffX1 + diffY1 * diffY1);
}
// else we didn't find even black-white-black; no estimate is really possible
return NaN;
} }
/** /**
@ -378,6 +329,7 @@ package com.google.zxing.qrcode.detector
allowanceFactor:Number):AlignmentPattern{ allowanceFactor:Number):AlignmentPattern{
// Look for an alignment pattern (3 modules in size) around where it // Look for an alignment pattern (3 modules in size) around where it
// should be // should be
var allowance:int = int((allowanceFactor * overallEstModuleSize)); var allowance:int = int((allowanceFactor * overallEstModuleSize));
var alignmentAreaLeftX:int = Math.max(0, estAlignmentX - allowance); var alignmentAreaLeftX:int = Math.max(0, estAlignmentX - allowance);
var alignmentAreaRightX:int = Math.min(image.getWidth() - 1, estAlignmentX + allowance); var alignmentAreaRightX:int = Math.min(image.getWidth() - 1, estAlignmentX + allowance);
@ -387,6 +339,10 @@ package com.google.zxing.qrcode.detector
var alignmentAreaTopY:int = Math.max(0, estAlignmentY - allowance); var alignmentAreaTopY:int = Math.max(0, estAlignmentY - allowance);
var alignmentAreaBottomY:int = Math.min(image.getHeight() - 1, estAlignmentY + allowance); var alignmentAreaBottomY:int = Math.min(image.getHeight() - 1, estAlignmentY + allowance);
if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3)
{
throw NotFoundException.getNotFoundInstance();
}
var alignmentFinder:AlignmentPatternFinder = var alignmentFinder:AlignmentPatternFinder =
new AlignmentPatternFinder( new AlignmentPatternFinder(
@ -395,7 +351,8 @@ package com.google.zxing.qrcode.detector
alignmentAreaTopY, alignmentAreaTopY,
alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaRightX - alignmentAreaLeftX,
alignmentAreaBottomY - alignmentAreaTopY, alignmentAreaBottomY - alignmentAreaTopY,
overallEstModuleSize); overallEstModuleSize,
resultPointCallback);
return alignmentFinder.find(); return alignmentFinder.find();
} }
@ -407,5 +364,45 @@ package com.google.zxing.qrcode.detector
return int(d + 0.5); return int(d + 0.5);
} }
public static function createTransform(topLeft:ResultPoint ,
topRight:ResultPoint ,
bottomLeft:ResultPoint ,
alignmentPattern:ResultPoint ,
dimension:int):PerspectiveTransform {
var dimMinusThree:Number = dimension - 3.5;
var bottomRightX:Number;
var bottomRightY:Number;
var sourceBottomRightX:Number;
var sourceBottomRightY:Number;
if (alignmentPattern != null) {
bottomRightX = alignmentPattern.getX();
bottomRightY = alignmentPattern.getY();
sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0;
} else {
// Don't have an alignment pattern, just make up the bottom-right point
bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX();
bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY();
sourceBottomRightX = sourceBottomRightY = dimMinusThree;
}
return PerspectiveTransform.quadrilateralToQuadrilateral(
3.5,
3.5,
dimMinusThree,
3.5,
sourceBottomRightX,
sourceBottomRightY,
3.5,
dimMinusThree,
topLeft.getX(),
topLeft.getY(),
topRight.getX(),
topRight.getY(),
bottomRightX,
bottomRightY,
bottomLeft.getX(),
bottomLeft.getY());
}
} }
} }

View file

@ -4,13 +4,13 @@ package com.google.zxing.qrcode.detector
public class FinderPattern extends ResultPoint public class FinderPattern extends ResultPoint
{ {
private var estimatedModuleSize:Number; protected var estimatedModuleSize:Number;
private var count:int; protected var count:int;
public function FinderPattern(posX:Number, posY:Number, estimatedModuleSize:Number) { public function FinderPattern(posX:Number, posY:Number, estimatedModuleSize:Number, count:int = 1) {
super(posX,posY); super(posX,posY);
this.estimatedModuleSize = estimatedModuleSize; this.estimatedModuleSize = estimatedModuleSize;
this.count = 1; this.count = count;
} }
public function getEstimatedModuleSize():Number public function getEstimatedModuleSize():Number
@ -37,10 +37,26 @@ package com.google.zxing.qrcode.detector
if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize)
{ {
var moduleSizeDiff:Number = Math.abs(moduleSize - estimatedModuleSize); var moduleSizeDiff:Number = Math.abs(moduleSize - estimatedModuleSize);
return moduleSizeDiff <= 1 || moduleSizeDiff / estimatedModuleSize <= 1; var result:Boolean = moduleSizeDiff <= 1 || moduleSizeDiff / estimatedModuleSize <= 1;
return result;
} }
return false; return false;
} }
/**
* Combines this object's current estimate of a finder pattern position and module size
* with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
* based on count.
*/
public function combineEstimate(i:Number,j:Number,newModuleSize:Number):FinderPattern
{
var combinedCount:int = count + 1;
var combinedX:Number = (count * getX() + j) / combinedCount;
var combinedY:Number = (count * getY() + i) / combinedCount;
var combinedModuleSize:Number = (count * getEstimatedModuleSize() + newModuleSize) / combinedCount;
return new FinderPattern(combinedX, combinedY, combinedModuleSize, combinedCount);
}
} }
} }

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