import FormatException from '../../FormatException' ; import CharacterSetECI from '../../common/CharacterSetECI' ; import DecoderResult from '../../common/DecoderResult' ; import PDF417ResultMetadata from '../PDF417ResultMetadata' ; import Arrays from '../../util/Arrays' ; import StringBuilder from '../../util/StringBuilder' ; import Integer from '../../util/Integer' ; import Long from '../../util/Long' ; import ByteArrayOutputStream from '../../util/ByteArrayOutputStream' ; import StringEncoding from '../../util/StringEncoding' ; import { int } from '../../../customTypings' ; enum Mode { ALPHA , LOWER , MIXED , PUNCT , ALPHA_SHIFT , PUNCT_SHIFT } /**
 * Indirectly access the global BigInt constructor, it
 * allows browsers that doesn't support BigInt to run
 * the library without breaking due to "undefined BigInt"
 * errors.
 */ function getBigIntConstructor ( ) { if ( typeof window !== 'undefined' ) { return window [ 'BigInt' ] || null ; } if ( typeof global !== 'undefined' ) { return global [ 'BigInt' ] || null ; } if ( typeof self !== 'undefined' ) { return self [ 'BigInt' ] || null ; } throw new Error ( 'Can\'t search globals for BigInt!' ) ; } /**
 * Used to store the BigInt constructor.
 */ let BigInteger ; /**
 * This function creates a bigint value. It allows browsers
 * that doesn't support BigInt to run the rest of the library
 * by not directly accessing the BigInt constructor.
 */ function createBigInt ( num ) { if ( typeof BigInteger === 'undefined' ) { BigInteger = getBigIntConstructor ( ) ; } if ( BigInteger === null ) { throw new Error ( 'BigInt is not supported!' ) ; } return BigInteger ( num ) ; } function getEXP900 ( ) { let EXP900 = [ ] ; EXP900 [ 0 ] = createBigInt ( 1 ) ; let nineHundred = createBigInt ( 900 ) ; EXP900 [ 1 ] = nineHundred ; for ( let i = 2 ; i < 16 ; i ++ ) { EXP900 [ i ] = EXP900 [ i - 1 ] * nineHundred ; } return EXP900 ; } author SITA Lab (kevin.osullivan sita author Guenther Grau
  export default class DecodedBitStreamParser { private static TEXT_COMPACTION_MODE_LATCH : int = 900 ; private static BYTE_COMPACTION_MODE_LATCH : int = 901 ; private static NUMERIC_COMPACTION_MODE_LATCH : int = 902 ; private static BYTE_COMPACTION_MODE_LATCH_6 : int = 924 ; private static ECI_USER_DEFINED : int = 925 ; private static ECI_GENERAL_PURPOSE : int = 926 ; private static ECI_CHARSET : int = 927 ; private static BEGIN_MACRO_PDF417_CONTROL_BLOCK : int = 928 ; private static BEGIN_MACRO_PDF417_OPTIONAL_FIELD : int = 923 ; private static MACRO_PDF417_TERMINATOR : int = 922 ; private static MODE_SHIFT_TO_BYTE_COMPACTION_MODE : int = 913 ; private static MAX_NUMERIC_CODEWORDS : int = 15 ; private static MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME : int = 0 ; private static MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT : int = 1 ; private static MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP : int = 2 ; private static MACRO_PDF417_OPTIONAL_FIELD_SENDER : int = 3 ; private static MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE : int = 4 ; private static MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE : int = 5 ; private static MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM : int = 6 ; private static PL : int = 25 ; private static LL : int = 27 ; private static AS : int = 27 ; private static ML : int = 28 ; private static AL : int = 28 ; private static PS : int = 29 ; private static PAL : int = 29 ; private static PUNCT_CHARS : string = ';<>@[\\]_`~!\r\t,:\n-.$/"|*()?{}\'' ; private static MIXED_CHARS : string = '0123456789&\r\t,:#-.$/+%*=^' ; /**
     * Table containing values for the exponent of 900.
     * This is used in the numeric compaction decode algorithm.
     */ private static EXP900 : bigint [ ] = getBigIntConstructor ( ) ? getEXP900 ( ) : [ ] ; private static NUMBER_OF_SEQUENCE_CODEWORDS : int = 2 ; param codewords param ecLevel throws static decode ( codewords , ecLevel ) { let result = new StringBuilder ( '' ) ; let encoding = CharacterSetECI . ISO8859_1 ; note result . enableDecoding ( encoding ) ; let codeIndex = 1 ; let code = codewords [ codeIndex ++ ] ; let resultMetadata = new PDF417ResultMetadata ( ) ; while ( codeIndex < codewords [ 0 ] ) { switch ( code ) { case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : codeIndex = DecodedBitStreamParser . textCompaction ( codewords , codeIndex , result ) ; break ; case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH_6 : codeIndex = DecodedBitStreamParser . byteCompaction ( code , codewords , encoding , codeIndex , result ) ; break ; case DecodedBitStreamParser . MODE_SHIFT_TO_BYTE_COMPACTION_MODE : result . append ( codewords [ codeIndex ++ ] ) ; break ; case DecodedBitStreamParser . NUMERIC_COMPACTION_MODE_LATCH : codeIndex = DecodedBitStreamParser . numericCompaction ( codewords , codeIndex , result ) ; break ; case DecodedBitStreamParser . ECI_CHARSET : let charsetECI = CharacterSetECI . getCharacterSetECIByValue ( codewords [ codeIndex ++ ] ) ; break ; case DecodedBitStreamParser . ECI_GENERAL_PURPOSE : codeIndex += 2 ; break ; case DecodedBitStreamParser . ECI_USER_DEFINED : codeIndex ++ ; break ; case DecodedBitStreamParser . BEGIN_MACRO_PDF417_CONTROL_BLOCK : codeIndex = DecodedBitStreamParser . decodeMacroBlock ( codewords , codeIndex , resultMetadata ) ; break ; case DecodedBitStreamParser . BEGIN_MACRO_PDF417_OPTIONAL_FIELD : case DecodedBitStreamParser . MACRO_PDF417_TERMINATOR : throw new FormatException ( ) ; default : codeIndex -- ; codeIndex = DecodedBitStreamParser . textCompaction ( codewords , codeIndex , result ) ; break ; } if ( codeIndex < codewords . length ) { code = codewords [ codeIndex ++ ] ; } else { throw FormatException . getFormatInstance ( ) ; } } if ( result . length ( ) === 0 ) { throw FormatException . getFormatInstance ( ) ; } let decoderResult = new DecoderResult ( null , result . toString ( ) , null , ecLevel ) ; decoderResult . setOther ( resultMetadata ) ; return decoderResult ; } param int param param1 param codewords param int param codeIndex param PDF417ResultMetadata param resultMetadata throws static decodeMacroBlock ( codewords , codeIndex , resultMetadata ) { if ( codeIndex + DecodedBitStreamParser . NUMBER_OF_SEQUENCE_CODEWORDS > codewords [ 0 ] ) { throw FormatException . getFormatInstance ( ) ; } let segmentIndexArray = new Int32Array ( DecodedBitStreamParser . NUMBER_OF_SEQUENCE_CODEWORDS ) ; for ( let i = 0 ; i < DecodedBitStreamParser . NUMBER_OF_SEQUENCE_CODEWORDS ; i ++ , codeIndex ++ ) { segmentIndexArray [ i ] = codewords [ codeIndex ] ; } resultMetadata . setSegmentIndex ( Integer . parseInt ( DecodedBitStreamParser . decodeBase900toBase10 ( segmentIndexArray , DecodedBitStreamParser . NUMBER_OF_SEQUENCE_CODEWORDS ) ) ) ; let fileId = new StringBuilder ( ) ; codeIndex = DecodedBitStreamParser . textCompaction ( codewords , codeIndex , fileId ) ; resultMetadata . setFileId ( fileId . toString ( ) ) ; let optionalFieldsStart = - 1 ; if ( codewords [ codeIndex ] === DecodedBitStreamParser . BEGIN_MACRO_PDF417_OPTIONAL_FIELD ) { optionalFieldsStart = codeIndex + 1 ; } while ( codeIndex < codewords [ 0 ] ) { switch ( codewords [ codeIndex ] ) { case DecodedBitStreamParser . BEGIN_MACRO_PDF417_OPTIONAL_FIELD : codeIndex ++ ; switch ( codewords [ codeIndex ] ) { case DecodedBitStreamParser . MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME : let fileName = new StringBuilder ( ) ; codeIndex = DecodedBitStreamParser . textCompaction ( codewords , codeIndex + 1 , fileName ) ; resultMetadata . setFileName ( fileName . toString ( ) ) ; break ; case DecodedBitStreamParser . MACRO_PDF417_OPTIONAL_FIELD_SENDER : let sender = new StringBuilder ( ) ; codeIndex = DecodedBitStreamParser . textCompaction ( codewords , codeIndex + 1 , sender ) ; resultMetadata . setSender ( sender . toString ( ) ) ; break ; case DecodedBitStreamParser . MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE : let addressee = new StringBuilder ( ) ; codeIndex = DecodedBitStreamParser . textCompaction ( codewords , codeIndex + 1 , addressee ) ; resultMetadata . setAddressee ( addressee . toString ( ) ) ; break ; case DecodedBitStreamParser . MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT : let segmentCount = new StringBuilder ( ) ; codeIndex = DecodedBitStreamParser . numericCompaction ( codewords , codeIndex + 1 , segmentCount ) ; resultMetadata . setSegmentCount ( Integer . parseInt ( segmentCount . toString ( ) ) ) ; break ; case DecodedBitStreamParser . MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP : let timestamp = new StringBuilder ( ) ; codeIndex = DecodedBitStreamParser . numericCompaction ( codewords , codeIndex + 1 , timestamp ) ; resultMetadata . setTimestamp ( Long . parseLong ( timestamp . toString ( ) ) ) ; break ; case DecodedBitStreamParser . MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM : let checksum = new StringBuilder ( ) ; codeIndex = DecodedBitStreamParser . numericCompaction ( codewords , codeIndex + 1 , checksum ) ; resultMetadata . setChecksum ( Integer . parseInt ( checksum . toString ( ) ) ) ; break ; case DecodedBitStreamParser . MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE : let fileSize = new StringBuilder ( ) ; codeIndex = DecodedBitStreamParser . numericCompaction ( codewords , codeIndex + 1 , fileSize ) ; resultMetadata . setFileSize ( Long . parseLong ( fileSize . toString ( ) ) ) ; break ; default : throw FormatException . getFormatInstance ( ) ; } break ; case DecodedBitStreamParser . MACRO_PDF417_TERMINATOR : codeIndex ++ ; resultMetadata . setLastSegment ( true ) ; break ; default : throw FormatException . getFormatInstance ( ) ; } } if ( optionalFieldsStart !== - 1 ) { let optionalFieldsLength = codeIndex - optionalFieldsStart ; if ( resultMetadata . isLastSegment ( ) ) { optionalFieldsLength -- ; } resultMetadata . setOptionalData ( Arrays . copyOfRange ( codewords , optionalFieldsStart , optionalFieldsStart + optionalFieldsLength ) ) ; } return codeIndex ; } param codewords param codeIndex param result return private static textCompaction ( codewords , codeIndex , result ) { let textCompactionData = new Int32Array ( ( codewords [ 0 ] - codeIndex ) * 2 ) ; let byteCompactionData = new Int32Array ( ( codewords [ 0 ] - codeIndex ) * 2 ) ; let index = 0 ; let end = false ; while ( ( codeIndex < codewords [ 0 ] ) && ! end ) { let code = codewords [ codeIndex ++ ] ; if ( code < DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH ) { textCompactionData [ index ] = code / 30 ; textCompactionData [ index + 1 ] = code % 30 ; index += 2 ; } else { switch ( code ) { case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : textCompactionData [ index ++ ] = DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH ; break ; case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH_6 : case DecodedBitStreamParser . NUMERIC_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BEGIN_MACRO_PDF417_CONTROL_BLOCK : case DecodedBitStreamParser . BEGIN_MACRO_PDF417_OPTIONAL_FIELD : case DecodedBitStreamParser . MACRO_PDF417_TERMINATOR : codeIndex -- ; end = true ; break ; case DecodedBitStreamParser . MODE_SHIFT_TO_BYTE_COMPACTION_MODE : textCompactionData [ index ] = DecodedBitStreamParser . MODE_SHIFT_TO_BYTE_COMPACTION_MODE ; code = codewords [ codeIndex ++ ] ; byteCompactionData [ index ] = code ; index ++ ; break ; } } } DecodedBitStreamParser . decodeTextCompaction ( textCompactionData , byteCompactionData , index , result ) ; return codeIndex ; } param textCompactionData param byteCompactionData param length param result private static decodeTextCompaction ( textCompactionData , byteCompactionData , length , result ) { let subMode = Mode . ALPHA ; let priorToShiftMode = Mode . ALPHA ; let i = 0 ; while ( i < length ) { let subModeCh = textCompactionData [ i ] ; let ch = '' ; switch ( subMode ) { case Mode . ALPHA : if ( subModeCh < 26 ) { ch = String . fromCharCode ( 65 + subModeCh ) ; } else { switch ( subModeCh ) { case 26 : ch = ' ' ; break ; case DecodedBitStreamParser . LL : subMode = Mode . LOWER ; break ; case DecodedBitStreamParser . ML : subMode = Mode . MIXED ; break ; case DecodedBitStreamParser . PS : priorToShiftMode = subMode ; subMode = Mode . PUNCT_SHIFT ; break ; case DecodedBitStreamParser . MODE_SHIFT_TO_BYTE_COMPACTION_MODE : result . append ( byteCompactionData [ i ] ) ; break ; case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : subMode = Mode . ALPHA ; break ; } } break ; case Mode . LOWER : if ( subModeCh < 26 ) { ch = String . fromCharCode ( 97 + subModeCh ) ; } else { switch ( subModeCh ) { case 26 : ch = ' ' ; break ; case DecodedBitStreamParser . AS : priorToShiftMode = subMode ; subMode = Mode . ALPHA_SHIFT ; break ; case DecodedBitStreamParser . ML : subMode = Mode . MIXED ; break ; case DecodedBitStreamParser . PS : priorToShiftMode = subMode ; subMode = Mode . PUNCT_SHIFT ; break ; case DecodedBitStreamParser . MODE_SHIFT_TO_BYTE_COMPACTION_MODE : result . append ( byteCompactionData [ i ] ) ; break ; case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : subMode = Mode . ALPHA ; break ; } } break ; case Mode . MIXED : if ( subModeCh < DecodedBitStreamParser . PL ) { ch = DecodedBitStreamParser . MIXED_CHARS [ subModeCh ] ; } else { switch ( subModeCh ) { case DecodedBitStreamParser . PL : subMode = Mode . PUNCT ; break ; case 26 : ch = ' ' ; break ; case DecodedBitStreamParser . LL : subMode = Mode . LOWER ; break ; case DecodedBitStreamParser . AL : subMode = Mode . ALPHA ; break ; case DecodedBitStreamParser . PS : priorToShiftMode = subMode ; subMode = Mode . PUNCT_SHIFT ; break ; case DecodedBitStreamParser . MODE_SHIFT_TO_BYTE_COMPACTION_MODE : result . append ( byteCompactionData [ i ] ) ; break ; case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : subMode = Mode . ALPHA ; break ; } } break ; case Mode . PUNCT : if ( subModeCh < DecodedBitStreamParser . PAL ) { ch = DecodedBitStreamParser . PUNCT_CHARS [ subModeCh ] ; } else { switch ( subModeCh ) { case DecodedBitStreamParser . PAL : subMode = Mode . ALPHA ; break ; case DecodedBitStreamParser . MODE_SHIFT_TO_BYTE_COMPACTION_MODE : result . append ( byteCompactionData [ i ] ) ; break ; case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : subMode = Mode . ALPHA ; break ; } } break ; case Mode . ALPHA_SHIFT : subMode = priorToShiftMode ; if ( subModeCh < 26 ) { ch = String . fromCharCode ( 65 + subModeCh ) ; } else { switch ( subModeCh ) { case 26 : ch = ' ' ; break ; case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : subMode = Mode . ALPHA ; break ; } } break ; case Mode . PUNCT_SHIFT : subMode = priorToShiftMode ; if ( subModeCh < DecodedBitStreamParser . PAL ) { ch = DecodedBitStreamParser . PUNCT_CHARS [ subModeCh ] ; } else { switch ( subModeCh ) { case DecodedBitStreamParser . PAL : subMode = Mode . ALPHA ; break ; case DecodedBitStreamParser . MODE_SHIFT_TO_BYTE_COMPACTION_MODE : result . append ( byteCompactionData [ i ] ) ; break ; case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : subMode = Mode . ALPHA ; break ; } } break ; } if ( ch !== '' ) { result . append ( ch ) ; } i ++ ; } } param mode param codewords param encoding param codeIndex param result return private static byteCompaction ( mode , codewords , encoding , codeIndex , result ) { let decodedBytes = new ByteArrayOutputStream ( ) ; let count = 0 ; let value = 0 ; let end = false ; switch ( mode ) { case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH : let byteCompactedCodewords = new Int32Array ( 6 ) ; let nextCode = codewords [ codeIndex ++ ] ; while ( ( codeIndex < codewords [ 0 ] ) && ! end ) { byteCompactedCodewords [ count ++ ] = nextCode ; value = 900 * value + nextCode ; nextCode = codewords [ codeIndex ++ ] ; switch ( nextCode ) { case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . NUMERIC_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH_6 : case DecodedBitStreamParser . BEGIN_MACRO_PDF417_CONTROL_BLOCK : case DecodedBitStreamParser . BEGIN_MACRO_PDF417_OPTIONAL_FIELD : case DecodedBitStreamParser . MACRO_PDF417_TERMINATOR : codeIndex -- ; end = true ; break ; default : if ( ( count % 5 === 0 ) && ( count > 0 ) ) { for ( let j = 0 ; j < 6 ; ++ j ) { decodedBytes . write ( Number ( createBigInt ( value ) >> createBigInt ( 8 * ( 5 - j ) ) ) ) ; } value = 0 ; count = 0 ; } break ; } } if ( codeIndex === codewords [ 0 ] && nextCode < DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH ) { byteCompactedCodewords [ count ++ ] = nextCode ; } for ( let i = 0 ; i < count ; i ++ ) { decodedBytes . write ( byteCompactedCodewords [ i ] ) ; } break ; case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH_6 : while ( codeIndex < codewords [ 0 ] && ! end ) { let code = codewords [ codeIndex ++ ] ; if ( code < DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH ) { count ++ ; value = 900 * value + code ; } else { switch ( code ) { case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . NUMERIC_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH_6 : case DecodedBitStreamParser . BEGIN_MACRO_PDF417_CONTROL_BLOCK : case DecodedBitStreamParser . BEGIN_MACRO_PDF417_OPTIONAL_FIELD : case DecodedBitStreamParser . MACRO_PDF417_TERMINATOR : codeIndex -- ; end = true ; break ; } } if ( ( count % 5 === 0 ) && ( count > 0 ) ) { for ( let j = 0 ; j < 6 ; ++ j ) { decodedBytes . write ( Number ( createBigInt ( value ) >> createBigInt ( 8 * ( 5 - j ) ) ) ) ; } value = 0 ; count = 0 ; } } break ; } result . append ( StringEncoding . decode ( decodedBytes . toByteArray ( ) , encoding ) ) ; return codeIndex ; } param codewords param codeIndex param result return throws private static numericCompaction ( codewords , codeIndex , result ) { let count = 0 ; let end = false ; let numericCodewords = new Int32Array ( DecodedBitStreamParser . MAX_NUMERIC_CODEWORDS ) ; while ( codeIndex < codewords [ 0 ] && ! end ) { let code = codewords [ codeIndex ++ ] ; if ( codeIndex === codewords [ 0 ] ) { end = true ; } if ( code < DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH ) { numericCodewords [ count ] = code ; count ++ ; } else { switch ( code ) { case DecodedBitStreamParser . TEXT_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH : case DecodedBitStreamParser . BYTE_COMPACTION_MODE_LATCH_6 : case DecodedBitStreamParser . BEGIN_MACRO_PDF417_CONTROL_BLOCK : case DecodedBitStreamParser . BEGIN_MACRO_PDF417_OPTIONAL_FIELD : case DecodedBitStreamParser . MACRO_PDF417_TERMINATOR : codeIndex -- ; end = true ; break ; } } if ( ( count % DecodedBitStreamParser . MAX_NUMERIC_CODEWORDS === 0 || code === DecodedBitStreamParser . NUMERIC_COMPACTION_MODE_LATCH || end ) && count > 0 ) { result . append ( DecodedBitStreamParser . decodeBase900toBase10 ( numericCodewords , count ) ) ; count = 0 ; } } return codeIndex ; } param codewords param count return throws private static decodeBase900toBase10 ( codewords , count ) { let result = createBigInt ( 0 ) ; for ( let i = 0 ; i < count ; i ++ ) { result += DecodedBitStreamParser . EXP900 [ count - i - 1 ] * createBigInt ( codewords [ i ] ) ; } let resultString = result . toString ( ) ; if ( resultString . charAt ( 0 ) !== '1' ) { throw new FormatException ( ) ; } return resultString . substring ( 1 ) ; } }