barcodes/linear/
code93.rs1#![forbid(unsafe_code)]
12
13use crate::common::{
14 buffer::SliceWriter, errors::EncodeError, traits::BarcodeEncoder, types::Encoded,
15};
16
17const MAX_DATA: usize = 256;
19
20const CODE93_CHARS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
24
25const CODE93_PATTERNS: [u16; 48] = [
32 0b100010100, 0b101001000, 0b101000100, 0b101000010, 0b100101000, 0b100100100, 0b100100010, 0b101010000, 0b100010010, 0b100001010, 0b110101000, 0b110100100, 0b110100010, 0b110010100, 0b110010010, 0b110001010, 0b101101000, 0b101100100, 0b101100010, 0b100110100, 0b100011010, 0b101011000, 0b101001100, 0b101000110, 0b100101100, 0b100010110, 0b110110100, 0b110110010, 0b110101100, 0b110100110, 0b110010110, 0b110011010, 0b101101100, 0b101100110, 0b100110110, 0b100111010, 0b100101110, 0b111010100, 0b111010010, 0b111001010, 0b101101110, 0b101110110, 0b110101110, 0b100100110, 0b111011010, 0b111010110, 0b100110010, 0b101011110, ];
81
82const START_STOP: usize = 47;
84
85pub struct Code93;
106
107impl BarcodeEncoder for Code93 {
108 type Input = str;
109
110 fn encode_into(input: &str, buf: &mut [bool]) -> Result<Encoded, EncodeError> {
111 if input.is_empty() {
112 return Err(EncodeError::InvalidInput("Code 93 input must not be empty"));
113 }
114
115 let mut values = [0usize; MAX_DATA + 2];
117 let mut n = 0;
118 for ch in input.chars() {
119 let v = char_value(ch).ok_or(EncodeError::InvalidCharacter(ch))?;
120 if n >= MAX_DATA {
121 return Err(EncodeError::DataTooLong);
122 }
123 values[n] = v;
124 n += 1;
125 }
126
127 let c = check_value(&values[..n], 20);
129 values[n] = c;
130 n += 1;
131 let k = check_value(&values[..n], 15);
132 values[n] = k;
133 n += 1;
134
135 let len = encode_bars(&values[..n], buf)?;
136 Ok(Encoded::Linear { len, height: 50 })
137 }
138
139 fn symbology_name() -> &'static str {
140 "Code 93"
141 }
142}
143
144fn char_value(ch: char) -> Option<usize> {
148 let byte = u8::try_from(ch as u32).ok()?;
149 CODE93_CHARS.iter().position(|&c| c == byte)
150}
151
152fn check_value(values: &[usize], max_weight: u32) -> usize {
157 let mut weight = 1u32;
158 let mut sum = 0u32;
159 for &v in values.iter().rev() {
160 sum += weight * v as u32;
161 weight = if weight == max_weight { 1 } else { weight + 1 };
162 }
163 (sum % 47) as usize
164}
165
166fn append_pattern(w: &mut SliceWriter, value: usize) -> Result<(), EncodeError> {
168 let pattern = CODE93_PATTERNS[value];
169 for i in (0..9).rev() {
170 w.push((pattern >> i) & 1 == 1)?;
171 }
172 Ok(())
173}
174
175fn encode_bars(values: &[usize], buf: &mut [bool]) -> Result<usize, EncodeError> {
177 let mut w = SliceWriter::new(buf);
178
179 append_pattern(&mut w, START_STOP)?;
180 for &v in values {
181 append_pattern(&mut w, v)?;
182 }
183 append_pattern(&mut w, START_STOP)?;
184
185 w.push(true)?;
187
188 Ok(w.len())
189}
190
191#[cfg(test)]
194mod tests {
195 use super::*;
196
197 fn encode_len(input: &str) -> usize {
198 let mut buf = [false; 4096];
199 match Code93::encode_into(input, &mut buf).unwrap() {
200 Encoded::Linear { len, .. } => len,
201 _ => panic!("expected linear"),
202 }
203 }
204
205 #[test]
206 fn test_encode_basic() {
207 assert!(encode_len("CODE93") > 0);
208 }
209
210 #[test]
211 fn test_encode_special_chars() {
212 assert!(encode_len("HELLO WORLD") > 0);
213 }
214
215 #[test]
216 fn test_invalid_lowercase() {
217 let mut buf = [false; 4096];
218 assert!(Code93::encode_into("hello", &mut buf).is_err());
219 }
220
221 #[test]
222 fn test_invalid_symbol() {
223 let mut buf = [false; 4096];
224 assert!(Code93::encode_into("ABC!DEF", &mut buf).is_err());
225 }
226
227 #[test]
228 fn test_empty_input() {
229 let mut buf = [false; 4096];
230 assert!(Code93::encode_into("", &mut buf).is_err());
231 }
232
233 #[test]
234 fn test_buffer_too_small() {
235 let mut buf = [false; 4];
236 assert_eq!(
237 Code93::encode_into("CODE93", &mut buf),
238 Err(EncodeError::BufferTooSmall)
239 );
240 }
241
242 #[test]
243 fn test_symbology_name() {
244 assert_eq!(Code93::symbology_name(), "Code 93");
245 }
246
247 #[test]
248 fn test_bar_count() {
249 assert_eq!(encode_len("A"), 5 * 9 + 1);
251 }
252
253 #[test]
254 fn test_check_values_known() {
255 let mut values = [0usize; 8];
257 let mut n = 0;
258 for ch in "CODE93".chars() {
259 values[n] = char_value(ch).unwrap();
260 n += 1;
261 }
262 let c = check_value(&values[..n], 20);
263 assert_eq!(c, char_value('P').unwrap());
264 values[n] = c;
265 n += 1;
266 let k = check_value(&values[..n], 15);
267 assert_eq!(k, char_value('V').unwrap());
268 }
269
270 #[cfg(feature = "alloc")]
271 #[test]
272 fn test_svg_output() {
273 let svg = Code93::encode("TEST").unwrap().to_svg_string();
274 assert!(svg.starts_with("<svg "));
275 }
276}