diff --git a/src/bits.rs b/src/bits.rs index 4f3487f..156b887 100644 --- a/src/bits.rs +++ b/src/bits.rs @@ -1004,3 +1004,69 @@ fn bench_find_min_version(bencher: &mut test::Bencher) { //}}} //------------------------------------------------------------------------------ +//{{{ Auto Micro QR code's version minimization + +/// Automatically determines the minimum version to store the data, and encode +/// the result. +/// +/// This method will not consider any QR code versions. +/// +/// # Errors +/// +/// Returns `Err(QrError::DataTooLong)` if the data is too long to fit even the +/// highest Micro QR code version. +pub fn encode_auto_micro(data: &[u8], ec_level: EcLevel) -> QrResult { + let segments = Parser::new(data).collect::>(); + let mut possible_versions = Vec::new(); + for version in 1..=4 { + let version = Version::Micro(version); + let opt_segments = Optimizer::new(segments.iter().copied(), version).collect::>(); + let total_len = total_encoded_len(&opt_segments, version); + let data_capacity = version.fetch(ec_level, &DATA_LENGTHS); + if let Ok(capacity) = data_capacity { + if total_len <= capacity { + possible_versions.push(version); + break; + } + } + } + + let min_version = possible_versions.iter().min_by_key(|v| v.width()); + + if let Some(version) = min_version { + let mut bits = Bits::new(*version); + let opt_segments = Optimizer::new(segments.iter().copied(), *version).collect::>(); + bits.reserve(total_encoded_len(&opt_segments, *version)); + bits.push_segments(data, opt_segments.into_iter())?; + bits.push_terminator(ec_level)?; + return Ok(bits); + } + Err(QrError::DataTooLong) +} + +#[cfg(test)] +mod encode_auto_micro_tests { + use crate::bits::encode_auto_micro; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_alpha_l() { + let bits = encode_auto_micro(b"HELLO WORLD", EcLevel::L).unwrap(); + assert_eq!(bits.version(), Version::Micro(3)); + } + + #[test] + fn test_alpha_q() { + let bits = encode_auto_micro(b"HELLO WORLD", EcLevel::Q).unwrap(); + assert_eq!(bits.version(), Version::Micro(4)); + } + + #[test] + fn test_mixed() { + let bits = encode_auto_micro(b"Mixed. 1234567890", EcLevel::M).unwrap(); + assert_eq!(bits.version(), Version::Micro(4)); + } +} + +//}}} +//------------------------------------------------------------------------------ diff --git a/src/lib.rs b/src/lib.rs index 52fb657..0e939dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,26 @@ impl QrCode { Self::with_error_correction_level(data, EcLevel::M) } + /// Constructs a new Micro QR code which automatically encodes the given + /// data. + /// + /// This method uses the "medium" error correction level and automatically + /// chooses the smallest Micro QR code. + /// + /// ``` + /// use qrcode::QrCode; + /// + /// let code = QrCode::new_micro(b"Some data").unwrap(); + /// ``` + /// + /// # Errors + /// + /// Returns error if the Micro QR code cannot be constructed, e.g. when the + /// data is too long. + pub fn new_micro>(data: D) -> QrResult { + Self::micro_with_error_correction_level(data, EcLevel::M) + } + /// Constructs a new QR code which automatically encodes the given data at a /// specific error correction level. /// @@ -104,6 +124,26 @@ impl QrCode { Self::with_bits(bits, ec_level) } + /// Constructs a new Micro QR code which automatically encodes the given + /// data at a specific error correction level. + /// + /// This method automatically chooses the smallest Micro QR code. + /// + /// ``` + /// use qrcode::{EcLevel, QrCode}; + /// + /// let code = QrCode::micro_with_error_correction_level(b"Some data", EcLevel::Q).unwrap(); + /// ``` + /// + /// # Errors + /// + /// Returns error if the Micro QR code cannot be constructed, e.g. when the + /// data is too long. + pub fn micro_with_error_correction_level>(data: D, ec_level: EcLevel) -> QrResult { + let bits = bits::encode_auto_micro(data.as_ref(), ec_level)?; + Self::with_bits(bits, ec_level) + } + /// Constructs a new QR code for the given version and error correction /// level. ///