///|
pub fn code128_checksum(symbols : ArrayView[Int]) -> Int {
if symbols.is_empty() {
0
} else {
let sum = for i = 1, acc = symbols[0]; i < symbols.length(); i = i + 1 {
continue i + 1, acc + symbols[i] * i
} nobreak {
acc
}
sum % 103
}
}
///|
pub fn verify_code128_symbols(symbols_with_check : ArrayView[Int]) -> Bool {
let len = symbols_with_check.length()
if len < 3 {
false
} else if symbols_with_check[len - 1] == 106 {
let check_pos = len - 2
code128_checksum(symbols_with_check[:check_pos]) ==
symbols_with_check[check_pos]
} else {
let body = symbols_with_check[:len - 1]
code128_checksum(body) == symbols_with_check[len - 1]
}
}
///|
pub fn is_valid_code128_symbol(symbol : Int) -> Bool {
symbol >= 0 && symbol <= 106
}
///|
pub struct Code128Encoded {
data : String
symbols : Array[Int]
checksum : Int
symbol_count : Int
module_count : Int
set_switches : Int
} derive(Debug, Eq)
///|
pub enum Code128Error {
UnsupportedCharacter(Int)
InvalidFNC1Placement
EmptyPayload
} derive(Debug, Eq)
///|
fn validate_fnc1_placement(data : String) -> Code128Error? {
if data.get_char(data.length() - 1) == Some('\u{1D}') {
Some(InvalidFNC1Placement)
} else {
None
}
}
///|
pub fn encode_code128(data : String) -> Result[Code128Encoded, Code128Error] {
if data.is_empty() {
return Err(EmptyPayload)
}
match validate_fnc1_placement(data) {
Some(error) => return Err(error)
None => ()
}
let symbols : Array[Int] = []
let mut index = 0
let mut switches = 0
let mut mode = if starts_with_fnc1(data) {
if has_numeric_pair(data, 1) {
2
} else {
1
}
} else if has_numeric_pair(data, 0) && numeric_run(data, 0) >= 4 {
2
} else {
1
}
symbols.push(if mode == 2 { 105 } else { 104 })
if starts_with_fnc1(data) {
symbols.push(102)
index = 1
}
while index < data.length() {
if mode == 2 {
if is_fnc1_at(data, index) {
symbols.push(102)
index = index + 1
} else if has_numeric_pair(data, index) {
symbols.push(two_digit_value(data, index))
index = index + 2
} else {
symbols.push(100)
mode = 1
switches = switches + 1
}
} else if is_fnc1_at(data, index) {
symbols.push(102)
index = index + 1
} else if numeric_run(data, index) >= 4 && has_numeric_pair(data, index) {
symbols.push(99)
mode = 2
switches = switches + 1
} else {
let ch = data.get_char(index).unwrap()
if ch.to_int() < 32 || ch.to_int() > 127 {
return Err(UnsupportedCharacter(ch.to_int()))
}
symbols.push(ch.to_int() - 32)
index = index + 1
}
}
let checksum = code128_checksum(symbols)
symbols.push(checksum)
symbols.push(106)
Ok({
data,
symbols,
checksum,
symbol_count: symbols.length(),
module_count: code128_module_count(symbols),
set_switches: switches,
})
}
///|
fn starts_with_fnc1(data : String) -> Bool {
data.get_char(0) == Some('\u{1D}')
}
///|
fn is_fnc1_at(data : String, index : Int) -> Bool {
data.get_char(index) == Some('\u{1D}')
}
///|
fn has_numeric_pair(data : String, index : Int) -> Bool {
data.get_char(index) is Some(first) &&
data.get_char(index + 1) is Some(second) &&
first.is_ascii_digit() &&
second.is_ascii_digit()
}
///|
fn numeric_run(data : String, index : Int) -> Int {
let mut count = 0
let mut pos = index
while pos < data.length() && data.get_char(pos).unwrap().is_ascii_digit() {
count = count + 1
pos = pos + 1
}
count
}
///|
fn two_digit_value(data : String, index : Int) -> Int {
(data[index].to_int() - ('0' : UInt16).to_int()) * 10 +
data[index + 1].to_int() -
('0' : UInt16).to_int()
}
///|
pub fn code128_module_count(symbols : ArrayView[Int]) -> Int {
let mut total = 0
for symbol in symbols {
let width = code128_width(symbol)
total = total + width
}
total
}
///|
pub fn encode_gs1_128(
message : GS1Message,
) -> Result[Code128Encoded, Code128Error] {
let parts : Array[String] = []
for index, element in message.elements {
parts.push(element.value)
if index + 1 < message.elements.length() && element.ai.kind is Variable {
parts.push("\u{1D}")
}
}
encode_code128("\u{1D}" + parts.join(""))
}
///|
pub fn render_code128_ascii(
encoded : Code128Encoded,
quiet_zone? : Int = 10,
bar? : String = "██",
space? : String = " ",
) -> String {
let output = StringBuilder()
for _ in 0.. Bool {
symbols.length() >= 2 && is_start_code(symbols[0]) && symbols[1] == 102
}
///|
fn is_start_code(symbol : Int) -> Bool {
symbol == 103 || symbol == 104 || symbol == 105
}