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 kanji_encoding(character: char) -> Option<(u16, [u8; 2])> {
841 let mut utf8 = [0; 4];
842 let text = character.encode_utf8(&mut utf8);
843 let (encoded, _, had_errors) = encoding_rs::SHIFT_JIS.encode(text);
844
845 if had_errors || encoded.len() != 2 {
846 return None;
847 }
848
849 let bytes = [encoded[0], encoded[1]];
850 let value = u16::from_be_bytes(bytes);
851
852 let adjusted = match value {
854 0x8140..=0x9FFC => value - 0x8140,
855 0xE040..=0xEBBF => value - 0xC140,
856 _ => return None,
857 };
858
859 Some(((adjusted >> 8) * 0xC0 + (adjusted & 0xFF), bytes))
860}
861
862#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
864#[inline]
865const fn mode_rank(mode: Mode) -> u8 {
866 match mode {
867 Mode::Numeric => 0,
868 Mode::Alphanumeric => 1,
869 Mode::Kanji => 2,
870 Mode::Byte => 3,
871 Mode::Eci => 4,
872 }
873}
874
875#[inline]
876pub(crate) const fn alphanumeric_value(byte: u8) -> Option<u8> {
877 match byte {
878 b'0'..=b'9' => Some(byte - b'0'),
879 b'A'..=b'Z' => Some(byte - b'A' + 10),
880 b' ' => Some(36),
881 b'$' => Some(37),
882 b'%' => Some(38),
883 b'*' => Some(39),
884 b'+' => Some(40),
885 b'-' => Some(41),
886 b'.' => Some(42),
887 b'/' => Some(43),
888 b':' => Some(44),
889 _ => None,
890 }
891}
892
893#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
896#[inline]
897pub(crate) const fn bch_remainder(data: u32, generator: u32, degree: u32) -> u32 {
898 let mut remainder = data;
899 let mut round = 0;
900
901 while round < degree {
902 remainder = (remainder << 1) ^ ((remainder >> (degree - 1)) * generator);
903 round += 1;
904 }
905
906 remainder
907}
908
909#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
911#[derive(Clone, Debug, Eq, PartialEq)]
912pub struct Symbol {
913 pub(crate) version: SymbolVersion,
914 pub(crate) error_correction: SymbolErrorCorrection,
915 pub(crate) mask: u8,
916 pub(crate) modules: Vec<bool>,
917 #[cfg(feature = "qr")]
918 pub(crate) structured_append: Option<StructuredAppendInfo>,
919}
920
921#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
922impl Symbol {
923 #[inline]
927 pub const fn size(&self) -> usize {
928 self.width()
929 }
930
931 #[inline]
933 pub const fn width(&self) -> usize {
934 match self.version {
935 #[cfg(feature = "qr")]
936 SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
937 #[cfg(feature = "micro-qr")]
938 SymbolVersion::Micro(version) => version.size(),
939 #[cfg(feature = "rmqr")]
940 SymbolVersion::Rmqr(version) => version.width(),
941 }
942 }
943
944 #[inline]
946 pub const fn height(&self) -> usize {
947 match self.version {
948 #[cfg(feature = "qr")]
949 SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
950 #[cfg(feature = "micro-qr")]
951 SymbolVersion::Micro(version) => version.size(),
952 #[cfg(feature = "rmqr")]
953 SymbolVersion::Rmqr(version) => version.height(),
954 }
955 }
956
957 #[inline]
959 pub const fn version(&self) -> SymbolVersion {
960 self.version
961 }
962
963 #[inline]
965 pub const fn error_correction(&self) -> SymbolErrorCorrection {
966 self.error_correction
967 }
968
969 #[inline]
971 pub const fn mask(&self) -> u8 {
972 self.mask
973 }
974
975 #[cfg(feature = "qr")]
977 #[inline]
978 pub const fn structured_append(&self) -> Option<StructuredAppendInfo> {
979 self.structured_append
980 }
981
982 #[inline]
984 pub fn module(&self, x: usize, y: usize) -> Option<bool> {
985 let width = self.width();
986 (x < width && y < self.height()).then(|| self.modules[y * width + x])
987 }
988
989 #[inline]
993 pub fn modules(&self) -> &[bool] {
994 &self.modules
995 }
996
997 pub fn to_matrix(&self) -> Vec<Vec<bool>> {
999 self.modules.chunks(self.width()).map(<[bool]>::to_vec).collect()
1000 }
1001}
1002
1003pub trait ToQRText {
1008 fn to_qr_text(&self) -> String;
1010}
1011
1012#[cfg(feature = "qr")]
1014#[derive(Clone, Debug)]
1015pub struct QrEncoder {
1016 error_correction: QrErrorCorrection,
1017 versions: RangeInclusive<QrVersion>,
1018 mask: Option<QrMask>,
1019 boost_error_correction: bool,
1020 fnc1: Option<Fnc1>,
1021}
1022
1023#[cfg(feature = "qr")]
1024impl QrEncoder {
1025 #[inline]
1027 pub const fn new(error_correction: QrErrorCorrection) -> Self {
1028 Self {
1029 error_correction,
1030 versions: QrVersion::MIN..=QrVersion::MAX,
1031 mask: None,
1032 boost_error_correction: true,
1033 fnc1: None,
1034 }
1035 }
1036
1037 #[must_use]
1039 #[inline]
1040 pub const fn version(mut self, version: QrVersion) -> Self {
1041 self.versions = version..=version;
1042 self
1043 }
1044
1045 #[must_use]
1047 #[inline]
1048 pub const fn version_range(mut self, versions: RangeInclusive<QrVersion>) -> Self {
1049 self.versions = versions;
1050 self
1051 }
1052
1053 #[must_use]
1055 #[inline]
1056 pub const fn mask(mut self, mask: QrMask) -> Self {
1057 self.mask = Some(mask);
1058 self
1059 }
1060
1061 #[must_use]
1063 #[inline]
1064 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1065 self.boost_error_correction = boost;
1066 self
1067 }
1068
1069 #[must_use]
1071 #[inline]
1072 pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1073 self.fnc1 = fnc1;
1074 self
1075 }
1076
1077 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1079 let data = data.as_ref();
1080 let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1081
1082 ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1083
1084 self.encode_optimized_bytes(data, None)
1085 }
1086
1087 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1089 let text = text.as_ref();
1090 let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1091
1092 ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1093
1094 self.encode_optimized_text(text, None, false)
1095 }
1096
1097 #[inline]
1099 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1100 &self,
1101 value: &T,
1102 ) -> Result<Symbol, EncodeError> {
1103 let text = value.to_qr_text();
1104 self.encode_text(&text)
1105 }
1106
1107 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1109 self.encode_segments_with_header(segments, None)
1110 }
1111
1112 pub fn encode_structured_append_bytes(
1114 &self,
1115 parts: &[&[u8]],
1116 ) -> Result<Vec<Symbol>, EncodeError> {
1117 validate_part_count(parts.len())?;
1118 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1119
1120 for part in parts {
1121 ensure_input_length(part.len(), capacity, capacity_bits, 1)?;
1122 }
1123
1124 let parity = parts
1125 .iter()
1126 .flat_map(|part| part.iter().copied())
1127 .fold(0, |parity, byte| parity ^ byte);
1128
1129 parts
1130 .iter()
1131 .enumerate()
1132 .map(|(index, part)| {
1133 self.encode_optimized_bytes(
1134 part,
1135 Some(StructuredAppendInfo {
1136 index: index as u8,
1137 total: parts.len() as u8,
1138 parity,
1139 }),
1140 )
1141 })
1142 .collect()
1143 }
1144
1145 pub fn encode_structured_append_text(
1147 &self,
1148 parts: &[&str],
1149 ) -> Result<Vec<Symbol>, EncodeError> {
1150 validate_part_count(parts.len())?;
1151 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1152
1153 for part in parts {
1154 ensure_input_length(part.chars().count(), capacity, capacity_bits, 1)?;
1155 }
1156
1157 let force_initial_eci = parts.iter().any(|part| requires_non_default_eci(part));
1158 let total = parts.len() as u8;
1159
1160 let mut plans = Vec::with_capacity(parts.len());
1162 let mut parity = 0u8;
1163
1164 for part in parts {
1165 let (version, segments) = self.qr_structured_plan(part, force_initial_eci)?;
1166
1167 for segment in &segments {
1169 for &byte in segment.source_bytes() {
1170 parity ^= byte;
1171 }
1172 }
1173
1174 plans.push((version, segments));
1175 }
1176
1177 plans
1178 .into_iter()
1179 .enumerate()
1180 .map(|(index, (version, segments))| {
1181 model2::encode(
1182 &segments,
1183 version,
1184 self.error_correction,
1185 self.mask,
1186 self.boost_error_correction,
1187 self.fnc1,
1188 Some(StructuredAppendInfo {
1189 index: index as u8,
1190 total,
1191 parity,
1192 }),
1193 )
1194 })
1195 .collect()
1196 }
1197
1198 fn qr_structured_plan(
1200 &self,
1201 text: &str,
1202 force_initial_eci: bool,
1203 ) -> Result<(QrVersion, Vec<Segment>), EncodeError> {
1204 if self.versions.start() > self.versions.end() {
1205 return Err(EncodeError::InvalidVersionRange);
1206 }
1207
1208 let header = StructuredAppendInfo {
1210 index: 0, total: 16, parity: 0
1211 };
1212 let range = self.versions.clone();
1213 let mut cache = GroupCache::new(|version| {
1214 optimizer::text(
1215 text,
1216 optimizer::Profile::qr(version),
1217 self.fnc1.is_some(),
1218 force_initial_eci,
1219 )
1220 });
1221
1222 for value in range.start().0..=range.end().0 {
1223 let version = QrVersion(value);
1224 let segments = cache.get(version)?;
1225
1226 if model2::fits(segments, version, self.error_correction, self.fnc1, Some(header)) {
1227 return Ok((version, segments.to_vec()));
1228 }
1229 }
1230
1231 let version = *range.end();
1233
1234 Ok((version, cache.take(version)?))
1235 }
1236
1237 pub fn encode_structured_append_segments(
1239 &self,
1240 parts: &[&[Segment]],
1241 ) -> Result<Vec<Symbol>, EncodeError> {
1242 validate_part_count(parts.len())?;
1243
1244 let parity = parts
1245 .iter()
1246 .flat_map(|part| part.iter())
1247 .flat_map(|segment| segment.source_bytes().iter().copied())
1248 .fold(0, |parity, byte| parity ^ byte);
1249
1250 parts
1251 .iter()
1252 .enumerate()
1253 .map(|(index, part)| {
1254 self.encode_segments_with_header(
1255 part,
1256 Some(StructuredAppendInfo {
1257 index: index as u8,
1258 total: parts.len() as u8,
1259 parity,
1260 }),
1261 )
1262 })
1263 .collect()
1264 }
1265
1266 pub fn encode_bytes_with_structured_append(
1270 &self,
1271 data: impl AsRef<[u8]>,
1272 ) -> Result<Vec<Symbol>, EncodeError> {
1273 let data = data.as_ref();
1274
1275 match self.encode_bytes(data) {
1276 Ok(symbol) => return Ok(vec![symbol]),
1277 Err(EncodeError::DataTooLong {
1278 ..
1279 }) => {},
1280 Err(error) => return Err(error),
1281 }
1282
1283 let range = self.structured_append_range()?;
1284 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1285
1286 ensure_input_length(data.len(), capacity, capacity_bits, 16)?;
1287
1288 let sequence_capacity_error = || EncodeError::DataTooLong {
1290 required_bits: None,
1291 capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1292 };
1293
1294 let minimum_parts =
1296 self.partition_bytes(data, range.clone()).map_err(|_| sequence_capacity_error())?.len();
1297
1298 let mut selected = None;
1299
1300 for value in range.start().value()..=range.end().value() {
1301 let candidate = QrVersion(value);
1302
1303 if let Ok(parts) = self.partition_bytes(data, *range.start()..=candidate)
1304 && parts.len() == minimum_parts
1305 {
1306 selected = Some(
1307 self.minimum_area_byte_partition(
1308 data,
1309 minimum_parts,
1310 *range.start()..=candidate,
1311 )
1312 .map_err(|_| sequence_capacity_error())?,
1313 );
1314 break;
1315 }
1316 }
1317
1318 let parts = selected.ok_or_else(sequence_capacity_error)?;
1319
1320 let slices: Vec<&[u8]> = parts.into_iter().map(|(start, end)| &data[start..end]).collect();
1321
1322 self.encode_structured_append_bytes(&slices)
1323 }
1324
1325 pub fn encode_text_with_structured_append(
1329 &self,
1330 text: impl AsRef<str>,
1331 ) -> Result<Vec<Symbol>, EncodeError> {
1332 let text = text.as_ref();
1333
1334 match self.encode_text(text) {
1335 Ok(symbol) => return Ok(vec![symbol]),
1336 Err(EncodeError::DataTooLong {
1337 ..
1338 }) => {},
1339 Err(error) => return Err(error),
1340 }
1341
1342 let range = self.structured_append_range()?;
1343 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1344
1345 ensure_input_length(text.chars().count(), capacity, capacity_bits, 16)?;
1346
1347 let offsets = text_boundaries(text);
1348
1349 let sequence_capacity_error = || EncodeError::DataTooLong {
1351 required_bits: None,
1352 capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1353 };
1354
1355 let minimum_parts = self
1357 .partition_text(text, &offsets, range.clone())
1358 .map_err(|_| sequence_capacity_error())?
1359 .len();
1360
1361 let mut selected = None;
1362
1363 for value in range.start().value()..=range.end().value() {
1364 let candidate = QrVersion(value);
1365
1366 if let Ok(parts) = self.partition_text(text, &offsets, *range.start()..=candidate)
1367 && parts.len() == minimum_parts
1368 {
1369 selected = Some(
1370 self.minimum_area_text_partition(
1371 text,
1372 &offsets,
1373 minimum_parts,
1374 *range.start()..=candidate,
1375 )
1376 .map_err(|_| sequence_capacity_error())?,
1377 );
1378 break;
1379 }
1380 }
1381
1382 let parts = selected.ok_or_else(sequence_capacity_error)?;
1383
1384 let slices: Vec<&str> =
1385 parts.into_iter().map(|(start, end)| &text[offsets[start]..offsets[end]]).collect();
1386 self.encode_structured_append_text(&slices)
1387 }
1388
1389 fn structured_append_range(&self) -> Result<RangeInclusive<QrVersion>, EncodeError> {
1390 if self.versions.start() > self.versions.end() {
1391 Err(EncodeError::InvalidVersionRange)
1392 } else {
1393 Ok(self.versions.clone())
1394 }
1395 }
1396
1397 fn input_capacity_upper_bound(
1398 &self,
1399 structured_append: bool,
1400 ) -> Result<(usize, usize), EncodeError> {
1401 if self.versions.start() > self.versions.end() {
1402 return Err(EncodeError::InvalidVersionRange);
1403 }
1404
1405 let version = *self.versions.end();
1406 let capacity_bits = model2::data_codewords(version, self.error_correction) * 8;
1407 let overhead_bits = 4
1408 + usize::from(Mode::Numeric.cci_bits(version))
1409 + usize::from(structured_append) * 20
1410 + match self.fnc1 {
1411 Some(Fnc1::Gs1) => 4,
1412 Some(Fnc1::Industry(_)) => 12,
1413 None => 0,
1414 };
1415
1416 Ok((numeric_character_capacity(capacity_bits, overhead_bits), capacity_bits))
1417 }
1418
1419 fn partition_bytes(
1420 &self,
1421 data: &[u8],
1422 range: RangeInclusive<QrVersion>,
1423 ) -> Result<Vec<(usize, usize)>, EncodeError> {
1424 let maximum_version = *range.end();
1425 let mut result = Vec::new();
1426 let mut start = 0;
1427
1428 while start < data.len() {
1429 if result.len() == 16 {
1430 return Err(EncodeError::DataTooLong {
1431 required_bits: None, capacity_bits: 0
1432 });
1433 }
1434
1435 let mut low = start + 1;
1436
1437 let mut high = data
1439 .len()
1440 .min(start.saturating_add(self.qr_character_upper_bound(maximum_version)));
1441
1442 let mut fitting = None;
1443
1444 while low <= high {
1445 let middle = low + (high - low) / 2;
1446
1447 if self.bytes_fit(&data[start..middle], range.clone()) {
1448 fitting = Some(middle);
1449 low = middle + 1;
1450 } else {
1451 high = middle - 1;
1452 }
1453 }
1454
1455 let end = fitting
1456 .ok_or(EncodeError::DataTooLong {
1457 required_bits: None, capacity_bits: 0
1458 })?;
1459
1460 result.push((start, end));
1461 start = end;
1462 }
1463 Ok(result)
1464 }
1465
1466 fn partition_text(
1467 &self,
1468 text: &str,
1469 offsets: &[usize],
1470 range: RangeInclusive<QrVersion>,
1471 ) -> Result<Vec<(usize, usize)>, EncodeError> {
1472 let maximum_version = *range.end();
1473 let force_initial_eci = requires_non_default_eci(text);
1474
1475 let mut result = Vec::new();
1476 let mut start = 0;
1477 let length = offsets.len() - 1;
1478
1479 while start < length {
1480 if result.len() == 16 {
1481 return Err(EncodeError::DataTooLong {
1482 required_bits: None, capacity_bits: 0
1483 });
1484 }
1485
1486 let mut low = start + 1;
1487
1488 let mut high =
1490 length.min(start.saturating_add(self.qr_character_upper_bound(maximum_version)));
1491
1492 let mut fitting = None;
1493
1494 while low <= high {
1495 let middle = low + (high - low) / 2;
1496
1497 if self.text_fits(
1498 &text[offsets[start]..offsets[middle]],
1499 range.clone(),
1500 force_initial_eci,
1501 ) {
1502 fitting = Some(middle);
1503 low = middle + 1;
1504 } else {
1505 high = middle - 1;
1506 }
1507 }
1508
1509 let end = fitting
1510 .ok_or(EncodeError::DataTooLong {
1511 required_bits: None, capacity_bits: 0
1512 })?;
1513
1514 result.push((start, end));
1515
1516 start = end;
1517 }
1518 Ok(result)
1519 }
1520
1521 fn minimum_area_byte_partition(
1522 &self,
1523 data: &[u8],
1524 part_count: usize,
1525 range: RangeInclusive<QrVersion>,
1526 ) -> Result<Vec<(usize, usize)>, EncodeError> {
1527 minimum_area_partition(data.len(), part_count, range, |start, version| {
1528 let high = data.len().min(start.saturating_add(self.qr_character_upper_bound(version)));
1529
1530 maximum_fitting_end(high, start, |end| {
1531 self.bytes_fit(&data[start..end], version..=version)
1532 })
1533 })
1534 }
1535
1536 fn minimum_area_text_partition(
1537 &self,
1538 text: &str,
1539 offsets: &[usize],
1540 part_count: usize,
1541 range: RangeInclusive<QrVersion>,
1542 ) -> Result<Vec<(usize, usize)>, EncodeError> {
1543 let length = offsets.len() - 1;
1544 let force_initial_eci = requires_non_default_eci(text);
1545
1546 minimum_area_partition(length, part_count, range, |start, version| {
1547 let high = length.min(start.saturating_add(self.qr_character_upper_bound(version)));
1548
1549 maximum_fitting_end(high, start, |end| {
1550 self.text_fits(
1551 &text[offsets[start]..offsets[end]],
1552 version..=version,
1553 force_initial_eci,
1554 )
1555 })
1556 })
1557 }
1558
1559 fn bytes_fit(&self, data: &[u8], range: RangeInclusive<QrVersion>) -> bool {
1561 self.qr_range_fits(range, |version| {
1562 optimizer::bytes(data, optimizer::Profile::qr(version), self.fnc1.is_some())
1563 })
1564 }
1565
1566 fn text_fits(
1568 &self,
1569 text: &str,
1570 range: RangeInclusive<QrVersion>,
1571 force_initial_eci: bool,
1572 ) -> bool {
1573 self.qr_range_fits(range, |version| {
1574 optimizer::text(
1575 text,
1576 optimizer::Profile::qr(version),
1577 self.fnc1.is_some(),
1578 force_initial_eci,
1579 )
1580 })
1581 }
1582
1583 fn qr_range_fits<F>(&self, range: RangeInclusive<QrVersion>, segments: F) -> bool
1585 where
1586 F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>, {
1587 if range.start() > range.end() {
1588 return false;
1589 }
1590
1591 let header = StructuredAppendInfo {
1593 index: 0, total: 16, parity: 0
1594 };
1595 let mut cache = GroupCache::new(segments);
1596
1597 for value in range.start().0..=range.end().0 {
1598 let version = QrVersion(value);
1599
1600 let Ok(candidate) = cache.get(version) else {
1601 return false;
1602 };
1603
1604 if model2::fits(candidate, version, self.error_correction, self.fnc1, Some(header)) {
1605 return true;
1606 }
1607 }
1608
1609 false
1610 }
1611
1612 #[inline]
1613 const fn qr_character_upper_bound(&self, version: QrVersion) -> usize {
1614 model2::data_codewords(version, self.error_correction) * 8 * 3 / 10 + 2
1615 }
1616
1617 fn encode_optimized_bytes(
1618 &self,
1619 data: &[u8],
1620 structured_append: Option<StructuredAppendInfo>,
1621 ) -> Result<Symbol, EncodeError> {
1622 self.encode_qr_range(
1623 self.versions.clone(),
1624 |version| optimizer::bytes(data, optimizer::Profile::qr(version), self.fnc1.is_some()),
1625 structured_append,
1626 )
1627 }
1628
1629 fn encode_optimized_text(
1630 &self,
1631 text: &str,
1632 structured_append: Option<StructuredAppendInfo>,
1633 force_initial_eci: bool,
1634 ) -> Result<Symbol, EncodeError> {
1635 self.encode_qr_range(
1636 self.versions.clone(),
1637 |version| {
1638 optimizer::text(
1639 text,
1640 optimizer::Profile::qr(version),
1641 self.fnc1.is_some(),
1642 force_initial_eci,
1643 )
1644 },
1645 structured_append,
1646 )
1647 }
1648
1649 fn encode_qr_range<F>(
1650 &self,
1651 range: RangeInclusive<QrVersion>,
1652 segments: F,
1653 structured_append: Option<StructuredAppendInfo>,
1654 ) -> Result<Symbol, EncodeError>
1655 where
1656 F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>, {
1657 if range.start() > range.end() {
1658 return Err(EncodeError::InvalidVersionRange);
1659 }
1660
1661 let mut cache = GroupCache::new(segments);
1662 let mut last_error = None;
1663
1664 for value in range.start().0..=range.end().0 {
1665 let version = QrVersion(value);
1666 let candidate = cache.get(version)?;
1667
1668 match model2::encode(
1669 candidate,
1670 version,
1671 self.error_correction,
1672 self.mask,
1673 self.boost_error_correction,
1674 self.fnc1,
1675 structured_append,
1676 ) {
1677 Ok(symbol) => return Ok(symbol),
1678 Err(
1679 error @ EncodeError::DataTooLong {
1680 ..
1681 },
1682 ) => last_error = Some(error),
1683 Err(error) => return Err(error),
1684 }
1685 }
1686
1687 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
1689 }
1690
1691 fn encode_segments_with_header(
1692 &self,
1693 segments: &[Segment],
1694 structured_append: Option<StructuredAppendInfo>,
1695 ) -> Result<Symbol, EncodeError> {
1696 let normalized;
1697
1698 let segments = if self.fnc1.is_some() {
1699 normalized = segments
1700 .iter()
1701 .map(|segment| {
1702 if segment.mode == Mode::Alphanumeric {
1703 Segment::fnc1_alphanumeric(segment.source_bytes())
1704 } else {
1705 Ok(segment.clone())
1706 }
1707 })
1708 .collect::<Result<Vec<_>, _>>()?;
1709 normalized.as_slice()
1710 } else {
1711 segments
1712 };
1713
1714 self.encode_qr_range(self.versions.clone(), |_| Ok(segments.to_vec()), structured_append)
1715 }
1716}
1717
1718#[cfg(feature = "rmqr")]
1720#[derive(Clone, Debug)]
1721pub struct RmqrEncoder {
1722 error_correction: RmqrErrorCorrection,
1723 versions: RangeInclusive<RmqrVersion>,
1724 boost_error_correction: bool,
1725 fnc1: Option<Fnc1>,
1726}
1727
1728#[cfg(feature = "rmqr")]
1729impl RmqrEncoder {
1730 #[inline]
1732 pub const fn new(error_correction: RmqrErrorCorrection) -> Self {
1733 Self {
1734 error_correction,
1735 versions: RmqrVersion::MIN..=RmqrVersion::MAX,
1736 boost_error_correction: true,
1737 fnc1: None,
1738 }
1739 }
1740
1741 #[must_use]
1743 #[inline]
1744 pub const fn version(mut self, version: RmqrVersion) -> Self {
1745 self.versions = version..=version;
1746 self
1747 }
1748
1749 #[must_use]
1751 #[inline]
1752 pub const fn version_range(mut self, versions: RangeInclusive<RmqrVersion>) -> Self {
1753 self.versions = versions;
1754 self
1755 }
1756
1757 #[must_use]
1759 #[inline]
1760 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1761 self.boost_error_correction = boost;
1762 self
1763 }
1764
1765 #[must_use]
1767 #[inline]
1768 pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1769 self.fnc1 = fnc1;
1770 self
1771 }
1772
1773 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1775 let data = data.as_ref();
1776 let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
1777
1778 ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1779 self.encode_optimized(|version| {
1780 optimizer::bytes(
1781 data,
1782 optimizer::Profile::rmqr(rmqr::cci(version)),
1783 self.fnc1.is_some(),
1784 )
1785 })
1786 }
1787
1788 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1790 let text = text.as_ref();
1791 let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
1792
1793 ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1794 self.encode_optimized(|version| {
1795 optimizer::text(
1796 text,
1797 optimizer::Profile::rmqr(rmqr::cci(version)),
1798 self.fnc1.is_some(),
1799 false,
1800 )
1801 })
1802 }
1803
1804 #[inline]
1806 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1807 &self,
1808 value: &T,
1809 ) -> Result<Symbol, EncodeError> {
1810 let text = value.to_qr_text();
1811 self.encode_text(&text)
1812 }
1813
1814 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1816 let normalized;
1817 let segments = if self.fnc1.is_some() {
1818 normalized = segments
1819 .iter()
1820 .map(|segment| {
1821 if segment.mode == Mode::Alphanumeric {
1822 Segment::fnc1_alphanumeric(segment.source_bytes())
1823 } else {
1824 Ok(segment.clone())
1825 }
1826 })
1827 .collect::<Result<Vec<_>, _>>()?;
1828 normalized.as_slice()
1829 } else {
1830 segments
1831 };
1832
1833 self.encode_optimized(|_| Ok(segments.to_vec()))
1834 }
1835
1836 fn input_capacity_upper_bound(&self) -> Result<(usize, usize), EncodeError> {
1837 let versions = self.candidates()?;
1838 let mut result = None;
1839
1840 for version in versions {
1841 let capacity_bits = rmqr::data_codewords(version, self.error_correction) * 8;
1842 let overhead_bits = 3
1843 + usize::from(rmqr::cci(version)[0])
1844 + match self.fnc1 {
1845 Some(Fnc1::Gs1) => 3,
1846 Some(Fnc1::Industry(_)) => 11,
1847 None => 0,
1848 };
1849 let capacity = numeric_character_capacity(capacity_bits, overhead_bits);
1850
1851 if result.is_none_or(|(best, _)| capacity > best) {
1852 result = Some((capacity, capacity_bits));
1853 }
1854 }
1855
1856 result.ok_or(EncodeError::InvalidVersionRange)
1857 }
1858
1859 fn encode_optimized<F>(&self, mut optimize: F) -> Result<Symbol, EncodeError>
1860 where
1861 F: FnMut(RmqrVersion) -> Result<Vec<Segment>, EncodeError>, {
1862 let versions = self.candidates()?;
1863 let mut cache: Vec<([u8; 4], Vec<Segment>)> = Vec::with_capacity(15);
1864 let mut last_error = None;
1865
1866 for version in versions {
1867 let profile = rmqr::cci(version);
1868 let index = match cache.iter().position(|(cci, _)| *cci == profile) {
1869 Some(index) => index,
1870 None => {
1871 cache.push((profile, optimize(version)?));
1872 cache.len() - 1
1873 },
1874 };
1875
1876 match rmqr::encode(
1877 &cache[index].1,
1878 version,
1879 self.error_correction,
1880 self.boost_error_correction,
1881 self.fnc1,
1882 ) {
1883 Ok(symbol) => return Ok(symbol),
1884 Err(
1885 error @ EncodeError::DataTooLong {
1886 ..
1887 },
1888 ) => last_error = Some(error),
1889 Err(error) => return Err(error),
1890 }
1891 }
1892
1893 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
1894 }
1895
1896 fn candidates(&self) -> Result<Vec<RmqrVersion>, EncodeError> {
1897 if self.versions.start() > self.versions.end() {
1898 return Err(EncodeError::InvalidVersionRange);
1899 }
1900
1901 Ok(RmqrVersion::ALL_BY_AREA
1902 .into_iter()
1903 .filter(|version| self.versions.contains(version))
1904 .collect())
1905 }
1906}
1907
1908#[cfg(feature = "micro-qr")]
1910#[derive(Clone, Debug)]
1911pub struct MicroEncoder {
1912 error_correction: MicroErrorCorrection,
1913 versions: RangeInclusive<MicroVersion>,
1914 mask: Option<MicroMask>,
1915 boost_error_correction: bool,
1916}
1917
1918#[cfg(feature = "micro-qr")]
1919impl MicroEncoder {
1920 #[inline]
1922 pub const fn new(error_correction: MicroErrorCorrection) -> Self {
1923 Self {
1924 error_correction,
1925 versions: MicroVersion::M1..=MicroVersion::M4,
1926 mask: None,
1927 boost_error_correction: true,
1928 }
1929 }
1930
1931 #[must_use]
1933 #[inline]
1934 pub const fn version(mut self, version: MicroVersion) -> Self {
1935 self.versions = version..=version;
1936 self
1937 }
1938
1939 #[must_use]
1941 #[inline]
1942 pub const fn version_range(mut self, versions: RangeInclusive<MicroVersion>) -> Self {
1943 self.versions = versions;
1944 self
1945 }
1946
1947 #[must_use]
1949 #[inline]
1950 pub const fn mask(mut self, mask: MicroMask) -> Self {
1951 self.mask = Some(mask);
1952 self
1953 }
1954
1955 #[must_use]
1957 #[inline]
1958 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1959 self.boost_error_correction = boost;
1960 self
1961 }
1962
1963 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1965 let data = data.as_ref();
1966 if let Some((version, capacity, capacity_bits)) = self.input_capacity_upper_bound()?
1967 && micro_bytes_are_representable(data, version)
1968 {
1969 ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1970 }
1971
1972 self.encode_range(|version| micro::optimize(data, version))
1973 }
1974
1975 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1977 let text = text.as_ref();
1978 if let Some((version, capacity, capacity_bits)) = self.input_capacity_upper_bound()?
1979 && micro_text_is_representable_without_kanji(text, version)
1980 {
1981 ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1982 }
1983
1984 self.encode_range(|version| micro::optimize_text(text, version))
1985 }
1986
1987 #[inline]
1989 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1990 &self,
1991 value: &T,
1992 ) -> Result<Symbol, EncodeError> {
1993 let text = value.to_qr_text();
1994 self.encode_text(&text)
1995 }
1996
1997 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1999 self.encode_range(|_| Ok(segments.to_vec()))
2000 }
2001
2002 fn input_capacity_upper_bound(
2003 &self,
2004 ) -> Result<Option<(MicroVersion, usize, usize)>, EncodeError> {
2005 if self.versions.start() > self.versions.end() {
2006 return Err(EncodeError::InvalidVersionRange);
2007 }
2008
2009 Ok(micro_versions(self.versions.clone())
2010 .filter_map(|version| {
2011 micro::input_capacity_upper_bound(version, self.error_correction)
2012 .map(|(capacity, capacity_bits)| (version, capacity, capacity_bits))
2013 })
2014 .max_by_key(|&(_, capacity, _)| capacity))
2015 }
2016
2017 fn encode_range<F>(&self, mut segments: F) -> Result<Symbol, EncodeError>
2018 where
2019 F: FnMut(MicroVersion) -> Result<Vec<Segment>, EncodeError>, {
2020 if self.versions.start() > self.versions.end() {
2021 return Err(EncodeError::InvalidVersionRange);
2022 }
2023
2024 let mut last_error = None;
2025
2026 for version in micro_versions(self.versions.clone()) {
2028 let result = segments(version).and_then(|segments| {
2029 micro::encode(
2030 &segments,
2031 version,
2032 self.error_correction,
2033 self.mask,
2034 self.boost_error_correction,
2035 )
2036 });
2037
2038 match result {
2039 Ok(symbol) => return Ok(symbol),
2040 Err(error) if candidate_rejection(&error) => last_error = Some(error),
2041 Err(error) => return Err(error),
2042 }
2043 }
2044
2045 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
2046 }
2047}
2048
2049#[cfg(all(feature = "qr", feature = "micro-qr"))]
2051#[derive(Clone, Debug)]
2052pub struct AutoEncoder {
2053 qr: QrEncoder,
2054 micro: MicroEncoder,
2055}
2056
2057#[cfg(all(feature = "qr", feature = "micro-qr"))]
2058impl AutoEncoder {
2059 #[inline]
2061 pub const fn new(qr: QrEncoder, micro: MicroEncoder) -> Self {
2062 Self {
2063 qr,
2064 micro,
2065 }
2066 }
2067
2068 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2070 let data = data.as_ref();
2071 match self.micro.encode_bytes(data) {
2072 Ok(symbol) => Ok(symbol),
2073 Err(error) if candidate_rejection(&error) => self.qr.encode_bytes(data),
2074 Err(error) => Err(error),
2075 }
2076 }
2077
2078 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2080 let text = text.as_ref();
2081 match self.micro.encode_text(text) {
2082 Ok(symbol) => Ok(symbol),
2083 Err(error) if candidate_rejection(&error) => self.qr.encode_text(text),
2084 Err(error) => Err(error),
2085 }
2086 }
2087
2088 #[inline]
2090 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2091 &self,
2092 value: &T,
2093 ) -> Result<Symbol, EncodeError> {
2094 let text = value.to_qr_text();
2095 self.encode_text(&text)
2096 }
2097
2098 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2100 match self.micro.encode_segments(segments) {
2101 Ok(symbol) => Ok(symbol),
2102 Err(error) if candidate_rejection(&error) => self.qr.encode_segments(segments),
2103 Err(error) => Err(error),
2104 }
2105 }
2106}
2107
2108#[cfg(feature = "qr")]
2109#[derive(Clone, Copy)]
2110struct PartitionState {
2111 end: usize,
2112 area: usize,
2113 previous: usize,
2114}
2115
2116#[cfg(feature = "qr")]
2117fn minimum_area_partition<F>(
2118 length: usize,
2119 part_count: usize,
2120 range: RangeInclusive<QrVersion>,
2121 mut maximum_end: F,
2122) -> Result<Vec<(usize, usize)>, EncodeError>
2123where
2124 F: FnMut(usize, QrVersion) -> Option<usize>, {
2125 let mut layers = vec![vec![PartitionState {
2127 end: 0, area: 0, previous: 0
2128 }]];
2129
2130 for _ in 0..part_count {
2131 let previous_layer = layers.last().expect("the initial layer exists");
2132 let mut by_end = BTreeMap::new();
2133
2134 for (previous, state) in previous_layer.iter().enumerate() {
2135 for value in range.start().value()..=range.end().value() {
2136 let version = QrVersion(value);
2137
2138 let Some(end) = maximum_end(state.end, version) else {
2139 continue;
2140 };
2141
2142 let size = usize::from(version.value()) * 4 + 17;
2143
2144 let candidate = PartitionState {
2145 end,
2146 area: state.area + size * size,
2147 previous,
2148 };
2149
2150 by_end
2151 .entry(end)
2152 .and_modify(|current: &mut PartitionState| {
2153 if candidate.area < current.area {
2154 *current = candidate;
2155 }
2156 })
2157 .or_insert(candidate);
2158 }
2159 }
2160
2161 let mut best_area = usize::MAX;
2162 let mut layer = Vec::new();
2163
2164 for state in by_end.into_values().rev() {
2165 if state.area < best_area {
2167 best_area = state.area;
2168 layer.push(state);
2169 }
2170 }
2171
2172 layer.reverse();
2173
2174 if layer.is_empty() {
2175 return Err(EncodeError::DataTooLong {
2176 required_bits: None, capacity_bits: 0
2177 });
2178 }
2179
2180 layers.push(layer);
2181 }
2182
2183 let mut state_index = layers[part_count].iter().position(|state| state.end == length).ok_or(
2184 EncodeError::DataTooLong {
2185 required_bits: None, capacity_bits: 0
2186 },
2187 )?;
2188
2189 let mut result = Vec::with_capacity(part_count);
2190
2191 for layer_index in (1..=part_count).rev() {
2192 let state = layers[layer_index][state_index];
2193 let previous = layers[layer_index - 1][state.previous];
2194 result.push((previous.end, state.end));
2195 state_index = state.previous;
2196 }
2197
2198 result.reverse();
2199
2200 Ok(result)
2201}
2202
2203#[cfg(feature = "qr")]
2204fn maximum_fitting_end<F>(length: usize, start: usize, mut fits: F) -> Option<usize>
2205where
2206 F: FnMut(usize) -> bool, {
2207 if start == length {
2208 return None;
2209 }
2210
2211 let mut low = start + 1;
2212 let mut high = length;
2213 let mut result = None;
2214
2215 while low <= high {
2217 let middle = low + (high - low) / 2;
2218
2219 if fits(middle) {
2220 result = Some(middle);
2221 low = middle + 1;
2222 } else {
2223 high = middle - 1;
2224 }
2225 }
2226 result
2227}
2228
2229#[cfg(feature = "micro-qr")]
2230fn micro_versions(versions: RangeInclusive<MicroVersion>) -> impl Iterator<Item = MicroVersion> {
2231 let start = *versions.start();
2232 let end = *versions.end();
2233
2234 [MicroVersion::M1, MicroVersion::M2, MicroVersion::M3, MicroVersion::M4]
2235 .into_iter()
2236 .filter(move |version| *version >= start && *version <= end)
2237}
2238
2239#[cfg(feature = "micro-qr")]
2240fn micro_bytes_are_representable(data: &[u8], version: MicroVersion) -> bool {
2241 match version {
2242 MicroVersion::M1 => data.iter().all(u8::is_ascii_digit),
2243 MicroVersion::M2 => data.iter().all(|byte| alphanumeric_value(*byte).is_some()),
2244 MicroVersion::M3 | MicroVersion::M4 => true,
2245 }
2246}
2247
2248#[cfg(feature = "micro-qr")]
2249fn micro_text_is_representable_without_kanji(text: &str, version: MicroVersion) -> bool {
2250 match version {
2251 MicroVersion::M1 => text.bytes().all(|byte| byte.is_ascii_digit()),
2252 MicroVersion::M2 => {
2253 text.is_ascii() && text.bytes().all(|byte| alphanumeric_value(byte).is_some())
2254 },
2255 MicroVersion::M3 | MicroVersion::M4 => {
2256 text.chars().all(|character| u32::from(character) <= 0xFF)
2257 },
2258 }
2259}
2260
2261#[cfg(feature = "micro-qr")]
2262#[inline]
2263const fn candidate_rejection(error: &EncodeError) -> bool {
2264 matches!(
2265 error,
2266 EncodeError::DataTooLong { .. }
2267 | EncodeError::UnsupportedMode { .. }
2268 | EncodeError::UnsupportedErrorCorrection { .. }
2269 | EncodeError::TextNotRepresentable { .. }
2270 )
2271}
2272
2273#[cfg(feature = "qr")]
2274#[inline]
2275const fn version_group(version: QrVersion) -> usize {
2276 if version.0 <= 9 {
2277 0
2278 } else if version.0 <= 26 {
2279 1
2280 } else {
2281 2
2282 }
2283}
2284
2285#[cfg(feature = "qr")]
2287struct GroupCache<F> {
2288 segments: F,
2289 cache: [Option<Vec<Segment>>; 3],
2290}
2291
2292#[cfg(feature = "qr")]
2293impl<F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>> GroupCache<F> {
2294 fn new(segments: F) -> Self {
2295 Self {
2296 segments,
2297 cache: [None, None, None],
2298 }
2299 }
2300
2301 fn get(&mut self, version: QrVersion) -> Result<&[Segment], EncodeError> {
2302 let group = version_group(version);
2303
2304 if self.cache[group].is_none() {
2305 self.cache[group] = Some((self.segments)(version)?);
2306 }
2307
2308 Ok(self.cache[group].as_deref().expect("the version group is cached"))
2309 }
2310
2311 fn take(&mut self, version: QrVersion) -> Result<Vec<Segment>, EncodeError> {
2312 self.get(version)?;
2313
2314 Ok(self.cache[version_group(version)].take().expect("the version group is cached"))
2315 }
2316}
2317
2318#[cfg(feature = "qr")]
2319#[inline]
2320const fn validate_part_count(count: usize) -> Result<(), EncodeError> {
2321 if count >= 1 && count <= 16 {
2322 Ok(())
2323 } else {
2324 Err(EncodeError::InvalidStructuredAppendPartCount {
2325 count,
2326 })
2327 }
2328}
2329
2330#[cfg(feature = "qr")]
2331fn text_boundaries(text: &str) -> Vec<usize> {
2332 let mut result: Vec<_> = text.char_indices().map(|(offset, _)| offset).collect();
2333 result.push(text.len());
2334 result
2335}
2336
2337#[cfg(feature = "qr")]
2338#[inline]
2339fn requires_non_default_eci(text: &str) -> bool {
2340 text.chars().any(|character| u32::from(character) > 0xFF)
2341}