Skip to main content

qrcode_generator/encode/
mod.rs

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/// The error correction level written to an encoded symbol.
58///
59/// The available variants depend on the enabled symbol family features.
60#[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    /// Detects errors in a Micro QR M1 symbol without correcting them.
65    #[cfg(feature = "micro-qr")]
66    DetectionOnly,
67    /// Uses the Low error correction level.
68    Low,
69    /// Uses the Medium error correction level.
70    Medium,
71    /// Uses the Quartile error correction level.
72    Quartile,
73    /// Uses the High error correction level.
74    #[cfg(any(feature = "qr", feature = "rmqr"))]
75    High,
76}
77
78/// An error correction level for a Model 2 QR Code.
79#[cfg(feature = "qr")]
80#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
81pub enum QrErrorCorrection {
82    /// Uses the Low error correction level.
83    Low,
84    /// Uses the Medium error correction level.
85    Medium,
86    /// Uses the Quartile error correction level.
87    Quartile,
88    /// Uses the High error correction level.
89    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/// An error correction level for a Micro QR Code.
129#[cfg(feature = "micro-qr")]
130#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
131pub enum MicroErrorCorrection {
132    /// Detects errors in an M1 symbol without correcting them.
133    DetectionOnly,
134    /// Uses the Low error correction level.
135    Low,
136    /// Uses the Medium error correction level.
137    Medium,
138    /// Uses the Quartile error correction level.
139    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/// An error correction level for a Rectangular Micro QR Code.
156#[cfg(feature = "rmqr")]
157#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
158pub enum RmqrErrorCorrection {
159    /// Uses the Medium error correction level.
160    Medium,
161    /// Uses the High error correction level.
162    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/// A validated Model 2 QR Code version.
177#[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    /// The largest Model 2 QR Code version.
184    pub const MAX: Self = Self(40);
185    /// The smallest Model 2 QR Code version.
186    pub const MIN: Self = Self(1);
187
188    /// Creates a Model 2 version from a value in `1..=40`.
189    #[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    /// Returns the numeric version value.
199    #[inline]
200    pub const fn value(self) -> u8 {
201        self.0
202    }
203}
204
205/// A Micro QR Code version.
206#[cfg(feature = "micro-qr")]
207#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
208pub enum MicroVersion {
209    /// An 11 by 11 Micro QR Code symbol.
210    M1,
211    /// A 13 by 13 Micro QR Code symbol.
212    M2,
213    /// A 15 by 15 Micro QR Code symbol.
214    M3,
215    /// A 17 by 17 Micro QR Code symbol.
216    M4,
217}
218
219#[cfg(feature = "micro-qr")]
220impl MicroVersion {
221    /// Returns the number of modules on each side.
222    #[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/// A Rectangular Micro QR Code version.
234#[cfg(feature = "rmqr")]
235#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
236#[repr(u8)]
237pub enum RmqrVersion {
238    /// A 7 by 43 rMQR symbol.
239    R7x43,
240    /// A 7 by 59 rMQR symbol.
241    R7x59,
242    /// A 7 by 77 rMQR symbol.
243    R7x77,
244    /// A 7 by 99 rMQR symbol.
245    R7x99,
246    /// A 7 by 139 rMQR symbol.
247    R7x139,
248    /// A 9 by 43 rMQR symbol.
249    R9x43,
250    /// A 9 by 59 rMQR symbol.
251    R9x59,
252    /// A 9 by 77 rMQR symbol.
253    R9x77,
254    /// A 9 by 99 rMQR symbol.
255    R9x99,
256    /// A 9 by 139 rMQR symbol.
257    R9x139,
258    /// An 11 by 27 rMQR symbol.
259    R11x27,
260    /// An 11 by 43 rMQR symbol.
261    R11x43,
262    /// An 11 by 59 rMQR symbol.
263    R11x59,
264    /// An 11 by 77 rMQR symbol.
265    R11x77,
266    /// An 11 by 99 rMQR symbol.
267    R11x99,
268    /// An 11 by 139 rMQR symbol.
269    R11x139,
270    /// A 13 by 27 rMQR symbol.
271    R13x27,
272    /// A 13 by 43 rMQR symbol.
273    R13x43,
274    /// A 13 by 59 rMQR symbol.
275    R13x59,
276    /// A 13 by 77 rMQR symbol.
277    R13x77,
278    /// A 13 by 99 rMQR symbol.
279    R13x99,
280    /// A 13 by 139 rMQR symbol.
281    R13x139,
282    /// A 15 by 43 rMQR symbol.
283    R15x43,
284    /// A 15 by 59 rMQR symbol.
285    R15x59,
286    /// A 15 by 77 rMQR symbol.
287    R15x77,
288    /// A 15 by 99 rMQR symbol.
289    R15x99,
290    /// A 15 by 139 rMQR symbol.
291    R15x139,
292    /// A 17 by 43 rMQR symbol.
293    R17x43,
294    /// A 17 by 59 rMQR symbol.
295    R17x59,
296    /// A 17 by 77 rMQR symbol.
297    R17x77,
298    /// A 17 by 99 rMQR symbol.
299    R17x99,
300    /// A 17 by 139 rMQR symbol.
301    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    // Versions ordered by area, then height, then width; sorted once at compile time for candidate search.
341    pub(crate) const ALL_BY_AREA: [Self; 32] = Self::sorted_by_area();
342    /// The last rMQR version in format indicator order.
343    pub const MAX: Self = Self::R17x139;
344    /// The first rMQR version in format indicator order.
345    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        // A selection sort is enough for a const array of thirty-two versions.
353        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    // Packs area, height and width into one number that orders the same way as the (area, height, width) tuple.
375    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    /// Returns the five-bit version indicator value.
383    #[inline]
384    pub const fn value(self) -> u8 {
385        self as u8
386    }
387
388    /// Returns the symbol width in modules.
389    #[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    /// Returns the symbol height in modules.
427    #[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/// The version of an encoded symbol.
451///
452/// The available variants depend on the enabled symbol family features.
453#[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    /// A Model 2 QR Code version.
458    #[cfg(feature = "qr")]
459    Qr(QrVersion),
460    /// A Micro QR Code version.
461    #[cfg(feature = "micro-qr")]
462    Micro(MicroVersion),
463    /// A Rectangular Micro QR Code version.
464    #[cfg(feature = "rmqr")]
465    Rmqr(RmqrVersion),
466}
467
468/// A validated Model 2 mask number.
469#[cfg(feature = "qr")]
470#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
471pub struct QrMask(u8);
472
473#[cfg(feature = "qr")]
474impl QrMask {
475    /// Creates a Model 2 mask from a value in `0..=7`.
476    #[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    /// Returns the mask value.
482    #[inline]
483    pub const fn value(self) -> u8 {
484        self.0
485    }
486}
487
488/// A validated Micro QR Code mask number.
489#[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    /// Creates a Micro QR Code mask from a value in `0..=3`.
496    #[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    /// Returns the mask value.
502    #[inline]
503    pub const fn value(self) -> u8 {
504        self.0
505    }
506}
507
508/// An Extended Channel Interpretation assignment number.
509#[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    /// The default ISO-8859-1 character set assignment.
516    pub const ISO_8859_1: Self = Self(3);
517    /// The Shift JIS character set assignment.
518    pub const SHIFT_JIS: Self = Self(20);
519    /// The UTF-8 character set assignment.
520    pub const UTF_8: Self = Self(26);
521
522    /// Creates an ECI assignment from a value in `0..=999999`.
523    #[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    /// Returns the ECI assignment number.
533    #[inline]
534    pub const fn value(self) -> u32 {
535        self.0
536    }
537}
538
539/// A validated FNC1 second-position application indicator.
540#[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    /// Creates a numeric application indicator from a value in `0..=99`.
547    #[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    /// Creates an application indicator from one ASCII letter.
553    #[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    /// Returns the encoded application indicator byte.
563    #[inline]
564    pub const fn value(self) -> u8 {
565        self.0
566    }
567}
568
569/// The FNC1 header applied to a Model 2 or rMQR symbol.
570#[cfg(any(feature = "qr", feature = "rmqr"))]
571#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
572pub enum Fnc1 {
573    /// Marks the symbol as GS1 data using FNC1 in first position.
574    Gs1,
575    /// Marks the symbol as industry data using FNC1 in second position.
576    Industry(ApplicationIndicator),
577}
578
579/// Metadata carried by a Structured Append symbol.
580#[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    /// Returns the zero-based symbol index.
591    #[inline]
592    pub const fn index(self) -> u8 {
593        self.index
594    }
595
596    /// Returns the total number of symbols.
597    #[inline]
598    pub const fn total(self) -> u8 {
599        self.total
600    }
601
602    /// Returns the parity byte shared by the symbol sequence.
603    #[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/// An explicitly constructed QR Code data or control segment.
649#[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    /// Creates a Numeric segment from ASCII digits.
659    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    /// Creates an Alphanumeric segment from the standard 45-character set.
687    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            // A group separator becomes percent and a literal percent is escaped by doubling it.
725            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    /// Creates a Byte segment without adding an ECI header.
759    pub fn bytes(data: impl AsRef<[u8]>) -> Self {
760        let data = data.as_ref();
761
762        // A Byte segment stores its payload once in `bits`, so `source` stays empty and is read back from there.
763        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    // Returns the original bytes a segment was built from, reading a Byte segment back from its bit buffer.
772    #[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    /// Creates an ECI control segment.
781    #[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        // Prefix bits select the shortest one, two or three-codeword ECI representation.
787        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    /// Creates a Kanji segment from characters in the supported Shift JIS ranges.
807    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    /// Returns the character count stored in this segment.
833    #[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    // WHATWG maps these onto bytes that read back as another character, so they cannot round-trip.
842    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    // The two valid Shift JIS ranges are shifted into one compact 13-bit index space.
868    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// Orders the modes used for deterministic tie-breaking in the segment optimizers.
878#[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// Computes the BCH remainder of the data polynomial times x^degree, reduced by the generator.
909// The generator is given without its leading term and degree is the number of parity bits.
910#[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/// An immutable encoded QR Code, Micro QR Code or rMQR symbol.
925#[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    /// Returns the symbol width in modules.
939    ///
940    /// The square QR Code and Micro QR Code families use this as the height as well, but Rectangular Micro QR Code symbols are not square, so read [`width`](Self::width) and [`height`](Self::height) separately for them.
941    #[inline]
942    pub const fn size(&self) -> usize {
943        self.width()
944    }
945
946    /// Returns the symbol width in modules.
947    #[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    /// Returns the symbol height in modules.
960    #[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    /// Returns the encoded symbol version.
973    #[inline]
974    pub const fn version(&self) -> SymbolVersion {
975        self.version
976    }
977
978    /// Returns the error correction level written to the symbol.
979    #[inline]
980    pub const fn error_correction(&self) -> SymbolErrorCorrection {
981        self.error_correction
982    }
983
984    /// Returns the selected mask number.
985    #[inline]
986    pub const fn mask(&self) -> u8 {
987        self.mask
988    }
989
990    /// Returns Structured Append metadata when this symbol belongs to a sequence.
991    #[cfg(feature = "qr")]
992    #[inline]
993    pub const fn structured_append(&self) -> Option<StructuredAppendInfo> {
994        self.structured_append
995    }
996
997    /// Returns a module or `None` when the coordinates are outside the symbol.
998    #[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    /// Returns every module as one row-major slice without copying.
1005    ///
1006    /// A dark module is `true`, and the module at `(x, y)` is at index `y * width + x`.
1007    #[inline]
1008    pub fn modules(&self) -> &[bool] {
1009        &self.modules
1010    }
1011
1012    /// Copies the symbol into a row-major Boolean matrix.
1013    pub fn to_matrix(&self) -> Vec<Vec<bool>> {
1014        self.modules.chunks(self.width()).map(<[bool]>::to_vec).collect()
1015    }
1016}
1017
1018/// Converts a value to text for QR Code encoding.
1019///
1020/// Implementations can use a shorter equivalent spelling when the value has more than one text representation.
1021/// The optional `url` feature implements this trait for `url::Url`.
1022pub trait ToQRText {
1023    /// Returns the text to encode.
1024    fn to_qr_text(&self) -> String;
1025}
1026
1027/// Configures and encodes Model 2 QR Code symbols.
1028#[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    /// Creates an encoder with automatic mask selection and Model 2 versions 1 through 40.
1041    #[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    /// Selects one exact Model 2 QR Code version.
1053    #[must_use]
1054    #[inline]
1055    pub const fn version(mut self, version: QrVersion) -> Self {
1056        self.versions = version..=version;
1057        self
1058    }
1059
1060    /// Selects the inclusive Model 2 QR Code version range considered during encoding.
1061    #[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    /// Forces an exact mask instead of selecting one automatically.
1069    #[must_use]
1070    #[inline]
1071    pub const fn mask(mut self, mask: QrMask) -> Self {
1072        self.mask = Some(mask);
1073        self
1074    }
1075
1076    /// Enables or disables upgrading the error correction level when the selected version has room.
1077    #[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    /// Sets the FNC1 interpretation applied to the complete symbol.
1085    #[must_use]
1086    #[inline]
1087    pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1088        self.fnc1 = fnc1;
1089        self
1090    }
1091
1092    /// Encodes raw bytes with globally optimized Numeric, Alphanumeric and Byte segments.
1093    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    /// Encodes text with globally optimized character modes and ECI transitions.
1103    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    /// Encodes a value after converting it to its QR Code text representation.
1113    #[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    /// Encodes explicit segments without changing their boundaries.
1123    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1124        self.encode_segments_with_header(segments, None)
1125    }
1126
1127    /// Encodes caller-selected byte parts as one Structured Append sequence.
1128    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    /// Encodes caller-selected text parts as one Structured Append sequence.
1161    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        // Each part is optimized once, and both the shared parity and the final symbols reuse the result.
1176        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            // Parity uses the byte representation selected by the optimizer, including Shift JIS or UTF-8 bytes.
1183            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    // Picks the smallest version that fits one Structured Append part and returns its optimized segments.
1214    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        // Only the presence of the header matters for the fit check, so the metadata values are placeholders.
1224        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        // No version fits, so the largest one is returned to reproduce the same capacity error later.
1247        let version = *range.end();
1248
1249        Ok((version, cache.take(version)?))
1250    }
1251
1252    /// Encodes caller-selected segment parts as one Structured Append sequence.
1253    ///
1254    /// Each part must include its own ECI headers before the data that needs them; ECI state is not copied from earlier parts.
1255    ///
1256    /// ```rust
1257    /// use qrcode_generator::{Segment, qr::{EciAssignment, Encoder, ErrorCorrection}};
1258    ///
1259    /// let first = [Segment::eci(EciAssignment::UTF_8), Segment::bytes("café".as_bytes())];
1260    /// let second = [Segment::eci(EciAssignment::UTF_8), Segment::bytes("世界".as_bytes())];
1261    /// let symbols = Encoder::new(ErrorCorrection::Medium)
1262    ///     .encode_structured_append_segments(&[&first, &second])
1263    ///     .unwrap();
1264    /// assert_eq!(2, symbols.len());
1265    /// ```
1266    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    /// Encodes bytes as one symbol or automatically splits them into at most 16 Structured Append symbols.
1295    ///
1296    /// When the data fits one symbol, that lone symbol is returned without Structured Append metadata, because a single symbol needs no sequence header.
1297    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        // Partitioning failures replace internal placeholder errors with the full sequence capacity.
1317        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        // Greedy maximum-size parts determine the minimum possible symbol count.
1323        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    /// Encodes text as one symbol or automatically splits it into at most 16 Structured Append symbols.
1354    ///
1355    /// When the text fits one symbol, that lone symbol is returned without Structured Append metadata, because a single symbol needs no sequence header.
1356    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        // Partitioning failures replace internal placeholder errors with the full sequence capacity.
1378        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        // Text partitions use scalar boundaries so no UTF-8 character is split between symbols.
1384        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            // Numeric mode gives the largest possible character capacity for any input.
1466            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            // Numeric mode gives the largest possible scalar capacity for any text input.
1517            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    // Reports whether the bytes fit some version in the range as one Structured Append part.
1588    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    // Reports whether the text fits some version in the range as one Structured Append part.
1595    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    // Runs the fit check without drawing a symbol, so probing skips the matrix and mask work.
1612    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        // Only the presence of the header matters for the fit check, so the values are placeholders.
1620        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        // The version range is verified to be non-empty, so at least one capacity error was stored.
1716        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/// Configures and encodes Rectangular Micro QR Code symbols.
1747#[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    /// Creates an encoder that considers every rMQR version.
1759    #[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    /// Selects one exact rMQR version.
1770    #[must_use]
1771    #[inline]
1772    pub const fn version(mut self, version: RmqrVersion) -> Self {
1773        self.versions = version..=version;
1774        self
1775    }
1776
1777    /// Selects an inclusive range in rMQR format indicator order.
1778    #[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    /// Enables or disables upgrading Medium error correction to High when the selected version has room.
1786    #[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    /// Sets the FNC1 interpretation applied to the complete symbol.
1794    #[must_use]
1795    #[inline]
1796    pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1797        self.fnc1 = fnc1;
1798        self
1799    }
1800
1801    /// Encodes raw bytes with globally optimized Numeric, Alphanumeric and Byte segments.
1802    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    /// Encodes text with globally optimized character modes and ECI transitions.
1817    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    /// Encodes a value after converting it to its QR Code text representation.
1833    #[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    /// Encodes explicit segments without changing their boundaries.
1843    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    // Segments are planned once per character count indicator profile, because versions sharing one profile share their plan.
1896    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    // Tries every candidate version in area order and keeps the last capacity error for the caller.
1922    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/// Configures and encodes Micro QR Code symbols.
1956#[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    /// Creates an encoder with automatic mask selection and all Micro QR Code versions.
1968    #[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    /// Selects one exact Micro QR Code version.
1979    #[must_use]
1980    #[inline]
1981    pub const fn version(mut self, version: MicroVersion) -> Self {
1982        self.versions = version..=version;
1983        self
1984    }
1985
1986    /// Selects the inclusive Micro QR Code version range considered during encoding.
1987    #[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    /// Forces an exact mask instead of selecting one automatically.
1995    #[must_use]
1996    #[inline]
1997    pub const fn mask(mut self, mask: MicroMask) -> Self {
1998        self.mask = Some(mask);
1999        self
2000    }
2001
2002    /// Enables or disables upgrading the error correction level when the selected version has room.
2003    #[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    /// Encodes raw bytes with globally optimized modes supported by each candidate version.
2011    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    /// Encodes text with globally optimized modes supported by each candidate version.
2021    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    /// Encodes a value after converting it to its QR Code text representation.
2030    #[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    /// Encodes explicit segments without changing their boundaries.
2040    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2041        self.encode_range(|_| Ok(segments.to_vec()))
2042    }
2043
2044    // Returns the candidate version holding the most characters, which is also the most permissive one.
2045    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        // Unsupported modes and error correction levels eliminate only the current candidate version.
2072        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/// Tries a configured Micro QR Code encoder before a configured Model 2 QR Code encoder, unless the Model 2 encoder requires FNC1.
2103#[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    /// Creates an encoder from independently configured Model 2 and Micro QR Code encoders.
2113    #[inline]
2114    pub const fn new(qr: QrEncoder, micro: MicroEncoder) -> Self {
2115        Self {
2116            qr,
2117            micro,
2118        }
2119    }
2120
2121    /// Encodes raw bytes using the smallest eligible symbol family.
2122    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    /// Encodes text using the smallest eligible symbol family.
2135    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    /// Encodes a value after converting it to its QR Code text representation once.
2148    #[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    /// Encodes explicit segments using the smallest eligible symbol family.
2158    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    // Each layer represents one additional symbol in the fixed-size Structured Append sequence.
2188    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            // A state that reaches farther with no greater area dominates every earlier state.
2228            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    // Encoding fit is monotonic as the candidate slice grows from a fixed start.
2278    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// Caches segments per version group during a version scan, because character count indicator widths change only at versions 10 and 27.
2326#[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}