22 * Code 16K encoder — stacked barcode based on Code 128
33 * Used in healthcare and electronics
44 *
5- * Structure: 2-16 rows, each row has start pattern + data + check + stop
6- * Each row encodes 5 Code 128 symbol characters
5+ * Structure: 2-16 rows, each with start pattern + 5 symbol characters + stop pattern
6+ * Uses Code 128 bar patterns with row-specific start codes
77 */
88
99import { InvalidInputError , CapacityError } from "../errors" ;
1010
11- // Simplified Code 16K: similar to Codablock F but with fixed 5 codewords per row
12- // and specific start/stop patterns
13-
14- const CODE16K_START = [ 2 , 1 , 2 , 2 , 2 , 2 ] ; // Start pattern (same as Code 128 value 0)
15- const CODE16K_STOP = [ 2 , 3 , 3 , 1 , 1 , 1 , 2 ] ; // Stop pattern
11+ // Code 128 encoding patterns (bar/space widths)
12+ // Each pattern is 6 elements: bar, space, bar, space, bar, space (11 modules total)
13+ // prettier-ignore
14+ const PATTERNS : number [ ] [ ] = [
15+ [ 2 , 1 , 2 , 2 , 2 , 2 ] , [ 2 , 2 , 2 , 1 , 2 , 2 ] , [ 2 , 2 , 2 , 2 , 2 , 1 ] , [ 1 , 2 , 1 , 2 , 2 , 3 ] , [ 1 , 2 , 1 , 3 , 2 , 2 ] ,
16+ [ 1 , 3 , 1 , 2 , 2 , 2 ] , [ 1 , 2 , 2 , 2 , 1 , 3 ] , [ 1 , 2 , 2 , 3 , 1 , 2 ] , [ 1 , 3 , 2 , 2 , 1 , 2 ] , [ 2 , 2 , 1 , 2 , 1 , 3 ] ,
17+ [ 2 , 2 , 1 , 3 , 1 , 2 ] , [ 2 , 3 , 1 , 2 , 1 , 2 ] , [ 1 , 1 , 2 , 2 , 3 , 2 ] , [ 1 , 2 , 2 , 1 , 3 , 2 ] , [ 1 , 2 , 2 , 2 , 3 , 1 ] ,
18+ [ 1 , 1 , 3 , 2 , 2 , 2 ] , [ 1 , 2 , 3 , 1 , 2 , 2 ] , [ 1 , 2 , 3 , 2 , 2 , 1 ] , [ 2 , 2 , 3 , 2 , 1 , 1 ] , [ 2 , 2 , 1 , 1 , 3 , 2 ] ,
19+ [ 2 , 2 , 1 , 2 , 3 , 1 ] , [ 2 , 1 , 3 , 2 , 1 , 2 ] , [ 2 , 2 , 3 , 1 , 1 , 2 ] , [ 3 , 1 , 2 , 1 , 3 , 1 ] , [ 3 , 1 , 1 , 2 , 2 , 2 ] ,
20+ [ 3 , 2 , 1 , 1 , 2 , 2 ] , [ 3 , 2 , 1 , 2 , 2 , 1 ] , [ 3 , 1 , 2 , 2 , 1 , 2 ] , [ 3 , 2 , 2 , 1 , 1 , 2 ] , [ 3 , 2 , 2 , 2 , 1 , 1 ] ,
21+ [ 2 , 1 , 2 , 1 , 2 , 3 ] , [ 2 , 1 , 2 , 3 , 2 , 1 ] , [ 2 , 3 , 2 , 1 , 2 , 1 ] , [ 1 , 1 , 1 , 3 , 2 , 3 ] , [ 1 , 3 , 1 , 1 , 2 , 3 ] ,
22+ [ 1 , 3 , 1 , 3 , 2 , 1 ] , [ 1 , 1 , 2 , 3 , 1 , 3 ] , [ 1 , 3 , 2 , 1 , 1 , 3 ] , [ 1 , 3 , 2 , 3 , 1 , 1 ] , [ 2 , 1 , 1 , 3 , 1 , 3 ] ,
23+ [ 2 , 3 , 1 , 1 , 1 , 3 ] , [ 2 , 3 , 1 , 3 , 1 , 1 ] , [ 1 , 1 , 2 , 1 , 3 , 3 ] , [ 1 , 1 , 2 , 3 , 3 , 1 ] , [ 1 , 3 , 2 , 1 , 3 , 1 ] ,
24+ [ 1 , 1 , 3 , 1 , 2 , 3 ] , [ 1 , 1 , 3 , 3 , 2 , 1 ] , [ 1 , 3 , 3 , 1 , 2 , 1 ] , [ 3 , 1 , 3 , 1 , 2 , 1 ] , [ 2 , 1 , 1 , 3 , 3 , 1 ] ,
25+ [ 2 , 3 , 1 , 1 , 3 , 1 ] , [ 2 , 1 , 3 , 1 , 1 , 3 ] , [ 2 , 1 , 3 , 3 , 1 , 1 ] , [ 2 , 1 , 3 , 1 , 3 , 1 ] , [ 3 , 1 , 1 , 1 , 2 , 3 ] ,
26+ [ 3 , 1 , 1 , 3 , 2 , 1 ] , [ 3 , 3 , 1 , 1 , 2 , 1 ] , [ 3 , 1 , 2 , 1 , 1 , 3 ] , [ 3 , 1 , 2 , 3 , 1 , 1 ] , [ 3 , 3 , 2 , 1 , 1 , 1 ] ,
27+ [ 2 , 1 , 2 , 1 , 3 , 2 ] , [ 2 , 1 , 2 , 2 , 3 , 1 ] , [ 2 , 1 , 2 , 3 , 1 , 2 ] , [ 1 , 4 , 2 , 1 , 1 , 2 ] , [ 1 , 1 , 4 , 2 , 1 , 2 ] ,
28+ [ 1 , 2 , 4 , 1 , 1 , 2 ] , [ 1 , 1 , 1 , 2 , 4 , 2 ] , [ 1 , 2 , 1 , 1 , 4 , 2 ] , [ 1 , 2 , 1 , 2 , 4 , 1 ] , [ 4 , 2 , 1 , 1 , 1 , 2 ] ,
29+ [ 4 , 2 , 1 , 2 , 1 , 1 ] , [ 4 , 1 , 2 , 1 , 1 , 2 ] , [ 2 , 4 , 1 , 2 , 1 , 1 ] , [ 2 , 2 , 1 , 4 , 1 , 1 ] , [ 4 , 1 , 1 , 2 , 1 , 2 ] ,
30+ [ 1 , 1 , 1 , 2 , 2 , 4 ] , [ 1 , 1 , 1 , 4 , 2 , 2 ] , [ 1 , 2 , 1 , 1 , 2 , 4 ] , [ 1 , 2 , 1 , 4 , 2 , 1 ] , [ 1 , 4 , 1 , 1 , 2 , 2 ] ,
31+ [ 1 , 4 , 1 , 2 , 2 , 1 ] , [ 1 , 1 , 2 , 2 , 1 , 4 ] , [ 1 , 1 , 2 , 4 , 1 , 2 ] , [ 1 , 2 , 2 , 1 , 1 , 4 ] , [ 1 , 2 , 2 , 4 , 1 , 1 ] ,
32+ [ 1 , 4 , 2 , 1 , 1 , 2 ] , [ 1 , 4 , 2 , 2 , 1 , 1 ] , [ 2 , 4 , 1 , 1 , 1 , 2 ] , [ 2 , 2 , 1 , 1 , 1 , 4 ] , [ 4 , 1 , 1 , 2 , 2 , 1 ] ,
33+ [ 4 , 2 , 2 , 1 , 1 , 1 ] , [ 2 , 1 , 2 , 1 , 4 , 1 ] , [ 2 , 1 , 4 , 1 , 2 , 1 ] , [ 4 , 1 , 2 , 1 , 2 , 1 ] , [ 1 , 1 , 1 , 1 , 4 , 3 ] ,
34+ [ 1 , 1 , 1 , 3 , 4 , 1 ] , [ 1 , 3 , 1 , 1 , 4 , 1 ] , [ 1 , 1 , 4 , 1 , 1 , 3 ] , [ 1 , 1 , 4 , 3 , 1 , 1 ] , [ 4 , 1 , 1 , 1 , 1 , 3 ] ,
35+ [ 4 , 1 , 1 , 3 , 1 , 1 ] , [ 1 , 1 , 3 , 1 , 4 , 1 ] , [ 1 , 1 , 4 , 1 , 3 , 1 ] , [ 2 , 1 , 1 , 4 , 1 , 2 ] , [ 2 , 1 , 1 , 2 , 1 , 4 ] ,
36+ [ 2 , 1 , 1 , 2 , 3 , 2 ] ,
37+ ] ;
38+
39+ const STOP_PATTERN = [ 2 , 3 , 3 , 1 , 1 , 1 , 2 ] ; // 7 elements, 13 modules
40+
41+ // Code 16K uses Code 128 value 96 (CODE_A) as the start for each row
42+ // The first data codeword in each row encodes mode + row information
43+ const CODE_B = 100 ;
1644
1745export interface Code16KResult {
1846 matrix : boolean [ ] [ ] ;
@@ -41,7 +69,7 @@ export function encodeCode16K(text: string): Code16KResult {
4169 values . push ( code - 32 ) ;
4270 }
4371
44- // 5 data codewords per row, max 16 rows = 80 data codewords
72+ // 5 data symbol characters per row, max 16 rows
4573 const cwPerRow = 5 ;
4674 const rows = Math . min ( 16 , Math . max ( 2 , Math . ceil ( values . length / cwPerRow ) ) ) ;
4775
@@ -51,57 +79,55 @@ export function encodeCode16K(text: string): Code16KResult {
5179
5280 // Pad to fill rows
5381 while ( values . length < rows * cwPerRow ) {
54- values . push ( 0 ) ; // space padding
82+ values . push ( 0 ) ; // space padding (Code B value 0 = space)
5583 }
5684
5785 const matrix : boolean [ ] [ ] = [ ] ;
5886
5987 for ( let r = 0 ; r < rows ; r ++ ) {
60- const modules : boolean [ ] = [ ] ;
88+ // Build codeword sequence for this row
89+ const rowCodes : number [ ] = [ ] ;
6190
62- // Start pattern
63- let isBar = true ;
64- for ( const w of CODE16K_START ) {
65- for ( let i = 0 ; i < w ; i ++ ) modules . push ( isBar ) ;
66- isBar = ! isBar ;
67- }
91+ // Start symbol: Code 128 Start B (value 104)
92+ rowCodes . push ( 104 ) ;
6893
69- // Row indicator (row number as 2 codewords)
70- const rowCodes = [ Math . floor ( r / 10 ) , r % 10 ] ;
71- for ( const code of rowCodes ) {
72- // Simple encoding: each digit as bar-space pattern
73- for ( let bit = 3 ; bit >= 0 ; bit -- ) {
74- const isOn = ( ( code >> bit ) & 1 ) === 1 ;
75- modules . push ( isOn ) ;
76- modules . push ( ! isOn ) ;
77- }
78- }
94+ // First codeword: row indicator using CODE_B + row number mod
95+ // For Code 16K, the first symbol after start encodes the row mode
96+ rowCodes . push ( CODE_B ) ; // Switch to Code B
97+ rowCodes . push ( r ) ; // Row number as first data codeword
7998
80- // Data codewords
99+ // Data codewords for this row
81100 for ( let c = 0 ; c < cwPerRow ; c ++ ) {
82- const v = values [ r * cwPerRow + c ] ! ;
83- // Encode as 11-module pattern (simplified)
84- for ( let bit = 6 ; bit >= 0 ; bit -- ) {
85- modules . push ( ( ( v >> bit ) & 1 ) === 1 ) ;
86- }
87- // 4-module separator
88- modules . push ( false , true , false , true ) ;
101+ rowCodes . push ( values [ r * cwPerRow + c ] ! ) ;
89102 }
90103
91- // Check digit
92- let checksum = r ;
93- for ( let c = 0 ; c < cwPerRow ; c ++ ) {
94- checksum += values [ r * cwPerRow + c ] ! * ( c + 1 ) ;
104+ // Calculate check digit (mod 103, same as Code 128)
105+ let checksum = rowCodes [ 0 ] ! ;
106+ for ( let i = 1 ; i < rowCodes . length ; i ++ ) {
107+ checksum += rowCodes [ i ] ! * i ;
95108 }
96- const check = checksum % 107 ;
97- for ( let bit = 6 ; bit >= 0 ; bit -- ) {
98- modules . push ( ( ( check >> bit ) & 1 ) === 1 ) ;
109+ rowCodes . push ( checksum % 103 ) ;
110+
111+ // Convert codewords to module pattern
112+ const modules : boolean [ ] = [ ] ;
113+ let isBar = true ;
114+
115+ // Encode all codewords using Code 128 patterns
116+ for ( const cw of rowCodes ) {
117+ const pattern = PATTERNS [ cw ] ! ;
118+ for ( const w of pattern ) {
119+ for ( let i = 0 ; i < w ; i ++ ) {
120+ modules . push ( isBar ) ;
121+ }
122+ isBar = ! isBar ;
123+ }
99124 }
100125
101126 // Stop pattern
102- isBar = true ;
103- for ( const w of CODE16K_STOP ) {
104- for ( let i = 0 ; i < w ; i ++ ) modules . push ( isBar ) ;
127+ for ( const w of STOP_PATTERN ) {
128+ for ( let i = 0 ; i < w ; i ++ ) {
129+ modules . push ( isBar ) ;
130+ }
105131 isBar = ! isBar ;
106132 }
107133
0 commit comments