1#![cfg_attr(feature = "image", doc = "```rust")]
6#![cfg_attr(not(feature = "image"), doc = "```ignore")]
7#![cfg_attr(docsrs, feature(doc_cfg))]
30#![cfg_attr(not(feature = "std"), no_std)]
31#![deny(missing_docs)]
32#![deny(clippy::uninlined_format_args, clippy::manual_range_contains, clippy::semicolon_if_nothing_returned)]
33#![allow(
34 clippy::must_use_candidate, )]
36
37extern crate alloc;
38
39pub mod render;
40pub mod structured_append;
41
42pub use qrcode_core::ConstVersion;
45pub use qrcode_core::{
46 AlphanumericMode, ByteMode, DynEncoder, DynRenderer, EncodeConfig, EncodedOutput, EncoderFactory, EncodingMode,
47 KanjiMode, ModuleGrid, NumericMode, PluginError, PluginRegistry, PostProcessor, QrPlugin, RenderConfig,
48 RenderOutput, RendererFactory, StaticVersion,
49};
50pub use qrcode_core::{bits, canvas, ec, optimize, plugin, traits, types};
51pub use qrcode_decode as decode;
52pub use qrcode_parse as parse;
53pub use crate::types::{Color, EcLevel, Mode, QrError, QrResult, Version};
56pub use qrcode_core::QrCodeRef;
57use qrcode_core::cast;
58pub use qrcode_core::traits::{
59 Builder, Encoder, ModuleSource, ModuleStorage, ModuleView, QrSymbol, Renderer as CoreRenderer,
60};
61
62#[cfg(not(feature = "std"))]
63#[allow(unused_imports)]
64use alloc::{
65 borrow::ToOwned,
66 format,
67 string::{String, ToString},
68 vec,
69 vec::Vec,
70};
71
72use crate::cast::As;
73use crate::render::{Pixel, Renderer};
74use core::iter::FusedIterator;
75use core::ops::Index;
76
77#[derive(Clone)]
83pub struct QrCode {
84 content: Vec<Color>,
85 version: Version,
86 ec_level: EcLevel,
87 width: usize,
88}
89
90pub struct QrCodePlugins<'a> {
96 code: &'a QrCode,
97 registry: &'a PluginRegistry,
98}
99
100impl QrCodePlugins<'_> {
101 #[must_use]
103 pub fn code(&self) -> &QrCode {
104 self.code
105 }
106
107 #[must_use]
109 pub fn registry(&self) -> &PluginRegistry {
110 self.registry
111 }
112
113 pub fn render(&self, renderer_name: &str, config: &RenderConfig) -> Result<RenderOutput, PluginError> {
121 self.code.render_with(self.registry, renderer_name, config)
122 }
123}
124
125const _: () = {
127 const fn assert_send_sync<T: Send + Sync>() {}
128 assert_send_sync::<QrCode>();
129};
130
131impl QrCode {
132 pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
146 AutoEncoder::default().encode(data.as_ref())
147 }
148
149 pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
163 AutoEncoder::new(ec_level).encode(data.as_ref())
164 }
165
166 pub fn new_micro<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
181 MicroEncoder::default().encode(data.as_ref())
182 }
183
184 pub fn micro_with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
199 MicroEncoder::new(ec_level).encode(data.as_ref())
200 }
201
202 pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
221 VersionEncoder::new(version, ec_level).encode(data.as_ref())
222 }
223
224 pub fn with_const_version<const N: i16, D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
247 ConstVersionEncoder::<N>::new(ec_level).encode(data.as_ref())
248 }
249
250 fn encode_auto(data: &[u8], ec_level: EcLevel) -> QrResult<Self> {
251 let bits = bits::encode_auto(data, ec_level)?;
252 Self::with_bits(bits, ec_level)
253 }
254
255 fn encode_auto_micro(data: &[u8], ec_level: EcLevel) -> QrResult<Self> {
256 let bits = bits::encode_auto_micro(data, ec_level)?;
257 Self::with_bits(bits, ec_level)
258 }
259
260 fn encode_with_version(data: &[u8], version: Version, ec_level: EcLevel) -> QrResult<Self> {
261 let mut bits = bits::Bits::new(version);
262 bits.push_optimal_data(data)?;
263 bits.push_terminator(ec_level)?;
264 Self::with_bits(bits, ec_level)
265 }
266
267 pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
295 let version = bits.version();
296 #[cfg(feature = "log")]
297 log::debug!("qrcode_rs: encoding at version {version:?}, ec {ec_level:?}");
298 let data = bits.into_bytes();
299 let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?;
300 let mut canvas = canvas::Canvas::new(version, ec_level);
301 canvas.draw_all_functional_patterns();
302 canvas.draw_data(&encoded_data, &ec_data);
303 let canvas = canvas.apply_best_mask();
304 let width = version.width().as_usize();
305 #[cfg(feature = "log")]
306 log::info!("qrcode_rs: encoded version {version:?} ec {ec_level:?} ({} modules)", width * width);
307 Ok(Self { content: canvas.into_colors(), version, ec_level, width })
308 }
309
310 pub fn batch<I, D>(inputs: I, ec_level: EcLevel) -> QrResult<Vec<Self>>
322 where
323 I: IntoIterator<Item = D>,
324 D: AsRef<[u8]>,
325 {
326 Self::stream_with_error_correction_level(inputs, ec_level).collect()
327 }
328
329 pub fn stream<I, D>(inputs: I) -> QrCodeStream<I::IntoIter>
347 where
348 I: IntoIterator<Item = D>,
349 D: AsRef<[u8]>,
350 {
351 Self::stream_with_error_correction_level(inputs, EcLevel::M)
352 }
353
354 pub fn stream_with_error_correction_level<I, D>(inputs: I, ec_level: EcLevel) -> QrCodeStream<I::IntoIter>
360 where
361 I: IntoIterator<Item = D>,
362 D: AsRef<[u8]>,
363 {
364 QrCodeStream { inputs: inputs.into_iter(), ec_level }
365 }
366
367 pub const fn version(&self) -> Version {
369 self.version
370 }
371
372 pub const fn error_correction_level(&self) -> EcLevel {
374 self.ec_level
375 }
376
377 pub const fn width(&self) -> usize {
381 self.width
382 }
383
384 pub fn max_allowed_errors(&self) -> usize {
388 ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
389 }
390
391 #[must_use]
406 pub fn info(&self) -> Info {
407 Info {
408 version: self.version,
409 ec_level: self.ec_level,
410 width: self.width,
411 module_count: self.width * self.width,
412 max_allowed_errors: self.max_allowed_errors(),
413 data_capacity_bytes: bits::data_capacity_bits(self.version, self.ec_level).map(|b| b / 8).unwrap_or(0),
414 }
415 }
416
417 #[must_use]
432 pub fn analyze(&self) -> Analysis {
433 let total = self.width * self.width;
434 let dark = self.content.iter().filter(|c| **c == Color::Dark).count();
435 let functional =
436 (0..self.width).map(|y| (0..self.width).filter(|x| self.is_functional(*x, y)).count()).sum::<usize>();
437 Analysis {
438 dark_ratio: if total == 0 { 0.0 } else { dark as f64 / total as f64 },
439 functional_modules: functional,
440 data_modules: total - functional,
441 }
442 }
443
444 pub fn is_functional(&self, x: usize, y: usize) -> bool {
447 let x = x.try_into().expect("coordinate is too large for QR code");
448 let y = y.try_into().expect("coordinate is too large for QR code");
449 canvas::is_functional(self.version, self.version.width(), x, y)
450 }
451
452 pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
455 self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build()
456 }
457
458 pub fn colors(&self) -> &[Color] {
475 &self.content
476 }
477
478 #[must_use]
495 pub fn module_view(&self) -> ModuleView<'_> {
496 ModuleView::new(&self.content, self.width).expect("QrCode stores a non-empty square module grid")
497 }
498
499 #[must_use]
517 pub fn as_ref(&self) -> QrCodeRef<'_> {
518 QrCodeRef::new(&self.content, self.width, self.version, self.ec_level)
519 .expect("QrCode stores a non-empty square module grid")
520 }
521
522 pub fn to_colors(&self) -> Vec<Color> {
524 self.content.clone()
525 }
526
527 pub fn into_colors(self) -> Vec<Color> {
529 self.content
530 }
531
532 #[cfg_attr(feature = "image", doc = " ```rust")]
540 #[cfg_attr(not(feature = "image"), doc = " ```ignore")]
541 pub fn render<P: Pixel>(&self) -> Renderer<'_, P> {
554 Renderer::from_symbol(self)
555 }
556
557 pub fn render_builder<P: Pixel>(&self) -> Renderer<'_, P> {
578 self.render()
579 }
580
581 #[cfg(feature = "async")]
604 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
605 pub async fn render_async<P>(&self) -> Result<P::Image, tokio::task::JoinError>
606 where
607 P: Pixel + Send + 'static,
608 P::Image: Send + 'static,
609 {
610 let code = self.clone();
611 tokio::task::spawn_blocking(move || code.render::<P>().build()).await
612 }
613
614 pub fn encode_with(
625 registry: &PluginRegistry,
626 encoder_name: &str,
627 input: &[u8],
628 config: &EncodeConfig,
629 ) -> Result<EncodedOutput, PluginError> {
630 registry.build_encoder(encoder_name, config)?.encode(input)
631 }
632
633 pub fn render_with(
646 &self,
647 registry: &PluginRegistry,
648 renderer_name: &str,
649 config: &RenderConfig,
650 ) -> Result<RenderOutput, PluginError> {
651 let mut modules = ModuleGrid::new(self.content.clone(), self.width, self.width)?;
652 registry.process_modules(&mut modules)?;
653 registry.build_renderer(renderer_name, config)?.render(&modules)
654 }
655
656 pub fn with_plugins<'a>(&'a self, registry: &'a PluginRegistry) -> QrCodePlugins<'a> {
662 QrCodePlugins { code: self, registry }
663 }
664}
665
666impl QrCode {
667 pub fn builder<D: AsRef<[u8]>>(data: D) -> QrCodeBuilder<D> {
685 QrCodeBuilder::new(data)
686 }
687
688 pub fn rows(&self) -> Rows<'_> {
706 Rows { code: self, y: 0 }
707 }
708
709 pub fn dark_modules(&self) -> DarkModules<'_> {
722 DarkModules { code: self, idx: 0 }
723 }
724
725 pub fn for_url<D: AsRef<[u8]>>(url: D) -> QrResult<Self> {
731 Self::with_error_correction_level(url, EcLevel::H)
732 }
733
734 pub fn for_text<D: AsRef<[u8]>>(text: D) -> QrResult<Self> {
740 Self::new(text)
741 }
742
743 pub fn for_wifi(ssid: &str, password: &str, auth: &str) -> QrResult<Self> {
761 Self::new(parse::wifi::encode_wifi(ssid, password, auth))
762 }
763
764 pub fn for_vcard(name: &str, phone: &str, email: &str) -> QrResult<Self> {
779 Self::new(parse::vcard::encode_vcard(name, phone, email))
780 }
781
782 pub fn for_gs1<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
800 let data = data.as_ref();
801 for v in 1..=40 {
802 let version = Version::Normal(v);
803 let mut bits = bits::Bits::new(version);
804 if bits.push_fnc1_first_position().is_err()
805 || bits.push_optimal_data(data).is_err()
806 || bits.push_terminator(EcLevel::M).is_err()
807 {
808 continue;
809 }
810 return Self::with_bits(bits, EcLevel::M);
811 }
812 Err(QrError::DataTooLong)
813 }
814
815 pub fn structured_append<D: AsRef<[u8]>>(payload: D, symbols: u8, ec: EcLevel) -> QrResult<Vec<Self>> {
841 let sa = structured_append::StructuredAppend::new(symbols, payload.as_ref())?;
842 sa.encode(ec)
843 }
844
845 #[must_use]
863 pub fn alt_text<D: AsRef<[u8]>>(data: D) -> String {
864 let text = String::from_utf8_lossy(data.as_ref());
865 if text.starts_with("http://") || text.starts_with("https://") {
866 format!("QR code linking to {text}")
867 } else {
868 format!("QR code containing: {text}")
869 }
870 }
871
872 #[must_use]
885 pub fn alt_text_custom<D: AsRef<[u8]>, F: FnOnce(&[u8]) -> String>(data: D, f: F) -> String {
886 f(data.as_ref())
887 }
888
889 fn with_mode<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel, mode: Mode) -> QrResult<Self> {
893 let mut bits = bits::Bits::new(version);
894 match mode {
895 Mode::Numeric => bits.push_numeric_data(data.as_ref())?,
896 Mode::Alphanumeric => bits.push_alphanumeric_data(data.as_ref())?,
897 Mode::Byte => bits.push_byte_data(data.as_ref())?,
898 Mode::Kanji => bits.push_kanji_data(data.as_ref())?,
899 }
900 bits.push_terminator(ec_level)?;
901 Self::with_bits(bits, ec_level)
902 }
903
904 fn with_mode_auto<D: AsRef<[u8]>>(data: D, ec_level: EcLevel, mode: Mode) -> QrResult<Self> {
910 let data = data.as_ref();
911 let mut last_err = QrError::DataTooLong;
912 for v in 1..=40 {
913 let version = Version::Normal(v);
914 let mut bits = bits::Bits::new(version);
915 let pushed = match mode {
916 Mode::Numeric => bits.push_numeric_data(data),
917 Mode::Alphanumeric => bits.push_alphanumeric_data(data),
918 Mode::Byte => bits.push_byte_data(data),
919 Mode::Kanji => bits.push_kanji_data(data),
920 };
921 if let Err(e) = pushed {
922 last_err = e;
923 continue;
924 }
925 if let Err(e) = bits.push_terminator(ec_level) {
926 last_err = e;
927 continue;
928 }
929 return Self::with_bits(bits, ec_level);
930 }
931 Err(last_err)
932 }
933}
934
935impl Index<(usize, usize)> for QrCode {
936 type Output = Color;
937
938 fn index(&self, (x, y): (usize, usize)) -> &Color {
939 let index = y * self.width + x;
940 &self.content[index]
941 }
942}
943
944impl ModuleStorage for QrCode {
945 fn get(&self, x: usize, y: usize) -> Color {
946 self[(x, y)]
947 }
948
949 fn set(&mut self, x: usize, y: usize, color: Color) {
950 let index = y * self.width + x;
951 self.content[index] = color;
952 }
953
954 fn width(&self) -> usize {
955 self.width
956 }
957
958 fn height(&self) -> usize {
959 self.width
960 }
961
962 fn modules(&self) -> &[Color] {
963 self.colors()
964 }
965}
966
967impl QrSymbol for QrCode {
968 fn version(&self) -> Version {
969 self.version
970 }
971
972 fn error_correction_level(&self) -> EcLevel {
973 self.ec_level
974 }
975}
976
977#[derive(Clone, Copy, Debug, PartialEq, Eq)]
985pub struct AutoEncoder {
986 ec_level: EcLevel,
987}
988
989impl AutoEncoder {
990 #[must_use]
992 pub const fn new(ec_level: EcLevel) -> Self {
993 Self { ec_level }
994 }
995
996 #[must_use]
998 pub const fn ec_level(&self) -> EcLevel {
999 self.ec_level
1000 }
1001}
1002
1003impl Default for AutoEncoder {
1004 fn default() -> Self {
1005 Self { ec_level: EcLevel::M }
1006 }
1007}
1008
1009impl Encoder for AutoEncoder {
1010 type Output = QrCode;
1011 type Error = QrError;
1012
1013 fn encode(&self, input: &[u8]) -> QrResult<QrCode> {
1014 QrCode::encode_auto(input, self.ec_level)
1015 }
1016}
1017
1018#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1023pub struct MicroEncoder {
1024 ec_level: EcLevel,
1025}
1026
1027impl MicroEncoder {
1028 #[must_use]
1030 pub const fn new(ec_level: EcLevel) -> Self {
1031 Self { ec_level }
1032 }
1033
1034 #[must_use]
1036 pub const fn ec_level(&self) -> EcLevel {
1037 self.ec_level
1038 }
1039}
1040
1041impl Default for MicroEncoder {
1042 fn default() -> Self {
1043 Self { ec_level: EcLevel::M }
1044 }
1045}
1046
1047impl Encoder for MicroEncoder {
1048 type Output = QrCode;
1049 type Error = QrError;
1050
1051 fn encode(&self, input: &[u8]) -> QrResult<QrCode> {
1052 QrCode::encode_auto_micro(input, self.ec_level)
1053 }
1054}
1055
1056#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1060pub struct VersionEncoder {
1061 version: Version,
1062 ec_level: EcLevel,
1063}
1064
1065impl VersionEncoder {
1066 #[must_use]
1068 pub const fn new(version: Version, ec_level: EcLevel) -> Self {
1069 Self { version, ec_level }
1070 }
1071
1072 #[must_use]
1074 pub const fn version(&self) -> Version {
1075 self.version
1076 }
1077
1078 #[must_use]
1080 pub const fn ec_level(&self) -> EcLevel {
1081 self.ec_level
1082 }
1083}
1084
1085impl Encoder for VersionEncoder {
1086 type Output = QrCode;
1087 type Error = QrError;
1088
1089 fn encode(&self, input: &[u8]) -> QrResult<QrCode> {
1090 QrCode::encode_with_version(input, self.version, self.ec_level)
1091 }
1092}
1093
1094#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1112pub struct ConstVersionEncoder<const N: i16> {
1113 ec_level: EcLevel,
1114}
1115
1116impl<const N: i16> ConstVersionEncoder<N> {
1117 #[must_use]
1119 pub const fn new(ec_level: EcLevel) -> Self {
1120 let _ = ConstVersion::<N>::VALUE;
1121 Self { ec_level }
1122 }
1123
1124 #[must_use]
1126 pub const fn version(&self) -> Version {
1127 ConstVersion::<N>::VALUE
1128 }
1129
1130 #[must_use]
1132 pub const fn ec_level(&self) -> EcLevel {
1133 self.ec_level
1134 }
1135}
1136
1137impl<const N: i16> Encoder for ConstVersionEncoder<N> {
1138 type Output = QrCode;
1139 type Error = QrError;
1140
1141 fn encode(&self, input: &[u8]) -> QrResult<QrCode> {
1142 QrCode::encode_with_version(input, ConstVersion::<N>::VALUE, self.ec_level)
1143 }
1144}
1145
1146#[derive(Clone, Debug)]
1156pub struct QrCodeBuilder<D: AsRef<[u8]>> {
1157 data: D,
1158 ec_level: EcLevel,
1159 version: Option<Version>,
1160 micro: bool,
1161 mode_hint: Option<Mode>,
1162}
1163
1164impl<D: AsRef<[u8]>> QrCodeBuilder<D> {
1165 fn new(data: D) -> Self {
1166 Self { data, ec_level: EcLevel::M, version: None, micro: false, mode_hint: None }
1167 }
1168
1169 #[must_use]
1171 pub fn ec_level(mut self, ec_level: EcLevel) -> Self {
1172 self.ec_level = ec_level;
1173 self
1174 }
1175
1176 #[must_use]
1180 pub fn version(mut self, version: Version) -> Self {
1181 self.version = Some(version);
1182 self
1183 }
1184
1185 #[must_use]
1189 pub fn micro(mut self, yes: bool) -> Self {
1190 self.micro = yes;
1191 self
1192 }
1193
1194 #[must_use]
1204 pub fn encoding_mode(mut self, mode: Mode) -> Self {
1205 self.mode_hint = Some(mode);
1206 self
1207 }
1208
1209 pub fn encoding_mode_typed<M: EncodingMode>(mut self) -> QrResult<Self> {
1237 if let Some((position, byte)) = M::invalid_character(self.data.as_ref()) {
1238 return Err(QrError::InvalidCharacter { position, byte });
1239 }
1240 self.mode_hint = Some(M::MODE);
1241 Ok(self)
1242 }
1243
1244 #[must_use]
1248 pub fn force_mode(self, mode: Mode) -> Self {
1249 self.encoding_mode(mode)
1250 }
1251
1252 pub fn force_mode_typed<M: EncodingMode>(self) -> QrResult<Self> {
1261 self.encoding_mode_typed::<M>()
1262 }
1263
1264 pub fn build(self) -> QrResult<QrCode> {
1272 if let Some(version) = self.version {
1273 if let Some(mode) = self.mode_hint {
1274 return QrCode::with_mode(self.data, version, self.ec_level, mode);
1275 }
1276 return QrCode::with_version(self.data, version, self.ec_level);
1277 }
1278 if let Some(mode) = self.mode_hint {
1279 return QrCode::with_mode_auto(self.data, self.ec_level, mode);
1280 }
1281 if self.micro {
1282 return QrCode::micro_with_error_correction_level(self.data, self.ec_level);
1283 }
1284 QrCode::with_error_correction_level(self.data, self.ec_level)
1285 }
1286}
1287
1288impl<D: AsRef<[u8]>> Builder for QrCodeBuilder<D> {
1289 type Output = QrCode;
1290 type Error = QrError;
1291
1292 fn build(self) -> QrResult<QrCode> {
1293 QrCodeBuilder::build(self)
1294 }
1295}
1296
1297#[derive(Clone, Debug)]
1304pub struct QrCodeStream<I> {
1305 inputs: I,
1306 ec_level: EcLevel,
1307}
1308
1309impl<I> Iterator for QrCodeStream<I>
1310where
1311 I: Iterator,
1312 I::Item: AsRef<[u8]>,
1313{
1314 type Item = QrResult<QrCode>;
1315
1316 fn next(&mut self) -> Option<Self::Item> {
1317 self.inputs.next().map(|input| QrCode::with_error_correction_level(input, self.ec_level))
1318 }
1319
1320 fn size_hint(&self) -> (usize, Option<usize>) {
1321 self.inputs.size_hint()
1322 }
1323}
1324
1325impl<I> FusedIterator for QrCodeStream<I>
1326where
1327 I: FusedIterator,
1328 I::Item: AsRef<[u8]>,
1329{
1330}
1331
1332impl<I> ExactSizeIterator for QrCodeStream<I>
1333where
1334 I: ExactSizeIterator,
1335 I::Item: AsRef<[u8]>,
1336{
1337 fn len(&self) -> usize {
1338 self.inputs.len()
1339 }
1340}
1341
1342#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1352#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1353#[non_exhaustive]
1354pub struct Info {
1355 version: Version,
1356 ec_level: EcLevel,
1357 width: usize,
1358 module_count: usize,
1359 max_allowed_errors: usize,
1360 data_capacity_bytes: usize,
1361}
1362
1363impl Info {
1364 #[must_use]
1366 pub const fn version(&self) -> Version {
1367 self.version
1368 }
1369
1370 #[must_use]
1372 pub const fn ec_level(&self) -> EcLevel {
1373 self.ec_level
1374 }
1375
1376 #[must_use]
1378 pub const fn width(&self) -> usize {
1379 self.width
1380 }
1381
1382 #[must_use]
1384 pub const fn module_count(&self) -> usize {
1385 self.module_count
1386 }
1387
1388 #[must_use]
1390 pub const fn max_allowed_errors(&self) -> usize {
1391 self.max_allowed_errors
1392 }
1393
1394 #[must_use]
1396 pub const fn data_capacity_bytes(&self) -> usize {
1397 self.data_capacity_bytes
1398 }
1399}
1400
1401#[cfg(feature = "serde")]
1409#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1410pub struct QrCodeData {
1411 pub version: Version,
1413 pub ec_level: EcLevel,
1415 pub width: usize,
1417 pub content: Vec<Color>,
1419}
1420
1421#[cfg(feature = "serde")]
1422impl QrCode {
1423 #[must_use]
1425 pub fn to_serializable(&self) -> QrCodeData {
1426 QrCodeData { version: self.version, ec_level: self.ec_level, width: self.width, content: self.content.clone() }
1427 }
1428
1429 #[must_use]
1434 pub fn from_serializable(data: QrCodeData) -> Self {
1435 debug_assert_eq!(data.content.len(), data.width * data.width, "malformed QrCodeData");
1436 Self { content: data.content, version: data.version, ec_level: data.ec_level, width: data.width }
1437 }
1438}
1439
1440#[derive(Clone, Copy, Debug, PartialEq)]
1446#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1447#[non_exhaustive]
1448pub struct Analysis {
1449 dark_ratio: f64,
1450 functional_modules: usize,
1451 data_modules: usize,
1452}
1453
1454impl Analysis {
1455 #[must_use]
1457 pub const fn dark_ratio(&self) -> f64 {
1458 self.dark_ratio
1459 }
1460
1461 #[must_use]
1463 pub const fn functional_modules(&self) -> usize {
1464 self.functional_modules
1465 }
1466
1467 #[must_use]
1469 pub const fn data_modules(&self) -> usize {
1470 self.data_modules
1471 }
1472}
1473
1474#[derive(Clone, Debug, PartialEq, Eq)]
1482#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1483pub struct QrTemplate {
1484 pub dark_color: String,
1486 pub light_color: String,
1488 pub module_size: Option<(u32, u32)>,
1490 pub quiet_zone: bool,
1492}
1493
1494impl QrTemplate {
1495 #[must_use]
1497 pub fn minimal() -> Self {
1498 Self { dark_color: "#000000".into(), light_color: "#ffffff".into(), module_size: None, quiet_zone: true }
1499 }
1500
1501 #[must_use]
1503 pub fn dark_mode() -> Self {
1504 Self { dark_color: "#e0e0e0".into(), light_color: "#1a1a2e".into(), module_size: None, quiet_zone: true }
1505 }
1506
1507 #[must_use]
1509 pub fn high_contrast() -> Self {
1510 Self { dark_color: "#000000".into(), light_color: "#ffffff".into(), module_size: None, quiet_zone: true }
1511 }
1512
1513 #[must_use]
1515 pub fn corporate() -> Self {
1516 Self { dark_color: "#003366".into(), light_color: "#ffffff".into(), module_size: None, quiet_zone: true }
1517 }
1518}
1519
1520impl qrcode_render::RenderTemplate for QrTemplate {
1521 fn dark_color(&self) -> &str {
1522 &self.dark_color
1523 }
1524
1525 fn light_color(&self) -> &str {
1526 &self.light_color
1527 }
1528
1529 fn module_size(&self) -> Option<(u32, u32)> {
1530 self.module_size
1531 }
1532
1533 fn quiet_zone(&self) -> bool {
1534 self.quiet_zone
1535 }
1536}
1537
1538pub struct Rows<'a> {
1544 code: &'a QrCode,
1545 y: usize,
1546}
1547
1548impl<'a> Iterator for Rows<'a> {
1549 type Item = Row<'a>;
1550
1551 fn next(&mut self) -> Option<Self::Item> {
1552 let w = self.code.width;
1553 if self.y < w {
1554 let row = Row { code: self.code, y: self.y, x: 0 };
1555 self.y += 1;
1556 Some(row)
1557 } else {
1558 None
1559 }
1560 }
1561
1562 fn size_hint(&self) -> (usize, Option<usize>) {
1563 let rem = self.code.width - self.y;
1564 (rem, Some(rem))
1565 }
1566}
1567
1568impl<'a> ExactSizeIterator for Rows<'a> {
1569 fn len(&self) -> usize {
1570 self.code.width - self.y
1571 }
1572}
1573
1574impl<'a> FusedIterator for Rows<'a> {}
1575
1576pub struct Row<'a> {
1579 code: &'a QrCode,
1580 y: usize,
1581 x: usize,
1582}
1583
1584impl<'a> Row<'a> {
1585 #[must_use]
1587 pub fn len(&self) -> usize {
1588 self.code.width
1589 }
1590
1591 #[must_use]
1593 pub fn is_empty(&self) -> bool {
1594 self.code.width == 0
1595 }
1596}
1597
1598impl<'a> Iterator for Row<'a> {
1599 type Item = Color;
1600
1601 fn next(&mut self) -> Option<Color> {
1602 let w = self.code.width;
1603 if self.x < w {
1604 let color = self.code.content[self.y * w + self.x];
1605 self.x += 1;
1606 Some(color)
1607 } else {
1608 None
1609 }
1610 }
1611
1612 fn size_hint(&self) -> (usize, Option<usize>) {
1613 let rem = self.code.width - self.x;
1614 (rem, Some(rem))
1615 }
1616}
1617
1618impl<'a> ExactSizeIterator for Row<'a> {
1619 fn len(&self) -> usize {
1620 self.code.width - self.x
1621 }
1622}
1623
1624impl<'a> FusedIterator for Row<'a> {}
1625
1626pub struct DarkModules<'a> {
1629 code: &'a QrCode,
1630 idx: usize,
1631}
1632
1633impl<'a> Iterator for DarkModules<'a> {
1634 type Item = (usize, usize);
1635
1636 fn next(&mut self) -> Option<(usize, usize)> {
1637 let w = self.code.width;
1638 let content = &self.code.content;
1639 while self.idx < content.len() {
1640 let i = self.idx;
1641 self.idx += 1;
1642 if content[i] == Color::Dark {
1643 return Some((i % w, i / w));
1644 }
1645 }
1646 None
1647 }
1648}
1649
1650impl<'a> FusedIterator for DarkModules<'a> {}
1651
1652#[cfg(test)]
1655mod tests {
1656 use crate::{EcLevel, QrCode, Version};
1657
1658 #[test]
1659 fn test_annex_i_qr() {
1660 let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
1662 assert_eq!(
1663 &*code.to_debug_str('#', '.'),
1664 "\
1665 #######..#.##.#######\n\
1666 #.....#..####.#.....#\n\
1667 #.###.#.#.....#.###.#\n\
1668 #.###.#.##....#.###.#\n\
1669 #.###.#.#.###.#.###.#\n\
1670 #.....#.#...#.#.....#\n\
1671 #######.#.#.#.#######\n\
1672 ........#..##........\n\
1673 #.#####..#..#.#####..\n\
1674 ...#.#.##.#.#..#.##..\n\
1675 ..#...##.#.#.#..#####\n\
1676 ....#....#.....####..\n\
1677 ...######..#.#..#....\n\
1678 ........#.#####..##..\n\
1679 #######..##.#.##.....\n\
1680 #.....#.#.#####...#.#\n\
1681 #.###.#.#...#..#.##..\n\
1682 #.###.#.##..#..#.....\n\
1683 #.###.#.#.##.#..#.#..\n\
1684 #.....#........##.##.\n\
1685 #######.####.#..#.#.."
1686 );
1687 }
1688
1689 #[test]
1690 fn test_annex_i_micro_qr() {
1691 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1692 assert_eq!(
1693 &*code.to_debug_str('#', '.'),
1694 "\
1695 #######.#.#.#\n\
1696 #.....#.###.#\n\
1697 #.###.#..##.#\n\
1698 #.###.#..####\n\
1699 #.###.#.###..\n\
1700 #.....#.#...#\n\
1701 #######..####\n\
1702 .........##..\n\
1703 ##.#....#...#\n\
1704 .##.#.#.#.#.#\n\
1705 ###..#######.\n\
1706 ...#.#....##.\n\
1707 ###.#..##.###"
1708 );
1709 }
1710}
1711
1712#[cfg(test)]
1713mod api_tests {
1714 use crate::{
1715 AutoEncoder, Builder as CoreBuilder, Color, ConstVersion, ConstVersionEncoder, DynEncoder, DynRenderer,
1716 EcLevel, EncodeConfig, EncodedOutput, EncoderFactory, MicroEncoder, Mode, ModuleView, NumericMode,
1717 PluginRegistry, PostProcessor, QrCode, QrError, QrSymbol, RenderConfig, RenderOutput, RendererFactory, Version,
1718 VersionEncoder,
1719 };
1720 use alloc::{
1721 boxed::Box,
1722 string::{String, ToString},
1723 vec,
1724 vec::Vec,
1725 };
1726 use qrcode_core::traits::{
1727 Encoder as CoreEncoder, ModuleSource as CoreModuleSource, ModuleStorage as CoreModuleStorage,
1728 Renderer as CoreRenderer,
1729 };
1730
1731 fn colors(code: &QrCode) -> Vec<Color> {
1732 code.to_colors()
1733 }
1734
1735 struct TextPluginRenderer;
1736
1737 impl DynRenderer for TextPluginRenderer {
1738 fn render(&self, code: &dyn CoreModuleSource) -> Result<RenderOutput, crate::PluginError> {
1739 let mut output = String::new();
1740 for y in 0..code.height() {
1741 for x in 0..code.width() {
1742 output.push(if code.get(x, y) == Color::Dark { '#' } else { '.' });
1743 }
1744 }
1745 Ok(RenderOutput::Text(output))
1746 }
1747 }
1748
1749 struct TextPluginFactory;
1750
1751 impl RendererFactory for TextPluginFactory {
1752 fn build(&self, _config: &RenderConfig) -> Box<dyn DynRenderer> {
1753 Box::new(TextPluginRenderer)
1754 }
1755 }
1756
1757 struct LengthPluginEncoder;
1758
1759 impl DynEncoder for LengthPluginEncoder {
1760 fn encode(&self, input: &[u8]) -> Result<EncodedOutput, crate::PluginError> {
1761 Ok(EncodedOutput::Bytes(input.len().to_string().into_bytes()))
1762 }
1763 }
1764
1765 struct LengthPluginFactory;
1766
1767 impl EncoderFactory for LengthPluginFactory {
1768 fn build(&self, _config: &EncodeConfig) -> Box<dyn DynEncoder> {
1769 Box::new(LengthPluginEncoder)
1770 }
1771 }
1772
1773 struct DarkenFirstModule;
1774
1775 impl PostProcessor for DarkenFirstModule {
1776 fn process(&self, modules: &mut dyn CoreModuleStorage) -> Result<(), crate::PluginError> {
1777 modules.set(0, 0, Color::Dark);
1778 Ok(())
1779 }
1780 }
1781
1782 #[test]
1783 fn builder_matches_with_error_correction_level() {
1784 let direct = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
1785 let built = QrCode::builder(b"Some data").ec_level(EcLevel::H).build().unwrap();
1786 assert_eq!(colors(&direct), colors(&built));
1787 assert_eq!(direct.version(), built.version());
1788 assert_eq!(direct.error_correction_level(), built.error_correction_level());
1789 }
1790
1791 #[test]
1792 fn stream_lazily_encodes_with_default_error_correction_level() {
1793 let codes = QrCode::stream(["alpha", "beta"]).collect::<Result<Vec<_>, _>>().unwrap();
1794
1795 assert_eq!(codes.iter().map(QrCode::error_correction_level).collect::<Vec<_>>(), [EcLevel::M, EcLevel::M]);
1796 }
1797
1798 #[test]
1799 fn stream_with_error_correction_level_matches_batch() {
1800 let inputs = ["alpha", "beta", "gamma"];
1801 let streamed =
1802 QrCode::stream_with_error_correction_level(inputs, EcLevel::H).collect::<Result<Vec<_>, _>>().unwrap();
1803 let batched = QrCode::batch(inputs, EcLevel::H).unwrap();
1804
1805 assert_eq!(streamed.iter().map(colors).collect::<Vec<_>>(), batched.iter().map(colors).collect::<Vec<_>>());
1806 }
1807
1808 #[test]
1809 fn stream_exposes_exact_remaining_len() {
1810 let mut stream = QrCode::stream(["alpha", "beta", "gamma"]);
1811
1812 assert_eq!(stream.len(), 3);
1813 assert!(stream.next().unwrap().is_ok());
1814 assert_eq!(stream.len(), 2);
1815 }
1816
1817 #[test]
1818 fn core_builder_trait_builds_qrcode_builder() {
1819 let code = CoreBuilder::build(QrCode::builder(b"Some data").ec_level(EcLevel::H)).unwrap();
1820
1821 assert_eq!(code.error_correction_level(), EcLevel::H);
1822 }
1823
1824 #[test]
1825 fn auto_encoder_matches_constructor() {
1826 let direct = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
1827 let encoded = AutoEncoder::new(EcLevel::H).encode(b"Some data").unwrap();
1828 assert_eq!(colors(&direct), colors(&encoded));
1829 }
1830
1831 #[test]
1832 fn micro_encoder_matches_constructor() {
1833 let direct = QrCode::micro_with_error_correction_level(b"123", EcLevel::L).unwrap();
1834 let encoded = MicroEncoder::new(EcLevel::L).encode(b"123").unwrap();
1835 assert_eq!(colors(&direct), colors(&encoded));
1836 }
1837
1838 #[test]
1839 fn version_encoder_matches_constructor() {
1840 let direct = QrCode::with_version(b"Some data", Version::Normal(1), EcLevel::M).unwrap();
1841 let encoded = VersionEncoder::new(Version::Normal(1), EcLevel::M).encode(b"Some data").unwrap();
1842 assert_eq!(colors(&direct), colors(&encoded));
1843 }
1844
1845 #[test]
1846 fn const_version_encoder_matches_dynamic_version() {
1847 const V5: Version = ConstVersion::<5>::VALUE;
1848 let direct = QrCode::with_version(b"Some data", Version::Normal(5), EcLevel::M).unwrap();
1849 let const_ctor = QrCode::with_const_version::<5, _>(b"Some data", EcLevel::M).unwrap();
1850 let encoded = ConstVersionEncoder::<5>::new(EcLevel::M).encode(b"Some data").unwrap();
1851
1852 assert_eq!(V5, Version::Normal(5));
1853 assert_eq!(ConstVersion::<5>::new().version(), Version::Normal(5));
1854 assert_eq!(ConstVersionEncoder::<5>::new(EcLevel::M).version(), Version::Normal(5));
1855 assert_eq!(colors(&direct), colors(&const_ctor));
1856 assert_eq!(colors(&direct), colors(&encoded));
1857 }
1858
1859 #[test]
1860 fn builder_matches_with_version() {
1861 let direct = QrCode::with_version(b"Some data", Version::Normal(1), EcLevel::M).unwrap();
1862 let built = QrCode::builder(b"Some data").version(Version::Normal(1)).build().unwrap();
1863 assert_eq!(colors(&direct), colors(&built));
1864 }
1865
1866 #[test]
1867 fn builder_micro_matches() {
1868 let direct = QrCode::micro_with_error_correction_level(b"123", EcLevel::L).unwrap();
1869 let built = QrCode::builder(b"123").ec_level(EcLevel::L).micro(true).build().unwrap();
1870 assert_eq!(colors(&direct), colors(&built));
1871 assert!(built.version().is_micro());
1872 }
1873
1874 #[test]
1875 fn render_with_uses_registered_renderer_and_postprocessors() {
1876 let code = QrCode::new(b"plugin").unwrap();
1877 let mut registry = PluginRegistry::new();
1878 registry.register_renderer("text", Box::new(TextPluginFactory));
1879 registry.register_postprocessor(Box::new(DarkenFirstModule));
1880
1881 let output = code.render_with(®istry, "text", &RenderConfig::new()).unwrap();
1882 let RenderOutput::Text(text) = output else {
1883 panic!("expected text output");
1884 };
1885
1886 assert_eq!(text.len(), code.width() * code.width());
1887 assert!(text.starts_with('#'));
1888 }
1889
1890 #[test]
1891 fn with_plugins_renders_through_bound_registry() {
1892 let code = QrCode::new(b"plugin").unwrap();
1893 let mut registry = PluginRegistry::new();
1894 registry.register_renderer("text", Box::new(TextPluginFactory));
1895 registry.register_postprocessor(Box::new(DarkenFirstModule));
1896
1897 let bound = code.with_plugins(®istry);
1898 let output = bound.render("text", &RenderConfig::new()).unwrap();
1899
1900 assert_eq!(bound.code().width(), code.width());
1901 assert!(bound.registry().renderer("text").is_some());
1902 assert_eq!(output, code.render_with(®istry, "text", &RenderConfig::new()).unwrap());
1903 }
1904
1905 #[test]
1906 fn render_with_reports_missing_renderer() {
1907 let code = QrCode::new(b"plugin").unwrap();
1908 let registry = PluginRegistry::new();
1909
1910 assert!(matches!(
1911 code.render_with(®istry, "missing", &RenderConfig::new()),
1912 Err(crate::PluginError::RendererNotFound(name)) if name == "missing"
1913 ));
1914 }
1915
1916 #[test]
1917 fn render_with_uses_builtin_plain_text_plugin() {
1918 let code = QrCode::new(b"plugin").unwrap();
1919 let mut registry = PluginRegistry::new();
1920 registry.register_plugin(&crate::render::plugin::PlainTextRendererPlugin);
1921 let config =
1922 RenderConfig::new().with_option("dark", "X").with_option("light", ".").with_option("quiet_zone", "0");
1923
1924 let output = code
1925 .render_with(®istry, crate::render::plugin::PlainTextRendererPlugin::RENDERER_NAME, &config)
1926 .unwrap();
1927 let expected =
1928 code.render::<char>().dark_color('X').light_color('.').quiet_zone(false).module_dimensions(1, 1).build();
1929
1930 assert_eq!(output, RenderOutput::Text(expected));
1931 }
1932
1933 #[test]
1934 fn render_with_uses_builtin_invert_modules_plugin() {
1935 let code = QrCode::new(b"plugin").unwrap();
1936 let mut registry = PluginRegistry::new();
1937 registry.register_plugin(&crate::render::plugin::PlainTextRendererPlugin);
1938 registry.register_plugin(&crate::render::plugin::InvertModulesPlugin);
1939 let config =
1940 RenderConfig::new().with_option("dark", "X").with_option("light", ".").with_option("quiet_zone", "0");
1941
1942 let output = code
1943 .render_with(®istry, crate::render::plugin::PlainTextRendererPlugin::RENDERER_NAME, &config)
1944 .unwrap();
1945 let direct =
1946 code.render::<char>().dark_color('.').light_color('X').quiet_zone(false).module_dimensions(1, 1).build();
1947
1948 assert_eq!(output, RenderOutput::Text(direct));
1949 }
1950
1951 #[test]
1952 fn encode_with_uses_registered_encoder() {
1953 let mut registry = PluginRegistry::new();
1954 registry.register_encoder("length", Box::new(LengthPluginFactory));
1955
1956 let output = QrCode::encode_with(®istry, "length", b"abcd", &EncodeConfig::new()).unwrap();
1957
1958 assert_eq!(output, EncodedOutput::Bytes(b"4".to_vec()));
1959 }
1960
1961 #[test]
1962 fn encode_with_reports_missing_encoder() {
1963 let registry = PluginRegistry::new();
1964
1965 assert!(matches!(
1966 QrCode::encode_with(®istry, "missing", b"abcd", &EncodeConfig::new()),
1967 Err(crate::PluginError::EncoderNotFound(name)) if name == "missing"
1968 ));
1969 }
1970
1971 #[test]
1972 fn builder_version_wins_over_micro() {
1973 let built = QrCode::builder(b"01234567").version(Version::Micro(2)).micro(true).build().unwrap();
1974 assert_eq!(built.version(), Version::Micro(2));
1975 }
1976
1977 #[test]
1978 fn builder_forces_byte_mode() {
1979 let optimal = QrCode::builder(b"01234567").version(Version::Normal(2)).build().unwrap();
1981 let byte = QrCode::builder(b"01234567").version(Version::Normal(2)).encoding_mode(Mode::Byte).build().unwrap();
1982 assert_ne!(colors(&optimal), colors(&byte));
1983 }
1984
1985 #[test]
1986 fn builder_typed_encoding_mode_matches_dynamic_mode() {
1987 let typed = QrCode::builder(b"01234567")
1988 .version(Version::Normal(2))
1989 .encoding_mode_typed::<NumericMode>()
1990 .unwrap()
1991 .build()
1992 .unwrap();
1993 let dynamic =
1994 QrCode::builder(b"01234567").version(Version::Normal(2)).encoding_mode(Mode::Numeric).build().unwrap();
1995
1996 assert_eq!(colors(&typed), colors(&dynamic));
1997 }
1998
1999 #[test]
2000 fn builder_typed_encoding_mode_rejects_invalid_input() {
2001 let err = QrCode::builder(b"12a").encoding_mode_typed::<NumericMode>().unwrap_err();
2002
2003 assert_eq!(err, QrError::InvalidCharacter { position: 2, byte: b'a' });
2004 }
2005
2006 #[test]
2007 fn rows_iterate_full_grid() {
2008 let code = QrCode::new(b"hello").unwrap();
2009 let w = code.width();
2010 let rows: Vec<Vec<Color>> = code.rows().map(|r| r.collect()).collect();
2011 assert_eq!(rows.len(), w);
2012 assert!(rows.iter().all(|r| r.len() == w));
2013 for y in 0..w {
2014 for x in 0..w {
2015 assert_eq!(rows[y][x], code[(x, y)]);
2016 }
2017 }
2018 }
2019
2020 #[test]
2021 fn rows_exact_size() {
2022 let code = QrCode::new(b"hello").unwrap();
2023 let mut rows = code.rows();
2024 let total = rows.len();
2025 let mut counted = 0;
2026 while rows.next().is_some() {
2027 counted += 1;
2028 assert_eq!(rows.len(), total - counted);
2029 }
2030 }
2031
2032 #[test]
2033 fn dark_modules_match_indexed_dark_cells() {
2034 let code = QrCode::new(b"hello").unwrap();
2035 let w = code.width();
2036 let expected: Vec<(usize, usize)> =
2037 (0..w).flat_map(|y| (0..w).map(move |x| (x, y))).filter(|&(x, y)| code[(x, y)] == Color::Dark).collect();
2038 let actual: Vec<(usize, usize)> = code.dark_modules().collect();
2039 assert_eq!(expected, actual);
2041 }
2042
2043 #[test]
2044 fn for_url_uses_high_ec() {
2045 let code = QrCode::for_url(b"https://example.com").unwrap();
2046 assert_eq!(code.error_correction_level(), EcLevel::H);
2047 }
2048
2049 #[test]
2050 fn for_wifi_encodes_with_special_chars() {
2051 let code = QrCode::for_wifi("My;Net", "a,b", "WPA").unwrap();
2052 assert!(code.width() > 0);
2053 }
2054
2055 #[test]
2056 fn for_vcard_encodes() {
2057 let code = QrCode::for_vcard("John Doe", "+1234567890", "john@example.com").unwrap();
2058 assert!(code.width() > 0);
2059 }
2060
2061 #[test]
2062 fn for_gs1_encodes() {
2063 let code = QrCode::for_gs1("010491234512345915970331301234561842").unwrap();
2064 assert!(code.width() > 0);
2065 assert!(!code.version().is_micro());
2067 assert_eq!(code.error_correction_level(), crate::EcLevel::M);
2068 }
2069
2070 #[test]
2071 fn structured_append_encodes_n_symbols() {
2072 let codes = QrCode::structured_append(b"hello structured append world", 3, EcLevel::M).unwrap();
2073 assert_eq!(codes.len(), 3);
2074 assert!(codes.iter().all(|c| !c.version().is_micro()));
2075 }
2076
2077 #[test]
2078 fn structured_append_rejects_invalid_symbol_count() {
2079 assert_eq!(
2080 QrCode::structured_append(b"x", 1, EcLevel::M).err(),
2081 Some(crate::QrError::InvalidStructuredAppend { value: 1 })
2082 );
2083 assert_eq!(
2084 QrCode::structured_append(b"x", 17, EcLevel::M).err(),
2085 Some(crate::QrError::InvalidStructuredAppend { value: 17 })
2086 );
2087 }
2088
2089 #[test]
2090 fn info_reports_metadata() {
2091 let code = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::M).unwrap();
2092 let info = code.info();
2093 assert_eq!(info.version(), Version::Normal(1));
2094 assert_eq!(info.ec_level(), crate::EcLevel::M);
2095 assert_eq!(info.width(), code.width());
2096 assert_eq!(info.module_count(), code.width() * code.width());
2097 assert!(info.data_capacity_bytes() > 0);
2098 let code_h = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::H).unwrap();
2100 assert!(info.data_capacity_bytes() > code_h.info().data_capacity_bytes());
2101 }
2102
2103 #[test]
2104 fn colors_borrows_without_clone() {
2105 let code = QrCode::new(b"hello").unwrap();
2106 let borrowed = code.colors();
2107 assert_eq!(borrowed.len(), code.width() * code.width());
2108 assert_eq!(borrowed, code.to_colors().as_slice());
2110 }
2111
2112 #[test]
2113 fn module_storage_reads_and_writes_grid() {
2114 let mut code = QrCode::new(b"hello").unwrap();
2115 let width = code.width();
2116 let before = CoreModuleStorage::get(&code, 0, 0);
2117 CoreModuleStorage::set(&mut code, 0, 0, !before);
2118 assert_eq!(CoreModuleStorage::width(&code), width);
2119 assert_eq!(CoreModuleStorage::height(&code), width);
2120 assert_eq!(CoreModuleStorage::modules(&code).len(), width * width);
2121 assert_eq!(CoreModuleStorage::get(&code, 0, 0), !before);
2122 }
2123
2124 #[test]
2125 fn module_source_exposes_read_only_grid() {
2126 let code = QrCode::new(b"hello").unwrap();
2127 let width = code.width();
2128 assert_eq!(CoreModuleSource::width(&code), width);
2129 assert_eq!(CoreModuleSource::height(&code), width);
2130 assert_eq!(CoreModuleSource::modules(&code), code.colors());
2131 assert_eq!(CoreModuleSource::get(&code, 0, 0), code[(0, 0)]);
2132 }
2133
2134 #[test]
2135 fn qr_symbol_exposes_metadata() {
2136 let code = QrCode::with_version(b"hello", Version::Normal(1), EcLevel::H).unwrap();
2137
2138 assert_eq!(QrSymbol::version(&code), Version::Normal(1));
2139 assert_eq!(QrSymbol::error_correction_level(&code), EcLevel::H);
2140 assert_eq!(QrSymbol::quiet_zone(&code), 4);
2141 }
2142
2143 #[test]
2144 fn qr_symbol_uses_micro_quiet_zone() {
2145 let code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
2146
2147 assert_eq!(QrSymbol::quiet_zone(&code), 2);
2148 }
2149
2150 #[test]
2151 fn module_view_exposes_borrowed_source() {
2152 let code = QrCode::new(b"hello").unwrap();
2153 let view = code.module_view();
2154
2155 assert_eq!(view.width(), code.width());
2156 assert_eq!(view.height(), code.width());
2157 assert_eq!(view.modules(), code.colors());
2158 assert_eq!(view.get(0, 0), code[(0, 0)]);
2159 }
2160
2161 #[test]
2162 fn qr_code_ref_exposes_borrowed_symbol() {
2163 let code = QrCode::with_version(b"hello", Version::Normal(1), EcLevel::H).unwrap();
2164 let borrowed = code.as_ref();
2165
2166 assert_eq!(borrowed.width(), code.width());
2167 assert_eq!(borrowed.height(), code.width());
2168 assert_eq!(borrowed.modules(), code.colors());
2169 assert_eq!(borrowed.get(0, 0), code[(0, 0)]);
2170 assert_eq!(borrowed.version(), code.version());
2171 assert_eq!(borrowed.error_correction_level(), code.error_correction_level());
2172 assert_eq!(borrowed.quiet_zone(), 4);
2173 }
2174
2175 #[test]
2176 fn render_error_is_available_from_facade() {
2177 let err = crate::render::RenderError::InvalidModuleSource { width: 3, height: 2, len: 4 };
2178
2179 assert_eq!(err.to_string(), "invalid module source dimensions: width=3, height=2, len=4");
2180 }
2181
2182 #[test]
2183 fn renderer_trait_path_matches_builder_output() {
2184 let code = QrCode::new(b"hello").unwrap();
2185 let builder_output = code.render::<char>().build();
2186 let renderer = code.render::<char>();
2187 let trait_output = CoreRenderer::render(&renderer, &code).unwrap();
2188 assert_eq!(trait_output, builder_output);
2189 }
2190
2191 #[test]
2192 fn render_builder_matches_render_output() {
2193 let code = QrCode::new(b"render builder").unwrap();
2194
2195 assert_eq!(code.render_builder::<char>().build(), code.render::<char>().build());
2196 }
2197
2198 #[cfg(feature = "async")]
2199 #[test]
2200 fn render_async_matches_render_output() {
2201 let code = QrCode::new(b"render async").unwrap();
2202 let expected = code.render::<char>().build();
2203 let runtime = tokio::runtime::Builder::new_current_thread().build().unwrap();
2204 let rendered = runtime.block_on(code.render_async::<char>()).unwrap();
2205
2206 assert_eq!(rendered, expected);
2207 }
2208
2209 #[test]
2210 fn renderer_from_symbol_matches_qrcode_render() {
2211 let code = QrCode::new(b"hello").unwrap();
2212
2213 let from_symbol = crate::render::Renderer::<char>::from_symbol(&code)
2214 .quiet_zone(false)
2215 .dark_color('X')
2216 .light_color('.')
2217 .build();
2218 let from_qrcode = code.render::<char>().quiet_zone(false).dark_color('X').light_color('.').build();
2219 assert_eq!(from_symbol, from_qrcode);
2220 }
2221
2222 #[test]
2223 fn renderer_from_borrowed_symbol_matches_owned_symbol() {
2224 let code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
2225 let borrowed = code.as_ref();
2226
2227 let from_borrowed =
2228 crate::render::Renderer::<char>::from_symbol(&borrowed).dark_color('X').light_color('.').build();
2229 let from_owned = crate::render::Renderer::<char>::from_symbol(&code).dark_color('X').light_color('.').build();
2230
2231 assert_eq!(borrowed.quiet_zone(), 2);
2232 assert_eq!(from_borrowed, from_owned);
2233 }
2234
2235 #[test]
2236 fn renderer_trait_accepts_read_only_module_source() {
2237 let code = QrCode::new(b"hello").unwrap();
2238 let mut inverted = code.to_colors();
2239 for color in &mut inverted {
2240 *color = !*color;
2241 }
2242 let view = ModuleView::new(&inverted, code.width()).unwrap();
2243 let mut renderer = code.render::<char>();
2244 renderer.quiet_zone(false).dark_color('X').light_color('.');
2245
2246 let trait_output = CoreRenderer::render(&renderer, &view).unwrap();
2247 let expected = crate::render::Renderer::<char>::from_source(&view, 4)
2248 .quiet_zone(false)
2249 .dark_color('X')
2250 .light_color('.')
2251 .build();
2252 let original = code.render::<char>().quiet_zone(false).dark_color('X').light_color('.').build();
2253 assert_eq!(trait_output, expected);
2254 assert_ne!(trait_output, original);
2255 }
2256
2257 #[test]
2258 fn batch_encodes_many_and_short_circuits() {
2259 let codes = QrCode::batch(vec![b"hi"; 1000], crate::EcLevel::M).unwrap();
2260 assert_eq!(codes.len(), 1000);
2261 let huge: Vec<u8> = (0..5000).map(|i| (i % 256) as u8).collect();
2263 let mixed: Vec<&[u8]> = vec![&b"ok"[..], &huge[..], &b"also ok"[..]];
2264 assert!(QrCode::batch(mixed, crate::EcLevel::L).is_err());
2265 }
2266
2267 #[cfg(feature = "eps")]
2268 #[test]
2269 fn template_applies_colors() {
2270 let code = QrCode::new(b"template").unwrap();
2271 let minimal = code.render::<crate::render::eps::Color>().template(&crate::QrTemplate::minimal()).build();
2272 let dark = code.render::<crate::render::eps::Color>().template(&crate::QrTemplate::dark_mode()).build();
2273 assert!(minimal.contains("0 0 0 setrgbcolor"), "minimal should use a black foreground");
2275 assert!(!dark.contains("0 0 0 setrgbcolor"), "dark_mode should change the foreground");
2276 assert_ne!(minimal, dark);
2277 }
2278
2279 #[test]
2280 fn analyze_reports_diagnostics() {
2281 let code = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::M).unwrap();
2282 let a = code.analyze();
2283 let total = code.width() * code.width();
2284 assert!(a.functional_modules() > 0, "should have functional modules");
2285 assert!(a.data_modules() > 0, "should have data modules");
2286 assert_eq!(a.functional_modules() + a.data_modules(), total);
2287 assert!(a.dark_ratio() > 0.0 && a.dark_ratio() < 1.0);
2288 let dark = code.colors().iter().filter(|c| **c == Color::Dark).count();
2289 assert!((a.dark_ratio() - dark as f64 / total as f64).abs() < 1e-9);
2290 }
2291
2292 #[test]
2293 fn force_mode_without_version_auto_selects() {
2294 let auto = QrCode::new(b"0123456789").unwrap();
2296 let forced_byte = QrCode::builder(b"0123456789").force_mode(Mode::Byte).build().unwrap();
2297 assert_ne!(colors(&auto), colors(&forced_byte));
2298 let forced_num = QrCode::builder(b"0123456789").force_mode(Mode::Numeric).build().unwrap();
2300 assert_eq!(colors(&auto), colors(&forced_num));
2301 let err = QrCode::builder(b"\x93").force_mode(Mode::Kanji).build();
2303 assert!(matches!(err, Err(crate::QrError::InvalidCharacter { .. })));
2304 }
2305}
2306
2307#[cfg(all(test, feature = "image"))]
2308mod image_tests {
2309 use crate::{EcLevel, QrCode, Version};
2310 use image::{Luma, Rgb, load_from_memory};
2311
2312 #[test]
2313 fn test_annex_i_qr_as_image() {
2314 let code = QrCode::new(b"01234567").unwrap();
2315 let image = code.render::<Luma<u8>>().build();
2316 let expected =
2317 load_from_memory(include_bytes!("../docs/images/test_annex_i_qr_as_image.png")).unwrap().to_luma8();
2318 assert_eq!(image.dimensions(), expected.dimensions());
2319 assert_eq!(image.into_raw(), expected.into_raw());
2320 }
2321
2322 #[test]
2323 fn test_annex_i_micro_qr_as_image() {
2324 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
2325 let image = code
2326 .render()
2327 .min_dimensions(200, 200)
2328 .dark_color(Rgb([128, 0, 0]))
2329 .light_color(Rgb([255, 255, 128]))
2330 .build();
2331 let expected =
2332 load_from_memory(include_bytes!("../docs/images/test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb8();
2333 assert_eq!(image.dimensions(), expected.dimensions());
2334 assert_eq!(image.into_raw(), expected.into_raw());
2335 }
2336}
2337
2338#[cfg(all(test, feature = "svg"))]
2339mod svg_tests {
2340 use crate::render::svg::Color as SvgColor;
2341 use crate::{EcLevel, QrCode, Version};
2342
2343 #[test]
2344 fn test_annex_i_qr_as_svg() {
2345 let code = QrCode::new(b"01234567").unwrap();
2346 let image = code.render::<SvgColor>().build();
2347 let expected = include_str!("../docs/images/test_annex_i_qr_as_svg.svg");
2348 assert_eq!(&image, expected);
2349 }
2350
2351 #[test]
2352 fn test_annex_i_micro_qr_as_svg() {
2353 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
2354 let image = code
2355 .render()
2356 .min_dimensions(200, 200)
2357 .dark_color(SvgColor("#800000"))
2358 .light_color(SvgColor("#ffff80"))
2359 .build();
2360 let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_svg.svg");
2361 assert_eq!(&image, expected);
2362 }
2363}
2364
2365#[cfg(all(test, feature = "eps"))]
2366mod eps_tests {
2367 use crate::render::eps::Color as EpsColor;
2368 use crate::{EcLevel, QrCode, Version};
2369
2370 #[test]
2371 fn test_annex_i_qr_as_eps() {
2372 let code = QrCode::new(b"01234567").unwrap();
2373 let image = code.render::<EpsColor>().build();
2374 let expected = include_str!("../docs/images/test_annex_i_qr_as_eps.eps");
2375 assert_eq!(&image, expected);
2376 }
2377
2378 #[test]
2379 fn test_annex_i_micro_qr_as_eps() {
2380 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
2381 let image = code
2382 .render()
2383 .min_dimensions(200, 200)
2384 .dark_color(EpsColor([0.5, 0.0, 0.0]))
2385 .light_color(EpsColor([1.0, 1.0, 0.5]))
2386 .build();
2387 let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_eps.eps");
2388 assert_eq!(&image, expected);
2389 }
2390}
2391
2392#[cfg(all(test, feature = "pic"))]
2393mod pic_tests {
2394 use crate::render::pic::Color as PicColor;
2395 use crate::{EcLevel, QrCode, Version};
2396
2397 #[test]
2398 fn test_annex_i_qr_as_pic() {
2399 let code = QrCode::new(b"01234567").unwrap();
2400 let image = code.render::<PicColor>().build();
2401 let expected = include_str!("../docs/images/test_annex_i_qr_as_pic.pic");
2402 assert_eq!(&image, expected);
2403 }
2404
2405 #[test]
2406 fn test_annex_i_micro_qr_as_pic() {
2407 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
2408 let image = code.render::<PicColor>().min_dimensions(1, 1).build();
2409 let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_pic.pic");
2410 assert_eq!(&image, expected);
2411 }
2412}