import BitSource from '../../common/BitSource' ; import CharacterSetECI from '../../common/CharacterSetECI' ; import DecoderResult from '../../common/DecoderResult' ; import StringUtils from '../../common/StringUtils' ; import DecodeHintType from '../../DecodeHintType' ; import FormatException from '../../FormatException' ; import StringBuilder from '../../util/StringBuilder' ; import StringEncoding from '../../util/StringEncoding' ; import ErrorCorrectionLevel from './ErrorCorrectionLevel' ; import Mode from './Mode' ; import Version from './Version' ; author Sean Owen
  export default class DecodedBitStreamParser { /**
     * See ISO 18004:2006, 6.4.4 Table 5
     */ private static ALPHANUMERIC_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:' ; private static GB2312_SUBSET = 1 ; public static decode ( bytes , version , ecLevel , hints ) { const bits = new BitSource ( bytes ) ; let result = new StringBuilder ( ) ; const byteSegments = new Array < Uint8Array > ( ) ; let symbolSequence = - 1 ; let parityData = - 1 ; try { let currentCharacterSetECI = null ; let fc1InEffect = false ; let mode ; do { if ( bits . available ( ) < 4 ) { mode = Mode . TERMINATOR ; } else { const modeBits = bits . readBits ( 4 ) ; mode = Mode . forBits ( modeBits ) ; } switch ( mode ) { case Mode . TERMINATOR : break ; case Mode . FNC1_FIRST_POSITION : case Mode . FNC1_SECOND_POSITION : fc1InEffect = true ; break ; case Mode . STRUCTURED_APPEND : if ( bits . available ( ) < 16 ) { throw new FormatException ( ) ; } symbolSequence = bits . readBits ( 8 ) ; parityData = bits . readBits ( 8 ) ; break ; case Mode . ECI : const value = DecodedBitStreamParser . parseECIValue ( bits ) ; currentCharacterSetECI = CharacterSetECI . getCharacterSetECIByValue ( value ) ; if ( currentCharacterSetECI === null ) { throw new FormatException ( ) ; } break ; case Mode . HANZI : const subset = bits . readBits ( 4 ) ; const countHanzi = bits . readBits ( mode . getCharacterCountBits ( version ) ) ; if ( subset === DecodedBitStreamParser . GB2312_SUBSET ) { DecodedBitStreamParser . decodeHanziSegment ( bits , result , countHanzi ) ; } break ; default : const count = bits . readBits ( mode . getCharacterCountBits ( version ) ) ; switch ( mode ) { case Mode . NUMERIC : DecodedBitStreamParser . decodeNumericSegment ( bits , result , count ) ; break ; case Mode . ALPHANUMERIC : DecodedBitStreamParser . decodeAlphanumericSegment ( bits , result , count , fc1InEffect ) ; break ; case Mode . BYTE : DecodedBitStreamParser . decodeByteSegment ( bits , result , count , currentCharacterSetECI , byteSegments , hints ) ; break ; case Mode . KANJI : DecodedBitStreamParser . decodeKanjiSegment ( bits , result , count ) ; break ; default : throw new FormatException ( ) ; } break ; } } while ( mode !== Mode . TERMINATOR ) ; } catch ( iae ) { throw new FormatException ( ) ; } return new DecoderResult ( bytes , result . toString ( ) , byteSegments . length === 0 ? null : byteSegments , ecLevel === null ? null : ecLevel . toString ( ) , symbolSequence , parityData ) ; } /**
     * See specification GBT 18284-2000
     */ private static decodeHanziSegment ( bits , result , count ) { if ( count * 13 > bits . available ( ) ) { throw new FormatException ( ) ; } const buffer = new Uint8Array ( 2 * count ) ; let offset = 0 ; while ( count > 0 ) { const twoBytes = bits . readBits ( 13 ) ; let assembledTwoBytes = ( ( ( twoBytes / 0x060 ) << 8 ) & 0xFFFFFFFF ) | ( twoBytes % 0x060 ) ; if ( assembledTwoBytes < 0x003BF ) { assembledTwoBytes += 0x0A1A1 ; } else { assembledTwoBytes += 0x0A6A1 ; } buffer [ offset ] = ( ( assembledTwoBytes >> 8 ) & 0xFF ) ; buffer [ offset + 1 ] = ( assembledTwoBytes & 0xFF ) ; offset += 2 ; count -- ; } try { result . append ( StringEncoding . decode ( buffer , StringUtils . GB2312 ) ) ; } catch ( ignored ) { throw new FormatException ( ignored ) ; } } private static decodeKanjiSegment ( bits , result , count ) { if ( count * 13 > bits . available ( ) ) { throw new FormatException ( ) ; } const buffer = new Uint8Array ( 2 * count ) ; let offset = 0 ; while ( count > 0 ) { const twoBytes = bits . readBits ( 13 ) ; let assembledTwoBytes = ( ( ( twoBytes / 0x0C0 ) << 8 ) & 0xFFFFFFFF ) | ( twoBytes % 0x0C0 ) ; if ( assembledTwoBytes < 0x01F00 ) { assembledTwoBytes += 0x08140 ; } else { assembledTwoBytes += 0x0C140 ; } buffer [ offset ] = ( assembledTwoBytes >> 8 ) ; buffer [ offset + 1 ] = assembledTwoBytes ; offset += 2 ; count -- ; } try { result . append ( StringEncoding . decode ( buffer , StringUtils . SHIFT_JIS ) ) ; } catch ( ignored ) { throw new FormatException ( ignored ) ; } } private static decodeByteSegment ( bits , result , count , currentCharacterSetECI , byteSegments , hints ) { if ( 8 * count > bits . available ( ) ) { throw new FormatException ( ) ; } const readBytes = new Uint8Array ( count ) ; for ( let i = 0 ; i < count ; i ++ ) { readBytes [ i ] = bits . readBits ( 8 ) ; } let encoding ; if ( currentCharacterSetECI === null ) { encoding = StringUtils . guessEncoding ( readBytes , hints ) ; } else { encoding = currentCharacterSetECI . getName ( ) ; } try { result . append ( StringEncoding . decode ( readBytes , encoding ) ) ; } catch ( ignored ) { throw new FormatException ( ignored ) ; } byteSegments . push ( readBytes ) ; } private static toAlphaNumericChar ( value ) { if ( value >= DecodedBitStreamParser . ALPHANUMERIC_CHARS . length ) { throw new FormatException ( ) ; } return DecodedBitStreamParser . ALPHANUMERIC_CHARS [ value ] ; } private static decodeAlphanumericSegment ( bits , result , count , fc1InEffect ) { const start = result . length ( ) ; while ( count > 1 ) { if ( bits . available ( ) < 11 ) { throw new FormatException ( ) ; } const nextTwoCharsBits = bits . readBits ( 11 ) ; result . append ( DecodedBitStreamParser . toAlphaNumericChar ( Math . floor ( nextTwoCharsBits / 45 ) ) ) ; result . append ( DecodedBitStreamParser . toAlphaNumericChar ( nextTwoCharsBits % 45 ) ) ; count -= 2 ; } if ( count === 1 ) { if ( bits . available ( ) < 6 ) { throw new FormatException ( ) ; } result . append ( DecodedBitStreamParser . toAlphaNumericChar ( bits . readBits ( 6 ) ) ) ; } if ( fc1InEffect ) { for ( let i = start ; i < result . length ( ) ; i ++ ) { if ( result . charAt ( i ) === '%' ) { if ( i < result . length ( ) - 1 && result . charAt ( i + 1 ) === '%' ) { result . deleteCharAt ( i + 1 ) ; } else { result . setCharAt ( i , String . fromCharCode ( 0x1D ) ) ; } } } } } private static decodeNumericSegment ( bits , result , count ) { while ( count >= 3 ) { if ( bits . available ( ) < 10 ) { throw new FormatException ( ) ; } const threeDigitsBits = bits . readBits ( 10 ) ; if ( threeDigitsBits >= 1000 ) { throw new FormatException ( ) ; } result . append ( DecodedBitStreamParser . toAlphaNumericChar ( Math . floor ( threeDigitsBits / 100 ) ) ) ; result . append ( DecodedBitStreamParser . toAlphaNumericChar ( Math . floor ( threeDigitsBits / 10 ) % 10 ) ) ; result . append ( DecodedBitStreamParser . toAlphaNumericChar ( threeDigitsBits % 10 ) ) ; count -= 3 ; } if ( count === 2 ) { if ( bits . available ( ) < 7 ) { throw new FormatException ( ) ; } const twoDigitsBits = bits . readBits ( 7 ) ; if ( twoDigitsBits >= 100 ) { throw new FormatException ( ) ; } result . append ( DecodedBitStreamParser . toAlphaNumericChar ( Math . floor ( twoDigitsBits / 10 ) ) ) ; result . append ( DecodedBitStreamParser . toAlphaNumericChar ( twoDigitsBits % 10 ) ) ; } else if ( count === 1 ) { if ( bits . available ( ) < 4 ) { throw new FormatException ( ) ; } const digitBits = bits . readBits ( 4 ) ; if ( digitBits >= 10 ) { throw new FormatException ( ) ; } result . append ( DecodedBitStreamParser . toAlphaNumericChar ( digitBits ) ) ; } } private static parseECIValue ( bits ) { const firstByte = bits . readBits ( 8 ) ; if ( ( firstByte & 0x80 ) === 0 ) { return firstByte & 0x7F ; } if ( ( firstByte & 0xC0 ) === 0x80 ) { const secondByte = bits . readBits ( 8 ) ; return ( ( ( firstByte & 0x3F ) << 8 ) & 0xFFFFFFFF ) | secondByte ; } if ( ( firstByte & 0xE0 ) === 0xC0 ) { const secondThirdBytes = bits . readBits ( 16 ) ; return ( ( ( firstByte & 0x1F ) << 16 ) & 0xFFFFFFFF ) | secondThirdBytes ; } throw new FormatException ( ) ; } }