Skip to main content

qrcode_generator/encode/
mod.rs

1#[cfg(any(feature = "qr", feature = "rmqr"))]
2use alloc::borrow::Cow;
3#[cfg(feature = "qr")]
4use alloc::collections::BTreeMap;
5#[cfg(feature = "qr")]
6use alloc::vec;
7use alloc::{string::String, vec::Vec};
8#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
9use core::ops::RangeInclusive;
10
11mod bits;
12#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
13mod reed_solomon;
14
15#[cfg(feature = "micro-qr")]
16mod micro;
17#[cfg(feature = "qr")]
18mod model2;
19#[cfg(any(feature = "qr", feature = "rmqr"))]
20mod optimizer;
21#[cfg(feature = "rmqr")]
22mod rmqr;
23
24use bits::BitBuffer;
25
26use crate::EncodeError;
27
28#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
29#[inline]
30const fn numeric_character_capacity(capacity_bits: usize, overhead_bits: usize) -> usize {
31    let payload_bits = capacity_bits.saturating_sub(overhead_bits);
32
33    payload_bits / 10 * 3
34        + match payload_bits % 10 {
35            4..=6 => 1,
36            7..=9 => 2,
37            _ => 0,
38        }
39}
40
41#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
42#[inline]
43const fn ensure_input_length(
44    length: usize,
45    per_symbol_capacity: usize,
46    capacity_bits: usize,
47    symbol_count: usize,
48) -> Result<(), EncodeError> {
49    if length > per_symbol_capacity.saturating_mul(symbol_count) {
50        Err(EncodeError::DataTooLong {
51            required_bits: None,
52            capacity_bits: capacity_bits.saturating_mul(symbol_count),
53        })
54    } else {
55        Ok(())
56    }
57}
58
59/// The error correction level written to an encoded symbol.
60///
61/// The available variants depend on the enabled symbol family features.
62#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
63#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
64#[non_exhaustive]
65pub enum SymbolErrorCorrection {
66    /// Detects errors in a Micro QR M1 symbol without correcting them.
67    #[cfg(feature = "micro-qr")]
68    DetectionOnly,
69    /// Uses the Low error correction level.
70    Low,
71    /// Uses the Medium error correction level.
72    Medium,
73    /// Uses the Quartile error correction level.
74    Quartile,
75    /// Uses the High error correction level.
76    #[cfg(any(feature = "qr", feature = "rmqr"))]
77    High,
78}
79
80/// An error correction level for a Model 2 QR Code.
81#[cfg(feature = "qr")]
82#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
83pub enum QrErrorCorrection {
84    /// Uses the Low error correction level.
85    Low,
86    /// Uses the Medium error correction level.
87    Medium,
88    /// Uses the Quartile error correction level.
89    Quartile,
90    /// Uses the High error correction level.
91    High,
92}
93
94#[cfg(feature = "qr")]
95impl QrErrorCorrection {
96    #[inline]
97    pub(crate) const fn qr_ordinal(self) -> usize {
98        match self {
99            Self::Low => 0,
100            Self::Medium => 1,
101            Self::Quartile => 2,
102            Self::High => 3,
103        }
104    }
105
106    #[inline]
107    pub(crate) const fn qr_format_bits(self) -> u8 {
108        match self {
109            Self::Low => 1,
110            Self::Medium => 0,
111            Self::Quartile => 3,
112            Self::High => 2,
113        }
114    }
115}
116
117#[cfg(feature = "qr")]
118impl From<QrErrorCorrection> for SymbolErrorCorrection {
119    #[inline]
120    fn from(value: QrErrorCorrection) -> Self {
121        match value {
122            QrErrorCorrection::Low => Self::Low,
123            QrErrorCorrection::Medium => Self::Medium,
124            QrErrorCorrection::Quartile => Self::Quartile,
125            QrErrorCorrection::High => Self::High,
126        }
127    }
128}
129
130/// An error correction level for a Micro QR Code.
131#[cfg(feature = "micro-qr")]
132#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
133pub enum MicroErrorCorrection {
134    /// Detects errors in an M1 symbol without correcting them.
135    DetectionOnly,
136    /// Uses the Low error correction level.
137    Low,
138    /// Uses the Medium error correction level.
139    Medium,
140    /// Uses the Quartile error correction level.
141    Quartile,
142}
143
144#[cfg(feature = "micro-qr")]
145impl From<MicroErrorCorrection> for SymbolErrorCorrection {
146    #[inline]
147    fn from(value: MicroErrorCorrection) -> Self {
148        match value {
149            MicroErrorCorrection::DetectionOnly => Self::DetectionOnly,
150            MicroErrorCorrection::Low => Self::Low,
151            MicroErrorCorrection::Medium => Self::Medium,
152            MicroErrorCorrection::Quartile => Self::Quartile,
153        }
154    }
155}
156
157/// An error correction level for a Rectangular Micro QR Code.
158#[cfg(feature = "rmqr")]
159#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
160pub enum RmqrErrorCorrection {
161    /// Uses the Medium error correction level.
162    Medium,
163    /// Uses the High error correction level.
164    High,
165}
166
167#[cfg(feature = "rmqr")]
168impl From<RmqrErrorCorrection> for SymbolErrorCorrection {
169    #[inline]
170    fn from(value: RmqrErrorCorrection) -> Self {
171        match value {
172            RmqrErrorCorrection::Medium => Self::Medium,
173            RmqrErrorCorrection::High => Self::High,
174        }
175    }
176}
177
178/// A validated Model 2 QR Code version.
179#[cfg(feature = "qr")]
180#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
181pub struct QrVersion(u8);
182
183#[cfg(feature = "qr")]
184impl QrVersion {
185    /// The largest Model 2 QR Code version.
186    pub const MAX: Self = Self(40);
187    /// The smallest Model 2 QR Code version.
188    pub const MIN: Self = Self(1);
189
190    /// Creates a Model 2 version from a value in `1..=40`.
191    #[inline]
192    pub const fn new(value: u8) -> Result<Self, EncodeError> {
193        if value >= 1 && value <= 40 {
194            Ok(Self(value))
195        } else {
196            Err(EncodeError::InvalidVersionRange)
197        }
198    }
199
200    /// Returns the numeric version value.
201    #[inline]
202    pub const fn value(self) -> u8 {
203        self.0
204    }
205}
206
207/// A Micro QR Code version.
208#[cfg(feature = "micro-qr")]
209#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
210pub enum MicroVersion {
211    /// An 11 by 11 Micro QR Code symbol.
212    M1,
213    /// A 13 by 13 Micro QR Code symbol.
214    M2,
215    /// A 15 by 15 Micro QR Code symbol.
216    M3,
217    /// A 17 by 17 Micro QR Code symbol.
218    M4,
219}
220
221#[cfg(feature = "micro-qr")]
222impl MicroVersion {
223    /// Returns the number of modules on each side.
224    #[inline]
225    pub const fn size(self) -> usize {
226        match self {
227            Self::M1 => 11,
228            Self::M2 => 13,
229            Self::M3 => 15,
230            Self::M4 => 17,
231        }
232    }
233}
234
235/// A Rectangular Micro QR Code version.
236#[cfg(feature = "rmqr")]
237#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
238#[repr(u8)]
239pub enum RmqrVersion {
240    /// A 7 by 43 rMQR symbol.
241    R7x43,
242    /// A 7 by 59 rMQR symbol.
243    R7x59,
244    /// A 7 by 77 rMQR symbol.
245    R7x77,
246    /// A 7 by 99 rMQR symbol.
247    R7x99,
248    /// A 7 by 139 rMQR symbol.
249    R7x139,
250    /// A 9 by 43 rMQR symbol.
251    R9x43,
252    /// A 9 by 59 rMQR symbol.
253    R9x59,
254    /// A 9 by 77 rMQR symbol.
255    R9x77,
256    /// A 9 by 99 rMQR symbol.
257    R9x99,
258    /// A 9 by 139 rMQR symbol.
259    R9x139,
260    /// An 11 by 27 rMQR symbol.
261    R11x27,
262    /// An 11 by 43 rMQR symbol.
263    R11x43,
264    /// An 11 by 59 rMQR symbol.
265    R11x59,
266    /// An 11 by 77 rMQR symbol.
267    R11x77,
268    /// An 11 by 99 rMQR symbol.
269    R11x99,
270    /// An 11 by 139 rMQR symbol.
271    R11x139,
272    /// A 13 by 27 rMQR symbol.
273    R13x27,
274    /// A 13 by 43 rMQR symbol.
275    R13x43,
276    /// A 13 by 59 rMQR symbol.
277    R13x59,
278    /// A 13 by 77 rMQR symbol.
279    R13x77,
280    /// A 13 by 99 rMQR symbol.
281    R13x99,
282    /// A 13 by 139 rMQR symbol.
283    R13x139,
284    /// A 15 by 43 rMQR symbol.
285    R15x43,
286    /// A 15 by 59 rMQR symbol.
287    R15x59,
288    /// A 15 by 77 rMQR symbol.
289    R15x77,
290    /// A 15 by 99 rMQR symbol.
291    R15x99,
292    /// A 15 by 139 rMQR symbol.
293    R15x139,
294    /// A 17 by 43 rMQR symbol.
295    R17x43,
296    /// A 17 by 59 rMQR symbol.
297    R17x59,
298    /// A 17 by 77 rMQR symbol.
299    R17x77,
300    /// A 17 by 99 rMQR symbol.
301    R17x99,
302    /// A 17 by 139 rMQR symbol.
303    R17x139,
304}
305
306#[cfg(feature = "rmqr")]
307impl RmqrVersion {
308    pub(crate) const ALL: [Self; 32] = [
309        Self::R7x43,
310        Self::R7x59,
311        Self::R7x77,
312        Self::R7x99,
313        Self::R7x139,
314        Self::R9x43,
315        Self::R9x59,
316        Self::R9x77,
317        Self::R9x99,
318        Self::R9x139,
319        Self::R11x27,
320        Self::R11x43,
321        Self::R11x59,
322        Self::R11x77,
323        Self::R11x99,
324        Self::R11x139,
325        Self::R13x27,
326        Self::R13x43,
327        Self::R13x59,
328        Self::R13x77,
329        Self::R13x99,
330        Self::R13x139,
331        Self::R15x43,
332        Self::R15x59,
333        Self::R15x77,
334        Self::R15x99,
335        Self::R15x139,
336        Self::R17x43,
337        Self::R17x59,
338        Self::R17x77,
339        Self::R17x99,
340        Self::R17x139,
341    ];
342    // Versions ordered by area, then height, then width; sorted once at compile time for candidate search.
343    pub(crate) const ALL_BY_AREA: [Self; 32] = Self::sorted_by_area();
344    /// The last rMQR version in format indicator order.
345    pub const MAX: Self = Self::R17x139;
346    /// The first rMQR version in format indicator order.
347    pub const MIN: Self = Self::R7x43;
348
349    const fn sorted_by_area() -> [Self; 32] {
350        let mut versions = Self::ALL;
351        let length = versions.len();
352        let mut index = 0;
353
354        // A selection sort is enough for a const array of thirty-two versions.
355        while index < length {
356            let mut smallest = index;
357            let mut candidate = index + 1;
358
359            while candidate < length {
360                if versions[candidate].area_key() < versions[smallest].area_key() {
361                    smallest = candidate;
362                }
363
364                candidate += 1;
365            }
366
367            let swap = versions[index];
368            versions[index] = versions[smallest];
369            versions[smallest] = swap;
370            index += 1;
371        }
372
373        versions
374    }
375
376    // Packs area, height and width into one number that orders the same way as the (area, height, width) tuple.
377    const fn area_key(self) -> u64 {
378        let width = self.width() as u64;
379        let height = self.height() as u64;
380
381        (width * height) * 1_000_000 + height * 1_000 + width
382    }
383
384    /// Returns the five-bit version indicator value.
385    #[inline]
386    pub const fn value(self) -> u8 {
387        self as u8
388    }
389
390    /// Returns the symbol width in modules.
391    #[inline]
392    pub const fn width(self) -> usize {
393        match self {
394            Self::R11x27 | Self::R13x27 => 27,
395            Self::R7x43
396            | Self::R9x43
397            | Self::R11x43
398            | Self::R13x43
399            | Self::R15x43
400            | Self::R17x43 => 43,
401            Self::R7x59
402            | Self::R9x59
403            | Self::R11x59
404            | Self::R13x59
405            | Self::R15x59
406            | Self::R17x59 => 59,
407            Self::R7x77
408            | Self::R9x77
409            | Self::R11x77
410            | Self::R13x77
411            | Self::R15x77
412            | Self::R17x77 => 77,
413            Self::R7x99
414            | Self::R9x99
415            | Self::R11x99
416            | Self::R13x99
417            | Self::R15x99
418            | Self::R17x99 => 99,
419            Self::R7x139
420            | Self::R9x139
421            | Self::R11x139
422            | Self::R13x139
423            | Self::R15x139
424            | Self::R17x139 => 139,
425        }
426    }
427
428    /// Returns the symbol height in modules.
429    #[inline]
430    pub const fn height(self) -> usize {
431        match self {
432            Self::R7x43 | Self::R7x59 | Self::R7x77 | Self::R7x99 | Self::R7x139 => 7,
433            Self::R9x43 | Self::R9x59 | Self::R9x77 | Self::R9x99 | Self::R9x139 => 9,
434            Self::R11x27
435            | Self::R11x43
436            | Self::R11x59
437            | Self::R11x77
438            | Self::R11x99
439            | Self::R11x139 => 11,
440            Self::R13x27
441            | Self::R13x43
442            | Self::R13x59
443            | Self::R13x77
444            | Self::R13x99
445            | Self::R13x139 => 13,
446            Self::R15x43 | Self::R15x59 | Self::R15x77 | Self::R15x99 | Self::R15x139 => 15,
447            Self::R17x43 | Self::R17x59 | Self::R17x77 | Self::R17x99 | Self::R17x139 => 17,
448        }
449    }
450}
451
452/// The version of an encoded symbol.
453///
454/// The available variants depend on the enabled symbol family features.
455#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
456#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
457#[non_exhaustive]
458pub enum SymbolVersion {
459    /// A Model 2 QR Code version.
460    #[cfg(feature = "qr")]
461    Qr(QrVersion),
462    /// A Micro QR Code version.
463    #[cfg(feature = "micro-qr")]
464    Micro(MicroVersion),
465    /// A Rectangular Micro QR Code version.
466    #[cfg(feature = "rmqr")]
467    Rmqr(RmqrVersion),
468}
469
470/// A validated Model 2 mask number.
471#[cfg(feature = "qr")]
472#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
473pub struct QrMask(u8);
474
475#[cfg(feature = "qr")]
476impl QrMask {
477    /// Creates a Model 2 mask from a value in `0..=7`.
478    #[inline]
479    pub const fn new(value: u8) -> Result<Self, EncodeError> {
480        if value < 8 { Ok(Self(value)) } else { Err(EncodeError::InvalidMask) }
481    }
482
483    /// Returns the mask value.
484    #[inline]
485    pub const fn value(self) -> u8 {
486        self.0
487    }
488}
489
490/// A validated Micro QR Code mask number.
491#[cfg(feature = "micro-qr")]
492#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
493pub struct MicroMask(u8);
494
495#[cfg(feature = "micro-qr")]
496impl MicroMask {
497    /// Creates a Micro QR Code mask from a value in `0..=3`.
498    #[inline]
499    pub const fn new(value: u8) -> Result<Self, EncodeError> {
500        if value < 4 { Ok(Self(value)) } else { Err(EncodeError::InvalidMask) }
501    }
502
503    /// Returns the mask value.
504    #[inline]
505    pub const fn value(self) -> u8 {
506        self.0
507    }
508}
509
510/// An Extended Channel Interpretation assignment number.
511#[cfg(any(feature = "qr", feature = "rmqr"))]
512#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
513pub struct EciAssignment(u32);
514
515#[cfg(any(feature = "qr", feature = "rmqr"))]
516impl EciAssignment {
517    /// The default ISO-8859-1 character set assignment.
518    pub const ISO_8859_1: Self = Self(3);
519    /// The Shift JIS character set assignment.
520    pub const SHIFT_JIS: Self = Self(20);
521    /// The UTF-8 character set assignment.
522    pub const UTF_8: Self = Self(26);
523
524    /// Creates an ECI assignment from a value in `0..=999999`.
525    #[inline]
526    pub const fn new(value: u32) -> Result<Self, EncodeError> {
527        if value <= 999_999 {
528            Ok(Self(value))
529        } else {
530            Err(EncodeError::InvalidEciAssignment(value))
531        }
532    }
533
534    /// Returns the ECI assignment number.
535    #[inline]
536    pub const fn value(self) -> u32 {
537        self.0
538    }
539}
540
541/// A validated FNC1 second-position application indicator.
542#[cfg(any(feature = "qr", feature = "rmqr"))]
543#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
544pub struct ApplicationIndicator(u8);
545
546#[cfg(any(feature = "qr", feature = "rmqr"))]
547impl ApplicationIndicator {
548    /// Creates a numeric application indicator from a value in `0..=99`.
549    #[inline]
550    pub const fn numeric(value: u8) -> Result<Self, EncodeError> {
551        if value <= 99 { Ok(Self(value)) } else { Err(EncodeError::InvalidApplicationIndicator) }
552    }
553
554    /// Creates an application indicator from one ASCII letter.
555    #[inline]
556    pub const fn letter(value: char) -> Result<Self, EncodeError> {
557        if value.is_ascii_alphabetic() {
558            Ok(Self(value as u8 + 100))
559        } else {
560            Err(EncodeError::InvalidApplicationIndicator)
561        }
562    }
563
564    /// Returns the encoded application indicator byte.
565    #[inline]
566    pub const fn value(self) -> u8 {
567        self.0
568    }
569}
570
571/// The FNC1 header applied to a Model 2 or rMQR symbol.
572#[cfg(any(feature = "qr", feature = "rmqr"))]
573#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
574pub enum Fnc1 {
575    /// Marks the symbol as GS1 data using FNC1 in first position.
576    Gs1,
577    /// Marks the symbol as industry data using FNC1 in second position.
578    Industry(ApplicationIndicator),
579}
580
581/// Metadata carried by a Structured Append symbol.
582#[cfg(feature = "qr")]
583#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
584pub struct StructuredAppendInfo {
585    index:  u8,
586    total:  u8,
587    parity: u8,
588}
589
590#[cfg(feature = "qr")]
591impl StructuredAppendInfo {
592    /// Returns the zero-based symbol index.
593    #[inline]
594    pub const fn index(self) -> u8 {
595        self.index
596    }
597
598    /// Returns the total number of symbols.
599    #[inline]
600    pub const fn total(self) -> u8 {
601        self.total
602    }
603
604    /// Returns the parity byte shared by the symbol sequence.
605    #[inline]
606    pub const fn parity(self) -> u8 {
607        self.parity
608    }
609}
610
611#[derive(Clone, Copy, Debug, Eq, PartialEq)]
612pub(crate) enum Mode {
613    Numeric,
614    Alphanumeric,
615    Byte,
616    #[cfg_attr(not(feature = "kanji"), allow(dead_code))]
617    Kanji,
618    #[cfg_attr(not(any(feature = "qr", feature = "rmqr")), allow(dead_code))]
619    Eci,
620}
621
622impl Mode {
623    #[cfg(feature = "qr")]
624    #[inline]
625    const fn qr_bits(self) -> u8 {
626        match self {
627            Self::Numeric => 0b0001,
628            Self::Alphanumeric => 0b0010,
629            Self::Byte => 0b0100,
630            Self::Kanji => 0b1000,
631            Self::Eci => 0b0111,
632        }
633    }
634
635    #[cfg(feature = "qr")]
636    #[inline]
637    const fn cci_bits(self, version: QrVersion) -> u8 {
638        let group = version_group(version);
639
640        match self {
641            Self::Numeric => [10, 12, 14][group],
642            Self::Alphanumeric => [9, 11, 13][group],
643            Self::Byte => [8, 16, 16][group],
644            Self::Kanji => [8, 10, 12][group],
645            Self::Eci => 0,
646        }
647    }
648}
649
650/// An explicitly constructed QR Code data or control segment.
651#[derive(Clone, Debug, Eq, PartialEq)]
652pub struct Segment {
653    mode:            Mode,
654    character_count: usize,
655    bits:            BitBuffer,
656    source:          Vec<u8>,
657}
658
659impl Segment {
660    /// Creates a Numeric segment from ASCII digits.
661    pub fn numeric(data: impl AsRef<str>) -> Result<Self, EncodeError> {
662        let data = data.as_ref();
663        let mut bits = BitBuffer::with_capacity(data.len() * 10 / 3 + 4);
664        let bytes = data.as_bytes();
665
666        for (offset, byte) in bytes.iter().enumerate() {
667            if !byte.is_ascii_digit() {
668                return Err(EncodeError::InvalidData {
669                    mode:        "numeric",
670                    byte_offset: offset,
671                });
672            }
673        }
674
675        for chunk in bytes.chunks(3) {
676            let value = chunk.iter().fold(0, |value, byte| value * 10 + u32::from(byte - b'0'));
677            bits.append(value, [0, 4, 7, 10][chunk.len()]);
678        }
679
680        Ok(Self {
681            mode: Mode::Numeric,
682            character_count: bytes.len(),
683            bits,
684            source: bytes.to_vec(),
685        })
686    }
687
688    /// Creates an Alphanumeric segment from the standard 45-character set.
689    pub fn alphanumeric(data: impl AsRef<str>) -> Result<Self, EncodeError> {
690        let data = data.as_ref();
691        let mut values = Vec::with_capacity(data.len());
692
693        for (offset, byte) in data.bytes().enumerate() {
694            let Some(value) = alphanumeric_value(byte) else {
695                return Err(EncodeError::InvalidData {
696                    mode:        "alphanumeric",
697                    byte_offset: offset,
698                });
699            };
700            values.push(value);
701        }
702
703        let mut bits = BitBuffer::with_capacity(values.len() * 11 / 2 + 6);
704
705        for chunk in values.chunks(2) {
706            if chunk.len() == 2 {
707                bits.append(u32::from(chunk[0]) * 45 + u32::from(chunk[1]), 11);
708            } else {
709                bits.append(u32::from(chunk[0]), 6);
710            }
711        }
712
713        Ok(Self {
714            mode: Mode::Alphanumeric,
715            character_count: values.len(),
716            bits,
717            source: data.as_bytes().to_vec(),
718        })
719    }
720
721    #[cfg(any(feature = "qr", feature = "rmqr"))]
722    fn fnc1_alphanumeric(data: &[u8]) -> Result<Self, EncodeError> {
723        let mut values = Vec::with_capacity(data.len());
724
725        for (offset, &byte) in data.iter().enumerate() {
726            // A group separator becomes percent and a literal percent is escaped by doubling it.
727            if byte == 0x1D {
728                values.push(alphanumeric_value(b'%').expect("percent is alphanumeric"));
729            } else if byte == b'%' {
730                let value = alphanumeric_value(byte).expect("percent is alphanumeric");
731                values.extend_from_slice(&[value, value]);
732            } else if let Some(value) = alphanumeric_value(byte) {
733                values.push(value);
734            } else {
735                return Err(EncodeError::InvalidData {
736                    mode:        "FNC1 alphanumeric",
737                    byte_offset: offset,
738                });
739            }
740        }
741
742        let mut bits = BitBuffer::with_capacity(values.len() * 11 / 2 + 6);
743
744        for chunk in values.chunks(2) {
745            if chunk.len() == 2 {
746                bits.append(u32::from(chunk[0]) * 45 + u32::from(chunk[1]), 11);
747            } else {
748                bits.append(u32::from(chunk[0]), 6);
749            }
750        }
751
752        Ok(Self {
753            mode: Mode::Alphanumeric,
754            character_count: values.len(),
755            bits,
756            source: data.to_vec(),
757        })
758    }
759
760    /// Creates a Byte segment without adding an ECI header.
761    pub fn bytes(data: impl AsRef<[u8]>) -> Self {
762        let data = data.as_ref();
763
764        // A Byte segment stores its payload once in `bits`, so `source` stays empty and is read back from there.
765        Self {
766            mode:            Mode::Byte,
767            character_count: data.len(),
768            bits:            BitBuffer::from_bytes(data.to_vec()),
769            source:          Vec::new(),
770        }
771    }
772
773    // Returns the original bytes a segment was built from, reading a Byte segment back from its bit buffer.
774    #[cfg(any(feature = "qr", feature = "rmqr"))]
775    fn source_bytes(&self) -> &[u8] {
776        match self.mode {
777            Mode::Byte => self.bits.as_bytes(),
778            _ => &self.source,
779        }
780    }
781
782    /// Creates an ECI control segment.
783    #[cfg(any(feature = "qr", feature = "rmqr"))]
784    pub fn eci(assignment: EciAssignment) -> Self {
785        let mut bits = BitBuffer::with_capacity(24);
786        let value = assignment.0;
787
788        // Prefix bits select the shortest one, two or three-codeword ECI representation.
789        if value < 128 {
790            bits.append(value, 8);
791        } else if value < 16_384 {
792            bits.append(0b10, 2);
793            bits.append(value, 14);
794        } else {
795            bits.append(0b110, 3);
796            bits.append(value, 21);
797        }
798
799        Self {
800            mode: Mode::Eci,
801            character_count: 0,
802            bits,
803            source: Vec::new(),
804        }
805    }
806
807    #[cfg(feature = "kanji")]
808    /// Creates a Kanji segment from JIS X 0208 characters.
809    ///
810    /// The eight JIS X 0208 positions whose Unicode mapping is disputed are rejected, as are characters outside JIS X 0208.
811    pub fn kanji(data: impl AsRef<str>) -> Result<Self, EncodeError> {
812        let data = data.as_ref();
813        let mut bits = BitBuffer::with_capacity(data.chars().count() * 13);
814        let mut source = Vec::with_capacity(data.len() * 2);
815        let mut count = 0;
816
817        for (offset, character) in data.char_indices() {
818            let Some((value, encoded)) = kanji_encoding(character) else {
819                return Err(EncodeError::InvalidData {
820                    mode: "Kanji", byte_offset: offset
821                });
822            };
823
824            bits.append(u32::from(value), 13);
825            source.extend_from_slice(&encoded);
826            count += 1;
827        }
828        Ok(Self {
829            mode: Mode::Kanji,
830            character_count: count,
831            bits,
832            source,
833        })
834    }
835
836    /// Returns the character count stored in this segment.
837    #[inline]
838    pub const fn character_count(&self) -> usize {
839        self.character_count
840    }
841}
842
843// Reports whether a character is in the ISO/IEC 18004 Table 6 byte set, which leaves 80 to 9F undefined.
844#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
845#[inline]
846pub(crate) const fn is_latin1_character(character: char) -> bool {
847    matches!(character as u32, 0x00..=0x7F | 0xA0..=0xFF)
848}
849
850// Reports whether a character keeps its Table 6 byte in a symbol that uses Kanji mode without any ECI header.
851// ISO/IEC 18004 warns that readers cannot tell such bytes in E0 to EB from Shift JIS lead bytes, and 80 to 9F are already outside Table 6.
852#[cfg(any(feature = "micro-qr", all(feature = "kanji", any(feature = "qr", feature = "rmqr"))))]
853#[inline]
854pub(crate) const fn is_legacy_byte_character(character: char) -> bool {
855    is_latin1_character(character) && !matches!(character as u32, 0xE0..=0xEB)
856}
857
858// Rewrites explicit Alphanumeric segments for FNC1, where a literal percent is doubled, and borrows the segments otherwise.
859#[cfg(any(feature = "qr", feature = "rmqr"))]
860fn normalize_fnc1_segments(
861    segments: &[Segment],
862    fnc1: bool,
863) -> Result<Cow<'_, [Segment]>, EncodeError> {
864    if !fnc1 {
865        return Ok(Cow::Borrowed(segments));
866    }
867
868    segments
869        .iter()
870        .map(|segment| {
871            if segment.mode == Mode::Alphanumeric {
872                Segment::fnc1_alphanumeric(segment.source_bytes())
873            } else {
874                Ok(segment.clone())
875            }
876        })
877        .collect::<Result<Vec<_>, _>>()
878        .map(Cow::Owned)
879}
880
881// Encodes a character as the JIS8 byte or the JIS X 0208 byte pair that ISO/IEC 18004 defines for ECI 000020.
882#[cfg(feature = "kanji")]
883fn shift_jis_encoding(character: char) -> Option<([u8; 2], usize)> {
884    let mut utf8 = [0; 4];
885    let text = character.encode_utf8(&mut utf8);
886    let (encoded, _, had_errors) = encoding_rs::SHIFT_JIS.encode(text);
887
888    if had_errors {
889        return None;
890    }
891
892    match *encoded {
893        // JIS8 reads 5C and 7E as the yen sign and the overline, so ASCII backslash and tilde have no JIS8 byte.
894        [byte @ (0x5C | 0x7E)] => (!character.is_ascii()).then_some(([byte, 0], 1)),
895        // JIS8 reserves 80, which WHATWG uses for U+0080.
896        [byte @ (0x00..=0x7F | 0xA1..=0xDF)] => Some(([byte, 0], 1)),
897        [lead, trail] if is_unambiguous_jis_x_0208(u16::from_be_bytes([lead, trail])) => {
898            Some(([lead, trail], 2))
899        },
900        _ => None,
901    }
902}
903
904// Reports whether a Shift JIS byte pair is a JIS X 0208 character with one agreed Unicode mapping.
905// NEC row 13 and the IBM extensions are not JIS X 0208 characters, so ISO/IEC 18004 defines no character for them.
906// ISO/IEC 18004 gives no Unicode mapping, and the published JIS and Windows tables disagree on the eight listed pairs.
907#[cfg(feature = "kanji")]
908#[inline]
909const fn is_unambiguous_jis_x_0208(value: u16) -> bool {
910    matches!(value, 0x8140..=0x84BE | 0x889F..=0x9FFC | 0xE040..=0xEAA4)
911        && !matches!(value, 0x815C | 0x815F | 0x8160 | 0x8161 | 0x817C | 0x8191 | 0x8192 | 0x81CA)
912}
913
914#[cfg(feature = "kanji")]
915fn kanji_encoding(character: char) -> Option<(u16, [u8; 2])> {
916    let (bytes, length) = shift_jis_encoding(character)?;
917
918    if length != 2 {
919        return None;
920    }
921
922    let value = u16::from_be_bytes(bytes);
923
924    // The two valid Shift JIS ranges are shifted into one compact 13-bit index space.
925    let adjusted = match value {
926        0x8140..=0x9FFC => value - 0x8140,
927        0xE040..=0xEBBF => value - 0xC140,
928        _ => return None,
929    };
930
931    Some(((adjusted >> 8) * 0xC0 + (adjusted & 0xFF), bytes))
932}
933
934// Orders the modes used for deterministic tie-breaking in the segment optimizers.
935#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
936#[inline]
937const fn mode_rank(mode: Mode) -> u8 {
938    match mode {
939        Mode::Numeric => 0,
940        Mode::Alphanumeric => 1,
941        Mode::Kanji => 2,
942        Mode::Byte => 3,
943        Mode::Eci => 4,
944    }
945}
946
947#[inline]
948pub(crate) const fn alphanumeric_value(byte: u8) -> Option<u8> {
949    match byte {
950        b'0'..=b'9' => Some(byte - b'0'),
951        b'A'..=b'Z' => Some(byte - b'A' + 10),
952        b' ' => Some(36),
953        b'$' => Some(37),
954        b'%' => Some(38),
955        b'*' => Some(39),
956        b'+' => Some(40),
957        b'-' => Some(41),
958        b'.' => Some(42),
959        b'/' => Some(43),
960        b':' => Some(44),
961        _ => None,
962    }
963}
964
965// Computes the BCH remainder of the data polynomial times x^degree, reduced by the generator.
966// The generator is given without its leading term and degree is the number of parity bits.
967#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
968#[inline]
969pub(crate) const fn bch_remainder(data: u32, generator: u32, degree: u32) -> u32 {
970    let mut remainder = data;
971    let mut round = 0;
972
973    while round < degree {
974        remainder = (remainder << 1) ^ ((remainder >> (degree - 1)) * generator);
975        round += 1;
976    }
977
978    remainder
979}
980
981/// An immutable encoded QR Code, Micro QR Code or rMQR symbol.
982#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
983#[derive(Clone, Debug, Eq, PartialEq)]
984pub struct Symbol {
985    pub(crate) version:           SymbolVersion,
986    pub(crate) error_correction:  SymbolErrorCorrection,
987    pub(crate) mask:              u8,
988    pub(crate) modules:           Vec<bool>,
989    #[cfg(feature = "qr")]
990    pub(crate) structured_append: Option<StructuredAppendInfo>,
991}
992
993#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
994impl Symbol {
995    /// Returns the symbol width in modules.
996    ///
997    /// 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.
998    #[inline]
999    pub const fn size(&self) -> usize {
1000        self.width()
1001    }
1002
1003    /// Returns the symbol width in modules.
1004    #[inline]
1005    pub const fn width(&self) -> usize {
1006        match self.version {
1007            #[cfg(feature = "qr")]
1008            SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
1009            #[cfg(feature = "micro-qr")]
1010            SymbolVersion::Micro(version) => version.size(),
1011            #[cfg(feature = "rmqr")]
1012            SymbolVersion::Rmqr(version) => version.width(),
1013        }
1014    }
1015
1016    /// Returns the symbol height in modules.
1017    #[inline]
1018    pub const fn height(&self) -> usize {
1019        match self.version {
1020            #[cfg(feature = "qr")]
1021            SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
1022            #[cfg(feature = "micro-qr")]
1023            SymbolVersion::Micro(version) => version.size(),
1024            #[cfg(feature = "rmqr")]
1025            SymbolVersion::Rmqr(version) => version.height(),
1026        }
1027    }
1028
1029    /// Returns the encoded symbol version.
1030    #[inline]
1031    pub const fn version(&self) -> SymbolVersion {
1032        self.version
1033    }
1034
1035    /// Returns the error correction level written to the symbol.
1036    #[inline]
1037    pub const fn error_correction(&self) -> SymbolErrorCorrection {
1038        self.error_correction
1039    }
1040
1041    /// Returns the selected mask number.
1042    #[inline]
1043    pub const fn mask(&self) -> u8 {
1044        self.mask
1045    }
1046
1047    /// Returns Structured Append metadata when this symbol belongs to a sequence.
1048    #[cfg(feature = "qr")]
1049    #[inline]
1050    pub const fn structured_append(&self) -> Option<StructuredAppendInfo> {
1051        self.structured_append
1052    }
1053
1054    /// Returns a module or `None` when the coordinates are outside the symbol.
1055    #[inline]
1056    pub fn module(&self, x: usize, y: usize) -> Option<bool> {
1057        let width = self.width();
1058        (x < width && y < self.height()).then(|| self.modules[y * width + x])
1059    }
1060
1061    /// Returns every module as one row-major slice without copying.
1062    ///
1063    /// A dark module is `true`, and the module at `(x, y)` is at index `y * width + x`.
1064    #[inline]
1065    pub fn modules(&self) -> &[bool] {
1066        &self.modules
1067    }
1068
1069    /// Copies the symbol into a row-major Boolean matrix.
1070    pub fn to_matrix(&self) -> Vec<Vec<bool>> {
1071        self.modules.chunks(self.width()).map(<[bool]>::to_vec).collect()
1072    }
1073}
1074
1075/// Converts a value to text for QR Code encoding.
1076///
1077/// Implementations can use a shorter equivalent spelling when the value has more than one text representation.
1078/// The optional `url` feature implements this trait for `url::Url`.
1079pub trait ToQRText {
1080    /// Returns the text to encode.
1081    fn to_qr_text(&self) -> String;
1082}
1083
1084/// Configures and encodes Model 2 QR Code symbols.
1085#[cfg(feature = "qr")]
1086#[derive(Clone, Debug)]
1087pub struct QrEncoder {
1088    error_correction:       QrErrorCorrection,
1089    versions:               RangeInclusive<QrVersion>,
1090    mask:                   Option<QrMask>,
1091    boost_error_correction: bool,
1092    fnc1:                   Option<Fnc1>,
1093}
1094
1095#[cfg(feature = "qr")]
1096impl QrEncoder {
1097    /// Creates an encoder with automatic mask selection and Model 2 versions 1 through 40.
1098    #[inline]
1099    pub const fn new(error_correction: QrErrorCorrection) -> Self {
1100        Self {
1101            error_correction,
1102            versions: QrVersion::MIN..=QrVersion::MAX,
1103            mask: None,
1104            boost_error_correction: true,
1105            fnc1: None,
1106        }
1107    }
1108
1109    /// Selects one exact Model 2 QR Code version.
1110    #[must_use]
1111    #[inline]
1112    pub const fn version(mut self, version: QrVersion) -> Self {
1113        self.versions = version..=version;
1114        self
1115    }
1116
1117    /// Selects the inclusive Model 2 QR Code version range considered during encoding.
1118    #[must_use]
1119    #[inline]
1120    pub const fn version_range(mut self, versions: RangeInclusive<QrVersion>) -> Self {
1121        self.versions = versions;
1122        self
1123    }
1124
1125    /// Forces an exact mask instead of selecting one automatically.
1126    #[must_use]
1127    #[inline]
1128    pub const fn mask(mut self, mask: QrMask) -> Self {
1129        self.mask = Some(mask);
1130        self
1131    }
1132
1133    /// Enables or disables upgrading the error correction level when the selected version has room.
1134    #[must_use]
1135    #[inline]
1136    pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1137        self.boost_error_correction = boost;
1138        self
1139    }
1140
1141    /// Sets the FNC1 interpretation applied to the complete symbol.
1142    #[must_use]
1143    #[inline]
1144    pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1145        self.fnc1 = fnc1;
1146        self
1147    }
1148
1149    /// Encodes raw bytes with globally optimized Numeric, Alphanumeric and Byte segments.
1150    pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1151        let data = data.as_ref();
1152        let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1153
1154        ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1155
1156        self.encode_optimized_bytes(data, None)
1157    }
1158
1159    /// Encodes text with globally optimized character modes and ECI transitions.
1160    pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1161        let text = text.as_ref();
1162        let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1163
1164        ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1165
1166        self.encode_optimized_text(text, None, optimizer::Start::Free)
1167    }
1168
1169    /// Encodes a value after converting it to its QR Code text representation.
1170    #[inline]
1171    pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1172        &self,
1173        value: &T,
1174    ) -> Result<Symbol, EncodeError> {
1175        let text = value.to_qr_text();
1176        self.encode_text(&text)
1177    }
1178
1179    /// Encodes explicit segments without changing their boundaries.
1180    ///
1181    /// The segments are written as given, so keep a Kanji segment after an explicit Shift JIS ECI segment unless the symbol has no ECI segment at all.
1182    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1183        self.encode_segments_with_header(segments, None)
1184    }
1185
1186    /// Encodes caller-selected byte parts as one Structured Append sequence.
1187    pub fn encode_structured_append_bytes(
1188        &self,
1189        parts: &[&[u8]],
1190    ) -> Result<Vec<Symbol>, EncodeError> {
1191        validate_part_count(parts.len())?;
1192        let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1193
1194        for part in parts {
1195            ensure_input_length(part.len(), capacity, capacity_bits, 1)?;
1196        }
1197
1198        let parity = parts
1199            .iter()
1200            .flat_map(|part| part.iter().copied())
1201            .fold(0, |parity, byte| parity ^ byte);
1202
1203        parts
1204            .iter()
1205            .enumerate()
1206            .map(|(index, part)| {
1207                self.encode_optimized_bytes(
1208                    part,
1209                    Some(StructuredAppendInfo {
1210                        index: index as u8,
1211                        total: parts.len() as u8,
1212                        parity,
1213                    }),
1214                )
1215            })
1216            .collect()
1217    }
1218
1219    /// Encodes caller-selected text parts as one Structured Append sequence.
1220    pub fn encode_structured_append_text(
1221        &self,
1222        parts: &[&str],
1223    ) -> Result<Vec<Symbol>, EncodeError> {
1224        validate_part_count(parts.len())?;
1225        let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1226
1227        for part in parts {
1228            ensure_input_length(part.chars().count(), capacity, capacity_bits, 1)?;
1229        }
1230
1231        let total = parts.len() as u8;
1232        let mut selected: Option<SequencePlan> = None;
1233
1234        // Ties keep the layout without ECI headers, which is listed first.
1235        for regime in SequenceRegime::ALL {
1236            if let Some(candidate) = self.structured_text_plans(parts, regime)?
1237                && selected.as_ref().is_none_or(|selected| {
1238                    (candidate.version, candidate.area) < (selected.version, selected.area)
1239                })
1240            {
1241                selected = Some(candidate);
1242            }
1243        }
1244
1245        let Some(SequencePlan {
1246            parts: plans, ..
1247        }) = selected
1248        else {
1249            return Err(self.structured_text_error(parts));
1250        };
1251
1252        // Parity uses the byte representation selected by the optimizer, including Shift JIS or UTF-8 bytes.
1253        let parity = plans
1254            .iter()
1255            .flat_map(|(_, segments)| segments)
1256            .flat_map(|segment| segment.source_bytes().iter().copied())
1257            .fold(0, |parity, byte| parity ^ byte);
1258
1259        plans
1260            .into_iter()
1261            .enumerate()
1262            .map(|(index, (version, segments))| {
1263                model2::encode(
1264                    &segments,
1265                    version,
1266                    self.error_correction,
1267                    self.mask,
1268                    self.boost_error_correction,
1269                    self.fnc1,
1270                    Some(StructuredAppendInfo {
1271                        index: index as u8,
1272                        total,
1273                        parity,
1274                    }),
1275                )
1276            })
1277            .collect()
1278    }
1279
1280    // Picks the smallest version that fits one Structured Append part and returns its optimized segments, or `None` when no version fits.
1281    fn qr_structured_plan(
1282        &self,
1283        text: &str,
1284        start: optimizer::Start,
1285    ) -> Result<Option<(QrVersion, Vec<Segment>)>, EncodeError> {
1286        if self.versions.start() > self.versions.end() {
1287            return Err(EncodeError::InvalidVersionRange);
1288        }
1289
1290        // Only the presence of the header matters for the fit check, so the metadata values are placeholders.
1291        let header = StructuredAppendInfo {
1292            index: 0, total: 16, parity: 0
1293        };
1294        let range = self.versions.clone();
1295        let tables = optimizer::TextTables::new(text, self.fnc1.is_some());
1296        let mut cache = GroupCache::new(|version| {
1297            optimizer::text(&tables, optimizer::Profile::qr(version), start)
1298        });
1299
1300        for value in range.start().0..=range.end().0 {
1301            let version = QrVersion(value);
1302
1303            // A start without ECI headers has no plan for characters that need one.
1304            let Ok(segments) = cache.get(version) else {
1305                return Ok(None);
1306            };
1307
1308            if model2::fits(segments, version, self.error_correction, self.fnc1, Some(header)) {
1309                return Ok(Some((version, segments.to_vec())));
1310            }
1311        }
1312
1313        Ok(None)
1314    }
1315
1316    // Plans every caller-selected part in one ECI layout, minimizing the largest version and then the total area.
1317    // A part that leaves an ECI in force makes every later part declare one again.
1318    fn structured_text_plans(
1319        &self,
1320        parts: &[&str],
1321        regime: SequenceRegime,
1322    ) -> Result<Option<SequencePlan>, EncodeError> {
1323        if !regime.suits(&parts.concat()) {
1324            return Ok(None);
1325        }
1326
1327        // Candidate plans of each part, indexed by whether an ECI is in force before it.
1328        let mut options = Vec::with_capacity(parts.len());
1329
1330        for (index, part) in parts.iter().enumerate() {
1331            let mut by_state: [Vec<(QrVersion, Vec<Segment>, bool)>; 2] = [Vec::new(), Vec::new()];
1332
1333            for eci_in_force in [false, true] {
1334                // The first part never follows an ECI, and the layout without ECI headers never has one in force.
1335                if eci_in_force && (index == 0 || regime != SequenceRegime::Standard) {
1336                    continue;
1337                }
1338
1339                for &(start, _) in regime.starts(eci_in_force) {
1340                    if let Some((version, segments)) = self.qr_structured_plan(part, start)? {
1341                        let leaves_eci = eci_in_force
1342                            || segments.iter().any(|segment| segment.mode == Mode::Eci);
1343
1344                        by_state[usize::from(eci_in_force)].push((version, segments, leaves_eci));
1345                    }
1346                }
1347            }
1348
1349            options.push(by_state);
1350        }
1351
1352        let mut caps: Vec<QrVersion> = options
1353            .iter()
1354            .flat_map(|by_state| by_state.iter().flatten().map(|(version, ..)| *version))
1355            .collect();
1356
1357        caps.sort_unstable();
1358        caps.dedup();
1359
1360        for cap in caps {
1361            // Each entry holds the smallest area to reach the part boundary with or without an ECI in force, and its back pointer.
1362            let mut best: Vec<[Option<(usize, usize, usize)>; 2]> =
1363                vec![[None; 2]; parts.len() + 1];
1364
1365            best[0][0] = Some((0, 0, 0));
1366
1367            for (index, by_state) in options.iter().enumerate() {
1368                for eci_in_force in [false, true] {
1369                    let Some((area, ..)) = best[index][usize::from(eci_in_force)] else {
1370                        continue;
1371                    };
1372
1373                    for (choice, (version, _, leaves_eci)) in
1374                        by_state[usize::from(eci_in_force)].iter().enumerate()
1375                    {
1376                        if *version > cap {
1377                            continue;
1378                        }
1379
1380                        let size = usize::from(version.value()) * 4 + 17;
1381                        let candidate = (area + size * size, usize::from(eci_in_force), choice);
1382                        let slot = &mut best[index + 1][usize::from(*leaves_eci)];
1383
1384                        if slot.is_none_or(|current| candidate.0 < current.0) {
1385                            *slot = Some(candidate);
1386                        }
1387                    }
1388                }
1389            }
1390
1391            let Some((mut state, area)) = (0..2)
1392                .filter_map(|state| best[parts.len()][state].map(|(area, ..)| (state, area)))
1393                .min_by_key(|&(_, area)| area)
1394            else {
1395                continue;
1396            };
1397
1398            let mut plans = Vec::with_capacity(parts.len());
1399
1400            for index in (0..parts.len()).rev() {
1401                let (_, previous, choice) = best[index + 1][state].expect("the path is reachable");
1402                let (version, segments, _) = options[index][previous][choice].clone();
1403
1404                plans.push((version, segments));
1405                state = previous;
1406            }
1407
1408            plans.reverse();
1409
1410            return Ok(Some(SequencePlan {
1411                version: cap,
1412                area,
1413                parts: plans,
1414            }));
1415        }
1416
1417        Ok(None)
1418    }
1419
1420    // Reproduces the capacity error of the first part that no version can hold.
1421    fn structured_text_error(&self, parts: &[&str]) -> EncodeError {
1422        let version = *self.versions.end();
1423        let capacity_error = EncodeError::DataTooLong {
1424            required_bits: None,
1425            capacity_bits: model2::data_codewords(version, self.error_correction) * 8,
1426        };
1427
1428        // Later parts may have to restate an ECI, so they are measured with an explicit one.
1429        for (index, part) in parts.iter().enumerate() {
1430            let start =
1431                if index == 0 { optimizer::Start::Default } else { optimizer::Start::Explicit };
1432            let tables = optimizer::TextTables::new(part, self.fnc1.is_some());
1433            let Ok(segments) = optimizer::text(&tables, optimizer::Profile::qr(version), start)
1434            else {
1435                continue;
1436            };
1437
1438            if let Err(error) = model2::encode(
1439                &segments,
1440                version,
1441                self.error_correction,
1442                self.mask,
1443                false,
1444                self.fnc1,
1445                Some(StructuredAppendInfo {
1446                    index:  index as u8,
1447                    total:  parts.len() as u8,
1448                    parity: 0,
1449                }),
1450            ) {
1451                return error;
1452            }
1453        }
1454
1455        capacity_error
1456    }
1457
1458    /// Encodes caller-selected segment parts as one Structured Append sequence.
1459    ///
1460    /// Each part must include its own ECI headers before the data that needs them; ECI state is not copied from earlier parts.
1461    /// A Kanji segment without a Shift JIS ECI segment before it reads as Shift JIS only when no part of the sequence has an ECI segment.
1462    ///
1463    /// ```rust
1464    /// use qrcode_generator::{Segment, qr::{EciAssignment, Encoder, ErrorCorrection}};
1465    ///
1466    /// let first = [Segment::eci(EciAssignment::UTF_8), Segment::bytes("café".as_bytes())];
1467    /// let second = [Segment::eci(EciAssignment::UTF_8), Segment::bytes("世界".as_bytes())];
1468    /// let symbols = Encoder::new(ErrorCorrection::Medium)
1469    ///     .encode_structured_append_segments(&[&first, &second])
1470    ///     .unwrap();
1471    /// assert_eq!(2, symbols.len());
1472    /// ```
1473    pub fn encode_structured_append_segments(
1474        &self,
1475        parts: &[&[Segment]],
1476    ) -> Result<Vec<Symbol>, EncodeError> {
1477        validate_part_count(parts.len())?;
1478
1479        let parity = parts
1480            .iter()
1481            .flat_map(|part| part.iter())
1482            .flat_map(|segment| segment.source_bytes().iter().copied())
1483            .fold(0, |parity, byte| parity ^ byte);
1484
1485        parts
1486            .iter()
1487            .enumerate()
1488            .map(|(index, part)| {
1489                self.encode_segments_with_header(
1490                    part,
1491                    Some(StructuredAppendInfo {
1492                        index: index as u8,
1493                        total: parts.len() as u8,
1494                        parity,
1495                    }),
1496                )
1497            })
1498            .collect()
1499    }
1500
1501    /// Encodes bytes as one symbol or automatically splits them into at most 16 Structured Append symbols.
1502    ///
1503    /// When the data fits one symbol, that lone symbol is returned without Structured Append metadata, because a single symbol needs no sequence header.
1504    pub fn encode_bytes_with_structured_append(
1505        &self,
1506        data: impl AsRef<[u8]>,
1507    ) -> Result<Vec<Symbol>, EncodeError> {
1508        let data = data.as_ref();
1509
1510        match self.encode_bytes(data) {
1511            Ok(symbol) => return Ok(vec![symbol]),
1512            Err(EncodeError::DataTooLong {
1513                ..
1514            }) => {},
1515            Err(error) => return Err(error),
1516        }
1517
1518        let range = self.structured_append_range()?;
1519        let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1520
1521        ensure_input_length(data.len(), capacity, capacity_bits, 16)?;
1522
1523        // Partitioning failures replace internal placeholder errors with the full sequence capacity.
1524        let sequence_capacity_error = || EncodeError::DataTooLong {
1525            required_bits: None,
1526            capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1527        };
1528
1529        let reach = self.qr_character_upper_bound(*range.end());
1530        let mut costs = PrefixCosts::new(|start: usize, version, ()| {
1531            let end = data.len().min(start.saturating_add(reach));
1532
1533            optimizer::byte_costs(
1534                &data[start..end],
1535                optimizer::Profile::qr(version),
1536                self.fnc1.is_some(),
1537            )
1538        });
1539
1540        // Greedy maximum-size parts determine the minimum possible symbol count.
1541        let minimum_parts = self
1542            .minimum_byte_parts(&mut costs, data.len(), range.clone())
1543            .ok_or_else(sequence_capacity_error)?;
1544
1545        let candidate = smallest_version_cap(&range, |candidate| {
1546            self.minimum_byte_parts(&mut costs, data.len(), *range.start()..=candidate)
1547                == Some(minimum_parts)
1548        })
1549        .ok_or_else(sequence_capacity_error)?;
1550        let parts = self
1551            .minimum_area_byte_partition(
1552                &mut costs,
1553                data.len(),
1554                minimum_parts,
1555                *range.start()..=candidate,
1556            )
1557            .map_err(|_| sequence_capacity_error())?;
1558
1559        let slices: Vec<&[u8]> = parts.into_iter().map(|(start, end)| &data[start..end]).collect();
1560
1561        self.encode_structured_append_bytes(&slices)
1562    }
1563
1564    /// Encodes text as one symbol or automatically splits it into at most 16 Structured Append symbols.
1565    ///
1566    /// When the text fits one symbol, that lone symbol is returned without Structured Append metadata, because a single symbol needs no sequence header.
1567    pub fn encode_text_with_structured_append(
1568        &self,
1569        text: impl AsRef<str>,
1570    ) -> Result<Vec<Symbol>, EncodeError> {
1571        let text = text.as_ref();
1572
1573        match self.encode_text(text) {
1574            Ok(symbol) => return Ok(vec![symbol]),
1575            Err(EncodeError::DataTooLong {
1576                ..
1577            }) => {},
1578            Err(error) => return Err(error),
1579        }
1580
1581        let range = self.structured_append_range()?;
1582        let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1583
1584        ensure_input_length(text.chars().count(), capacity, capacity_bits, 16)?;
1585
1586        let input = SplitText::new(text);
1587        let reach = self.qr_character_upper_bound(*range.end());
1588        let mut costs = PrefixCosts::new(|start: usize, version, mode| {
1589            let end = input.len().min(start.saturating_add(reach));
1590
1591            optimizer::text_costs(
1592                input.slice(start, end),
1593                optimizer::Profile::qr(version),
1594                self.fnc1.is_some(),
1595                mode,
1596            )
1597        });
1598
1599        // Partitioning failures replace internal placeholder errors with the full sequence capacity.
1600        let sequence_capacity_error = || EncodeError::DataTooLong {
1601            required_bits: None,
1602            capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1603        };
1604
1605        // Each ECI layout is searched on its own, because one ECI anywhere changes how Kanji mode reads in every part.
1606        // The sequence then minimizes the symbol count, the largest version and the total area, and ties keep the layout without ECI headers.
1607        let mut selected: Option<SequencePartition> = None;
1608
1609        for regime in SequenceRegime::ALL {
1610            if !regime.suits(text) {
1611                continue;
1612            }
1613
1614            // Text partitions use scalar boundaries so no UTF-8 character is split between symbols.
1615            let Some(minimum_parts) =
1616                self.minimum_text_parts(&input, &mut costs, range.clone(), regime)
1617            else {
1618                continue;
1619            };
1620
1621            if selected.as_ref().is_some_and(|selected| minimum_parts > selected.count) {
1622                continue;
1623            }
1624
1625            let Some(candidate) = smallest_version_cap(&range, |candidate| {
1626                self.minimum_text_parts(&input, &mut costs, *range.start()..=candidate, regime)
1627                    == Some(minimum_parts)
1628            }) else {
1629                continue;
1630            };
1631
1632            let (area, parts) = self
1633                .minimum_area_text_partition(
1634                    &input,
1635                    &mut costs,
1636                    minimum_parts,
1637                    *range.start()..=candidate,
1638                    regime,
1639                )
1640                .map_err(|_| sequence_capacity_error())?;
1641
1642            if selected.as_ref().is_none_or(|selected| {
1643                (minimum_parts, candidate, area) < (selected.count, selected.version, selected.area)
1644            }) {
1645                selected = Some(SequencePartition {
1646                    count: minimum_parts,
1647                    version: candidate,
1648                    area,
1649                    parts,
1650                });
1651            }
1652        }
1653
1654        let parts = selected.ok_or_else(sequence_capacity_error)?.parts;
1655
1656        let slices: Vec<&str> =
1657            parts.into_iter().map(|(start, end)| input.slice(start, end)).collect();
1658        self.encode_structured_append_text(&slices)
1659    }
1660
1661    fn structured_append_range(&self) -> Result<RangeInclusive<QrVersion>, EncodeError> {
1662        if self.versions.start() > self.versions.end() {
1663            Err(EncodeError::InvalidVersionRange)
1664        } else {
1665            Ok(self.versions.clone())
1666        }
1667    }
1668
1669    fn input_capacity_upper_bound(
1670        &self,
1671        structured_append: bool,
1672    ) -> Result<(usize, usize), EncodeError> {
1673        if self.versions.start() > self.versions.end() {
1674            return Err(EncodeError::InvalidVersionRange);
1675        }
1676
1677        let version = *self.versions.end();
1678        let capacity_bits = model2::data_codewords(version, self.error_correction) * 8;
1679        let overhead_bits = 4
1680            + usize::from(Mode::Numeric.cci_bits(version))
1681            + usize::from(structured_append) * 20
1682            + match self.fnc1 {
1683                Some(Fnc1::Gs1) => 4,
1684                Some(Fnc1::Industry(_)) => 12,
1685                None => 0,
1686            };
1687
1688        Ok((numeric_character_capacity(capacity_bits, overhead_bits), capacity_bits))
1689    }
1690
1691    // Counts the fewest byte parts by taking the longest part that fits each time.
1692    fn minimum_byte_parts<F>(
1693        &self,
1694        costs: &mut PrefixCosts<(), F>,
1695        length: usize,
1696        range: RangeInclusive<QrVersion>,
1697    ) -> Option<usize>
1698    where
1699        F: FnMut(usize, QrVersion, ()) -> Vec<Option<usize>>, {
1700        let mut start = 0;
1701        let mut count = 0;
1702
1703        while start < length {
1704            if count == 16 {
1705                return None;
1706            }
1707
1708            start = self.farthest_part_end(costs, start, (), range.clone())?;
1709            count += 1;
1710        }
1711
1712        Some(count)
1713    }
1714
1715    // Finds the farthest end of a Structured Append part from `start` that fits some version in the range.
1716    fn farthest_part_end<K, F>(
1717        &self,
1718        costs: &mut PrefixCosts<K, F>,
1719        start: usize,
1720        key: K,
1721        range: RangeInclusive<QrVersion>,
1722    ) -> Option<usize>
1723    where
1724        K: Copy + Ord,
1725        F: FnMut(usize, QrVersion, K) -> Vec<Option<usize>>, {
1726        let header_bits = model2::header_bits(self.fnc1, true);
1727        let mut result = None;
1728
1729        for value in range.start().value()..=range.end().value() {
1730            let version = QrVersion(value);
1731            let capacity_bits = model2::data_codewords(version, self.error_correction) * 8;
1732
1733            // Prefix costs never decrease, so the prefixes that fit all come before the ones that do not.
1734            let count = costs.get(start, version, key)[1..].partition_point(|cost| {
1735                cost.is_some_and(|bits| header_bits + bits <= capacity_bits)
1736            });
1737
1738            if count > 0 {
1739                result = result.max(Some(start + count));
1740            }
1741        }
1742
1743        result
1744    }
1745
1746    // Counts the fewest parts of one ECI layout, keeping per layer the farthest end with and without an ECI in force.
1747    fn minimum_text_parts<F>(
1748        &self,
1749        input: &SplitText<'_>,
1750        costs: &mut PrefixCosts<optimizer::Start, F>,
1751        range: RangeInclusive<QrVersion>,
1752        regime: SequenceRegime,
1753    ) -> Option<usize>
1754    where
1755        F: FnMut(usize, QrVersion, optimizer::Start) -> Vec<Option<usize>>, {
1756        let length = input.len();
1757        let mut layer = vec![(0, false)];
1758
1759        for count in 1..=16 {
1760            let mut farthest = [None; 2];
1761
1762            for &(start, eci_in_force) in &layer {
1763                for (end, leaves_eci) in self
1764                    .text_part_ends(input, costs, start, eci_in_force, regime, range.clone())
1765                    .into_iter()
1766                    .flatten()
1767                {
1768                    if end == length {
1769                        return Some(count);
1770                    }
1771
1772                    let slot = &mut farthest[usize::from(leaves_eci)];
1773
1774                    if slot.is_none_or(|current| end > current) {
1775                        *slot = Some(end);
1776                    }
1777                }
1778            }
1779
1780            // A start without an ECI in force dominates one with it at the same or a nearer end.
1781            layer.clear();
1782            layer.extend(farthest[0].map(|end| (end, false)));
1783
1784            if let Some(end) = farthest[1]
1785                && farthest[0].is_none_or(|free| end > free)
1786            {
1787                layer.push((end, true));
1788            }
1789
1790            if layer.is_empty() {
1791                return None;
1792            }
1793        }
1794
1795        None
1796    }
1797
1798    // Finds the farthest ends one text part can reach from a start, each with whether it may leave an ECI in force.
1799    // A start that may leave an ECI in force is only listed when it reaches farther than the starts that cannot.
1800    fn text_part_ends<F>(
1801        &self,
1802        input: &SplitText<'_>,
1803        costs: &mut PrefixCosts<optimizer::Start, F>,
1804        start: usize,
1805        eci_in_force: bool,
1806        regime: SequenceRegime,
1807        range: RangeInclusive<QrVersion>,
1808    ) -> [Option<(usize, bool)>; 2]
1809    where
1810        F: FnMut(usize, QrVersion, optimizer::Start) -> Vec<Option<usize>>, {
1811        // Numeric mode gives the largest possible scalar capacity for any text input.
1812        let high =
1813            input.len().min(start.saturating_add(self.qr_character_upper_bound(*range.end())));
1814        let mut result = [None; 2];
1815        let mut farthest = None;
1816
1817        for (index, &(mode, leaves_eci)) in regime.starts(eci_in_force).iter().enumerate() {
1818            // Without a character in reach that an ECI could help, a plan allowed to declare one reaches no farther.
1819            if leaves_eci && !eci_in_force && input.eci_useful_from[start] >= high {
1820                continue;
1821            }
1822
1823            let end = self.farthest_part_end(costs, start, mode, range.clone());
1824
1825            if let Some(end) = end
1826                && farthest.is_none_or(|farthest| end > farthest)
1827            {
1828                result[index] = Some((end, leaves_eci));
1829                farthest = Some(end);
1830            }
1831        }
1832
1833        result
1834    }
1835
1836    fn minimum_area_byte_partition<F>(
1837        &self,
1838        costs: &mut PrefixCosts<(), F>,
1839        length: usize,
1840        part_count: usize,
1841        range: RangeInclusive<QrVersion>,
1842    ) -> Result<Vec<(usize, usize)>, EncodeError>
1843    where
1844        F: FnMut(usize, QrVersion, ()) -> Vec<Option<usize>>, {
1845        // Byte data never declares an ECI, so every part ends without one in force.
1846        let (_, parts) = minimum_area_partition(length, part_count, range, |start, _, version| {
1847            let end = self.farthest_part_end(costs, start, (), version..=version);
1848
1849            [end.map(|end| (end, false)), None]
1850        })?;
1851
1852        Ok(parts)
1853    }
1854
1855    fn minimum_area_text_partition<F>(
1856        &self,
1857        input: &SplitText<'_>,
1858        costs: &mut PrefixCosts<optimizer::Start, F>,
1859        part_count: usize,
1860        range: RangeInclusive<QrVersion>,
1861        regime: SequenceRegime,
1862    ) -> Result<(usize, Vec<(usize, usize)>), EncodeError>
1863    where
1864        F: FnMut(usize, QrVersion, optimizer::Start) -> Vec<Option<usize>>, {
1865        minimum_area_partition(input.len(), part_count, range, |start, eci_in_force, version| {
1866            self.text_part_ends(input, costs, start, eci_in_force, regime, version..=version)
1867        })
1868    }
1869
1870    #[inline]
1871    const fn qr_character_upper_bound(&self, version: QrVersion) -> usize {
1872        model2::data_codewords(version, self.error_correction) * 8 * 3 / 10 + 2
1873    }
1874
1875    fn encode_optimized_bytes(
1876        &self,
1877        data: &[u8],
1878        structured_append: Option<StructuredAppendInfo>,
1879    ) -> Result<Symbol, EncodeError> {
1880        self.encode_qr_range(
1881            self.versions.clone(),
1882            |version| optimizer::bytes(data, optimizer::Profile::qr(version), self.fnc1.is_some()),
1883            structured_append,
1884        )
1885    }
1886
1887    fn encode_optimized_text(
1888        &self,
1889        text: &str,
1890        structured_append: Option<StructuredAppendInfo>,
1891        start: optimizer::Start,
1892    ) -> Result<Symbol, EncodeError> {
1893        let tables = optimizer::TextTables::new(text, self.fnc1.is_some());
1894
1895        self.encode_qr_range(
1896            self.versions.clone(),
1897            |version| optimizer::text(&tables, optimizer::Profile::qr(version), start),
1898            structured_append,
1899        )
1900    }
1901
1902    fn encode_qr_range<F>(
1903        &self,
1904        range: RangeInclusive<QrVersion>,
1905        segments: F,
1906        structured_append: Option<StructuredAppendInfo>,
1907    ) -> Result<Symbol, EncodeError>
1908    where
1909        F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>, {
1910        if range.start() > range.end() {
1911            return Err(EncodeError::InvalidVersionRange);
1912        }
1913
1914        let mut cache = GroupCache::new(segments);
1915        let mut last_error = None;
1916
1917        for value in range.start().0..=range.end().0 {
1918            let version = QrVersion(value);
1919            let candidate = cache.get(version)?;
1920
1921            match model2::encode(
1922                candidate,
1923                version,
1924                self.error_correction,
1925                self.mask,
1926                self.boost_error_correction,
1927                self.fnc1,
1928                structured_append,
1929            ) {
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        // The version range is verified to be non-empty, so at least one capacity error was stored.
1941        Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
1942    }
1943
1944    fn encode_segments_with_header(
1945        &self,
1946        segments: &[Segment],
1947        structured_append: Option<StructuredAppendInfo>,
1948    ) -> Result<Symbol, EncodeError> {
1949        let segments = normalize_fnc1_segments(segments, self.fnc1.is_some())?;
1950
1951        self.encode_qr_range(self.versions.clone(), |_| Ok(segments.to_vec()), structured_append)
1952    }
1953}
1954
1955/// Configures and encodes Rectangular Micro QR Code symbols.
1956#[cfg(feature = "rmqr")]
1957#[derive(Clone, Debug)]
1958pub struct RmqrEncoder {
1959    error_correction:       RmqrErrorCorrection,
1960    versions:               RangeInclusive<RmqrVersion>,
1961    boost_error_correction: bool,
1962    fnc1:                   Option<Fnc1>,
1963}
1964
1965#[cfg(feature = "rmqr")]
1966impl RmqrEncoder {
1967    /// Creates an encoder that considers every rMQR version.
1968    #[inline]
1969    pub const fn new(error_correction: RmqrErrorCorrection) -> Self {
1970        Self {
1971            error_correction,
1972            versions: RmqrVersion::MIN..=RmqrVersion::MAX,
1973            boost_error_correction: true,
1974            fnc1: None,
1975        }
1976    }
1977
1978    /// Selects one exact rMQR version.
1979    #[must_use]
1980    #[inline]
1981    pub const fn version(mut self, version: RmqrVersion) -> Self {
1982        self.versions = version..=version;
1983        self
1984    }
1985
1986    /// Selects an inclusive range in rMQR format indicator order.
1987    #[must_use]
1988    #[inline]
1989    pub const fn version_range(mut self, versions: RangeInclusive<RmqrVersion>) -> Self {
1990        self.versions = versions;
1991        self
1992    }
1993
1994    /// Enables or disables upgrading Medium error correction to High when the selected version has room.
1995    #[must_use]
1996    #[inline]
1997    pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1998        self.boost_error_correction = boost;
1999        self
2000    }
2001
2002    /// Sets the FNC1 interpretation applied to the complete symbol.
2003    #[must_use]
2004    #[inline]
2005    pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
2006        self.fnc1 = fnc1;
2007        self
2008    }
2009
2010    /// Encodes raw bytes with globally optimized Numeric, Alphanumeric and Byte segments.
2011    pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2012        let data = data.as_ref();
2013        let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2014
2015        ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
2016        self.encode_optimized(|version| {
2017            optimizer::bytes(
2018                data,
2019                optimizer::Profile::rmqr(rmqr::cci(version)),
2020                self.fnc1.is_some(),
2021            )
2022        })
2023    }
2024
2025    /// Encodes text with globally optimized character modes and ECI transitions.
2026    pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2027        let text = text.as_ref();
2028        let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2029
2030        ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
2031
2032        let tables = optimizer::TextTables::new(text, self.fnc1.is_some());
2033
2034        self.encode_optimized(|version| {
2035            optimizer::text(
2036                &tables,
2037                optimizer::Profile::rmqr(rmqr::cci(version)),
2038                optimizer::Start::Free,
2039            )
2040        })
2041    }
2042
2043    /// Encodes a value after converting it to its QR Code text representation.
2044    #[inline]
2045    pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2046        &self,
2047        value: &T,
2048    ) -> Result<Symbol, EncodeError> {
2049        let text = value.to_qr_text();
2050        self.encode_text(&text)
2051    }
2052
2053    /// Encodes explicit segments without changing their boundaries.
2054    ///
2055    /// The segments are written as given, so keep a Kanji segment after an explicit Shift JIS ECI segment unless the symbol has no ECI segment at all.
2056    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2057        let segments = normalize_fnc1_segments(segments, self.fnc1.is_some())?;
2058
2059        self.encode_candidates(|version| {
2060            rmqr::encode(
2061                &segments,
2062                version,
2063                self.error_correction,
2064                self.boost_error_correction,
2065                self.fnc1,
2066            )
2067        })
2068    }
2069
2070    fn input_capacity_upper_bound(&self) -> Result<(usize, usize), EncodeError> {
2071        let versions = self.candidates()?;
2072        let mut result = None;
2073
2074        for version in versions {
2075            let capacity_bits = rmqr::data_codewords(version, self.error_correction) * 8;
2076            let overhead_bits = 3
2077                + usize::from(rmqr::cci(version)[0])
2078                + match self.fnc1 {
2079                    Some(Fnc1::Gs1) => 3,
2080                    Some(Fnc1::Industry(_)) => 11,
2081                    None => 0,
2082                };
2083            let capacity = numeric_character_capacity(capacity_bits, overhead_bits);
2084
2085            if result.is_none_or(|(best, _)| capacity > best) {
2086                result = Some((capacity, capacity_bits));
2087            }
2088        }
2089
2090        result.ok_or(EncodeError::InvalidVersionRange)
2091    }
2092
2093    // Segments are planned once per character count indicator profile, because versions sharing one profile share their plan.
2094    fn encode_optimized<F>(&self, mut optimize: F) -> Result<Symbol, EncodeError>
2095    where
2096        F: FnMut(RmqrVersion) -> Result<Vec<Segment>, EncodeError>, {
2097        let mut cache: Vec<([u8; 4], Vec<Segment>)> = Vec::with_capacity(15);
2098
2099        self.encode_candidates(|version| {
2100            let profile = rmqr::cci(version);
2101            let index = match cache.iter().position(|(cci, _)| *cci == profile) {
2102                Some(index) => index,
2103                None => {
2104                    cache.push((profile, optimize(version)?));
2105                    cache.len() - 1
2106                },
2107            };
2108
2109            rmqr::encode(
2110                &cache[index].1,
2111                version,
2112                self.error_correction,
2113                self.boost_error_correction,
2114                self.fnc1,
2115            )
2116        })
2117    }
2118
2119    // Tries every candidate version in area order and keeps the last capacity error for the caller.
2120    fn encode_candidates<F>(&self, mut encode: F) -> Result<Symbol, EncodeError>
2121    where
2122        F: FnMut(RmqrVersion) -> Result<Symbol, EncodeError>, {
2123        let versions = self.candidates()?;
2124        let mut last_error = None;
2125
2126        for version in versions {
2127            match encode(version) {
2128                Ok(symbol) => return Ok(symbol),
2129                Err(
2130                    error @ EncodeError::DataTooLong {
2131                        ..
2132                    },
2133                ) => last_error = Some(error),
2134                Err(error) => return Err(error),
2135            }
2136        }
2137
2138        Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
2139    }
2140
2141    fn candidates(&self) -> Result<Vec<RmqrVersion>, EncodeError> {
2142        if self.versions.start() > self.versions.end() {
2143            return Err(EncodeError::InvalidVersionRange);
2144        }
2145
2146        Ok(RmqrVersion::ALL_BY_AREA
2147            .into_iter()
2148            .filter(|version| self.versions.contains(version))
2149            .collect())
2150    }
2151}
2152
2153/// Configures and encodes Micro QR Code symbols.
2154#[cfg(feature = "micro-qr")]
2155#[derive(Clone, Debug)]
2156pub struct MicroEncoder {
2157    error_correction:       MicroErrorCorrection,
2158    versions:               RangeInclusive<MicroVersion>,
2159    mask:                   Option<MicroMask>,
2160    boost_error_correction: bool,
2161}
2162
2163#[cfg(feature = "micro-qr")]
2164impl MicroEncoder {
2165    /// Creates an encoder with automatic mask selection and all Micro QR Code versions.
2166    #[inline]
2167    pub const fn new(error_correction: MicroErrorCorrection) -> Self {
2168        Self {
2169            error_correction,
2170            versions: MicroVersion::M1..=MicroVersion::M4,
2171            mask: None,
2172            boost_error_correction: true,
2173        }
2174    }
2175
2176    /// Selects one exact Micro QR Code version.
2177    #[must_use]
2178    #[inline]
2179    pub const fn version(mut self, version: MicroVersion) -> Self {
2180        self.versions = version..=version;
2181        self
2182    }
2183
2184    /// Selects the inclusive Micro QR Code version range considered during encoding.
2185    #[must_use]
2186    #[inline]
2187    pub const fn version_range(mut self, versions: RangeInclusive<MicroVersion>) -> Self {
2188        self.versions = versions;
2189        self
2190    }
2191
2192    /// Forces an exact mask instead of selecting one automatically.
2193    #[must_use]
2194    #[inline]
2195    pub const fn mask(mut self, mask: MicroMask) -> Self {
2196        self.mask = Some(mask);
2197        self
2198    }
2199
2200    /// Enables or disables upgrading the error correction level when the selected version has room.
2201    #[must_use]
2202    #[inline]
2203    pub const fn boost_error_correction(mut self, boost: bool) -> Self {
2204        self.boost_error_correction = boost;
2205        self
2206    }
2207
2208    /// Encodes raw bytes with globally optimized modes supported by each candidate version.
2209    pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2210        let data = data.as_ref();
2211        let (version, capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2212
2213        micro::check_bytes(data, version, capacity, capacity_bits)?;
2214
2215        self.encode_range(|version| micro::optimize(data, version))
2216    }
2217
2218    /// Encodes text with globally optimized modes supported by each candidate version.
2219    pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2220        let text = text.as_ref();
2221        let (version, capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2222        let input = micro::Text::new(text, version, capacity, capacity_bits)?;
2223
2224        self.encode_range(|version| micro::optimize_text(&input, version))
2225    }
2226
2227    /// Encodes a value after converting it to its QR Code text representation.
2228    #[inline]
2229    pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2230        &self,
2231        value: &T,
2232    ) -> Result<Symbol, EncodeError> {
2233        let text = value.to_qr_text();
2234        self.encode_text(&text)
2235    }
2236
2237    /// Encodes explicit segments without changing their boundaries.
2238    ///
2239    /// The segments are written as given, so avoid Byte segments holding E0 to EB in a symbol with Kanji segments, because readers could take them for Shift JIS lead bytes.
2240    /// An empty Numeric segment is left out, because Micro QR Code writes it with the same bits as the terminator.
2241    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2242        self.encode_range(|_| {
2243            Ok(segments
2244                .iter()
2245                .filter(|segment| segment.mode != Mode::Numeric || segment.character_count > 0)
2246                .cloned()
2247                .collect())
2248        })
2249    }
2250
2251    // Returns the candidate version holding the most characters, which is also the most permissive one.
2252    fn input_capacity_upper_bound(&self) -> Result<(MicroVersion, usize, usize), EncodeError> {
2253        if self.versions.start() > self.versions.end() {
2254            return Err(EncodeError::InvalidVersionRange);
2255        }
2256
2257        micro_versions(self.versions.clone())
2258            .filter_map(|version| {
2259                micro::input_capacity_upper_bound(version, self.error_correction)
2260                    .map(|(capacity, capacity_bits)| (version, capacity, capacity_bits))
2261            })
2262            .max_by_key(|&(_, capacity, _)| capacity)
2263            .ok_or(EncodeError::UnsupportedErrorCorrection {
2264                version:          SymbolVersion::Micro(*self.versions.end()),
2265                error_correction: self.error_correction.into(),
2266            })
2267    }
2268
2269    fn encode_range<F>(&self, mut segments: F) -> Result<Symbol, EncodeError>
2270    where
2271        F: FnMut(MicroVersion) -> Result<Vec<Segment>, EncodeError>, {
2272        if self.versions.start() > self.versions.end() {
2273            return Err(EncodeError::InvalidVersionRange);
2274        }
2275
2276        let mut last_error = None;
2277
2278        // Unsupported modes and error correction levels eliminate only the current candidate version.
2279        for version in micro_versions(self.versions.clone()) {
2280            if micro::input_capacity_upper_bound(version, self.error_correction).is_none() {
2281                last_error = Some(EncodeError::UnsupportedErrorCorrection {
2282                    version:          SymbolVersion::Micro(version),
2283                    error_correction: self.error_correction.into(),
2284                });
2285                continue;
2286            }
2287
2288            let result = segments(version).and_then(|segments| {
2289                micro::encode(
2290                    &segments,
2291                    version,
2292                    self.error_correction,
2293                    self.mask,
2294                    self.boost_error_correction,
2295                )
2296            });
2297
2298            match result {
2299                Ok(symbol) => return Ok(symbol),
2300                Err(error) if candidate_rejection(&error) => last_error = Some(error),
2301                Err(error) => return Err(error),
2302            }
2303        }
2304
2305        Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
2306    }
2307}
2308
2309/// Tries a configured Micro QR Code encoder before a configured Model 2 QR Code encoder, unless the Model 2 encoder requires FNC1.
2310#[cfg(all(feature = "qr", feature = "micro-qr"))]
2311#[derive(Clone, Debug)]
2312pub struct AutoEncoder {
2313    qr:    QrEncoder,
2314    micro: MicroEncoder,
2315}
2316
2317#[cfg(all(feature = "qr", feature = "micro-qr"))]
2318impl AutoEncoder {
2319    /// Creates an encoder from independently configured Model 2 and Micro QR Code encoders.
2320    #[inline]
2321    pub const fn new(qr: QrEncoder, micro: MicroEncoder) -> Self {
2322        Self {
2323            qr,
2324            micro,
2325        }
2326    }
2327
2328    /// Encodes raw bytes using the smallest eligible symbol family.
2329    pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2330        let data = data.as_ref();
2331        if self.qr.fnc1.is_some() {
2332            return self.qr.encode_bytes(data);
2333        }
2334        match self.micro.encode_bytes(data) {
2335            Ok(symbol) => Ok(symbol),
2336            Err(error) if candidate_rejection(&error) => self.qr.encode_bytes(data),
2337            Err(error) => Err(error),
2338        }
2339    }
2340
2341    /// Encodes text using the smallest eligible symbol family.
2342    pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2343        let text = text.as_ref();
2344        if self.qr.fnc1.is_some() {
2345            return self.qr.encode_text(text);
2346        }
2347        match self.micro.encode_text(text) {
2348            Ok(symbol) => Ok(symbol),
2349            Err(error) if candidate_rejection(&error) => self.qr.encode_text(text),
2350            Err(error) => Err(error),
2351        }
2352    }
2353
2354    /// Encodes a value after converting it to its QR Code text representation once.
2355    #[inline]
2356    pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2357        &self,
2358        value: &T,
2359    ) -> Result<Symbol, EncodeError> {
2360        let text = value.to_qr_text();
2361        self.encode_text(&text)
2362    }
2363
2364    /// Encodes explicit segments using the smallest eligible symbol family.
2365    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2366        if self.qr.fnc1.is_some() {
2367            return self.qr.encode_segments(segments);
2368        }
2369        match self.micro.encode_segments(segments) {
2370            Ok(symbol) => Ok(symbol),
2371            Err(error) if candidate_rejection(&error) => self.qr.encode_segments(segments),
2372            Err(error) => Err(error),
2373        }
2374    }
2375}
2376
2377#[cfg(feature = "qr")]
2378#[derive(Clone, Copy)]
2379struct PartitionState {
2380    end:          usize,
2381    area:         usize,
2382    previous:     usize,
2383    // Whether an ECI is in force at the end, so the next part must declare one again.
2384    eci_in_force: bool,
2385}
2386
2387// Finds the partition into exactly `part_count` parts with the smallest total symbol area, and returns that area with the parts.
2388// `part_ends` lists the farthest ends one part can reach from a start in one version, each with the ECI state it leaves.
2389#[cfg(feature = "qr")]
2390fn minimum_area_partition<F>(
2391    length: usize,
2392    part_count: usize,
2393    range: RangeInclusive<QrVersion>,
2394    mut part_ends: F,
2395) -> Result<(usize, Vec<(usize, usize)>), EncodeError>
2396where
2397    F: FnMut(usize, bool, QrVersion) -> [Option<(usize, bool)>; 2], {
2398    // Each layer represents one additional symbol in the fixed-size Structured Append sequence.
2399    let mut layers = vec![vec![PartitionState {
2400        end:          0,
2401        area:         0,
2402        previous:     0,
2403        eci_in_force: false,
2404    }]];
2405
2406    for _ in 0..part_count {
2407        let previous_layer = layers.last().expect("the initial layer exists");
2408        let mut by_end = BTreeMap::new();
2409
2410        for (previous, state) in previous_layer.iter().enumerate() {
2411            for value in range.start().value()..=range.end().value() {
2412                let version = QrVersion(value);
2413                let size = usize::from(version.value()) * 4 + 17;
2414
2415                for (end, eci_in_force) in
2416                    part_ends(state.end, state.eci_in_force, version).into_iter().flatten()
2417                {
2418                    let candidate = PartitionState {
2419                        end,
2420                        area: state.area + size * size,
2421                        previous,
2422                        eci_in_force,
2423                    };
2424
2425                    // The key orders states without an ECI in force after the others at the same end, so they come first in reverse.
2426                    by_end
2427                        .entry((end, !eci_in_force))
2428                        .and_modify(|current: &mut PartitionState| {
2429                            if candidate.area < current.area {
2430                                *current = candidate;
2431                            }
2432                        })
2433                        .or_insert(candidate);
2434                }
2435            }
2436        }
2437
2438        let mut best_free_area = usize::MAX;
2439        let mut best_area = usize::MAX;
2440        let mut layer = Vec::new();
2441
2442        // A state that reaches farther with no greater area dominates every earlier state, unless only the earlier one is free of an ECI in force.
2443        for state in by_end.into_values().rev() {
2444            if state.eci_in_force {
2445                if state.area < best_area {
2446                    best_area = state.area;
2447                    layer.push(state);
2448                }
2449            } else if state.area < best_free_area {
2450                best_free_area = state.area;
2451                best_area = best_area.min(state.area);
2452                layer.push(state);
2453            }
2454        }
2455
2456        layer.reverse();
2457
2458        if layer.is_empty() {
2459            return Err(EncodeError::DataTooLong {
2460                required_bits: None, capacity_bits: 0
2461            });
2462        }
2463
2464        layers.push(layer);
2465    }
2466
2467    let (mut state_index, area) = layers[part_count]
2468        .iter()
2469        .enumerate()
2470        .filter(|(_, state)| state.end == length)
2471        .map(|(index, state)| (index, state.area))
2472        .min_by_key(|&(_, area)| area)
2473        .ok_or(EncodeError::DataTooLong {
2474            required_bits: None, capacity_bits: 0
2475        })?;
2476
2477    let mut result = Vec::with_capacity(part_count);
2478
2479    for layer_index in (1..=part_count).rev() {
2480        let state = layers[layer_index][state_index];
2481        let previous = layers[layer_index - 1][state.previous];
2482        result.push((previous.end, state.end));
2483        state_index = state.previous;
2484    }
2485
2486    result.reverse();
2487
2488    Ok((area, result))
2489}
2490
2491// Finds the smallest largest version that still reaches the wanted symbol count, because raising it never adds symbols.
2492#[cfg(feature = "qr")]
2493fn smallest_version_cap<F>(
2494    range: &RangeInclusive<QrVersion>,
2495    mut reaches_count: F,
2496) -> Option<QrVersion>
2497where
2498    F: FnMut(QrVersion) -> bool, {
2499    let mut low = range.start().value();
2500    let mut high = range.end().value();
2501    let mut result = None;
2502
2503    while low <= high {
2504        let middle = low + (high - low) / 2;
2505
2506        if reaches_count(QrVersion(middle)) {
2507            result = Some(QrVersion(middle));
2508            high = middle - 1;
2509        } else {
2510            low = middle + 1;
2511        }
2512    }
2513
2514    result
2515}
2516
2517#[cfg(feature = "micro-qr")]
2518fn micro_versions(versions: RangeInclusive<MicroVersion>) -> impl Iterator<Item = MicroVersion> {
2519    let start = *versions.start();
2520    let end = *versions.end();
2521
2522    [MicroVersion::M1, MicroVersion::M2, MicroVersion::M3, MicroVersion::M4]
2523        .into_iter()
2524        .filter(move |version| *version >= start && *version <= end)
2525}
2526
2527#[cfg(feature = "micro-qr")]
2528#[inline]
2529const fn candidate_rejection(error: &EncodeError) -> bool {
2530    matches!(
2531        error,
2532        EncodeError::DataTooLong { .. }
2533            | EncodeError::UnsupportedMode { .. }
2534            | EncodeError::UnsupportedErrorCorrection { .. }
2535            | EncodeError::TextNotRepresentable { .. }
2536    )
2537}
2538
2539#[cfg(feature = "qr")]
2540#[inline]
2541const fn version_group(version: QrVersion) -> usize {
2542    if version.0 <= 9 {
2543        0
2544    } else if version.0 <= 26 {
2545        1
2546    } else {
2547        2
2548    }
2549}
2550
2551// Caches segments per version group during a version scan, because character count indicator widths change only at versions 10 and 27.
2552#[cfg(feature = "qr")]
2553struct GroupCache<F> {
2554    segments: F,
2555    cache:    [Option<Vec<Segment>>; 3],
2556}
2557
2558#[cfg(feature = "qr")]
2559impl<F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>> GroupCache<F> {
2560    fn new(segments: F) -> Self {
2561        Self {
2562            segments,
2563            cache: [None, None, None],
2564        }
2565    }
2566
2567    fn get(&mut self, version: QrVersion) -> Result<&[Segment], EncodeError> {
2568        let group = version_group(version);
2569
2570        if self.cache[group].is_none() {
2571            self.cache[group] = Some((self.segments)(version)?);
2572        }
2573
2574        Ok(self.cache[group].as_deref().expect("the version group is cached"))
2575    }
2576}
2577
2578#[cfg(feature = "qr")]
2579#[inline]
2580const fn validate_part_count(count: usize) -> Result<(), EncodeError> {
2581    if count >= 1 && count <= 16 {
2582        Ok(())
2583    } else {
2584        Err(EncodeError::InvalidStructuredAppendPartCount {
2585            count,
2586        })
2587    }
2588}
2589
2590// Caches the fewest bits of every prefix from one start per version group and key, so partition probes share one optimizer run.
2591#[cfg(feature = "qr")]
2592struct PrefixCosts<K, F> {
2593    compute: F,
2594    costs:   BTreeMap<(usize, usize, K), Vec<Option<usize>>>,
2595}
2596
2597#[cfg(feature = "qr")]
2598impl<K: Copy + Ord, F: FnMut(usize, QrVersion, K) -> Vec<Option<usize>>> PrefixCosts<K, F> {
2599    #[inline]
2600    fn new(compute: F) -> Self {
2601        Self {
2602            compute,
2603            costs: BTreeMap::new(),
2604        }
2605    }
2606
2607    // Returns the prefix costs from `start`, indexed by the prefix length, using the profile of the version group.
2608    fn get(&mut self, start: usize, version: QrVersion, key: K) -> &[Option<usize>] {
2609        let compute = &mut self.compute;
2610
2611        self.costs
2612            .entry((start, version_group(version), key))
2613            .or_insert_with(|| compute(start, version, key))
2614    }
2615}
2616
2617// A text prepared for Structured Append splitting at character boundaries.
2618#[cfg(feature = "qr")]
2619struct SplitText<'a> {
2620    text:            &'a str,
2621    // Byte offsets of every character boundary, ending with the text length.
2622    offsets:         Vec<usize>,
2623    // The index of the first character at or after each position that could make an ECI header worthwhile, or the length when none follows.
2624    eci_useful_from: Vec<usize>,
2625}
2626
2627#[cfg(feature = "qr")]
2628impl<'a> SplitText<'a> {
2629    fn new(text: &'a str) -> Self {
2630        let mut offsets: Vec<usize> = text.char_indices().map(|(offset, _)| offset).collect();
2631
2632        offsets.push(text.len());
2633
2634        let useful: Vec<bool> = text.chars().map(eci_may_help).collect();
2635        let mut eci_useful_from = vec![useful.len(); useful.len() + 1];
2636
2637        for index in (0..useful.len()).rev() {
2638            eci_useful_from[index] = if useful[index] { index } else { eci_useful_from[index + 1] };
2639        }
2640
2641        Self {
2642            text,
2643            offsets,
2644            eci_useful_from,
2645        }
2646    }
2647
2648    // Returns the number of characters.
2649    #[inline]
2650    fn len(&self) -> usize {
2651        self.offsets.len() - 1
2652    }
2653
2654    // Returns the characters in `start..end`.
2655    #[inline]
2656    fn slice(&self, start: usize, end: usize) -> &'a str {
2657        &self.text[self.offsets[start]..self.offsets[end]]
2658    }
2659}
2660
2661// Reports whether an ECI header could shorten the plan of a character: it has no Table 6 byte, or Kanji mode under ECI 000020 can hold it.
2662#[cfg(feature = "qr")]
2663#[inline]
2664fn eci_may_help(character: char) -> bool {
2665    #[cfg(feature = "kanji")]
2666    if kanji_encoding(character).is_some() {
2667        return true;
2668    }
2669
2670    !is_latin1_character(character)
2671}
2672
2673// The parts planned for a Structured Append text sequence, with its largest version and total area.
2674#[cfg(feature = "qr")]
2675struct SequencePlan {
2676    version: QrVersion,
2677    area:    usize,
2678    parts:   Vec<(QrVersion, Vec<Segment>)>,
2679}
2680
2681// The character ranges chosen for an automatically split Structured Append text sequence, with its symbol count, largest version and total area.
2682#[cfg(feature = "qr")]
2683struct SequencePartition {
2684    count:   usize,
2685    version: QrVersion,
2686    area:    usize,
2687    parts:   Vec<(usize, usize)>,
2688}
2689
2690// The ECI layouts a Structured Append text sequence can use, because a reader joins its parts into one data stream.
2691#[cfg(feature = "qr")]
2692#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2693enum SequenceRegime {
2694    // No part declares an ECI, so Kanji mode without an ECI header reads as Shift JIS throughout the sequence.
2695    #[cfg(feature = "kanji")]
2696    Legacy,
2697    // Kanji mode follows an explicit Shift JIS ECI, and every part after the first ECI declares its own.
2698    Standard,
2699}
2700
2701#[cfg(feature = "qr")]
2702impl SequenceRegime {
2703    #[cfg(feature = "kanji")]
2704    const ALL: [Self; 2] = [Self::Legacy, Self::Standard];
2705    #[cfg(not(feature = "kanji"))]
2706    const ALL: [Self; 1] = [Self::Standard];
2707
2708    // Lists the start modes of a part, each with whether its plan may leave an ECI in force for the next part.
2709    #[inline]
2710    const fn starts(self, eci_in_force: bool) -> &'static [(optimizer::Start, bool)] {
2711        match self {
2712            #[cfg(feature = "kanji")]
2713            Self::Legacy => &[(optimizer::Start::Legacy, false)],
2714            // A part after an ECI must declare it again right after the Structured Append header.
2715            Self::Standard if eci_in_force => &[(optimizer::Start::Explicit, true)],
2716            Self::Standard => {
2717                &[(optimizer::Start::DefaultEciFree, false), (optimizer::Start::Default, true)]
2718            },
2719        }
2720    }
2721
2722    // Reports whether this layout can hold every character and may do better than the standard layout.
2723    #[cfg_attr(not(feature = "kanji"), allow(unused_variables))]
2724    fn suits(self, text: &str) -> bool {
2725        match self {
2726            // Without any Kanji character, the standard layout already covers every plan without ECI headers.
2727            #[cfg(feature = "kanji")]
2728            Self::Legacy => {
2729                text.chars().any(|character| kanji_encoding(character).is_some())
2730                    && text.chars().all(|character| {
2731                        is_legacy_byte_character(character) || kanji_encoding(character).is_some()
2732                    })
2733            },
2734            Self::Standard => true,
2735        }
2736    }
2737}