rxing/datamatrix/encoder/
high_level_encoder.rs1use std::sync::Arc;
18
19use crate::common::{CharacterSet, Result};
20use crate::{Dimension, Exceptions};
21
22use super::{
23 ASCIIEncoder, Base256Encoder, C40Encoder, EdifactEncoder, Encoder, EncoderContext,
24 SymbolInfoLookup, SymbolShapeHint, TextEncoder, X12Encoder,
25};
26#[allow(dead_code)]
27const DEFAULT_ENCODING: CharacterSet = CharacterSet::ISO8859_1;
28
29const PAD: u8 = 129;
37pub const LATCH_TO_C40: u8 = 230;
41pub const LATCH_TO_BASE256: u8 = 231;
45pub const UPPER_SHIFT: u8 = 235;
61const MACRO_05: u8 = 236;
65const MACRO_06: u8 = 237;
69pub const LATCH_TO_ANSIX12: u8 = 238;
73pub const LATCH_TO_TEXT: u8 = 239;
77pub const LATCH_TO_EDIFACT: u8 = 240;
81pub const C40_UNLATCH: u8 = 254;
90pub const X12_UNLATCH: u8 = 254;
94
95pub const MACRO_05_HEADER: &str = "[)>\u{001E}05\u{001D}"; pub const MACRO_06_HEADER: &str = "[)>\u{001E}06\u{001D}"; pub const MACRO_TRAILER: &str = "\u{001E}\u{0004}";
107
108pub const ASCII_ENCODATION: usize = 0;
109pub const C40_ENCODATION: usize = 1;
110pub const TEXT_ENCODATION: usize = 2;
111pub const X12_ENCODATION: usize = 3;
112pub const EDIFACT_ENCODATION: usize = 4;
113pub const BASE256_ENCODATION: usize = 5;
114
115fn randomize253State(codewordPosition: u32) -> String {
116 let pseudoRandom = ((149 * codewordPosition) % 253) + 1;
117 let tempVariable = PAD as u32 + pseudoRandom;
118 if tempVariable <= 254 {
119 char::from_u32(tempVariable)
120 } else {
121 char::from_u32(tempVariable - 254)
122 }
123 .unwrap_or(char::default())
125 .to_string()
126 }
128
129pub fn encodeHighLevel(msg: &str) -> Result<String> {
137 encodeHighLevelWithDimensionForceC40(msg, SymbolShapeHint::FORCE_NONE, None, None, false)
138}
139
140pub fn encodeHighLevelSIL(msg: &str, symbol_lookup: Option<SymbolInfoLookup>) -> Result<String> {
148 encodeHighLevelWithDimensionForceC40WithSymbolInfoLookup(
149 msg,
150 SymbolShapeHint::FORCE_NONE,
151 None,
152 None,
153 false,
154 symbol_lookup,
155 )
156}
157
158pub fn encodeHighLevelWithDimension(
170 msg: &str,
171 shape: SymbolShapeHint,
172 minSize: Option<Dimension>,
173 maxSize: Option<Dimension>,
174) -> Result<String> {
175 encodeHighLevelWithDimensionForceC40(msg, shape, minSize, maxSize, false)
176}
177
178pub fn encodeHighLevelWithDimensionForceC40WithSymbolInfoLookup(
179 msg: &str,
180 shape: SymbolShapeHint,
181 minSize: Option<Dimension>,
182 maxSize: Option<Dimension>,
183 forceC40: bool,
184 symbol_lookup: Option<SymbolInfoLookup>,
185) -> Result<String> {
186 let c40Encoder = Arc::new(C40Encoder::new());
188 let encoders: [Arc<dyn Encoder>; 6] = [
189 Arc::new(ASCIIEncoder::new()),
190 c40Encoder.clone(),
191 Arc::new(TextEncoder::new()),
192 Arc::new(X12Encoder::new()),
193 Arc::new(EdifactEncoder::new()),
194 Arc::new(Base256Encoder::new()),
195 ];
196
197 let mut context = if let Some(symbol_table) = symbol_lookup {
198 EncoderContext::with_symbol_info_lookup(msg, symbol_table)?
199 } else {
200 EncoderContext::new(msg)?
201 };
202 context.setSymbolShape(shape);
204 context.setSizeConstraints(minSize, maxSize);
205
206 if msg.starts_with(MACRO_05_HEADER) && msg.ends_with(MACRO_TRAILER) {
207 context.writeCodeword(MACRO_05);
208 context.setSkipAtEnd(2);
209 context.pos += MACRO_05_HEADER.chars().count() as u32;
210 } else if msg.starts_with(MACRO_06_HEADER) && msg.ends_with(MACRO_TRAILER) {
211 context.writeCodeword(MACRO_06);
212 context.setSkipAtEnd(2);
213 context.pos += MACRO_06_HEADER.chars().count() as u32;
214 }
215
216 let mut encodingMode = ASCII_ENCODATION; if forceC40 {
219 c40Encoder.encodeMaximalC40(&mut context)?;
220 encodingMode = context.getNewEncoding().ok_or(Exceptions::ILLEGAL_STATE)?;
221 context.resetEncoderSignal();
222 }
223
224 while context.hasMoreCharacters() {
225 encoders[encodingMode].encode(&mut context)?;
226 if context.getNewEncoding().is_some() {
227 encodingMode = context.getNewEncoding().ok_or(Exceptions::ILLEGAL_STATE)?;
228 context.resetEncoderSignal();
229 }
230 }
231 let len = context.getCodewordCount();
232 context.updateSymbolInfo();
233 let capacity = context
234 .getSymbolInfo()
235 .ok_or(Exceptions::ILLEGAL_STATE)?
236 .getDataCapacity();
237 if len < capacity as usize
238 && encodingMode != ASCII_ENCODATION
239 && encodingMode != BASE256_ENCODATION
240 && encodingMode != EDIFACT_ENCODATION
241 {
242 context.writeCodeword(0xfe); }
245 if context.getCodewords().chars().count() < capacity as usize {
248 context.writeCodeword(PAD)
250 }
251 while context.getCodewords().chars().count() < capacity as usize {
252 context.writeCodewords(&randomize253State(
254 context.getCodewords().chars().count() as u32 + 1,
255 ))
256 }
257
258 Ok(context.getCodewords().to_owned())
259}
260
261pub fn encodeHighLevelWithDimensionForceC40(
274 msg: &str,
275 shape: SymbolShapeHint,
276 minSize: Option<Dimension>,
277 maxSize: Option<Dimension>,
278 forceC40: bool,
279) -> Result<String> {
280 encodeHighLevelWithDimensionForceC40WithSymbolInfoLookup(
281 msg, shape, minSize, maxSize, forceC40, None,
282 )
283}
284
285pub fn lookAheadTest(msg: &str, startpos: u32, currentMode: u32) -> usize {
286 let newMode = lookAheadTestIntern(msg, startpos, currentMode);
287 if currentMode as usize == X12_ENCODATION && newMode == X12_ENCODATION {
288 let endpos = (startpos + 3).min(msg.chars().count() as u32);
290 for i in startpos..endpos {
291 if let Some(c) = msg.chars().nth(i as usize) {
293 if !isNativeX12(c) {
294 return ASCII_ENCODATION;
295 }
296 }
297 }
298 } else if currentMode as usize == EDIFACT_ENCODATION && newMode == EDIFACT_ENCODATION {
299 let endpos = (startpos + 4).min(msg.chars().count() as u32);
301 for i in startpos..endpos {
302 if let Some(c) = msg.chars().nth(i as usize) {
304 if !isNativeEDIFACT(c) {
305 return ASCII_ENCODATION;
306 }
307 }
308 }
309 }
310 newMode
311}
312
313fn lookAheadTestIntern(msg: &str, startpos: u32, currentMode: u32) -> usize {
314 if startpos as usize >= msg.chars().count() {
315 return currentMode as usize;
316 }
317 let mut charCounts: [f32; 6];
318 if currentMode == ASCII_ENCODATION as u32 {
320 charCounts = [0.0, 1.0, 1.0, 1.0, 1.0, 1.25];
321 } else {
322 charCounts = [1.0, 2.0, 2.0, 2.0, 2.0, 2.25];
323 charCounts[currentMode as usize] = 0.0;
324 }
325
326 let mut charsProcessed = 0;
327 let mut mins = [0u8; 6];
328 let mut intCharCounts = [0u32; 6];
329 loop {
330 if (startpos + charsProcessed) == msg.chars().count() as u32 {
332 mins.fill(0);
333 intCharCounts.fill(0);
334 let min = findMinimums(&charCounts, &mut intCharCounts, u32::MAX, &mut mins);
337 let minCount = getMinimumCount(&mins);
338
339 if intCharCounts[ASCII_ENCODATION] == min {
340 return ASCII_ENCODATION;
341 }
342 if minCount == 1 {
343 if mins[BASE256_ENCODATION] > 0 {
344 return BASE256_ENCODATION;
345 }
346 if mins[EDIFACT_ENCODATION] > 0 {
347 return EDIFACT_ENCODATION;
348 }
349 if mins[TEXT_ENCODATION] > 0 {
350 return TEXT_ENCODATION;
351 }
352 if mins[X12_ENCODATION] > 0 {
353 return X12_ENCODATION;
354 }
355 }
356 return C40_ENCODATION;
357 }
358
359 let Some(c) = msg.chars().nth((startpos + charsProcessed) as usize) else {
360 break 0;
361 };
362 charsProcessed += 1;
363
364 if isDigit(c) {
366 charCounts[ASCII_ENCODATION] += 0.5;
367 } else if isExtendedASCII(c) {
368 charCounts[ASCII_ENCODATION] = charCounts[ASCII_ENCODATION].ceil();
369 charCounts[ASCII_ENCODATION] += 2.0;
370 } else {
371 charCounts[ASCII_ENCODATION] = charCounts[ASCII_ENCODATION].ceil();
372 charCounts[ASCII_ENCODATION] += 1.0;
373 }
374
375 if isNativeC40(c) {
377 charCounts[C40_ENCODATION] += 2.0 / 3.0;
378 } else if isExtendedASCII(c) {
379 charCounts[C40_ENCODATION] += 8.0 / 3.0;
380 } else {
381 charCounts[C40_ENCODATION] += 4.0 / 3.0;
382 }
383
384 if isNativeText(c) {
386 charCounts[TEXT_ENCODATION] += 2.0 / 3.0;
387 } else if isExtendedASCII(c) {
388 charCounts[TEXT_ENCODATION] += 8.0 / 3.0;
389 } else {
390 charCounts[TEXT_ENCODATION] += 4.0 / 3.0;
391 }
392
393 if isNativeX12(c) {
395 charCounts[X12_ENCODATION] += 2.0 / 3.0;
396 } else if isExtendedASCII(c) {
397 charCounts[X12_ENCODATION] += 13.0 / 3.0;
398 } else {
399 charCounts[X12_ENCODATION] += 10.0 / 3.0;
400 }
401
402 if isNativeEDIFACT(c) {
404 charCounts[EDIFACT_ENCODATION] += 3.0 / 4.0;
405 } else if isExtendedASCII(c) {
406 charCounts[EDIFACT_ENCODATION] += 17.0 / 4.0;
407 } else {
408 charCounts[EDIFACT_ENCODATION] += 13.0 / 4.0;
409 }
410
411 if isSpecialB256(c) {
413 charCounts[BASE256_ENCODATION] += 4.0;
414 } else {
415 charCounts[BASE256_ENCODATION] += 1.0;
416 }
417
418 if charsProcessed >= 4 {
420 mins.fill(0);
421 intCharCounts.fill(0);
422 findMinimums(&charCounts, &mut intCharCounts, u32::MAX, &mut mins);
425
426 if intCharCounts[ASCII_ENCODATION]
427 < min5(
428 intCharCounts[BASE256_ENCODATION],
429 intCharCounts[C40_ENCODATION],
430 intCharCounts[TEXT_ENCODATION],
431 intCharCounts[X12_ENCODATION],
432 intCharCounts[EDIFACT_ENCODATION],
433 )
434 {
435 return ASCII_ENCODATION;
436 }
437 if intCharCounts[BASE256_ENCODATION] < intCharCounts[ASCII_ENCODATION]
438 || intCharCounts[BASE256_ENCODATION] + 1
439 < min4(
440 intCharCounts[C40_ENCODATION],
441 intCharCounts[TEXT_ENCODATION],
442 intCharCounts[X12_ENCODATION],
443 intCharCounts[EDIFACT_ENCODATION],
444 )
445 {
446 return BASE256_ENCODATION;
447 }
448 if intCharCounts[EDIFACT_ENCODATION] + 1
449 < min5(
450 intCharCounts[BASE256_ENCODATION],
451 intCharCounts[C40_ENCODATION],
452 intCharCounts[TEXT_ENCODATION],
453 intCharCounts[X12_ENCODATION],
454 intCharCounts[ASCII_ENCODATION],
455 )
456 {
457 return EDIFACT_ENCODATION;
458 }
459 if intCharCounts[TEXT_ENCODATION] + 1
460 < min5(
461 intCharCounts[BASE256_ENCODATION],
462 intCharCounts[C40_ENCODATION],
463 intCharCounts[EDIFACT_ENCODATION],
464 intCharCounts[X12_ENCODATION],
465 intCharCounts[ASCII_ENCODATION],
466 )
467 {
468 return TEXT_ENCODATION;
469 }
470 if intCharCounts[X12_ENCODATION] + 1
471 < min5(
472 intCharCounts[BASE256_ENCODATION],
473 intCharCounts[C40_ENCODATION],
474 intCharCounts[EDIFACT_ENCODATION],
475 intCharCounts[TEXT_ENCODATION],
476 intCharCounts[ASCII_ENCODATION],
477 )
478 {
479 return X12_ENCODATION;
480 }
481 if intCharCounts[C40_ENCODATION] + 1
482 < min4(
483 intCharCounts[ASCII_ENCODATION],
484 intCharCounts[BASE256_ENCODATION],
485 intCharCounts[EDIFACT_ENCODATION],
486 intCharCounts[TEXT_ENCODATION],
487 )
488 {
489 if intCharCounts[C40_ENCODATION] < intCharCounts[X12_ENCODATION] {
490 return C40_ENCODATION;
491 }
492 if intCharCounts[C40_ENCODATION] == intCharCounts[X12_ENCODATION] {
493 let mut _p = startpos + charsProcessed + 1;
494 for tc in msg.chars() {
495 if isX12TermSep(tc) {
498 return X12_ENCODATION;
499 }
500 if !isNativeX12(tc) {
501 break;
502 }
503 _p += 1;
504 }
505 return C40_ENCODATION;
506 }
507 }
508 }
509 }
510}
511
512fn min5(f1: u32, f2: u32, f3: u32, f4: u32, f5: u32) -> u32 {
513 min4(f1, f2, f3, f4).min(f5)
514}
515
516fn min4(f1: u32, f2: u32, f3: u32, f4: u32) -> u32 {
517 f1.min(f2.min(f3.min(f4)))
518 }
520
521fn findMinimums(
522 charCounts: &[f32; 6],
523 intCharCounts: &mut [u32; 6],
524 min: u32,
525 mins: &mut [u8],
526) -> u32 {
527 let mut min = min;
528 for i in 0..6 {
529 intCharCounts[i] = charCounts[i].ceil() as u32;
531 let current = intCharCounts[i]; if min > current {
533 min = current;
534 mins.fill(0);
535 }
537 if min == current {
538 mins[i] += 1;
539 }
540 }
541 min
542}
543
544fn getMinimumCount(mins: &[u8]) -> u32 {
545 mins.iter().take(6).sum::<u8>() as u32
546}
547
548#[inline]
549pub const fn isDigit(ch: char) -> bool {
550 ch.is_ascii_digit()
551}
552
553#[inline]
554pub fn isExtendedASCII(ch: char) -> bool {
555 (ch as u8) >= 128 }
557
558pub fn isNativeC40(ch: char) -> bool {
559 (ch == ' ') || ch.is_ascii_digit() || ch.is_ascii_uppercase()
560}
561
562pub fn isNativeText(ch: char) -> bool {
563 (ch == ' ') || ch.is_ascii_digit() || ch.is_ascii_lowercase()
564}
565
566pub fn isNativeX12(ch: char) -> bool {
567 isX12TermSep(ch) || (ch == ' ') || ch.is_ascii_digit() || ch.is_ascii_uppercase()
568}
569
570fn isX12TermSep(ch: char) -> bool {
571 (ch == '\r') || (ch == '*')
573 || (ch == '>')
574}
575
576pub fn isNativeEDIFACT(ch: char) -> bool {
577 (' '..='^').contains(&ch)
578}
579
580fn isSpecialB256(_ch: char) -> bool {
581 false }
584
585pub fn determineConsecutiveDigitCount(msg: &str, startpos: u32) -> u32 {
593 let len = msg.chars().count(); let mut idx = startpos;
595 while (idx as usize) < len && isDigit(msg.chars().nth(idx as usize).unwrap_or_default()) {
597 idx += 1;
598 }
599 idx - startpos
600}
601
602pub fn illegalCharacter(c: char) -> Result<()> {
603 Err(Exceptions::illegal_argument_with(format!(
606 "Illegal character: {c} (0x{c})"
607 )))
608}