code128/
lib.rs

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