88
99import { InvalidInputError , CapacityError } from "../errors" ;
1010
11- // Code 128 patterns (same as code128.ts but self-contained for independence)
11+ // Code 128 constants
1212const START_C = 105 ;
13- const _STOP = 106 ;
13+ const CODE_A = 101 ;
1414const CODE_B = 100 ;
15+ const CODE_C = 99 ;
1516
17+ // Full Code 128 encoding patterns (bar/space widths), indices 0-105
18+ // Each pattern is 6 elements: bar, space, bar, space, bar, space
1619const PATTERNS : number [ ] [ ] = [
17- [ 2 , 1 , 2 , 2 , 2 , 2 ] ,
18- [ 2 , 2 , 2 , 1 , 2 , 2 ] ,
19- [ 2 , 2 , 2 , 2 , 2 , 1 ] ,
20- [ 1 , 2 , 1 , 2 , 2 , 3 ] ,
21- [ 1 , 2 , 1 , 3 , 2 , 2 ] ,
22- [ 1 , 3 , 1 , 2 , 2 , 2 ] ,
23- [ 1 , 2 , 2 , 2 , 1 , 3 ] ,
24- [ 1 , 2 , 2 , 3 , 1 , 2 ] ,
25- [ 1 , 3 , 2 , 2 , 1 , 2 ] ,
26- [ 2 , 2 , 1 , 2 , 1 , 3 ] ,
27- [ 2 , 2 , 1 , 3 , 1 , 2 ] ,
28- [ 2 , 3 , 1 , 2 , 1 , 2 ] ,
29- [ 1 , 1 , 2 , 2 , 3 , 2 ] ,
30- [ 1 , 2 , 2 , 1 , 3 , 2 ] ,
31- [ 1 , 2 , 2 , 2 , 3 , 1 ] ,
32- [ 1 , 1 , 3 , 2 , 2 , 2 ] ,
33- [ 1 , 2 , 3 , 1 , 2 , 2 ] ,
34- [ 1 , 2 , 3 , 2 , 2 , 1 ] ,
35- [ 2 , 2 , 3 , 2 , 1 , 1 ] ,
36- [ 2 , 2 , 1 , 1 , 3 , 2 ] ,
37- [ 2 , 2 , 1 , 2 , 3 , 1 ] ,
38- [ 2 , 1 , 3 , 2 , 1 , 2 ] ,
39- [ 2 , 2 , 3 , 1 , 1 , 2 ] ,
40- [ 3 , 1 , 2 , 1 , 3 , 1 ] ,
41- [ 3 , 1 , 1 , 2 , 2 , 2 ] ,
42- [ 3 , 2 , 1 , 1 , 2 , 2 ] ,
43- [ 3 , 2 , 1 , 2 , 2 , 1 ] ,
44- [ 3 , 1 , 2 , 2 , 1 , 2 ] ,
45- [ 3 , 2 , 2 , 1 , 1 , 2 ] ,
46- [ 3 , 2 , 2 , 2 , 1 , 1 ] ,
47- [ 2 , 1 , 2 , 1 , 2 , 3 ] ,
48- [ 2 , 1 , 2 , 3 , 2 , 1 ] ,
49- [ 2 , 3 , 2 , 1 , 2 , 1 ] ,
20+ [ 2 , 1 , 2 , 2 , 2 , 2 ] , // 0
21+ [ 2 , 2 , 2 , 1 , 2 , 2 ] , // 1
22+ [ 2 , 2 , 2 , 2 , 2 , 1 ] , // 2
23+ [ 1 , 2 , 1 , 2 , 2 , 3 ] , // 3
24+ [ 1 , 2 , 1 , 3 , 2 , 2 ] , // 4
25+ [ 1 , 3 , 1 , 2 , 2 , 2 ] , // 5
26+ [ 1 , 2 , 2 , 2 , 1 , 3 ] , // 6
27+ [ 1 , 2 , 2 , 3 , 1 , 2 ] , // 7
28+ [ 1 , 3 , 2 , 2 , 1 , 2 ] , // 8
29+ [ 2 , 2 , 1 , 2 , 1 , 3 ] , // 9
30+ [ 2 , 2 , 1 , 3 , 1 , 2 ] , // 10
31+ [ 2 , 3 , 1 , 2 , 1 , 2 ] , // 11
32+ [ 1 , 1 , 2 , 2 , 3 , 2 ] , // 12
33+ [ 1 , 2 , 2 , 1 , 3 , 2 ] , // 13
34+ [ 1 , 2 , 2 , 2 , 3 , 1 ] , // 14
35+ [ 1 , 1 , 3 , 2 , 2 , 2 ] , // 15
36+ [ 1 , 2 , 3 , 1 , 2 , 2 ] , // 16
37+ [ 1 , 2 , 3 , 2 , 2 , 1 ] , // 17
38+ [ 2 , 2 , 3 , 2 , 1 , 1 ] , // 18
39+ [ 2 , 2 , 1 , 1 , 3 , 2 ] , // 19
40+ [ 2 , 2 , 1 , 2 , 3 , 1 ] , // 20
41+ [ 2 , 1 , 3 , 2 , 1 , 2 ] , // 21
42+ [ 2 , 2 , 3 , 1 , 1 , 2 ] , // 22
43+ [ 3 , 1 , 2 , 1 , 3 , 1 ] , // 23
44+ [ 3 , 1 , 1 , 2 , 2 , 2 ] , // 24
45+ [ 3 , 2 , 1 , 1 , 2 , 2 ] , // 25
46+ [ 3 , 2 , 1 , 2 , 2 , 1 ] , // 26
47+ [ 3 , 1 , 2 , 2 , 1 , 2 ] , // 27
48+ [ 3 , 2 , 2 , 1 , 1 , 2 ] , // 28
49+ [ 3 , 2 , 2 , 2 , 1 , 1 ] , // 29
50+ [ 2 , 1 , 2 , 1 , 2 , 3 ] , // 30
51+ [ 2 , 1 , 2 , 3 , 2 , 1 ] , // 31
52+ [ 2 , 3 , 2 , 1 , 2 , 1 ] , // 32
53+ [ 1 , 1 , 1 , 3 , 2 , 3 ] , // 33
54+ [ 1 , 3 , 1 , 1 , 2 , 3 ] , // 34
55+ [ 1 , 3 , 1 , 3 , 2 , 1 ] , // 35
56+ [ 1 , 1 , 2 , 3 , 1 , 3 ] , // 36
57+ [ 1 , 3 , 2 , 1 , 1 , 3 ] , // 37
58+ [ 1 , 3 , 2 , 3 , 1 , 1 ] , // 38
59+ [ 2 , 1 , 1 , 3 , 1 , 3 ] , // 39
60+ [ 2 , 3 , 1 , 1 , 1 , 3 ] , // 40
61+ [ 2 , 3 , 1 , 3 , 1 , 1 ] , // 41
62+ [ 1 , 1 , 2 , 1 , 3 , 3 ] , // 42
63+ [ 1 , 1 , 2 , 3 , 3 , 1 ] , // 43
64+ [ 1 , 3 , 2 , 1 , 3 , 1 ] , // 44
65+ [ 1 , 1 , 3 , 1 , 2 , 3 ] , // 45
66+ [ 1 , 1 , 3 , 3 , 2 , 1 ] , // 46
67+ [ 1 , 3 , 3 , 1 , 2 , 1 ] , // 47
68+ [ 3 , 1 , 3 , 1 , 2 , 1 ] , // 48
69+ [ 2 , 1 , 1 , 3 , 3 , 1 ] , // 49
70+ [ 2 , 3 , 1 , 1 , 3 , 1 ] , // 50
71+ [ 2 , 1 , 3 , 1 , 1 , 3 ] , // 51
72+ [ 2 , 1 , 3 , 3 , 1 , 1 ] , // 52
73+ [ 2 , 1 , 3 , 1 , 3 , 1 ] , // 53
74+ [ 3 , 1 , 1 , 1 , 2 , 3 ] , // 54
75+ [ 3 , 1 , 1 , 3 , 2 , 1 ] , // 55
76+ [ 3 , 3 , 1 , 1 , 2 , 1 ] , // 56
77+ [ 3 , 1 , 2 , 1 , 1 , 3 ] , // 57
78+ [ 3 , 1 , 2 , 3 , 1 , 1 ] , // 58
79+ [ 3 , 3 , 2 , 1 , 1 , 1 ] , // 59
80+ [ 2 , 1 , 2 , 1 , 3 , 2 ] , // 60
81+ [ 2 , 1 , 2 , 2 , 3 , 1 ] , // 61
82+ [ 2 , 1 , 2 , 3 , 1 , 2 ] , // 62
83+ [ 1 , 4 , 2 , 1 , 1 , 2 ] , // 63
84+ [ 1 , 1 , 4 , 2 , 1 , 2 ] , // 64
85+ [ 1 , 2 , 4 , 1 , 1 , 2 ] , // 65
86+ [ 1 , 1 , 1 , 2 , 4 , 2 ] , // 66
87+ [ 1 , 2 , 1 , 1 , 4 , 2 ] , // 67
88+ [ 1 , 2 , 1 , 2 , 4 , 1 ] , // 68
89+ [ 4 , 2 , 1 , 1 , 1 , 2 ] , // 69
90+ [ 4 , 2 , 1 , 2 , 1 , 1 ] , // 70
91+ [ 4 , 1 , 2 , 1 , 1 , 2 ] , // 71
92+ [ 2 , 4 , 1 , 2 , 1 , 1 ] , // 72
93+ [ 2 , 2 , 1 , 4 , 1 , 1 ] , // 73
94+ [ 4 , 1 , 1 , 2 , 1 , 2 ] , // 74
95+ [ 1 , 1 , 1 , 2 , 2 , 4 ] , // 75
96+ [ 1 , 1 , 1 , 4 , 2 , 2 ] , // 76
97+ [ 1 , 2 , 1 , 1 , 2 , 4 ] , // 77
98+ [ 1 , 2 , 1 , 4 , 2 , 1 ] , // 78
99+ [ 1 , 4 , 1 , 1 , 2 , 2 ] , // 79
100+ [ 1 , 4 , 1 , 2 , 2 , 1 ] , // 80
101+ [ 1 , 1 , 2 , 2 , 1 , 4 ] , // 81
102+ [ 1 , 1 , 2 , 4 , 1 , 2 ] , // 82
103+ [ 1 , 2 , 2 , 1 , 1 , 4 ] , // 83
104+ [ 1 , 2 , 2 , 4 , 1 , 1 ] , // 84
105+ [ 1 , 4 , 2 , 1 , 1 , 2 ] , // 85
106+ [ 1 , 4 , 2 , 2 , 1 , 1 ] , // 86
107+ [ 2 , 4 , 1 , 1 , 1 , 2 ] , // 87
108+ [ 2 , 2 , 1 , 1 , 1 , 4 ] , // 88
109+ [ 4 , 1 , 1 , 2 , 2 , 1 ] , // 89
110+ [ 4 , 2 , 2 , 1 , 1 , 1 ] , // 90
111+ [ 2 , 1 , 2 , 1 , 4 , 1 ] , // 91
112+ [ 2 , 1 , 4 , 1 , 2 , 1 ] , // 92
113+ [ 4 , 1 , 2 , 1 , 2 , 1 ] , // 93
114+ [ 1 , 1 , 1 , 1 , 4 , 3 ] , // 94
115+ [ 1 , 1 , 1 , 3 , 4 , 1 ] , // 95
116+ [ 1 , 3 , 1 , 1 , 4 , 1 ] , // 96 (CODE_A)
117+ [ 1 , 1 , 4 , 1 , 1 , 3 ] , // 97 (CODE_B)
118+ [ 1 , 1 , 4 , 3 , 1 , 1 ] , // 98 (CODE_C)
119+ [ 4 , 1 , 1 , 1 , 1 , 3 ] , // 99 (CODE_C)
120+ [ 4 , 1 , 1 , 3 , 1 , 1 ] , // 100 (CODE_B)
121+ [ 1 , 1 , 3 , 1 , 4 , 1 ] , // 101 (CODE_A)
122+ [ 1 , 1 , 4 , 1 , 3 , 1 ] , // 102 (FNC1)
123+ [ 2 , 1 , 1 , 4 , 1 , 2 ] , // 103 (START_A)
124+ [ 2 , 1 , 1 , 2 , 1 , 4 ] , // 104 (START_B)
125+ [ 2 , 1 , 1 , 2 , 3 , 2 ] , // 105 (START_C)
50126] ;
51- // Only first 33 patterns shown — full table exists in code128.ts
52- // For Codablock F we only need values 0-106
53127
54128const STOP_PATTERN = [ 2 , 3 , 3 , 1 , 1 , 1 , 2 ] ;
55129
@@ -59,6 +133,115 @@ export interface CodablockFResult {
59133 cols : number ;
60134}
61135
136+ /** Count consecutive digit characters from a given position */
137+ function countDigitsFrom ( text : string , pos : number ) : number {
138+ let count = 0 ;
139+ while ( pos + count < text . length ) {
140+ const c = text . charCodeAt ( pos + count ) ;
141+ if ( c < 48 || c > 57 ) break ;
142+ count ++ ;
143+ }
144+ return count ;
145+ }
146+
147+ /**
148+ * Determine the optimal Code 128 charset for a character.
149+ * Returns "A" for control chars (0-31), "B" for printable ASCII (32-126), or null if unsupported.
150+ */
151+ function charsetFor ( charCode : number ) : "A" | "B" | null {
152+ if ( charCode >= 0 && charCode < 32 ) return "A" ;
153+ if ( charCode >= 32 && charCode <= 126 ) return "B" ;
154+ return null ;
155+ }
156+
157+ /**
158+ * Encode text into Code 128 codeword values with automatic charset switching.
159+ * Supports Code A (control chars), Code B (printable ASCII), and Code C (digit pairs).
160+ */
161+ function encodeValues ( text : string ) : number [ ] {
162+ const values : number [ ] = [ ] ;
163+ let pos = 0 ;
164+
165+ // Determine initial charset
166+ const initialDigits = countDigitsFrom ( text , 0 ) ;
167+ let currentCharset : "A" | "B" | "C" ;
168+ if ( initialDigits >= 4 || ( initialDigits >= 2 && initialDigits === text . length ) ) {
169+ currentCharset = "C" ;
170+ } else if ( text . length > 0 && text . charCodeAt ( 0 ) < 32 ) {
171+ currentCharset = "A" ;
172+ } else {
173+ currentCharset = "B" ;
174+ }
175+
176+ while ( pos < text . length ) {
177+ if ( currentCharset === "C" ) {
178+ const digits = countDigitsFrom ( text , pos ) ;
179+ if ( digits >= 2 ) {
180+ // Encode digit pairs
181+ const pairCount = Math . floor ( digits / 2 ) ;
182+ for ( let i = 0 ; i < pairCount ; i ++ ) {
183+ const d1 = text . charCodeAt ( pos ) - 48 ;
184+ const d2 = text . charCodeAt ( pos + 1 ) - 48 ;
185+ values . push ( d1 * 10 + d2 ) ;
186+ pos += 2 ;
187+ }
188+ } else {
189+ // Switch out of Code C
190+ const charCode = pos < text . length ? text . charCodeAt ( pos ) : - 1 ;
191+ const cs = charCode >= 0 ? charsetFor ( charCode ) : "B" ;
192+ if ( cs === "A" ) {
193+ values . push ( CODE_A ) ;
194+ currentCharset = "A" ;
195+ } else {
196+ values . push ( CODE_B ) ;
197+ currentCharset = "B" ;
198+ }
199+ }
200+ } else {
201+ // Code A or Code B
202+ const numRun = countDigitsFrom ( text , pos ) ;
203+ if ( numRun >= 4 || ( numRun >= 2 && pos + numRun >= text . length ) ) {
204+ values . push ( CODE_C ) ;
205+ currentCharset = "C" ;
206+ continue ;
207+ }
208+
209+ const charCode = text . charCodeAt ( pos ) ;
210+ const needed = charsetFor ( charCode ) ;
211+ if ( needed === null ) {
212+ throw new InvalidInputError (
213+ `Codablock F: unsupported character "${ text [ pos ] } " (code ${ charCode } )` ,
214+ ) ;
215+ }
216+
217+ if ( needed !== currentCharset ) {
218+ if ( needed === "A" ) {
219+ values . push ( CODE_A ) ;
220+ currentCharset = "A" ;
221+ } else {
222+ values . push ( CODE_B ) ;
223+ currentCharset = "B" ;
224+ }
225+ }
226+
227+ if ( currentCharset === "A" ) {
228+ // Code A: control chars 0-31 → values 64-95, printable 32-95 → values 0-63
229+ if ( charCode < 32 ) {
230+ values . push ( charCode + 64 ) ;
231+ } else {
232+ values . push ( charCode - 32 ) ;
233+ }
234+ } else {
235+ // Code B: printable 32-126 → values 0-94
236+ values . push ( charCode - 32 ) ;
237+ }
238+ pos ++ ;
239+ }
240+ }
241+
242+ return values ;
243+ }
244+
62245/**
63246 * Encode text as Codablock F (stacked Code 128)
64247 *
@@ -70,16 +253,8 @@ export function encodeCodablockF(text: string, options?: { columns?: number }):
70253 throw new InvalidInputError ( "Codablock F input must not be empty" ) ;
71254 }
72255
73- // Encode all characters as Code 128B values
74- const values : number [ ] = [ ] ;
75- for ( const ch of text ) {
76- const code = ch . charCodeAt ( 0 ) ;
77- if ( code >= 32 && code <= 126 ) {
78- values . push ( code - 32 ) ; // Code B encoding
79- } else {
80- throw new InvalidInputError ( `Codablock F: unsupported character "${ ch } " (code ${ code } )` ) ;
81- }
82- }
256+ // Encode text into Code 128 codeword values
257+ const values = encodeValues ( text ) ;
83258
84259 // Determine columns per row
85260 const cols = options ?. columns ?? Math . min ( 10 , Math . max ( 4 , Math . ceil ( values . length / 5 ) ) ) ;
@@ -129,7 +304,7 @@ export function encodeCodablockF(text: string, options?: { columns?: number }):
129304 const modules : boolean [ ] = [ ] ;
130305
131306 for ( const code of codes ) {
132- const pattern = PATTERNS [ code % PATTERNS . length ] ! ;
307+ const pattern = PATTERNS [ code ] ! ;
133308 let isBar = true ;
134309 for ( const w of pattern ) {
135310 for ( let i = 0 ; i < w ; i ++ ) {
0 commit comments