1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
//! This crate implements encoding and decoding of Code 128 linear barcodes
//! as defined in ISO/IEC 15417:2007.
//!
//! To achieve a minimal encoding size a dynamic programming approach is used.
//! The full 256 bit range can be encoded. For compatibility it is recommended
//! to stay within printable ASCII though.
//!
//! ## Example
//!
//! ```rust
//! use code128::{Code128, modules_to_blocks};
//!
//! let code = Code128::encode(b"Hello!");
//! println!("{}", modules_to_blocks(code.modules()));
//! ```
//! To create other outputs check out the [Code128] documentation.
//!
//! ## Charsets
//!
//! For best compatibility it is recommended to only encode printable
//! [ASCII](https://en.wikipedia.org/wiki/ASCII#Character_set)
//! characters, i.e. `0x20` (space) to `0x7E` (tilde).
//!
//! Code 128 interprets the range `0x00` to `0x7F` as ASCII, and `0xA0` to
//! `0xFF` as [ISO/IEC 8859-1](https://en.wikipedia.org/wiki/ISO/IEC_8859-1)
//! (Latin 1). The remaining range from `0x80` to `0x9F` can also be encoded
//! but has no visual representation.
//!
//! In the real world most implementations only handle `0x00` to `0x7F`, or
//! interpret results as UTF-8, although that is not covered by the standard.
//! However, if you are in control of encoding and decoding this is technically
//! possible and maybe even a contemporary choice.
mod decode;
mod encode;
mod latin1;
mod unicode;
pub use decode::{decode, decode_str, DecodingError};
pub use unicode::modules_to_blocks;
const SHIFT_MODE: u8 = 98;
const SWITCH_C: u8 = 99;
const SWITCH_B: u8 = 100;
const SWITCH_A: u8 = 101;
const START_A: u8 = 103;
const START_B: u8 = 104;
const START_C: u8 = 105;
const STOP: u8 = 108;
fn checksum(symbols: impl Iterator<Item = u8>) -> u8 {
(symbols
.enumerate()
.map(|(i, idx)| (i.max(1) as u64) * idx as u64)
.sum::<u64>()
% 103) as u8
}
/// Representation of a "black line" in the code.
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct Module {
/// The width of the line.
///
/// Ranges from one to four.
pub width: u8,
/// White space after the line.
pub space: u8,
}
/// A Code 128.
///
/// You can use the [modules iterator](Self::modules) and the [size](Self::len)
/// to compute a visualization. A module corresponds to a "black
/// line" of the code and has a unitless width between one and four, as well
/// as a free space after it, also sized between one and four.
///
/// ## Pseudo code for visualization
///
/// Be aware that the standard demands a quiet zone of size 10 around the code.
/// To compute the size of a block, multiply its width with the available space
/// for the code (with a unit) divided by `20 + len()`. Here 20 corresponds to
/// the quiet zone.
///
/// ```rust
/// # use code128::Code128;
/// let code = Code128::encode(b"Code128 <3");
/// let available_space = 100.0; // unit is, say, "pt"
/// let line_width = available_space / code.len() as f64;
/// let mut pos = 10;
/// for module in code.modules() {
/// let x = pos as f64 * line_width;
/// let width = module.width as f64 * line_width;
/// // print line at `x` pt, `width` pt wide
/// pos += module.width + module.space;
/// }
/// ```
pub struct Code128 {
indices: Vec<u8>,
}
impl Code128 {
/// Encode the bytes as Code 128.
///
/// See the [module documentation](crate) for hints on charsets.
pub fn encode(data: &[u8]) -> Self {
Code128Builder::default().encode(data)
}
/// Encode the string as Code 128 using Latin 1.
///
/// The functions returns `None` if the string includes characters not
/// included in Latin 1.
///
/// The control characters of ASCII, `0x00` to `0x19`, are also encoded.
pub fn encode_str(text: &str) -> Option<Self> {
Code128Builder::default().encode_str(text)
}
/// Get the sequence of modules this Code 128 consists of.
pub fn modules(&self) -> impl Iterator<Item = Module> + '_ {
self.indices
.iter()
.flat_map(|idx| encode::bits_to_modules(encode::PATTERNS[*idx as usize]))
}
/// Get the total width of the code in units of the [Module](crate::Module)
/// with the quiet zone included.
pub fn len(&self) -> usize {
self.indices.len() * 11 + 2 + 20
}
/// Whether this Code 128 encodes empty data.
pub fn is_empty(&self) -> bool {
self.indices.len() == 3
}
}
#[doc(hidden)]
#[derive(Copy, Clone)]
pub enum Encoder {
Mixed,
DynamicProgramming,
}
/// Builder for encoding a Code 128 with more control.
#[doc(hidden)]
pub struct Code128Builder {
encoder: Encoder,
}
impl Code128Builder {
/// Which encoding should be used.
pub fn with_encoder(self, encoder: Encoder) -> Self {
Self { encoder }
}
/// Encode the bytes as Code 128.
///
/// See the [module documentation](crate) for hints on charsets.
pub fn encode(self, data: &[u8]) -> Code128 {
let mut indices = match self.encoder {
Encoder::Mixed => encode::encode_as_indices(data),
Encoder::DynamicProgramming => encode::encode_as_indices_dp(data, vec![]),
};
indices.push(checksum(indices.iter().cloned()));
indices.push(STOP);
Code128 { indices }
}
/// Encode the string as Code 128 using Latin 1.
///
/// The functions returns `None` if the string includes characters not
/// included in Latin 1.
///
/// The control characters of ASCII, `0x00` to `0x19`, are also encoded.
pub fn encode_str(self, text: &str) -> Option<Code128> {
latin1::utf8_to_latin1(text).map(|data| self.encode(&data))
}
}
#[allow(clippy::derivable_impls)]
impl Default for Code128Builder {
fn default() -> Self {
Self {
encoder: Encoder::Mixed,
}
}
}
#[test]
fn test_module_size() {
for pattern in &encode::PATTERNS[0..107] {
let size: u32 = encode::bits_to_modules(*pattern)
.into_iter()
.map(|m| m.width as u32 + m.space as u32)
.sum();
assert_eq!(size, 11);
}
let size: u32 = encode::bits_to_modules(encode::PATTERNS[STOP as usize])
.into_iter()
.map(|m| m.width as u32 + m.space as u32)
.sum();
assert_eq!(size, 13);
}
#[test]
fn test_code_size() {
let code = Code128::encode(b"foo");
let size = code
.modules()
.map(|m| m.width as u32 + m.space as u32)
.sum::<u32>()
+ 20;
assert_eq!(code.len(), size as usize);
}
#[test]
fn test_is_empty() {
assert!(Code128::encode(b"").is_empty());
assert!(!Code128::encode(b".").is_empty());
}
#[test]
fn test_256() {
for x in 0..=255 {
let code = Code128::encode(&[x]);
let modules: Vec<Module> = code.modules().collect();
assert_eq!(decode(&modules), Ok(vec![x]));
}
}
#[test]
fn test_string_encoding_example() {
let code = Code128::encode_str("Füße").unwrap();
let modules: Vec<_> = code.modules().collect();
assert_eq!(decode_str(&modules), Ok("Füße".into()));
}