2010-02-05 11:52:53 -08:00
using System ;
2013-01-18 12:14:03 -08:00
using System.Collections ;
using System.Collections.Generic ;
using System.Text ;
/ *
* 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 .
* /
2010-02-05 11:52:53 -08:00
namespace com.google.zxing.oned
{
2013-01-18 12:14:03 -08:00
using BarcodeFormat = com . google . zxing . BarcodeFormat ;
using ChecksumException = com . google . zxing . ChecksumException ;
using DecodeHintType = com . google . zxing . DecodeHintType ;
using FormatException = com . google . zxing . FormatException ;
using NotFoundException = com . google . zxing . NotFoundException ;
using ReaderException = com . google . zxing . ReaderException ;
using Result = com . google . zxing . Result ;
using ResultMetadataType = com . google . zxing . ResultMetadataType ;
using ResultPoint = com . google . zxing . ResultPoint ;
using ResultPointCallback = com . google . zxing . ResultPointCallback ;
using BitArray = com . google . zxing . common . BitArray ;
using com.google.zxing.common ;
public static class ArrayExtensions
{
public static void Fill ( this int [ ] arr , int value )
{
for ( int i = 0 ; i < arr . Length ; i + + )
{
arr . SetValue ( value , i ) ;
}
}
}
/// <summary>
/// <p>Encapsulates functionality and implementation that is common to UPC and EAN families
2010-02-05 11:52:53 -08:00
/// of one-dimensional barcodes.</p>
///
2013-01-18 12:14:03 -08:00
/// @author dswitkin@google.com (Daniel Switkin)
/// @author Sean Owen
/// @author alasdair@google.com (Alasdair Mackintosh)
2010-02-05 11:52:53 -08:00
/// </summary>
2013-01-18 12:14:03 -08:00
public abstract class UPCEANReader : OneDReader
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
// These two values are critical for determining how permissive the decoding will be.
// We've arrived at these values through a lot of trial and error. Setting them any higher
// lets false positives creep in quickly.
private static readonly int MAX_AVG_VARIANCE = ( int ) ( PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.48f ) ;
private static readonly int MAX_INDIVIDUAL_VARIANCE = ( int ) ( PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f ) ;
/// <summary>
/// Start/end guard pattern.
/// </summary>
internal static readonly int [ ] START_END_PATTERN = { 1 , 1 , 1 } ;
/// <summary>
/// Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
/// </summary>
internal static readonly int [ ] MIDDLE_PATTERN = { 1 , 1 , 1 , 1 , 1 } ;
/// <summary>
/// "Odd", or "L" patterns used to encode UPC/EAN digits.
/// </summary>
internal static readonly int [ ] [ ] L_PATTERNS = { new int [ ] { 3 , 2 , 1 , 1 } , new int [ ] { 2 , 2 , 2 , 1 } , new int [ ] { 2 , 1 , 2 , 2 } , new int [ ] { 1 , 4 , 1 , 1 } , new int [ ] { 1 , 1 , 3 , 2 } , new int [ ] { 1 , 2 , 3 , 1 } , new int [ ] { 1 , 1 , 1 , 4 } , new int [ ] { 1 , 3 , 1 , 2 } , new int [ ] { 1 , 2 , 1 , 3 } , new int [ ] { 3 , 1 , 1 , 2 } } ;
/// <summary>
/// As above but also including the "even", or "G" patterns used to encode UPC/EAN digits.
/// </summary>
internal static readonly int [ ] [ ] L_AND_G_PATTERNS ;
static UPCEANReader ( )
{
L_AND_G_PATTERNS = new int [ 20 ] [ ] ;
Array . Copy ( L_PATTERNS , 0 , L_AND_G_PATTERNS , 0 , 10 ) ;
for ( int i = 10 ; i < 20 ; i + + )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
int [ ] widths = L_PATTERNS [ i - 10 ] ;
int [ ] reversedWidths = new int [ widths . Length ] ;
for ( int j = 0 ; j < widths . Length ; j + + )
{
reversedWidths [ j ] = widths [ widths . Length - j - 1 ] ;
}
L_AND_G_PATTERNS [ i ] = reversedWidths ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
}
private readonly StringBuilder decodeRowStringBuffer ;
private readonly UPCEANExtensionSupport extensionReader ;
private readonly EANManufacturerOrgSupport eanManSupport ;
protected internal UPCEANReader ( )
{
decodeRowStringBuffer = new StringBuilder ( 20 ) ;
extensionReader = new UPCEANExtensionSupport ( ) ;
eanManSupport = new EANManufacturerOrgSupport ( ) ;
}
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: static int[] findStartGuardPattern(com.google.zxing.common.BitArray row) throws com.google.zxing.NotFoundException
internal static int [ ] findStartGuardPattern ( BitArray row )
{
bool foundStart = false ;
int [ ] startRange = null ;
int nextStart = 0 ;
int [ ] counters = new int [ START_END_PATTERN . Length ] ;
while ( ! foundStart )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
//Arrays.fill(counters, 0, START_END_PATTERN.Length, 0);
counters . Fill ( 0 ) ;
startRange = findGuardPattern ( row , nextStart , false , START_END_PATTERN , counters ) ;
int start = 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.
int quietStart = start - ( nextStart - start ) ;
if ( quietStart > = 0 )
{
foundStart = row . isRange ( quietStart , start , false ) ;
}
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
return startRange ;
}
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public com.google.zxing.Result decodeRow(int rowNumber, com.google.zxing.common.BitArray row, java.util.Map<com.google.zxing.DecodeHintType,?> hints) throws com.google.zxing.NotFoundException, com.google.zxing.ChecksumException, com.google.zxing.FormatException
public override Result decodeRow ( int rowNumber , BitArray row , IDictionary < DecodeHintType , object > hints )
{
return decodeRow ( rowNumber , row , findStartGuardPattern ( row ) , hints ) ;
}
/// <summary>
/// <p>Like <seealso cref="#decodeRow(int, BitArray, java.util.Map)"/>, but
/// 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>
/// </summary>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public com.google.zxing.Result decodeRow(int rowNumber, com.google.zxing.common.BitArray row, int[] startGuardRange, java.util.Map<com.google.zxing.DecodeHintType,?> hints) throws com.google.zxing.NotFoundException, com.google.zxing.ChecksumException, com.google.zxing.FormatException
public virtual Result decodeRow ( int rowNumber , BitArray row , int [ ] startGuardRange , IDictionary < DecodeHintType , object > hints )
{
//ResultPointCallback resultPointCallback = hints == null ? null : (ResultPointCallback) hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK];
ResultPointCallback resultPointCallback = null ;
if ( hints ! = null & & hints . ContainsKey ( DecodeHintType . NEED_RESULT_POINT_CALLBACK ) )
{
resultPointCallback = ( ResultPointCallback ) hints [ DecodeHintType . NEED_RESULT_POINT_CALLBACK ] ;
}
if ( resultPointCallback ! = null )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
resultPointCallback . foundPossibleResultPoint ( new ResultPoint ( ( startGuardRange [ 0 ] + startGuardRange [ 1 ] ) / 2.0f , rowNumber ) ) ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
StringBuilder result = decodeRowStringBuffer ;
result . Length = 0 ;
int endStart = decodeMiddle ( row , startGuardRange , result ) ;
if ( resultPointCallback ! = null )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
resultPointCallback . foundPossibleResultPoint ( new ResultPoint ( endStart , rowNumber ) ) ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
int [ ] endRange = decodeEnd ( row , endStart ) ;
if ( resultPointCallback ! = null )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
resultPointCallback . foundPossibleResultPoint ( new ResultPoint ( ( endRange [ 0 ] + endRange [ 1 ] ) / 2.0f , rowNumber ) ) ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
// 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.
int end = endRange [ 1 ] ;
int quietEnd = end + ( end - endRange [ 0 ] ) ;
if ( quietEnd > = row . Size | | ! row . isRange ( end , quietEnd , false ) )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
throw NotFoundException . NotFoundInstance ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
string resultString = result . ToString ( ) ;
if ( ! checkChecksum ( resultString ) )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
throw ChecksumException . ChecksumInstance ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
float left = ( float ) ( startGuardRange [ 1 ] + startGuardRange [ 0 ] ) / 2.0f ;
float right = ( float ) ( endRange [ 1 ] + endRange [ 0 ] ) / 2.0f ;
BarcodeFormat format = BarcodeFormat ;
Result decodeResult = new Result ( resultString , null , new ResultPoint [ ] { new ResultPoint ( left , ( float ) rowNumber ) , new ResultPoint ( right , ( float ) rowNumber ) } , format ) ; // no natural byte representation for these barcodes
try
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
Result extensionResult = extensionReader . decodeRow ( rowNumber , row , endRange [ 1 ] ) ;
decodeResult . putMetadata ( ResultMetadataType . UPC_EAN_EXTENSION , extensionResult . Text ) ;
decodeResult . putAllMetadata ( extensionResult . ResultMetadata ) ;
decodeResult . addResultPoints ( extensionResult . ResultPoints ) ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
catch ( ReaderException re )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
// continue
}
if ( format = = BarcodeFormat . EAN_13 | | format = = BarcodeFormat . UPC_A )
{
string countryID = eanManSupport . lookupCountryIdentifier ( resultString ) ;
if ( countryID ! = null )
{
decodeResult . putMetadata ( ResultMetadataType . POSSIBLE_COUNTRY , countryID ) ;
}
}
return decodeResult ;
}
/// <returns> <seealso cref="#checkStandardUPCEANChecksum(string)"/> </returns>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: boolean checkChecksum(String s) throws com.google.zxing.ChecksumException, com.google.zxing.FormatException
protected internal virtual bool checkChecksum ( string s )
{
return checkStandardUPCEANChecksum ( s ) ;
}
/// <summary>
/// Computes the UPC/EAN checksum on a string of digits, and reports
/// whether the checksum is correct or not.
/// </summary>
/// <param name="s"> string of digits to check </param>
/// <returns> true iff string of digits passes the UPC/EAN checksum algorithm </returns>
/// <exception cref="FormatException"> if the string does not contain only digits </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: static boolean checkStandardUPCEANChecksum(CharSequence s) throws com.google.zxing.FormatException
internal static bool checkStandardUPCEANChecksum ( string s )
{
int length = s . Length ;
if ( length = = 0 )
{
return false ;
}
int sum = 0 ;
for ( int i = length - 2 ; i > = 0 ; i - = 2 )
{
int digit = ( int ) s [ i ] - ( int ) '0' ;
if ( digit < 0 | | digit > 9 )
{
throw FormatException . FormatInstance ;
}
sum + = digit ;
}
sum * = 3 ;
for ( int i = length - 1 ; i > = 0 ; i - = 2 )
{
int digit = ( int ) s [ i ] - ( int ) '0' ;
if ( digit < 0 | | digit > 9 )
{
throw FormatException . FormatInstance ;
}
sum + = digit ;
}
return sum % 10 = = 0 ;
}
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: int[] decodeEnd(com.google.zxing.common.BitArray row, int endStart) throws com.google.zxing.NotFoundException
protected internal virtual int [ ] decodeEnd ( BitArray row , int endStart )
{
return findGuardPattern ( row , endStart , false , START_END_PATTERN ) ;
}
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: static int[] findGuardPattern(com.google.zxing.common.BitArray row, int rowOffset, boolean whiteFirst, int[] pattern) throws com.google.zxing.NotFoundException
internal static int [ ] findGuardPattern ( BitArray row , int rowOffset , bool whiteFirst , int [ ] pattern )
{
return findGuardPattern ( row , rowOffset , whiteFirst , pattern , new int [ pattern . Length ] ) ;
}
/// <param name="row"> row of black/white values to search </param>
/// <param name="rowOffset"> position to start search </param>
/// <param name="whiteFirst"> if true, indicates that the pattern specifies white/black/white/...
/// pixel counts, otherwise, it is interpreted as black/white/black/... </param>
/// <param name="pattern"> pattern of counts of number of black and white pixels that are being
/// searched for as a pattern </param>
/// <param name="counters"> array of counters, as long as pattern, to re-use </param>
/// <returns> start/end horizontal offset of guard pattern, as an array of two ints </returns>
/// <exception cref="NotFoundException"> if pattern is not found </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: private static int[] findGuardPattern(com.google.zxing.common.BitArray row, int rowOffset, boolean whiteFirst, int[] pattern, int[] counters) throws com.google.zxing.NotFoundException
private static int [ ] findGuardPattern ( BitArray row , int rowOffset , bool whiteFirst , int [ ] pattern , int [ ] counters )
{
int patternLength = pattern . Length ;
int width = row . Size ;
bool isWhite = whiteFirst ;
rowOffset = whiteFirst ? row . getNextUnset ( rowOffset ) : row . getNextSet ( rowOffset ) ;
int counterPosition = 0 ;
int patternStart = rowOffset ;
for ( int x = rowOffset ; x < width ; x + + )
{
if ( row . get ( x ) ^ isWhite )
{
counters [ counterPosition ] + + ;
}
else
{
if ( counterPosition = = patternLength - 1 )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
if ( patternMatchVariance ( counters , pattern , MAX_INDIVIDUAL_VARIANCE ) < MAX_AVG_VARIANCE )
{
return new int [ ] { patternStart , x } ;
}
patternStart + = counters [ 0 ] + counters [ 1 ] ;
Array . Copy ( counters , 2 , counters , 0 , patternLength - 2 ) ;
counters [ patternLength - 2 ] = 0 ;
counters [ patternLength - 1 ] = 0 ;
counterPosition - - ;
2010-02-05 11:52:53 -08:00
}
else
{
2013-01-18 12:14:03 -08:00
counterPosition + + ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
counters [ counterPosition ] = 1 ;
isWhite = ! isWhite ;
}
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
throw NotFoundException . NotFoundInstance ;
}
/// <summary>
/// Attempts to decode a single UPC/EAN-encoded digit.
/// </summary>
/// <param name="row"> row of black/white values to decode </param>
/// <param name="counters"> the counts of runs of observed black/white/black/... values </param>
/// <param name="rowOffset"> horizontal offset to start decoding from </param>
/// <param name="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 </param>
/// <returns> horizontal offset of first pixel beyond the decoded digit </returns>
/// <exception cref="NotFoundException"> if digit cannot be decoded </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: static int decodeDigit(com.google.zxing.common.BitArray row, int[] counters, int rowOffset, int[][] patterns) throws com.google.zxing.NotFoundException
internal static int decodeDigit ( BitArray row , int [ ] counters , int rowOffset , int [ ] [ ] patterns )
{
recordPattern ( row , rowOffset , counters ) ;
int bestVariance = MAX_AVG_VARIANCE ; // worst variance we'll accept
int bestMatch = - 1 ;
int max = patterns . Length ;
for ( int i = 0 ; i < max ; i + + )
2010-02-05 11:52:53 -08:00
{
2013-01-18 12:14:03 -08:00
int [ ] pattern = patterns [ i ] ;
int variance = patternMatchVariance ( counters , pattern , MAX_INDIVIDUAL_VARIANCE ) ;
if ( variance < bestVariance )
{
bestVariance = variance ;
bestMatch = i ;
}
}
if ( bestMatch > = 0 )
{
return bestMatch ;
}
else
{
throw NotFoundException . NotFoundInstance ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
}
/// <summary>
/// Get the format of this decoder.
/// </summary>
/// <returns> The 1D format. </returns>
internal abstract BarcodeFormat BarcodeFormat { get ; }
/// <summary>
/// Subclasses override this to decode the portion of a barcode between the start
/// and end guard patterns.
/// </summary>
/// <param name="row"> row of black/white values to search </param>
/// <param name="startRange"> start/end offset of start guard pattern </param>
/// <param name="resultString"> <seealso cref="StringBuilder"/> to append decoded chars to </param>
/// <returns> horizontal offset of first pixel after the "middle" that was decoded </returns>
/// <exception cref="NotFoundException"> if decoding could not complete successfully </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: protected abstract int decodeMiddle(com.google.zxing.common.BitArray row, int[] startRange, StringBuilder resultString) throws com.google.zxing.NotFoundException;
protected internal abstract int decodeMiddle ( BitArray row , int [ ] startRange , StringBuilder resultString ) ;
2010-02-05 11:52:53 -08:00
}
2013-01-18 12:14:03 -08:00
2010-02-05 11:52:53 -08:00
}