Skip to content

Commit 5232d84

Browse files
fix(#60): rewrite Code 16K to use proper Code 128 patterns
Replace BCD/binary encoding with proper Code 128 bar/space width patterns. Each row now has Start B + CODE_B + row indicator + 5 data codewords + mod 103 check digit + Stop pattern. Also format web demo files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 421f1d4 commit 5232d84

4 files changed

Lines changed: 731 additions & 235 deletions

File tree

src/encoders/code16k.ts

Lines changed: 70 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,45 @@
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

99
import { 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

1745
export 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

Comments
 (0)