1#[cfg(feature = "qr")]
2use alloc::collections::BTreeMap;
3#[cfg(feature = "qr")]
4use alloc::vec;
5use alloc::{string::String, vec::Vec};
6#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
7use core::ops::RangeInclusive;
8
9mod bits;
10#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
11mod reed_solomon;
12
13#[cfg(feature = "micro-qr")]
14mod micro;
15#[cfg(feature = "qr")]
16mod model2;
17#[cfg(any(feature = "qr", feature = "rmqr"))]
18mod optimizer;
19#[cfg(feature = "rmqr")]
20mod rmqr;
21
22use bits::BitBuffer;
23
24use crate::EncodeError;
25
26#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
27#[inline]
28const fn numeric_character_capacity(capacity_bits: usize, overhead_bits: usize) -> usize {
29 let payload_bits = capacity_bits.saturating_sub(overhead_bits);
30
31 payload_bits / 10 * 3
32 + match payload_bits % 10 {
33 4..=6 => 1,
34 7..=9 => 2,
35 _ => 0,
36 }
37}
38
39#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
40#[inline]
41const fn ensure_input_length(
42 length: usize,
43 per_symbol_capacity: usize,
44 capacity_bits: usize,
45 symbol_count: usize,
46) -> Result<(), EncodeError> {
47 if length > per_symbol_capacity.saturating_mul(symbol_count) {
48 Err(EncodeError::DataTooLong {
49 required_bits: None,
50 capacity_bits: capacity_bits.saturating_mul(symbol_count),
51 })
52 } else {
53 Ok(())
54 }
55}
56
57#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
61#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
62#[non_exhaustive]
63pub enum SymbolErrorCorrection {
64 #[cfg(feature = "micro-qr")]
66 DetectionOnly,
67 Low,
69 Medium,
71 Quartile,
73 #[cfg(any(feature = "qr", feature = "rmqr"))]
75 High,
76}
77
78#[cfg(feature = "qr")]
80#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
81pub enum QrErrorCorrection {
82 Low,
84 Medium,
86 Quartile,
88 High,
90}
91
92#[cfg(feature = "qr")]
93impl QrErrorCorrection {
94 #[inline]
95 pub(crate) const fn qr_ordinal(self) -> usize {
96 match self {
97 Self::Low => 0,
98 Self::Medium => 1,
99 Self::Quartile => 2,
100 Self::High => 3,
101 }
102 }
103
104 #[inline]
105 pub(crate) const fn qr_format_bits(self) -> u8 {
106 match self {
107 Self::Low => 1,
108 Self::Medium => 0,
109 Self::Quartile => 3,
110 Self::High => 2,
111 }
112 }
113}
114
115#[cfg(feature = "qr")]
116impl From<QrErrorCorrection> for SymbolErrorCorrection {
117 #[inline]
118 fn from(value: QrErrorCorrection) -> Self {
119 match value {
120 QrErrorCorrection::Low => Self::Low,
121 QrErrorCorrection::Medium => Self::Medium,
122 QrErrorCorrection::Quartile => Self::Quartile,
123 QrErrorCorrection::High => Self::High,
124 }
125 }
126}
127
128#[cfg(feature = "micro-qr")]
130#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
131pub enum MicroErrorCorrection {
132 DetectionOnly,
134 Low,
136 Medium,
138 Quartile,
140}
141
142#[cfg(feature = "micro-qr")]
143impl From<MicroErrorCorrection> for SymbolErrorCorrection {
144 #[inline]
145 fn from(value: MicroErrorCorrection) -> Self {
146 match value {
147 MicroErrorCorrection::DetectionOnly => Self::DetectionOnly,
148 MicroErrorCorrection::Low => Self::Low,
149 MicroErrorCorrection::Medium => Self::Medium,
150 MicroErrorCorrection::Quartile => Self::Quartile,
151 }
152 }
153}
154
155#[cfg(feature = "rmqr")]
157#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
158pub enum RmqrErrorCorrection {
159 Medium,
161 High,
163}
164
165#[cfg(feature = "rmqr")]
166impl From<RmqrErrorCorrection> for SymbolErrorCorrection {
167 #[inline]
168 fn from(value: RmqrErrorCorrection) -> Self {
169 match value {
170 RmqrErrorCorrection::Medium => Self::Medium,
171 RmqrErrorCorrection::High => Self::High,
172 }
173 }
174}
175
176#[cfg(feature = "qr")]
178#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
179pub struct QrVersion(u8);
180
181#[cfg(feature = "qr")]
182impl QrVersion {
183 pub const MAX: Self = Self(40);
185 pub const MIN: Self = Self(1);
187
188 #[inline]
190 pub const fn new(value: u8) -> Result<Self, EncodeError> {
191 if value >= 1 && value <= 40 {
192 Ok(Self(value))
193 } else {
194 Err(EncodeError::InvalidVersionRange)
195 }
196 }
197
198 #[inline]
200 pub const fn value(self) -> u8 {
201 self.0
202 }
203}
204
205#[cfg(feature = "micro-qr")]
207#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
208pub enum MicroVersion {
209 M1,
211 M2,
213 M3,
215 M4,
217}
218
219#[cfg(feature = "micro-qr")]
220impl MicroVersion {
221 #[inline]
223 pub const fn size(self) -> usize {
224 match self {
225 Self::M1 => 11,
226 Self::M2 => 13,
227 Self::M3 => 15,
228 Self::M4 => 17,
229 }
230 }
231}
232
233#[cfg(feature = "rmqr")]
235#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
236#[repr(u8)]
237pub enum RmqrVersion {
238 R7x43,
240 R7x59,
242 R7x77,
244 R7x99,
246 R7x139,
248 R9x43,
250 R9x59,
252 R9x77,
254 R9x99,
256 R9x139,
258 R11x27,
260 R11x43,
262 R11x59,
264 R11x77,
266 R11x99,
268 R11x139,
270 R13x27,
272 R13x43,
274 R13x59,
276 R13x77,
278 R13x99,
280 R13x139,
282 R15x43,
284 R15x59,
286 R15x77,
288 R15x99,
290 R15x139,
292 R17x43,
294 R17x59,
296 R17x77,
298 R17x99,
300 R17x139,
302}
303
304#[cfg(feature = "rmqr")]
305impl RmqrVersion {
306 pub(crate) const ALL: [Self; 32] = [
307 Self::R7x43,
308 Self::R7x59,
309 Self::R7x77,
310 Self::R7x99,
311 Self::R7x139,
312 Self::R9x43,
313 Self::R9x59,
314 Self::R9x77,
315 Self::R9x99,
316 Self::R9x139,
317 Self::R11x27,
318 Self::R11x43,
319 Self::R11x59,
320 Self::R11x77,
321 Self::R11x99,
322 Self::R11x139,
323 Self::R13x27,
324 Self::R13x43,
325 Self::R13x59,
326 Self::R13x77,
327 Self::R13x99,
328 Self::R13x139,
329 Self::R15x43,
330 Self::R15x59,
331 Self::R15x77,
332 Self::R15x99,
333 Self::R15x139,
334 Self::R17x43,
335 Self::R17x59,
336 Self::R17x77,
337 Self::R17x99,
338 Self::R17x139,
339 ];
340 pub(crate) const ALL_BY_AREA: [Self; 32] = Self::sorted_by_area();
342 pub const MAX: Self = Self::R17x139;
344 pub const MIN: Self = Self::R7x43;
346
347 const fn sorted_by_area() -> [Self; 32] {
348 let mut versions = Self::ALL;
349 let length = versions.len();
350 let mut index = 0;
351
352 while index < length {
354 let mut smallest = index;
355 let mut candidate = index + 1;
356
357 while candidate < length {
358 if versions[candidate].area_key() < versions[smallest].area_key() {
359 smallest = candidate;
360 }
361
362 candidate += 1;
363 }
364
365 let swap = versions[index];
366 versions[index] = versions[smallest];
367 versions[smallest] = swap;
368 index += 1;
369 }
370
371 versions
372 }
373
374 const fn area_key(self) -> u64 {
376 let width = self.width() as u64;
377 let height = self.height() as u64;
378
379 (width * height) * 1_000_000 + height * 1_000 + width
380 }
381
382 #[inline]
384 pub const fn value(self) -> u8 {
385 self as u8
386 }
387
388 #[inline]
390 pub const fn width(self) -> usize {
391 match self {
392 Self::R11x27 | Self::R13x27 => 27,
393 Self::R7x43
394 | Self::R9x43
395 | Self::R11x43
396 | Self::R13x43
397 | Self::R15x43
398 | Self::R17x43 => 43,
399 Self::R7x59
400 | Self::R9x59
401 | Self::R11x59
402 | Self::R13x59
403 | Self::R15x59
404 | Self::R17x59 => 59,
405 Self::R7x77
406 | Self::R9x77
407 | Self::R11x77
408 | Self::R13x77
409 | Self::R15x77
410 | Self::R17x77 => 77,
411 Self::R7x99
412 | Self::R9x99
413 | Self::R11x99
414 | Self::R13x99
415 | Self::R15x99
416 | Self::R17x99 => 99,
417 Self::R7x139
418 | Self::R9x139
419 | Self::R11x139
420 | Self::R13x139
421 | Self::R15x139
422 | Self::R17x139 => 139,
423 }
424 }
425
426 #[inline]
428 pub const fn height(self) -> usize {
429 match self {
430 Self::R7x43 | Self::R7x59 | Self::R7x77 | Self::R7x99 | Self::R7x139 => 7,
431 Self::R9x43 | Self::R9x59 | Self::R9x77 | Self::R9x99 | Self::R9x139 => 9,
432 Self::R11x27
433 | Self::R11x43
434 | Self::R11x59
435 | Self::R11x77
436 | Self::R11x99
437 | Self::R11x139 => 11,
438 Self::R13x27
439 | Self::R13x43
440 | Self::R13x59
441 | Self::R13x77
442 | Self::R13x99
443 | Self::R13x139 => 13,
444 Self::R15x43 | Self::R15x59 | Self::R15x77 | Self::R15x99 | Self::R15x139 => 15,
445 Self::R17x43 | Self::R17x59 | Self::R17x77 | Self::R17x99 | Self::R17x139 => 17,
446 }
447 }
448}
449
450#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
454#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
455#[non_exhaustive]
456pub enum SymbolVersion {
457 #[cfg(feature = "qr")]
459 Qr(QrVersion),
460 #[cfg(feature = "micro-qr")]
462 Micro(MicroVersion),
463 #[cfg(feature = "rmqr")]
465 Rmqr(RmqrVersion),
466}
467
468#[cfg(feature = "qr")]
470#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
471pub struct QrMask(u8);
472
473#[cfg(feature = "qr")]
474impl QrMask {
475 #[inline]
477 pub const fn new(value: u8) -> Result<Self, EncodeError> {
478 if value < 8 { Ok(Self(value)) } else { Err(EncodeError::InvalidMask) }
479 }
480
481 #[inline]
483 pub const fn value(self) -> u8 {
484 self.0
485 }
486}
487
488#[cfg(feature = "micro-qr")]
490#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
491pub struct MicroMask(u8);
492
493#[cfg(feature = "micro-qr")]
494impl MicroMask {
495 #[inline]
497 pub const fn new(value: u8) -> Result<Self, EncodeError> {
498 if value < 4 { Ok(Self(value)) } else { Err(EncodeError::InvalidMask) }
499 }
500
501 #[inline]
503 pub const fn value(self) -> u8 {
504 self.0
505 }
506}
507
508#[cfg(any(feature = "qr", feature = "rmqr"))]
510#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
511pub struct EciAssignment(u32);
512
513#[cfg(any(feature = "qr", feature = "rmqr"))]
514impl EciAssignment {
515 pub const ISO_8859_1: Self = Self(3);
517 pub const SHIFT_JIS: Self = Self(20);
519 pub const UTF_8: Self = Self(26);
521
522 #[inline]
524 pub const fn new(value: u32) -> Result<Self, EncodeError> {
525 if value <= 999_999 {
526 Ok(Self(value))
527 } else {
528 Err(EncodeError::InvalidEciAssignment(value))
529 }
530 }
531
532 #[inline]
534 pub const fn value(self) -> u32 {
535 self.0
536 }
537}
538
539#[cfg(any(feature = "qr", feature = "rmqr"))]
541#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
542pub struct ApplicationIndicator(u8);
543
544#[cfg(any(feature = "qr", feature = "rmqr"))]
545impl ApplicationIndicator {
546 #[inline]
548 pub const fn numeric(value: u8) -> Result<Self, EncodeError> {
549 if value <= 99 { Ok(Self(value)) } else { Err(EncodeError::InvalidApplicationIndicator) }
550 }
551
552 #[inline]
554 pub const fn letter(value: char) -> Result<Self, EncodeError> {
555 if value.is_ascii_alphabetic() {
556 Ok(Self(value as u8 + 100))
557 } else {
558 Err(EncodeError::InvalidApplicationIndicator)
559 }
560 }
561
562 #[inline]
564 pub const fn value(self) -> u8 {
565 self.0
566 }
567}
568
569#[cfg(any(feature = "qr", feature = "rmqr"))]
571#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
572pub enum Fnc1 {
573 Gs1,
575 Industry(ApplicationIndicator),
577}
578
579#[cfg(feature = "qr")]
581#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
582pub struct StructuredAppendInfo {
583 index: u8,
584 total: u8,
585 parity: u8,
586}
587
588#[cfg(feature = "qr")]
589impl StructuredAppendInfo {
590 #[inline]
592 pub const fn index(self) -> u8 {
593 self.index
594 }
595
596 #[inline]
598 pub const fn total(self) -> u8 {
599 self.total
600 }
601
602 #[inline]
604 pub const fn parity(self) -> u8 {
605 self.parity
606 }
607}
608
609#[derive(Clone, Copy, Debug, Eq, PartialEq)]
610pub(crate) enum Mode {
611 Numeric,
612 Alphanumeric,
613 Byte,
614 #[cfg_attr(not(feature = "kanji"), allow(dead_code))]
615 Kanji,
616 #[cfg_attr(not(any(feature = "qr", feature = "rmqr")), allow(dead_code))]
617 Eci,
618}
619
620impl Mode {
621 #[cfg(feature = "qr")]
622 #[inline]
623 const fn qr_bits(self) -> u8 {
624 match self {
625 Self::Numeric => 0b0001,
626 Self::Alphanumeric => 0b0010,
627 Self::Byte => 0b0100,
628 Self::Kanji => 0b1000,
629 Self::Eci => 0b0111,
630 }
631 }
632
633 #[cfg(feature = "qr")]
634 #[inline]
635 const fn cci_bits(self, version: QrVersion) -> u8 {
636 let group = version_group(version);
637
638 match self {
639 Self::Numeric => [10, 12, 14][group],
640 Self::Alphanumeric => [9, 11, 13][group],
641 Self::Byte => [8, 16, 16][group],
642 Self::Kanji => [8, 10, 12][group],
643 Self::Eci => 0,
644 }
645 }
646}
647
648#[derive(Clone, Debug, Eq, PartialEq)]
650pub struct Segment {
651 mode: Mode,
652 character_count: usize,
653 bits: BitBuffer,
654 source: Vec<u8>,
655}
656
657impl Segment {
658 pub fn numeric(data: impl AsRef<str>) -> Result<Self, EncodeError> {
660 let data = data.as_ref();
661 let mut bits = BitBuffer::with_capacity(data.len() * 10 / 3 + 4);
662 let bytes = data.as_bytes();
663
664 for (offset, byte) in bytes.iter().enumerate() {
665 if !byte.is_ascii_digit() {
666 return Err(EncodeError::InvalidData {
667 mode: "numeric",
668 byte_offset: offset,
669 });
670 }
671 }
672
673 for chunk in bytes.chunks(3) {
674 let value = chunk.iter().fold(0, |value, byte| value * 10 + u32::from(byte - b'0'));
675 bits.append(value, [0, 4, 7, 10][chunk.len()]);
676 }
677
678 Ok(Self {
679 mode: Mode::Numeric,
680 character_count: bytes.len(),
681 bits,
682 source: bytes.to_vec(),
683 })
684 }
685
686 pub fn alphanumeric(data: impl AsRef<str>) -> Result<Self, EncodeError> {
688 let data = data.as_ref();
689 let mut values = Vec::with_capacity(data.len());
690
691 for (offset, byte) in data.bytes().enumerate() {
692 let Some(value) = alphanumeric_value(byte) else {
693 return Err(EncodeError::InvalidData {
694 mode: "alphanumeric",
695 byte_offset: offset,
696 });
697 };
698 values.push(value);
699 }
700
701 let mut bits = BitBuffer::with_capacity(values.len() * 11 / 2 + 6);
702
703 for chunk in values.chunks(2) {
704 if chunk.len() == 2 {
705 bits.append(u32::from(chunk[0]) * 45 + u32::from(chunk[1]), 11);
706 } else {
707 bits.append(u32::from(chunk[0]), 6);
708 }
709 }
710
711 Ok(Self {
712 mode: Mode::Alphanumeric,
713 character_count: values.len(),
714 bits,
715 source: data.as_bytes().to_vec(),
716 })
717 }
718
719 #[cfg(any(feature = "qr", feature = "rmqr"))]
720 fn fnc1_alphanumeric(data: &[u8]) -> Result<Self, EncodeError> {
721 let mut values = Vec::with_capacity(data.len());
722
723 for (offset, &byte) in data.iter().enumerate() {
724 if byte == 0x1D {
726 values.push(alphanumeric_value(b'%').expect("percent is alphanumeric"));
727 } else if byte == b'%' {
728 let value = alphanumeric_value(byte).expect("percent is alphanumeric");
729 values.extend_from_slice(&[value, value]);
730 } else if let Some(value) = alphanumeric_value(byte) {
731 values.push(value);
732 } else {
733 return Err(EncodeError::InvalidData {
734 mode: "FNC1 alphanumeric",
735 byte_offset: offset,
736 });
737 }
738 }
739
740 let mut bits = BitBuffer::with_capacity(values.len() * 11 / 2 + 6);
741
742 for chunk in values.chunks(2) {
743 if chunk.len() == 2 {
744 bits.append(u32::from(chunk[0]) * 45 + u32::from(chunk[1]), 11);
745 } else {
746 bits.append(u32::from(chunk[0]), 6);
747 }
748 }
749
750 Ok(Self {
751 mode: Mode::Alphanumeric,
752 character_count: values.len(),
753 bits,
754 source: data.to_vec(),
755 })
756 }
757
758 pub fn bytes(data: impl AsRef<[u8]>) -> Self {
760 let data = data.as_ref();
761
762 Self {
764 mode: Mode::Byte,
765 character_count: data.len(),
766 bits: BitBuffer::from_bytes(data.to_vec()),
767 source: Vec::new(),
768 }
769 }
770
771 #[cfg(any(feature = "qr", feature = "rmqr"))]
773 fn source_bytes(&self) -> &[u8] {
774 match self.mode {
775 Mode::Byte => self.bits.as_bytes(),
776 _ => &self.source,
777 }
778 }
779
780 #[cfg(any(feature = "qr", feature = "rmqr"))]
782 pub fn eci(assignment: EciAssignment) -> Self {
783 let mut bits = BitBuffer::with_capacity(24);
784 let value = assignment.0;
785
786 if value < 128 {
788 bits.append(value, 8);
789 } else if value < 16_384 {
790 bits.append(0b10, 2);
791 bits.append(value, 14);
792 } else {
793 bits.append(0b110, 3);
794 bits.append(value, 21);
795 }
796
797 Self {
798 mode: Mode::Eci,
799 character_count: 0,
800 bits,
801 source: Vec::new(),
802 }
803 }
804
805 #[cfg(feature = "kanji")]
806 pub fn kanji(data: impl AsRef<str>) -> Result<Self, EncodeError> {
808 let data = data.as_ref();
809 let mut bits = BitBuffer::with_capacity(data.chars().count() * 13);
810 let mut source = Vec::with_capacity(data.len() * 2);
811 let mut count = 0;
812
813 for (offset, character) in data.char_indices() {
814 let Some((value, encoded)) = kanji_encoding(character) else {
815 return Err(EncodeError::InvalidData {
816 mode: "Kanji", byte_offset: offset
817 });
818 };
819
820 bits.append(u32::from(value), 13);
821 source.extend_from_slice(&encoded);
822 count += 1;
823 }
824 Ok(Self {
825 mode: Mode::Kanji,
826 character_count: count,
827 bits,
828 source,
829 })
830 }
831
832 #[inline]
834 pub const fn character_count(&self) -> usize {
835 self.character_count
836 }
837}
838
839#[cfg(feature = "kanji")]
840fn shift_jis_encoding(character: char) -> Option<([u8; 2], usize)> {
841 if matches!(character, 'Â¥' | '\u{203E}' | '\u{2212}') {
843 return None;
844 }
845
846 let mut utf8 = [0; 4];
847 let text = character.encode_utf8(&mut utf8);
848 let (encoded, _, had_errors) = encoding_rs::SHIFT_JIS.encode(text);
849
850 if had_errors || encoded.is_empty() || encoded.len() > 2 {
851 return None;
852 }
853
854 Some(([encoded[0], encoded.get(1).copied().unwrap_or(0)], encoded.len()))
855}
856
857#[cfg(feature = "kanji")]
858fn kanji_encoding(character: char) -> Option<(u16, [u8; 2])> {
859 let (bytes, length) = shift_jis_encoding(character)?;
860
861 if length != 2 {
862 return None;
863 }
864
865 let value = u16::from_be_bytes(bytes);
866
867 let adjusted = match value {
869 0x8140..=0x9FFC => value - 0x8140,
870 0xE040..=0xEBBF => value - 0xC140,
871 _ => return None,
872 };
873
874 Some(((adjusted >> 8) * 0xC0 + (adjusted & 0xFF), bytes))
875}
876
877#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
879#[inline]
880const fn mode_rank(mode: Mode) -> u8 {
881 match mode {
882 Mode::Numeric => 0,
883 Mode::Alphanumeric => 1,
884 Mode::Kanji => 2,
885 Mode::Byte => 3,
886 Mode::Eci => 4,
887 }
888}
889
890#[inline]
891pub(crate) const fn alphanumeric_value(byte: u8) -> Option<u8> {
892 match byte {
893 b'0'..=b'9' => Some(byte - b'0'),
894 b'A'..=b'Z' => Some(byte - b'A' + 10),
895 b' ' => Some(36),
896 b'$' => Some(37),
897 b'%' => Some(38),
898 b'*' => Some(39),
899 b'+' => Some(40),
900 b'-' => Some(41),
901 b'.' => Some(42),
902 b'/' => Some(43),
903 b':' => Some(44),
904 _ => None,
905 }
906}
907
908#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
911#[inline]
912pub(crate) const fn bch_remainder(data: u32, generator: u32, degree: u32) -> u32 {
913 let mut remainder = data;
914 let mut round = 0;
915
916 while round < degree {
917 remainder = (remainder << 1) ^ ((remainder >> (degree - 1)) * generator);
918 round += 1;
919 }
920
921 remainder
922}
923
924#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
926#[derive(Clone, Debug, Eq, PartialEq)]
927pub struct Symbol {
928 pub(crate) version: SymbolVersion,
929 pub(crate) error_correction: SymbolErrorCorrection,
930 pub(crate) mask: u8,
931 pub(crate) modules: Vec<bool>,
932 #[cfg(feature = "qr")]
933 pub(crate) structured_append: Option<StructuredAppendInfo>,
934}
935
936#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
937impl Symbol {
938 #[inline]
942 pub const fn size(&self) -> usize {
943 self.width()
944 }
945
946 #[inline]
948 pub const fn width(&self) -> usize {
949 match self.version {
950 #[cfg(feature = "qr")]
951 SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
952 #[cfg(feature = "micro-qr")]
953 SymbolVersion::Micro(version) => version.size(),
954 #[cfg(feature = "rmqr")]
955 SymbolVersion::Rmqr(version) => version.width(),
956 }
957 }
958
959 #[inline]
961 pub const fn height(&self) -> usize {
962 match self.version {
963 #[cfg(feature = "qr")]
964 SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
965 #[cfg(feature = "micro-qr")]
966 SymbolVersion::Micro(version) => version.size(),
967 #[cfg(feature = "rmqr")]
968 SymbolVersion::Rmqr(version) => version.height(),
969 }
970 }
971
972 #[inline]
974 pub const fn version(&self) -> SymbolVersion {
975 self.version
976 }
977
978 #[inline]
980 pub const fn error_correction(&self) -> SymbolErrorCorrection {
981 self.error_correction
982 }
983
984 #[inline]
986 pub const fn mask(&self) -> u8 {
987 self.mask
988 }
989
990 #[cfg(feature = "qr")]
992 #[inline]
993 pub const fn structured_append(&self) -> Option<StructuredAppendInfo> {
994 self.structured_append
995 }
996
997 #[inline]
999 pub fn module(&self, x: usize, y: usize) -> Option<bool> {
1000 let width = self.width();
1001 (x < width && y < self.height()).then(|| self.modules[y * width + x])
1002 }
1003
1004 #[inline]
1008 pub fn modules(&self) -> &[bool] {
1009 &self.modules
1010 }
1011
1012 pub fn to_matrix(&self) -> Vec<Vec<bool>> {
1014 self.modules.chunks(self.width()).map(<[bool]>::to_vec).collect()
1015 }
1016}
1017
1018pub trait ToQRText {
1023 fn to_qr_text(&self) -> String;
1025}
1026
1027#[cfg(feature = "qr")]
1029#[derive(Clone, Debug)]
1030pub struct QrEncoder {
1031 error_correction: QrErrorCorrection,
1032 versions: RangeInclusive<QrVersion>,
1033 mask: Option<QrMask>,
1034 boost_error_correction: bool,
1035 fnc1: Option<Fnc1>,
1036}
1037
1038#[cfg(feature = "qr")]
1039impl QrEncoder {
1040 #[inline]
1042 pub const fn new(error_correction: QrErrorCorrection) -> Self {
1043 Self {
1044 error_correction,
1045 versions: QrVersion::MIN..=QrVersion::MAX,
1046 mask: None,
1047 boost_error_correction: true,
1048 fnc1: None,
1049 }
1050 }
1051
1052 #[must_use]
1054 #[inline]
1055 pub const fn version(mut self, version: QrVersion) -> Self {
1056 self.versions = version..=version;
1057 self
1058 }
1059
1060 #[must_use]
1062 #[inline]
1063 pub const fn version_range(mut self, versions: RangeInclusive<QrVersion>) -> Self {
1064 self.versions = versions;
1065 self
1066 }
1067
1068 #[must_use]
1070 #[inline]
1071 pub const fn mask(mut self, mask: QrMask) -> Self {
1072 self.mask = Some(mask);
1073 self
1074 }
1075
1076 #[must_use]
1078 #[inline]
1079 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1080 self.boost_error_correction = boost;
1081 self
1082 }
1083
1084 #[must_use]
1086 #[inline]
1087 pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1088 self.fnc1 = fnc1;
1089 self
1090 }
1091
1092 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1094 let data = data.as_ref();
1095 let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1096
1097 ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1098
1099 self.encode_optimized_bytes(data, None)
1100 }
1101
1102 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1104 let text = text.as_ref();
1105 let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1106
1107 ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1108
1109 self.encode_optimized_text(text, None, false)
1110 }
1111
1112 #[inline]
1114 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1115 &self,
1116 value: &T,
1117 ) -> Result<Symbol, EncodeError> {
1118 let text = value.to_qr_text();
1119 self.encode_text(&text)
1120 }
1121
1122 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1124 self.encode_segments_with_header(segments, None)
1125 }
1126
1127 pub fn encode_structured_append_bytes(
1129 &self,
1130 parts: &[&[u8]],
1131 ) -> Result<Vec<Symbol>, EncodeError> {
1132 validate_part_count(parts.len())?;
1133 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1134
1135 for part in parts {
1136 ensure_input_length(part.len(), capacity, capacity_bits, 1)?;
1137 }
1138
1139 let parity = parts
1140 .iter()
1141 .flat_map(|part| part.iter().copied())
1142 .fold(0, |parity, byte| parity ^ byte);
1143
1144 parts
1145 .iter()
1146 .enumerate()
1147 .map(|(index, part)| {
1148 self.encode_optimized_bytes(
1149 part,
1150 Some(StructuredAppendInfo {
1151 index: index as u8,
1152 total: parts.len() as u8,
1153 parity,
1154 }),
1155 )
1156 })
1157 .collect()
1158 }
1159
1160 pub fn encode_structured_append_text(
1162 &self,
1163 parts: &[&str],
1164 ) -> Result<Vec<Symbol>, EncodeError> {
1165 validate_part_count(parts.len())?;
1166 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1167
1168 for part in parts {
1169 ensure_input_length(part.chars().count(), capacity, capacity_bits, 1)?;
1170 }
1171
1172 let force_initial_eci = parts.iter().any(|part| requires_non_default_eci(part));
1173 let total = parts.len() as u8;
1174
1175 let mut plans = Vec::with_capacity(parts.len());
1177 let mut parity = 0u8;
1178
1179 for part in parts {
1180 let (version, segments) = self.qr_structured_plan(part, force_initial_eci)?;
1181
1182 for segment in &segments {
1184 for &byte in segment.source_bytes() {
1185 parity ^= byte;
1186 }
1187 }
1188
1189 plans.push((version, segments));
1190 }
1191
1192 plans
1193 .into_iter()
1194 .enumerate()
1195 .map(|(index, (version, segments))| {
1196 model2::encode(
1197 &segments,
1198 version,
1199 self.error_correction,
1200 self.mask,
1201 self.boost_error_correction,
1202 self.fnc1,
1203 Some(StructuredAppendInfo {
1204 index: index as u8,
1205 total,
1206 parity,
1207 }),
1208 )
1209 })
1210 .collect()
1211 }
1212
1213 fn qr_structured_plan(
1215 &self,
1216 text: &str,
1217 force_initial_eci: bool,
1218 ) -> Result<(QrVersion, Vec<Segment>), EncodeError> {
1219 if self.versions.start() > self.versions.end() {
1220 return Err(EncodeError::InvalidVersionRange);
1221 }
1222
1223 let header = StructuredAppendInfo {
1225 index: 0, total: 16, parity: 0
1226 };
1227 let range = self.versions.clone();
1228 let mut cache = GroupCache::new(|version| {
1229 optimizer::text(
1230 text,
1231 optimizer::Profile::qr(version),
1232 self.fnc1.is_some(),
1233 force_initial_eci,
1234 )
1235 });
1236
1237 for value in range.start().0..=range.end().0 {
1238 let version = QrVersion(value);
1239 let segments = cache.get(version)?;
1240
1241 if model2::fits(segments, version, self.error_correction, self.fnc1, Some(header)) {
1242 return Ok((version, segments.to_vec()));
1243 }
1244 }
1245
1246 let version = *range.end();
1248
1249 Ok((version, cache.take(version)?))
1250 }
1251
1252 pub fn encode_structured_append_segments(
1267 &self,
1268 parts: &[&[Segment]],
1269 ) -> Result<Vec<Symbol>, EncodeError> {
1270 validate_part_count(parts.len())?;
1271
1272 let parity = parts
1273 .iter()
1274 .flat_map(|part| part.iter())
1275 .flat_map(|segment| segment.source_bytes().iter().copied())
1276 .fold(0, |parity, byte| parity ^ byte);
1277
1278 parts
1279 .iter()
1280 .enumerate()
1281 .map(|(index, part)| {
1282 self.encode_segments_with_header(
1283 part,
1284 Some(StructuredAppendInfo {
1285 index: index as u8,
1286 total: parts.len() as u8,
1287 parity,
1288 }),
1289 )
1290 })
1291 .collect()
1292 }
1293
1294 pub fn encode_bytes_with_structured_append(
1298 &self,
1299 data: impl AsRef<[u8]>,
1300 ) -> Result<Vec<Symbol>, EncodeError> {
1301 let data = data.as_ref();
1302
1303 match self.encode_bytes(data) {
1304 Ok(symbol) => return Ok(vec![symbol]),
1305 Err(EncodeError::DataTooLong {
1306 ..
1307 }) => {},
1308 Err(error) => return Err(error),
1309 }
1310
1311 let range = self.structured_append_range()?;
1312 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1313
1314 ensure_input_length(data.len(), capacity, capacity_bits, 16)?;
1315
1316 let sequence_capacity_error = || EncodeError::DataTooLong {
1318 required_bits: None,
1319 capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1320 };
1321
1322 let minimum_parts =
1324 self.partition_bytes(data, range.clone()).map_err(|_| sequence_capacity_error())?.len();
1325
1326 let mut selected = None;
1327
1328 for value in range.start().value()..=range.end().value() {
1329 let candidate = QrVersion(value);
1330
1331 if let Ok(parts) = self.partition_bytes(data, *range.start()..=candidate)
1332 && parts.len() == minimum_parts
1333 {
1334 selected = Some(
1335 self.minimum_area_byte_partition(
1336 data,
1337 minimum_parts,
1338 *range.start()..=candidate,
1339 )
1340 .map_err(|_| sequence_capacity_error())?,
1341 );
1342 break;
1343 }
1344 }
1345
1346 let parts = selected.ok_or_else(sequence_capacity_error)?;
1347
1348 let slices: Vec<&[u8]> = parts.into_iter().map(|(start, end)| &data[start..end]).collect();
1349
1350 self.encode_structured_append_bytes(&slices)
1351 }
1352
1353 pub fn encode_text_with_structured_append(
1357 &self,
1358 text: impl AsRef<str>,
1359 ) -> Result<Vec<Symbol>, EncodeError> {
1360 let text = text.as_ref();
1361
1362 match self.encode_text(text) {
1363 Ok(symbol) => return Ok(vec![symbol]),
1364 Err(EncodeError::DataTooLong {
1365 ..
1366 }) => {},
1367 Err(error) => return Err(error),
1368 }
1369
1370 let range = self.structured_append_range()?;
1371 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1372
1373 ensure_input_length(text.chars().count(), capacity, capacity_bits, 16)?;
1374
1375 let offsets = text_boundaries(text);
1376
1377 let sequence_capacity_error = || EncodeError::DataTooLong {
1379 required_bits: None,
1380 capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1381 };
1382
1383 let minimum_parts = self
1385 .partition_text(text, &offsets, range.clone())
1386 .map_err(|_| sequence_capacity_error())?
1387 .len();
1388
1389 let mut selected = None;
1390
1391 for value in range.start().value()..=range.end().value() {
1392 let candidate = QrVersion(value);
1393
1394 if let Ok(parts) = self.partition_text(text, &offsets, *range.start()..=candidate)
1395 && parts.len() == minimum_parts
1396 {
1397 selected = Some(
1398 self.minimum_area_text_partition(
1399 text,
1400 &offsets,
1401 minimum_parts,
1402 *range.start()..=candidate,
1403 )
1404 .map_err(|_| sequence_capacity_error())?,
1405 );
1406 break;
1407 }
1408 }
1409
1410 let parts = selected.ok_or_else(sequence_capacity_error)?;
1411
1412 let slices: Vec<&str> =
1413 parts.into_iter().map(|(start, end)| &text[offsets[start]..offsets[end]]).collect();
1414 self.encode_structured_append_text(&slices)
1415 }
1416
1417 fn structured_append_range(&self) -> Result<RangeInclusive<QrVersion>, EncodeError> {
1418 if self.versions.start() > self.versions.end() {
1419 Err(EncodeError::InvalidVersionRange)
1420 } else {
1421 Ok(self.versions.clone())
1422 }
1423 }
1424
1425 fn input_capacity_upper_bound(
1426 &self,
1427 structured_append: bool,
1428 ) -> Result<(usize, usize), EncodeError> {
1429 if self.versions.start() > self.versions.end() {
1430 return Err(EncodeError::InvalidVersionRange);
1431 }
1432
1433 let version = *self.versions.end();
1434 let capacity_bits = model2::data_codewords(version, self.error_correction) * 8;
1435 let overhead_bits = 4
1436 + usize::from(Mode::Numeric.cci_bits(version))
1437 + usize::from(structured_append) * 20
1438 + match self.fnc1 {
1439 Some(Fnc1::Gs1) => 4,
1440 Some(Fnc1::Industry(_)) => 12,
1441 None => 0,
1442 };
1443
1444 Ok((numeric_character_capacity(capacity_bits, overhead_bits), capacity_bits))
1445 }
1446
1447 fn partition_bytes(
1448 &self,
1449 data: &[u8],
1450 range: RangeInclusive<QrVersion>,
1451 ) -> Result<Vec<(usize, usize)>, EncodeError> {
1452 let maximum_version = *range.end();
1453 let mut result = Vec::new();
1454 let mut start = 0;
1455
1456 while start < data.len() {
1457 if result.len() == 16 {
1458 return Err(EncodeError::DataTooLong {
1459 required_bits: None, capacity_bits: 0
1460 });
1461 }
1462
1463 let mut low = start + 1;
1464
1465 let mut high = data
1467 .len()
1468 .min(start.saturating_add(self.qr_character_upper_bound(maximum_version)));
1469
1470 let mut fitting = None;
1471
1472 while low <= high {
1473 let middle = low + (high - low) / 2;
1474
1475 if self.bytes_fit(&data[start..middle], range.clone()) {
1476 fitting = Some(middle);
1477 low = middle + 1;
1478 } else {
1479 high = middle - 1;
1480 }
1481 }
1482
1483 let end = fitting
1484 .ok_or(EncodeError::DataTooLong {
1485 required_bits: None, capacity_bits: 0
1486 })?;
1487
1488 result.push((start, end));
1489 start = end;
1490 }
1491 Ok(result)
1492 }
1493
1494 fn partition_text(
1495 &self,
1496 text: &str,
1497 offsets: &[usize],
1498 range: RangeInclusive<QrVersion>,
1499 ) -> Result<Vec<(usize, usize)>, EncodeError> {
1500 let maximum_version = *range.end();
1501 let force_initial_eci = requires_non_default_eci(text);
1502
1503 let mut result = Vec::new();
1504 let mut start = 0;
1505 let length = offsets.len() - 1;
1506
1507 while start < length {
1508 if result.len() == 16 {
1509 return Err(EncodeError::DataTooLong {
1510 required_bits: None, capacity_bits: 0
1511 });
1512 }
1513
1514 let mut low = start + 1;
1515
1516 let mut high =
1518 length.min(start.saturating_add(self.qr_character_upper_bound(maximum_version)));
1519
1520 let mut fitting = None;
1521
1522 while low <= high {
1523 let middle = low + (high - low) / 2;
1524
1525 if self.text_fits(
1526 &text[offsets[start]..offsets[middle]],
1527 range.clone(),
1528 force_initial_eci,
1529 ) {
1530 fitting = Some(middle);
1531 low = middle + 1;
1532 } else {
1533 high = middle - 1;
1534 }
1535 }
1536
1537 let end = fitting
1538 .ok_or(EncodeError::DataTooLong {
1539 required_bits: None, capacity_bits: 0
1540 })?;
1541
1542 result.push((start, end));
1543
1544 start = end;
1545 }
1546 Ok(result)
1547 }
1548
1549 fn minimum_area_byte_partition(
1550 &self,
1551 data: &[u8],
1552 part_count: usize,
1553 range: RangeInclusive<QrVersion>,
1554 ) -> Result<Vec<(usize, usize)>, EncodeError> {
1555 minimum_area_partition(data.len(), part_count, range, |start, version| {
1556 let high = data.len().min(start.saturating_add(self.qr_character_upper_bound(version)));
1557
1558 maximum_fitting_end(high, start, |end| {
1559 self.bytes_fit(&data[start..end], version..=version)
1560 })
1561 })
1562 }
1563
1564 fn minimum_area_text_partition(
1565 &self,
1566 text: &str,
1567 offsets: &[usize],
1568 part_count: usize,
1569 range: RangeInclusive<QrVersion>,
1570 ) -> Result<Vec<(usize, usize)>, EncodeError> {
1571 let length = offsets.len() - 1;
1572 let force_initial_eci = requires_non_default_eci(text);
1573
1574 minimum_area_partition(length, part_count, range, |start, version| {
1575 let high = length.min(start.saturating_add(self.qr_character_upper_bound(version)));
1576
1577 maximum_fitting_end(high, start, |end| {
1578 self.text_fits(
1579 &text[offsets[start]..offsets[end]],
1580 version..=version,
1581 force_initial_eci,
1582 )
1583 })
1584 })
1585 }
1586
1587 fn bytes_fit(&self, data: &[u8], range: RangeInclusive<QrVersion>) -> bool {
1589 self.qr_range_fits(range, |version| {
1590 optimizer::bytes(data, optimizer::Profile::qr(version), self.fnc1.is_some())
1591 })
1592 }
1593
1594 fn text_fits(
1596 &self,
1597 text: &str,
1598 range: RangeInclusive<QrVersion>,
1599 force_initial_eci: bool,
1600 ) -> bool {
1601 self.qr_range_fits(range, |version| {
1602 optimizer::text(
1603 text,
1604 optimizer::Profile::qr(version),
1605 self.fnc1.is_some(),
1606 force_initial_eci,
1607 )
1608 })
1609 }
1610
1611 fn qr_range_fits<F>(&self, range: RangeInclusive<QrVersion>, segments: F) -> bool
1613 where
1614 F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>, {
1615 if range.start() > range.end() {
1616 return false;
1617 }
1618
1619 let header = StructuredAppendInfo {
1621 index: 0, total: 16, parity: 0
1622 };
1623 let mut cache = GroupCache::new(segments);
1624
1625 for value in range.start().0..=range.end().0 {
1626 let version = QrVersion(value);
1627
1628 let Ok(candidate) = cache.get(version) else {
1629 return false;
1630 };
1631
1632 if model2::fits(candidate, version, self.error_correction, self.fnc1, Some(header)) {
1633 return true;
1634 }
1635 }
1636
1637 false
1638 }
1639
1640 #[inline]
1641 const fn qr_character_upper_bound(&self, version: QrVersion) -> usize {
1642 model2::data_codewords(version, self.error_correction) * 8 * 3 / 10 + 2
1643 }
1644
1645 fn encode_optimized_bytes(
1646 &self,
1647 data: &[u8],
1648 structured_append: Option<StructuredAppendInfo>,
1649 ) -> Result<Symbol, EncodeError> {
1650 self.encode_qr_range(
1651 self.versions.clone(),
1652 |version| optimizer::bytes(data, optimizer::Profile::qr(version), self.fnc1.is_some()),
1653 structured_append,
1654 )
1655 }
1656
1657 fn encode_optimized_text(
1658 &self,
1659 text: &str,
1660 structured_append: Option<StructuredAppendInfo>,
1661 force_initial_eci: bool,
1662 ) -> Result<Symbol, EncodeError> {
1663 self.encode_qr_range(
1664 self.versions.clone(),
1665 |version| {
1666 optimizer::text(
1667 text,
1668 optimizer::Profile::qr(version),
1669 self.fnc1.is_some(),
1670 force_initial_eci,
1671 )
1672 },
1673 structured_append,
1674 )
1675 }
1676
1677 fn encode_qr_range<F>(
1678 &self,
1679 range: RangeInclusive<QrVersion>,
1680 segments: F,
1681 structured_append: Option<StructuredAppendInfo>,
1682 ) -> Result<Symbol, EncodeError>
1683 where
1684 F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>, {
1685 if range.start() > range.end() {
1686 return Err(EncodeError::InvalidVersionRange);
1687 }
1688
1689 let mut cache = GroupCache::new(segments);
1690 let mut last_error = None;
1691
1692 for value in range.start().0..=range.end().0 {
1693 let version = QrVersion(value);
1694 let candidate = cache.get(version)?;
1695
1696 match model2::encode(
1697 candidate,
1698 version,
1699 self.error_correction,
1700 self.mask,
1701 self.boost_error_correction,
1702 self.fnc1,
1703 structured_append,
1704 ) {
1705 Ok(symbol) => return Ok(symbol),
1706 Err(
1707 error @ EncodeError::DataTooLong {
1708 ..
1709 },
1710 ) => last_error = Some(error),
1711 Err(error) => return Err(error),
1712 }
1713 }
1714
1715 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
1717 }
1718
1719 fn encode_segments_with_header(
1720 &self,
1721 segments: &[Segment],
1722 structured_append: Option<StructuredAppendInfo>,
1723 ) -> Result<Symbol, EncodeError> {
1724 let normalized;
1725
1726 let segments = if self.fnc1.is_some() {
1727 normalized = segments
1728 .iter()
1729 .map(|segment| {
1730 if segment.mode == Mode::Alphanumeric {
1731 Segment::fnc1_alphanumeric(segment.source_bytes())
1732 } else {
1733 Ok(segment.clone())
1734 }
1735 })
1736 .collect::<Result<Vec<_>, _>>()?;
1737 normalized.as_slice()
1738 } else {
1739 segments
1740 };
1741
1742 self.encode_qr_range(self.versions.clone(), |_| Ok(segments.to_vec()), structured_append)
1743 }
1744}
1745
1746#[cfg(feature = "rmqr")]
1748#[derive(Clone, Debug)]
1749pub struct RmqrEncoder {
1750 error_correction: RmqrErrorCorrection,
1751 versions: RangeInclusive<RmqrVersion>,
1752 boost_error_correction: bool,
1753 fnc1: Option<Fnc1>,
1754}
1755
1756#[cfg(feature = "rmqr")]
1757impl RmqrEncoder {
1758 #[inline]
1760 pub const fn new(error_correction: RmqrErrorCorrection) -> Self {
1761 Self {
1762 error_correction,
1763 versions: RmqrVersion::MIN..=RmqrVersion::MAX,
1764 boost_error_correction: true,
1765 fnc1: None,
1766 }
1767 }
1768
1769 #[must_use]
1771 #[inline]
1772 pub const fn version(mut self, version: RmqrVersion) -> Self {
1773 self.versions = version..=version;
1774 self
1775 }
1776
1777 #[must_use]
1779 #[inline]
1780 pub const fn version_range(mut self, versions: RangeInclusive<RmqrVersion>) -> Self {
1781 self.versions = versions;
1782 self
1783 }
1784
1785 #[must_use]
1787 #[inline]
1788 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1789 self.boost_error_correction = boost;
1790 self
1791 }
1792
1793 #[must_use]
1795 #[inline]
1796 pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1797 self.fnc1 = fnc1;
1798 self
1799 }
1800
1801 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1803 let data = data.as_ref();
1804 let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
1805
1806 ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1807 self.encode_optimized(|version| {
1808 optimizer::bytes(
1809 data,
1810 optimizer::Profile::rmqr(rmqr::cci(version)),
1811 self.fnc1.is_some(),
1812 )
1813 })
1814 }
1815
1816 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1818 let text = text.as_ref();
1819 let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
1820
1821 ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1822 self.encode_optimized(|version| {
1823 optimizer::text(
1824 text,
1825 optimizer::Profile::rmqr(rmqr::cci(version)),
1826 self.fnc1.is_some(),
1827 false,
1828 )
1829 })
1830 }
1831
1832 #[inline]
1834 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1835 &self,
1836 value: &T,
1837 ) -> Result<Symbol, EncodeError> {
1838 let text = value.to_qr_text();
1839 self.encode_text(&text)
1840 }
1841
1842 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1844 let normalized;
1845 let segments = if self.fnc1.is_some() {
1846 normalized = segments
1847 .iter()
1848 .map(|segment| {
1849 if segment.mode == Mode::Alphanumeric {
1850 Segment::fnc1_alphanumeric(segment.source_bytes())
1851 } else {
1852 Ok(segment.clone())
1853 }
1854 })
1855 .collect::<Result<Vec<_>, _>>()?;
1856 normalized.as_slice()
1857 } else {
1858 segments
1859 };
1860
1861 self.encode_candidates(|version| {
1862 rmqr::encode(
1863 segments,
1864 version,
1865 self.error_correction,
1866 self.boost_error_correction,
1867 self.fnc1,
1868 )
1869 })
1870 }
1871
1872 fn input_capacity_upper_bound(&self) -> Result<(usize, usize), EncodeError> {
1873 let versions = self.candidates()?;
1874 let mut result = None;
1875
1876 for version in versions {
1877 let capacity_bits = rmqr::data_codewords(version, self.error_correction) * 8;
1878 let overhead_bits = 3
1879 + usize::from(rmqr::cci(version)[0])
1880 + match self.fnc1 {
1881 Some(Fnc1::Gs1) => 3,
1882 Some(Fnc1::Industry(_)) => 11,
1883 None => 0,
1884 };
1885 let capacity = numeric_character_capacity(capacity_bits, overhead_bits);
1886
1887 if result.is_none_or(|(best, _)| capacity > best) {
1888 result = Some((capacity, capacity_bits));
1889 }
1890 }
1891
1892 result.ok_or(EncodeError::InvalidVersionRange)
1893 }
1894
1895 fn encode_optimized<F>(&self, mut optimize: F) -> Result<Symbol, EncodeError>
1897 where
1898 F: FnMut(RmqrVersion) -> Result<Vec<Segment>, EncodeError>, {
1899 let mut cache: Vec<([u8; 4], Vec<Segment>)> = Vec::with_capacity(15);
1900
1901 self.encode_candidates(|version| {
1902 let profile = rmqr::cci(version);
1903 let index = match cache.iter().position(|(cci, _)| *cci == profile) {
1904 Some(index) => index,
1905 None => {
1906 cache.push((profile, optimize(version)?));
1907 cache.len() - 1
1908 },
1909 };
1910
1911 rmqr::encode(
1912 &cache[index].1,
1913 version,
1914 self.error_correction,
1915 self.boost_error_correction,
1916 self.fnc1,
1917 )
1918 })
1919 }
1920
1921 fn encode_candidates<F>(&self, mut encode: F) -> Result<Symbol, EncodeError>
1923 where
1924 F: FnMut(RmqrVersion) -> Result<Symbol, EncodeError>, {
1925 let versions = self.candidates()?;
1926 let mut last_error = None;
1927
1928 for version in versions {
1929 match encode(version) {
1930 Ok(symbol) => return Ok(symbol),
1931 Err(
1932 error @ EncodeError::DataTooLong {
1933 ..
1934 },
1935 ) => last_error = Some(error),
1936 Err(error) => return Err(error),
1937 }
1938 }
1939
1940 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
1941 }
1942
1943 fn candidates(&self) -> Result<Vec<RmqrVersion>, EncodeError> {
1944 if self.versions.start() > self.versions.end() {
1945 return Err(EncodeError::InvalidVersionRange);
1946 }
1947
1948 Ok(RmqrVersion::ALL_BY_AREA
1949 .into_iter()
1950 .filter(|version| self.versions.contains(version))
1951 .collect())
1952 }
1953}
1954
1955#[cfg(feature = "micro-qr")]
1957#[derive(Clone, Debug)]
1958pub struct MicroEncoder {
1959 error_correction: MicroErrorCorrection,
1960 versions: RangeInclusive<MicroVersion>,
1961 mask: Option<MicroMask>,
1962 boost_error_correction: bool,
1963}
1964
1965#[cfg(feature = "micro-qr")]
1966impl MicroEncoder {
1967 #[inline]
1969 pub const fn new(error_correction: MicroErrorCorrection) -> Self {
1970 Self {
1971 error_correction,
1972 versions: MicroVersion::M1..=MicroVersion::M4,
1973 mask: None,
1974 boost_error_correction: true,
1975 }
1976 }
1977
1978 #[must_use]
1980 #[inline]
1981 pub const fn version(mut self, version: MicroVersion) -> Self {
1982 self.versions = version..=version;
1983 self
1984 }
1985
1986 #[must_use]
1988 #[inline]
1989 pub const fn version_range(mut self, versions: RangeInclusive<MicroVersion>) -> Self {
1990 self.versions = versions;
1991 self
1992 }
1993
1994 #[must_use]
1996 #[inline]
1997 pub const fn mask(mut self, mask: MicroMask) -> Self {
1998 self.mask = Some(mask);
1999 self
2000 }
2001
2002 #[must_use]
2004 #[inline]
2005 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
2006 self.boost_error_correction = boost;
2007 self
2008 }
2009
2010 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2012 let data = data.as_ref();
2013 let (version, capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2014
2015 micro::check_bytes(data, version, capacity, capacity_bits)?;
2016
2017 self.encode_range(|version| micro::optimize(data, version))
2018 }
2019
2020 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2022 let text = text.as_ref();
2023 let (version, capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2024 let input = micro::Text::new(text, version, capacity, capacity_bits)?;
2025
2026 self.encode_range(|version| micro::optimize_text(&input, version))
2027 }
2028
2029 #[inline]
2031 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2032 &self,
2033 value: &T,
2034 ) -> Result<Symbol, EncodeError> {
2035 let text = value.to_qr_text();
2036 self.encode_text(&text)
2037 }
2038
2039 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2041 self.encode_range(|_| Ok(segments.to_vec()))
2042 }
2043
2044 fn input_capacity_upper_bound(&self) -> Result<(MicroVersion, usize, usize), EncodeError> {
2046 if self.versions.start() > self.versions.end() {
2047 return Err(EncodeError::InvalidVersionRange);
2048 }
2049
2050 micro_versions(self.versions.clone())
2051 .filter_map(|version| {
2052 micro::input_capacity_upper_bound(version, self.error_correction)
2053 .map(|(capacity, capacity_bits)| (version, capacity, capacity_bits))
2054 })
2055 .max_by_key(|&(_, capacity, _)| capacity)
2056 .ok_or(EncodeError::UnsupportedErrorCorrection {
2057 version: SymbolVersion::Micro(*self.versions.end()),
2058 error_correction: self.error_correction.into(),
2059 })
2060 }
2061
2062 fn encode_range<F>(&self, mut segments: F) -> Result<Symbol, EncodeError>
2063 where
2064 F: FnMut(MicroVersion) -> Result<Vec<Segment>, EncodeError>, {
2065 if self.versions.start() > self.versions.end() {
2066 return Err(EncodeError::InvalidVersionRange);
2067 }
2068
2069 let mut last_error = None;
2070
2071 for version in micro_versions(self.versions.clone()) {
2073 if micro::input_capacity_upper_bound(version, self.error_correction).is_none() {
2074 last_error = Some(EncodeError::UnsupportedErrorCorrection {
2075 version: SymbolVersion::Micro(version),
2076 error_correction: self.error_correction.into(),
2077 });
2078 continue;
2079 }
2080
2081 let result = segments(version).and_then(|segments| {
2082 micro::encode(
2083 &segments,
2084 version,
2085 self.error_correction,
2086 self.mask,
2087 self.boost_error_correction,
2088 )
2089 });
2090
2091 match result {
2092 Ok(symbol) => return Ok(symbol),
2093 Err(error) if candidate_rejection(&error) => last_error = Some(error),
2094 Err(error) => return Err(error),
2095 }
2096 }
2097
2098 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
2099 }
2100}
2101
2102#[cfg(all(feature = "qr", feature = "micro-qr"))]
2104#[derive(Clone, Debug)]
2105pub struct AutoEncoder {
2106 qr: QrEncoder,
2107 micro: MicroEncoder,
2108}
2109
2110#[cfg(all(feature = "qr", feature = "micro-qr"))]
2111impl AutoEncoder {
2112 #[inline]
2114 pub const fn new(qr: QrEncoder, micro: MicroEncoder) -> Self {
2115 Self {
2116 qr,
2117 micro,
2118 }
2119 }
2120
2121 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2123 let data = data.as_ref();
2124 if self.qr.fnc1.is_some() {
2125 return self.qr.encode_bytes(data);
2126 }
2127 match self.micro.encode_bytes(data) {
2128 Ok(symbol) => Ok(symbol),
2129 Err(error) if candidate_rejection(&error) => self.qr.encode_bytes(data),
2130 Err(error) => Err(error),
2131 }
2132 }
2133
2134 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2136 let text = text.as_ref();
2137 if self.qr.fnc1.is_some() {
2138 return self.qr.encode_text(text);
2139 }
2140 match self.micro.encode_text(text) {
2141 Ok(symbol) => Ok(symbol),
2142 Err(error) if candidate_rejection(&error) => self.qr.encode_text(text),
2143 Err(error) => Err(error),
2144 }
2145 }
2146
2147 #[inline]
2149 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2150 &self,
2151 value: &T,
2152 ) -> Result<Symbol, EncodeError> {
2153 let text = value.to_qr_text();
2154 self.encode_text(&text)
2155 }
2156
2157 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2159 if self.qr.fnc1.is_some() {
2160 return self.qr.encode_segments(segments);
2161 }
2162 match self.micro.encode_segments(segments) {
2163 Ok(symbol) => Ok(symbol),
2164 Err(error) if candidate_rejection(&error) => self.qr.encode_segments(segments),
2165 Err(error) => Err(error),
2166 }
2167 }
2168}
2169
2170#[cfg(feature = "qr")]
2171#[derive(Clone, Copy)]
2172struct PartitionState {
2173 end: usize,
2174 area: usize,
2175 previous: usize,
2176}
2177
2178#[cfg(feature = "qr")]
2179fn minimum_area_partition<F>(
2180 length: usize,
2181 part_count: usize,
2182 range: RangeInclusive<QrVersion>,
2183 mut maximum_end: F,
2184) -> Result<Vec<(usize, usize)>, EncodeError>
2185where
2186 F: FnMut(usize, QrVersion) -> Option<usize>, {
2187 let mut layers = vec![vec![PartitionState {
2189 end: 0, area: 0, previous: 0
2190 }]];
2191
2192 for _ in 0..part_count {
2193 let previous_layer = layers.last().expect("the initial layer exists");
2194 let mut by_end = BTreeMap::new();
2195
2196 for (previous, state) in previous_layer.iter().enumerate() {
2197 for value in range.start().value()..=range.end().value() {
2198 let version = QrVersion(value);
2199
2200 let Some(end) = maximum_end(state.end, version) else {
2201 continue;
2202 };
2203
2204 let size = usize::from(version.value()) * 4 + 17;
2205
2206 let candidate = PartitionState {
2207 end,
2208 area: state.area + size * size,
2209 previous,
2210 };
2211
2212 by_end
2213 .entry(end)
2214 .and_modify(|current: &mut PartitionState| {
2215 if candidate.area < current.area {
2216 *current = candidate;
2217 }
2218 })
2219 .or_insert(candidate);
2220 }
2221 }
2222
2223 let mut best_area = usize::MAX;
2224 let mut layer = Vec::new();
2225
2226 for state in by_end.into_values().rev() {
2227 if state.area < best_area {
2229 best_area = state.area;
2230 layer.push(state);
2231 }
2232 }
2233
2234 layer.reverse();
2235
2236 if layer.is_empty() {
2237 return Err(EncodeError::DataTooLong {
2238 required_bits: None, capacity_bits: 0
2239 });
2240 }
2241
2242 layers.push(layer);
2243 }
2244
2245 let mut state_index = layers[part_count].iter().position(|state| state.end == length).ok_or(
2246 EncodeError::DataTooLong {
2247 required_bits: None, capacity_bits: 0
2248 },
2249 )?;
2250
2251 let mut result = Vec::with_capacity(part_count);
2252
2253 for layer_index in (1..=part_count).rev() {
2254 let state = layers[layer_index][state_index];
2255 let previous = layers[layer_index - 1][state.previous];
2256 result.push((previous.end, state.end));
2257 state_index = state.previous;
2258 }
2259
2260 result.reverse();
2261
2262 Ok(result)
2263}
2264
2265#[cfg(feature = "qr")]
2266fn maximum_fitting_end<F>(length: usize, start: usize, mut fits: F) -> Option<usize>
2267where
2268 F: FnMut(usize) -> bool, {
2269 if start == length {
2270 return None;
2271 }
2272
2273 let mut low = start + 1;
2274 let mut high = length;
2275 let mut result = None;
2276
2277 while low <= high {
2279 let middle = low + (high - low) / 2;
2280
2281 if fits(middle) {
2282 result = Some(middle);
2283 low = middle + 1;
2284 } else {
2285 high = middle - 1;
2286 }
2287 }
2288 result
2289}
2290
2291#[cfg(feature = "micro-qr")]
2292fn micro_versions(versions: RangeInclusive<MicroVersion>) -> impl Iterator<Item = MicroVersion> {
2293 let start = *versions.start();
2294 let end = *versions.end();
2295
2296 [MicroVersion::M1, MicroVersion::M2, MicroVersion::M3, MicroVersion::M4]
2297 .into_iter()
2298 .filter(move |version| *version >= start && *version <= end)
2299}
2300
2301#[cfg(feature = "micro-qr")]
2302#[inline]
2303const fn candidate_rejection(error: &EncodeError) -> bool {
2304 matches!(
2305 error,
2306 EncodeError::DataTooLong { .. }
2307 | EncodeError::UnsupportedMode { .. }
2308 | EncodeError::UnsupportedErrorCorrection { .. }
2309 | EncodeError::TextNotRepresentable { .. }
2310 )
2311}
2312
2313#[cfg(feature = "qr")]
2314#[inline]
2315const fn version_group(version: QrVersion) -> usize {
2316 if version.0 <= 9 {
2317 0
2318 } else if version.0 <= 26 {
2319 1
2320 } else {
2321 2
2322 }
2323}
2324
2325#[cfg(feature = "qr")]
2327struct GroupCache<F> {
2328 segments: F,
2329 cache: [Option<Vec<Segment>>; 3],
2330}
2331
2332#[cfg(feature = "qr")]
2333impl<F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>> GroupCache<F> {
2334 fn new(segments: F) -> Self {
2335 Self {
2336 segments,
2337 cache: [None, None, None],
2338 }
2339 }
2340
2341 fn get(&mut self, version: QrVersion) -> Result<&[Segment], EncodeError> {
2342 let group = version_group(version);
2343
2344 if self.cache[group].is_none() {
2345 self.cache[group] = Some((self.segments)(version)?);
2346 }
2347
2348 Ok(self.cache[group].as_deref().expect("the version group is cached"))
2349 }
2350
2351 fn take(&mut self, version: QrVersion) -> Result<Vec<Segment>, EncodeError> {
2352 self.get(version)?;
2353
2354 Ok(self.cache[version_group(version)].take().expect("the version group is cached"))
2355 }
2356}
2357
2358#[cfg(feature = "qr")]
2359#[inline]
2360const fn validate_part_count(count: usize) -> Result<(), EncodeError> {
2361 if count >= 1 && count <= 16 {
2362 Ok(())
2363 } else {
2364 Err(EncodeError::InvalidStructuredAppendPartCount {
2365 count,
2366 })
2367 }
2368}
2369
2370#[cfg(feature = "qr")]
2371fn text_boundaries(text: &str) -> Vec<usize> {
2372 let mut result: Vec<_> = text.char_indices().map(|(offset, _)| offset).collect();
2373 result.push(text.len());
2374 result
2375}
2376
2377#[cfg(feature = "qr")]
2378#[inline]
2379fn requires_non_default_eci(text: &str) -> bool {
2380 text.chars().any(|character| u32::from(character) > 0xFF)
2381}