'use strict' ; type bit = number ; type byte = number ; type int = number ; export class QrCode { public static encodeText ( text , ecl ) { const segs = QrSegment . makeSegments ( text ) ; return QrCode . encodeSegments ( segs , ecl ) ; } public static encodeBinary ( data , ecl ) { const seg = QrSegment . makeBytes ( data ) ; return QrCode . encodeSegments ( [ seg ] , ecl ) ; } public static encodeSegments ( segs , ecl , minVersion = 1 , maxVersion = 40 , mask = - 1 , boostEcl = true ) { if ( ! ( QrCode . MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode . MAX_VERSION ) || mask < - 1 || mask > 7 ) throw 'Invalid value' ; let version ; let dataUsedBits ; for ( version = minVersion ; ; version ++ ) { const dataCapacityBits = QrCode . getNumDataCodewords ( version , ecl ) * 8 ; const usedBits = QrSegment . getTotalBits ( segs , version ) ; if ( usedBits <= dataCapacityBits ) { dataUsedBits = usedBits ; break ; } if ( version >= maxVersion ) throw 'Data too long' ; } for ( const newEcl of [ QrCode . Ecc . MEDIUM , QrCode . Ecc . QUARTILE , QrCode . Ecc . HIGH ] ) { if ( boostEcl && dataUsedBits <= QrCode . getNumDataCodewords ( version , newEcl ) * 8 ) ecl = newEcl ; } let bb = new BitBuffer ( ) ; for ( const seg of segs ) { bb . appendBits ( seg . mode . modeBits , 4 ) ; bb . appendBits ( seg . numChars , seg . mode . numCharCountBits ( version ) ) ; for ( const b of seg . getData ( ) ) bb . array . push ( b ) ; } if ( bb . array . length != dataUsedBits ) throw 'Assertion error' ; const dataCapacityBits = QrCode . getNumDataCodewords ( version , ecl ) * 8 ; if ( bb . array . length > dataCapacityBits ) throw 'Assertion error' ; bb . appendBits ( 0 , Math . min ( 4 , dataCapacityBits - bb . array . length ) ) ; bb . appendBits ( 0 , ( 8 - ( bb . array . length % 8 ) ) % 8 ) ; if ( bb . array . length % 8 != 0 ) throw 'Assertion error' ; for ( let padByte = 0xec ; bb . array . length < dataCapacityBits ; padByte ^= 0xec ^ 0x11 ) bb . appendBits ( padByte , 8 ) ; let dataCodewords = [ ] ; while ( dataCodewords . length * 8 < bb . array . length ) dataCodewords . push ( 0 ) ; bb . array . forEach ( ( b , i ) => ( dataCodewords [ i >>> 3 ] |= b << ( 7 - ( i & 7 ) ) ) ) ; return new QrCode ( version , ecl , dataCodewords , mask ) ; } public readonly size : int ; public readonly modules : Array < Array < boolean > > = [ ] ; private readonly isFunction : Array < Array < boolean > > = [ ] ; public constructor ( public readonly version , public readonly errorCorrectionLevel , dataCodewords , public readonly mask ) { if ( version < QrCode . MIN_VERSION || version > QrCode . MAX_VERSION ) throw 'Version value out of range' ; if ( mask < - 1 || mask > 7 ) throw 'Mask value out of range' ; this . size = version * 4 + 17 ; let row = [ ] ; for ( let i = 0 ; i < this . size ; i ++ ) row . push ( false ) ; for ( let i = 0 ; i < this . size ; i ++ ) { this . modules . push ( row . slice ( ) ) ; this . isFunction . push ( row . slice ( ) ) ; } this . drawFunctionPatterns ( ) ; const allCodewords = this . addEccAndInterleave ( dataCodewords ) ; this . drawCodewords ( allCodewords ) ; if ( mask == - 1 ) { let minPenalty = 1000000000 ; for ( let i = 0 ; i < 8 ; i ++ ) { this . applyMask ( i ) ; this . drawFormatBits ( i ) ; const penalty = this . getPenaltyScore ( ) ; if ( penalty < minPenalty ) { mask = i ; minPenalty = penalty ; } this . applyMask ( i ) ; } } if ( mask < 0 || mask > 7 ) throw 'Assertion error' ; this . mask = mask ; this . applyMask ( mask ) ; this . drawFormatBits ( mask ) ; this . isFunction = [ ] ; } public getModule ( x , y ) { return 0 <= x && x < this . size && 0 <= y && y < this . size && this . modules [ y ] [ x ] ; } public drawCanvas ( scale , border , canvas ) { if ( scale <= 0 || border < 0 ) throw 'Value out of range' ; const width = ( this . size + border * 2 ) * scale ; canvas . width = width ; canvas . height = width ; let ctx = canvas . getContext ( '2d' ) as CanvasRenderingContext2D ; for ( let y = - border ; y < this . size + border ; y ++ ) { for ( let x = - border ; x < this . size + border ; x ++ ) { ctx . fillStyle = this . getModule ( x , y ) ? '#000000' : '#FFFFFF' ; ctx . fillRect ( ( x + border ) * scale , ( y + border ) * scale , scale , scale ) ; } } } public toSvgString ( border ) { if ( border < 0 ) throw 'Border must be non-negative' ; let parts = [ ] ; for ( let y = 0 ; y < this . size ; y ++ ) { for ( let x = 0 ; x < this . size ; x ++ ) { if ( this . getModule ( x , y ) ) parts . push ( `M${ x + border },${ y + border }h1v1h-1z` ) ; } } return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 ${ this . size + border * 2 } ${ this . size + border * 2 }" stroke="none">
	<rect width="100%" height="100%" fill="#FFFFFF"/>
	<path d="${ parts . join ( ' ' ) }" fill="#000000"/>
</svg>
` ; } private drawFunctionPatterns ( ) { for ( let i = 0 ; i < this . size ; i ++ ) { this . setFunctionModule ( 6 , i , i % 2 == 0 ) ; this . setFunctionModule ( i , 6 , i % 2 == 0 ) ; } this . drawFinderPattern ( 3 , 3 ) ; this . drawFinderPattern ( this . size - 4 , 3 ) ; this . drawFinderPattern ( 3 , this . size - 4 ) ; const alignPatPos = this . getAlignmentPatternPositions ( ) ; const numAlign = alignPatPos . length ; for ( let i = 0 ; i < numAlign ; i ++ ) { for ( let j = 0 ; j < numAlign ; j ++ ) { if ( ! ( ( i == 0 && j == 0 ) || ( i == 0 && j == numAlign - 1 ) || ( i == numAlign - 1 && j == 0 ) ) ) this . drawAlignmentPattern ( alignPatPos [ i ] , alignPatPos [ j ] ) ; } } this . drawFormatBits ( 0 ) ; this . drawVersion ( ) ; } private drawFormatBits ( mask ) { const data = ( this . errorCorrectionLevel . formatBits << 3 ) | mask ; let rem = data ; for ( let i = 0 ; i < 10 ; i ++ ) rem = ( rem << 1 ) ^ ( ( rem >>> 9 ) * 0x537 ) ; const bits = ( ( data << 10 ) | rem ) ^ 0x5412 ; if ( bits >>> 15 != 0 ) throw 'Assertion error' ; for ( let i = 0 ; i <= 5 ; i ++ ) this . setFunctionModule ( 8 , i , getBit ( bits , i ) ) ; this . setFunctionModule ( 8 , 7 , getBit ( bits , 6 ) ) ; this . setFunctionModule ( 8 , 8 , getBit ( bits , 7 ) ) ; this . setFunctionModule ( 7 , 8 , getBit ( bits , 8 ) ) ; for ( let i = 9 ; i < 15 ; i ++ ) this . setFunctionModule ( 14 - i , 8 , getBit ( bits , i ) ) ; for ( let i = 0 ; i < 8 ; i ++ ) this . setFunctionModule ( this . size - 1 - i , 8 , getBit ( bits , i ) ) ; for ( let i = 8 ; i < 15 ; i ++ ) this . setFunctionModule ( 8 , this . size - 15 + i , getBit ( bits , i ) ) ; this . setFunctionModule ( 8 , this . size - 8 , true ) ; } private drawVersion ( ) { if ( this . version < 7 ) return ; let rem = this . version ; for ( let i = 0 ; i < 12 ; i ++ ) rem = ( rem << 1 ) ^ ( ( rem >>> 11 ) * 0x1f25 ) ; const bits = ( this . version << 12 ) | rem ; if ( bits >>> 18 != 0 ) throw 'Assertion error' ; for ( let i = 0 ; i < 18 ; i ++ ) { const color = getBit ( bits , i ) ; const a = this . size - 11 + ( i % 3 ) ; const b = Math . floor ( i / 3 ) ; this . setFunctionModule ( a , b , color ) ; this . setFunctionModule ( b , a , color ) ; } } private drawFinderPattern ( x , y ) { for ( let dy = - 4 ; dy <= 4 ; dy ++ ) { for ( let dx = - 4 ; dx <= 4 ; dx ++ ) { const dist = Math . max ( Math . abs ( dx ) , Math . abs ( dy ) ) ; const xx = x + dx ; const yy = y + dy ; if ( 0 <= xx && xx < this . size && 0 <= yy && yy < this . size ) this . setFunctionModule ( xx , yy , dist != 2 && dist != 4 ) ; } } } private drawAlignmentPattern ( x , y ) { for ( let dy = - 2 ; dy <= 2 ; dy ++ ) { for ( let dx = - 2 ; dx <= 2 ; dx ++ ) this . setFunctionModule ( x + dx , y + dy , Math . max ( Math . abs ( dx ) , Math . abs ( dy ) ) != 1 ) ; } } private setFunctionModule ( x , y , isBlack ) { this . modules [ y ] [ x ] = isBlack ; this . isFunction [ y ] [ x ] = true ; } private addEccAndInterleave ( data ) { const ver = this . version ; const ecl = this . errorCorrectionLevel ; if ( data . length != QrCode . getNumDataCodewords ( ver , ecl ) ) throw 'Invalid argument' ; const numBlocks = QrCode . NUM_ERROR_CORRECTION_BLOCKS [ ecl . ordinal ] [ ver ] ; const blockEccLen = QrCode . ECC_CODEWORDS_PER_BLOCK [ ecl . ordinal ] [ ver ] ; const rawCodewords = Math . floor ( QrCode . getNumRawDataModules ( ver ) / 8 ) ; const numShortBlocks = numBlocks - ( rawCodewords % numBlocks ) ; const shortBlockLen = Math . floor ( rawCodewords / numBlocks ) ; let blocks = [ ] ; const rs = new ReedSolomonGenerator ( blockEccLen ) ; for ( let i = 0 , k = 0 ; i < numBlocks ; i ++ ) { let dat = data . slice ( k , k + shortBlockLen - blockEccLen + ( i < numShortBlocks ? 0 : 1 ) ) ; k += dat . length ; const ecc = rs . getRemainder ( dat ) ; if ( i < numShortBlocks ) dat . push ( 0 ) ; blocks . push ( dat . concat ( ecc ) ) ; } let result = [ ] ; for ( let i = 0 ; i < blocks [ 0 ] . length ; i ++ ) { for ( let j = 0 ; j < blocks . length ; j ++ ) { if ( i != shortBlockLen - blockEccLen || j >= numShortBlocks ) result . push ( blocks [ j ] [ i ] ) ; } } if ( result . length != rawCodewords ) throw 'Assertion error' ; return result ; } private drawCodewords ( data ) { if ( data . length != Math . floor ( QrCode . getNumRawDataModules ( this . version ) / 8 ) ) throw 'Invalid argument' ; let i = 0 ; for ( let right = this . size - 1 ; right >= 1 ; right -= 2 ) { if ( right == 6 ) right = 5 ; for ( let vert = 0 ; vert < this . size ; vert ++ ) { for ( let j = 0 ; j < 2 ; j ++ ) { const x = right - j ; const upward = ( ( right + 1 ) & 2 ) == 0 ; const y = upward ? this . size - 1 - vert : vert ; if ( ! this . isFunction [ y ] [ x ] && i < data . length * 8 ) { this . modules [ y ] [ x ] = getBit ( data [ i >>> 3 ] , 7 - ( i & 7 ) ) ; i ++ ; } } } } if ( i != data . length * 8 ) throw 'Assertion error' ; } private applyMask ( mask ) { if ( mask < 0 || mask > 7 ) throw 'Mask value out of range' ; for ( let y = 0 ; y < this . size ; y ++ ) { for ( let x = 0 ; x < this . size ; x ++ ) { let invert ; switch ( mask ) { case 0 : invert = ( x + y ) % 2 == 0 ; break ; case 1 : invert = y % 2 == 0 ; break ; case 2 : invert = x % 3 == 0 ; break ; case 3 : invert = ( x + y ) % 3 == 0 ; break ; case 4 : invert = ( Math . floor ( x / 3 ) + Math . floor ( y / 2 ) ) % 2 == 0 ; break ; case 5 : invert = ( ( x * y ) % 2 ) + ( ( x * y ) % 3 ) == 0 ; break ; case 6 : invert = ( ( ( x * y ) % 2 ) + ( ( x * y ) % 3 ) ) % 2 == 0 ; break ; case 7 : invert = ( ( ( x + y ) % 2 ) + ( ( x * y ) % 3 ) ) % 2 == 0 ; break ; default : throw 'Assertion error' ; } if ( ! this . isFunction [ y ] [ x ] && invert ) this . modules [ y ] [ x ] = ! this . modules [ y ] [ x ] ; } } } private getPenaltyScore ( ) { let result = 0 ; for ( let y = 0 ; y < this . size ; y ++ ) { let runHistory = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ; let color = false ; let runX = 0 ; for ( let x = 0 ; x < this . size ; x ++ ) { if ( this . modules [ y ] [ x ] == color ) { runX ++ ; if ( runX == 5 ) result += QrCode . PENALTY_N1 ; else if ( runX > 5 ) result ++ ; } else { QrCode . addRunToHistory ( runX , runHistory ) ; if ( ! color && QrCode . hasFinderLikePattern ( runHistory ) ) result += QrCode . PENALTY_N3 ; color = this . modules [ y ] [ x ] ; runX = 1 ; } } QrCode . addRunToHistory ( runX , runHistory ) ; if ( color ) QrCode . addRunToHistory ( 0 , runHistory ) ; if ( QrCode . hasFinderLikePattern ( runHistory ) ) result += QrCode . PENALTY_N3 ; } for ( let x = 0 ; x < this . size ; x ++ ) { let runHistory = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ; let color = false ; let runY = 0 ; for ( let y = 0 ; y < this . size ; y ++ ) { if ( this . modules [ y ] [ x ] == color ) { runY ++ ; if ( runY == 5 ) result += QrCode . PENALTY_N1 ; else if ( runY > 5 ) result ++ ; } else { QrCode . addRunToHistory ( runY , runHistory ) ; if ( ! color && QrCode . hasFinderLikePattern ( runHistory ) ) result += QrCode . PENALTY_N3 ; color = this . modules [ y ] [ x ] ; runY = 1 ; } } QrCode . addRunToHistory ( runY , runHistory ) ; if ( color ) QrCode . addRunToHistory ( 0 , runHistory ) ; if ( QrCode . hasFinderLikePattern ( runHistory ) ) result += QrCode . PENALTY_N3 ; } for ( let y = 0 ; y < this . size - 1 ; y ++ ) { for ( let x = 0 ; x < this . size - 1 ; x ++ ) { const color = this . modules [ y ] [ x ] ; if ( color == this . modules [ y ] [ x + 1 ] && color == this . modules [ y + 1 ] [ x ] && color == this . modules [ y + 1 ] [ x + 1 ] ) result += QrCode . PENALTY_N2 ; } } let black = 0 ; for ( const row of this . modules ) { for ( const color of row ) { if ( color ) black ++ ; } } const total = this . size * this . size ; const k = Math . ceil ( Math . abs ( black * 20 - total * 10 ) / total ) - 1 ; result += k * QrCode . PENALTY_N4 ; return result ; } private getAlignmentPatternPositions ( ) { if ( this . version == 1 ) return [ ] ; else { const numAlign = Math . floor ( this . version / 7 ) + 2 ; const step = this . version == 32 ? 26 : Math . ceil ( ( this . size - 13 ) / ( numAlign * 2 - 2 ) ) * 2 ; let result = [ 6 ] ; for ( let pos = this . size - 7 ; result . length < numAlign ; pos -= step ) result . splice ( 1 , 0 , pos ) ; return result ; } } private static getNumRawDataModules ( ver ) { if ( ver < QrCode . MIN_VERSION || ver > QrCode . MAX_VERSION ) throw 'Version number out of range' ; let result = ( 16 * ver + 128 ) * ver + 64 ; if ( ver >= 2 ) { const numAlign = Math . floor ( ver / 7 ) + 2 ; result -= ( 25 * numAlign - 10 ) * numAlign - 55 ; if ( ver >= 7 ) result -= 36 ; } return result ; } private static getNumDataCodewords ( ver , ecl ) { return ( Math . floor ( QrCode . getNumRawDataModules ( ver ) / 8 ) - QrCode . ECC_CODEWORDS_PER_BLOCK [ ecl . ordinal ] [ ver ] * QrCode . NUM_ERROR_CORRECTION_BLOCKS [ ecl . ordinal ] [ ver ] ) ; } private static addRunToHistory ( run , history ) { history . pop ( ) ; history . unshift ( run ) ; } private static hasFinderLikePattern ( runHistory ) { const n = runHistory [ 1 ] ; return ( n > 0 && runHistory [ 2 ] == n && runHistory [ 4 ] == n && runHistory [ 5 ] == n && runHistory [ 3 ] == n * 3 && Math . max ( runHistory [ 0 ] , runHistory [ 6 ] ) >= n * 4 ) ; } public static readonly MIN_VERSION : int = 1 ; public static readonly MAX_VERSION : int = 40 ; private static readonly PENALTY_N1 : int = 3 ; private static readonly PENALTY_N2 : int = 3 ; private static readonly PENALTY_N3 : int = 40 ; private static readonly PENALTY_N4 : int = 10 ; private static readonly ECC_CODEWORDS_PER_BLOCK : Array < Array < int > > = [ [ - 1 , 7 , 10 , 15 , 20 , 26 , 18 , 20 , 24 , 30 , 18 , 20 , 24 , 26 , 30 , 22 , 24 , 28 , 30 , 28 , 28 , 28 , 28 , 30 , 30 , 26 , 28 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 ] , [ - 1 , 10 , 16 , 26 , 18 , 24 , 16 , 18 , 22 , 22 , 26 , 30 , 22 , 22 , 24 , 24 , 28 , 28 , 26 , 26 , 26 , 26 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 , 28 ] , [ - 1 , 13 , 22 , 18 , 26 , 18 , 24 , 18 , 22 , 20 , 24 , 28 , 26 , 24 , 20 , 30 , 24 , 28 , 28 , 26 , 30 , 28 , 30 , 30 , 30 , 30 , 28 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 ] , [ - 1 , 17 , 28 , 22 , 16 , 22 , 28 , 26 , 26 , 24 , 28 , 24 , 28 , 22 , 24 , 24 , 30 , 28 , 28 , 26 , 28 , 30 , 24 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 , 30 ] ] ; private static readonly NUM_ERROR_CORRECTION_BLOCKS : Array < Array < int > > = [ [ - 1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 2 , 4 , 4 , 4 , 4 , 4 , 6 , 6 , 6 , 6 , 7 , 8 , 8 , 9 , 9 , 10 , 12 , 12 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 19 , 20 , 21 , 22 , 24 , 25 ] , [ - 1 , 1 , 1 , 1 , 2 , 2 , 4 , 4 , 4 , 5 , 5 , 5 , 8 , 9 , 9 , 10 , 10 , 11 , 13 , 14 , 16 , 17 , 17 , 18 , 20 , 21 , 23 , 25 , 26 , 28 , 29 , 31 , 33 , 35 , 37 , 38 , 40 , 43 , 45 , 47 , 49 ] , [ - 1 , 1 , 1 , 2 , 2 , 4 , 4 , 6 , 6 , 8 , 8 , 8 , 10 , 12 , 16 , 12 , 17 , 16 , 18 , 21 , 20 , 23 , 23 , 25 , 27 , 29 , 34 , 34 , 35 , 38 , 40 , 43 , 45 , 48 , 51 , 53 , 56 , 59 , 62 , 65 , 68 ] , [ - 1 , 1 , 1 , 2 , 4 , 4 , 4 , 5 , 6 , 8 , 8 , 11 , 11 , 16 , 16 , 18 , 16 , 19 , 21 , 25 , 25 , 25 , 34 , 30 , 32 , 35 , 37 , 40 , 42 , 45 , 48 , 51 , 54 , 57 , 60 , 63 , 66 , 70 , 74 , 77 , 81 ] ] ; } function getBit ( x , i ) { return ( ( x >>> i ) & 1 ) != 0 ; } export class QrSegment { public static makeBytes ( data ) { let bb = new BitBuffer ( ) ; for ( const b of data ) bb . appendBits ( b , 8 ) ; return new QrSegment ( QrSegment . Mode . BYTE , data . length , bb . array ) ; } public static makeNumeric ( digits ) { if ( ! this . NUMERIC_REGEX . test ( digits ) ) throw 'String contains non-numeric characters' ; let bb = new BitBuffer ( ) ; for ( let i = 0 ; i < digits . length ; ) { const n = Math . min ( digits . length - i , 3 ) ; bb . appendBits ( parseInt ( digits . substr ( i , n ) , 10 ) , n * 3 + 1 ) ; i += n ; } return new QrSegment ( QrSegment . Mode . NUMERIC , digits . length , bb . array ) ; } public static makeAlphanumeric ( text ) { if ( ! this . ALPHANUMERIC_REGEX . test ( text ) ) throw 'String contains unencodable characters in alphanumeric mode' ; let bb = new BitBuffer ( ) ; let i ; for ( i = 0 ; i + 2 <= text . length ; i += 2 ) { let temp = QrSegment . ALPHANUMERIC_CHARSET . indexOf ( text . charAt ( i ) ) * 45 ; temp += QrSegment . ALPHANUMERIC_CHARSET . indexOf ( text . charAt ( i + 1 ) ) ; bb . appendBits ( temp , 11 ) ; } if ( i < text . length ) bb . appendBits ( QrSegment . ALPHANUMERIC_CHARSET . indexOf ( text . charAt ( i ) ) , 6 ) ; return new QrSegment ( QrSegment . Mode . ALPHANUMERIC , text . length , bb . array ) ; } public static makeSegments ( text ) { if ( text == '' ) return [ ] ; else if ( this . NUMERIC_REGEX . test ( text ) ) return [ QrSegment . makeNumeric ( text ) ] ; else if ( this . ALPHANUMERIC_REGEX . test ( text ) ) return [ QrSegment . makeAlphanumeric ( text ) ] ; else return [ QrSegment . makeBytes ( QrSegment . toUtf8ByteArray ( text ) ) ] ; } public static makeEci ( assignVal ) { let bb = new BitBuffer ( ) ; if ( assignVal < 0 ) throw 'ECI assignment value out of range' ; else if ( assignVal < 1 << 7 ) bb . appendBits ( assignVal , 8 ) ; else if ( assignVal < 1 << 14 ) { bb . appendBits ( 2 , 2 ) ; bb . appendBits ( assignVal , 14 ) ; } else if ( assignVal < 1000000 ) { bb . appendBits ( 6 , 3 ) ; bb . appendBits ( assignVal , 21 ) ; } else throw 'ECI assignment value out of range' ; return new QrSegment ( QrSegment . Mode . ECI , 0 , bb . array ) ; } public constructor ( public readonly mode , public readonly numChars , private readonly bitData ) { if ( numChars < 0 ) throw 'Invalid argument' ; this . bitData = bitData . slice ( ) ; } public getData ( ) { return this . bitData . slice ( ) ; } public static getTotalBits ( segs , version ) { let result = 0 ; for ( const seg of segs ) { const ccbits = seg . mode . numCharCountBits ( version ) ; if ( seg . numChars >= 1 << ccbits ) return Infinity ; result += 4 + ccbits + seg . bitData . length ; } return result ; } private static toUtf8ByteArray ( str ) { str = encodeURI ( str ) ; let result = [ ] ; for ( let i = 0 ; i < str . length ; i ++ ) { if ( str . charAt ( i ) != '%' ) result . push ( str . charCodeAt ( i ) ) ; else { result . push ( parseInt ( str . substr ( i + 1 , 2 ) , 16 ) ) ; i += 2 ; } } return result ; } public static readonly NUMERIC_REGEX : RegExp = /^[0-9]*$/ ; public static readonly ALPHANUMERIC_REGEX : RegExp = /^[A-Z0-9 $%*+.\/:-]*$/ ; private static readonly ALPHANUMERIC_CHARSET : string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:' ; } class ReedSolomonGenerator { private readonly coefficients : Array < byte > = [ ] ; public constructor ( degree ) { if ( degree < 1 || degree > 255 ) throw 'Degree out of range' ; let coefs = this . coefficients ; for ( let i = 0 ; i < degree - 1 ; i ++ ) coefs . push ( 0 ) ; coefs . push ( 1 ) ; let root = 1 ; for ( let i = 0 ; i < degree ; i ++ ) { for ( let j = 0 ; j < coefs . length ; j ++ ) { coefs [ j ] = ReedSolomonGenerator . multiply ( coefs [ j ] , root ) ; if ( j + 1 < coefs . length ) coefs [ j ] ^= coefs [ j + 1 ] ; } root = ReedSolomonGenerator . multiply ( root , 0x02 ) ; } } public getRemainder ( data ) { let result = this . coefficients . map ( _ => 0 ) ; for ( const b of data ) { const factor = b ^ ( result . shift ( ) as byte ) ; result . push ( 0 ) ; this . coefficients . forEach ( ( coef , i ) => ( result [ i ] ^= ReedSolomonGenerator . multiply ( coef , factor ) ) ) ; } return result ; } private static multiply ( x , y ) { if ( x >>> 8 != 0 || y >>> 8 != 0 ) throw 'Byte out of range' ; let z = 0 ; for ( let i = 7 ; i >= 0 ; i -- ) { z = ( z << 1 ) ^ ( ( z >>> 7 ) * 0x11d ) ; z ^= ( ( y >>> i ) & 1 ) * x ; } if ( z >>> 8 != 0 ) throw 'Assertion error' ; return z as byte ; } } class BitBuffer { public array : Array < bit > = [ ] ; public appendBits ( val , len ) { if ( len < 0 || len > 31 || val >>> len != 0 ) throw 'Value out of range' ; for ( let i = len - 1 ; i >= 0 ; i -- ) this . array . push ( ( val >>> i ) & 1 ) ; } } export namespace QrCode { type int = number ; export class Ecc { public static readonly LOW = new Ecc ( 0 , 1 ) ; public static readonly MEDIUM = new Ecc ( 1 , 0 ) ; public static readonly QUARTILE = new Ecc ( 2 , 3 ) ; public static readonly HIGH = new Ecc ( 3 , 2 ) ; private constructor ( public readonly ordinal , public readonly formatBits ) { } } } export namespace QrSegment { type int = number ; export class Mode { public static readonly NUMERIC = new Mode ( 0x1 , [ 10 , 12 , 14 ] ) ; public static readonly ALPHANUMERIC = new Mode ( 0x2 , [ 9 , 11 , 13 ] ) ; public static readonly BYTE = new Mode ( 0x4 , [ 8 , 16 , 16 ] ) ; public static readonly KANJI = new Mode ( 0x8 , [ 8 , 10 , 12 ] ) ; public static readonly ECI = new Mode ( 0x7 , [ 0 , 0 , 0 ] ) ; private constructor ( public readonly modeBits , private readonly numBitsCharCount ) { } public numCharCountBits ( ver ) { return this . numBitsCharCount [ Math . floor ( ( ver + 7 ) / 17 ) ] ; } } }