1#![cfg_attr(feature = "image", doc = "```rust")]
6#![cfg_attr(not(feature = "image"), doc = "```ignore")]
7#![cfg_attr(docsrs, feature(doc_cfg))]
30#![deny(clippy::uninlined_format_args, clippy::manual_range_contains, clippy::semicolon_if_nothing_returned)]
31#![allow(
32 clippy::must_use_candidate, )]
34
35pub mod bits;
36pub mod canvas;
37mod cast;
38pub mod ec;
39pub mod optimize;
40pub mod render;
41pub mod types;
42pub use crate::types::{Color, EcLevel, Mode, QrError, QrResult, Version};
43
44use crate::cast::As;
45use crate::render::{Pixel, Renderer};
46use std::iter::FusedIterator;
47use std::ops::Index;
48
49#[derive(Clone)]
51pub struct QrCode {
52 content: Vec<Color>,
53 version: Version,
54 ec_level: EcLevel,
55 width: usize,
56}
57
58impl QrCode {
59 pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
73 Self::with_error_correction_level(data, EcLevel::M)
74 }
75
76 pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
90 let bits = bits::encode_auto(data.as_ref(), ec_level)?;
91 Self::with_bits(bits, ec_level)
92 }
93
94 pub fn new_micro<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
109 Self::micro_with_error_correction_level(data, EcLevel::M)
110 }
111
112 pub fn micro_with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
127 let bits = bits::encode_auto_micro(data.as_ref(), ec_level)?;
128 Self::with_bits(bits, ec_level)
129 }
130
131 pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
150 let mut bits = bits::Bits::new(version);
151 bits.push_optimal_data(data.as_ref())?;
152 bits.push_terminator(ec_level)?;
153 Self::with_bits(bits, ec_level)
154 }
155
156 pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
184 let version = bits.version();
185 let data = bits.into_bytes();
186 let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?;
187 let mut canvas = canvas::Canvas::new(version, ec_level);
188 canvas.draw_all_functional_patterns();
189 canvas.draw_data(&encoded_data, &ec_data);
190 let canvas = canvas.apply_best_mask();
191 Ok(Self { content: canvas.into_colors(), version, ec_level, width: version.width().as_usize() })
192 }
193
194 pub const fn version(&self) -> Version {
196 self.version
197 }
198
199 pub const fn error_correction_level(&self) -> EcLevel {
201 self.ec_level
202 }
203
204 pub const fn width(&self) -> usize {
208 self.width
209 }
210
211 pub fn max_allowed_errors(&self) -> usize {
215 ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
216 }
217
218 #[must_use]
233 pub fn info(&self) -> Info {
234 Info {
235 version: self.version,
236 ec_level: self.ec_level,
237 width: self.width,
238 module_count: self.width * self.width,
239 max_allowed_errors: self.max_allowed_errors(),
240 data_capacity_bytes: bits::data_capacity_bits(self.version, self.ec_level).map(|b| b / 8).unwrap_or(0),
241 }
242 }
243
244 pub fn is_functional(&self, x: usize, y: usize) -> bool {
247 let x = x.try_into().expect("coordinate is too large for QR code");
248 let y = y.try_into().expect("coordinate is too large for QR code");
249 canvas::is_functional(self.version, self.version.width(), x, y)
250 }
251
252 pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
255 self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build()
256 }
257
258 pub fn to_colors(&self) -> Vec<Color> {
260 self.content.clone()
261 }
262
263 pub fn into_colors(self) -> Vec<Color> {
265 self.content
266 }
267
268 #[cfg_attr(feature = "image", doc = " ```rust")]
276 #[cfg_attr(not(feature = "image"), doc = " ```ignore")]
277 pub fn render<P: Pixel>(&self) -> Renderer<'_, P> {
290 let quiet_zone = if self.version.is_micro() { 2 } else { 4 };
291 Renderer::new(&self.content, self.width, quiet_zone)
292 }
293}
294
295impl QrCode {
296 pub fn builder<D: AsRef<[u8]>>(data: D) -> QrCodeBuilder<D> {
314 QrCodeBuilder::new(data)
315 }
316
317 pub fn rows(&self) -> Rows<'_> {
335 Rows { code: self, y: 0 }
336 }
337
338 pub fn dark_modules(&self) -> DarkModules<'_> {
351 DarkModules { code: self, idx: 0 }
352 }
353
354 pub fn for_url<D: AsRef<[u8]>>(url: D) -> QrResult<Self> {
360 Self::with_error_correction_level(url, EcLevel::H)
361 }
362
363 pub fn for_text<D: AsRef<[u8]>>(text: D) -> QrResult<Self> {
369 Self::new(text)
370 }
371
372 pub fn for_wifi(ssid: &str, password: &str, auth: &str) -> QrResult<Self> {
390 let mut payload = String::from("WIFI:T:");
391 payload.push_str(auth);
392 payload.push_str(";S:");
393 push_escaped_wifi(&mut payload, ssid);
394 payload.push_str(";P:");
395 push_escaped_wifi(&mut payload, password);
396 payload.push_str(";;");
397 Self::new(payload)
398 }
399
400 pub fn for_vcard(name: &str, phone: &str, email: &str) -> QrResult<Self> {
415 let vcard = format!("BEGIN:VCARD\r\nVERSION:3.0\r\nFN:{name}\r\nTEL:{phone}\r\nEMAIL:{email}\r\nEND:VCARD\r\n");
416 Self::new(vcard)
417 }
418
419 pub fn for_gs1<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
437 let data = data.as_ref();
438 for v in 1..=40 {
439 let version = Version::Normal(v);
440 let mut bits = bits::Bits::new(version);
441 if bits.push_fnc1_first_position().is_err()
442 || bits.push_optimal_data(data).is_err()
443 || bits.push_terminator(EcLevel::M).is_err()
444 {
445 continue;
446 }
447 return Self::with_bits(bits, EcLevel::M);
448 }
449 Err(QrError::DataTooLong)
450 }
451
452 #[must_use]
470 pub fn alt_text<D: AsRef<[u8]>>(data: D) -> String {
471 let text = String::from_utf8_lossy(data.as_ref());
472 if text.starts_with("http://") || text.starts_with("https://") {
473 format!("QR code linking to {text}")
474 } else {
475 format!("QR code containing: {text}")
476 }
477 }
478
479 #[must_use]
492 pub fn alt_text_custom<D: AsRef<[u8]>, F: FnOnce(&[u8]) -> String>(data: D, f: F) -> String {
493 f(data.as_ref())
494 }
495
496 fn with_mode<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel, mode: Mode) -> QrResult<Self> {
500 let mut bits = bits::Bits::new(version);
501 match mode {
502 Mode::Numeric => bits.push_numeric_data(data.as_ref())?,
503 Mode::Alphanumeric => bits.push_alphanumeric_data(data.as_ref())?,
504 Mode::Byte => bits.push_byte_data(data.as_ref())?,
505 Mode::Kanji => bits.push_kanji_data(data.as_ref())?,
506 }
507 bits.push_terminator(ec_level)?;
508 Self::with_bits(bits, ec_level)
509 }
510
511 fn with_mode_auto<D: AsRef<[u8]>>(data: D, ec_level: EcLevel, mode: Mode) -> QrResult<Self> {
517 let data = data.as_ref();
518 let mut last_err = QrError::DataTooLong;
519 for v in 1..=40 {
520 let version = Version::Normal(v);
521 let mut bits = bits::Bits::new(version);
522 let pushed = match mode {
523 Mode::Numeric => bits.push_numeric_data(data),
524 Mode::Alphanumeric => bits.push_alphanumeric_data(data),
525 Mode::Byte => bits.push_byte_data(data),
526 Mode::Kanji => bits.push_kanji_data(data),
527 };
528 if let Err(e) = pushed {
529 last_err = e;
530 continue;
531 }
532 if let Err(e) = bits.push_terminator(ec_level) {
533 last_err = e;
534 continue;
535 }
536 return Self::with_bits(bits, ec_level);
537 }
538 Err(last_err)
539 }
540}
541
542fn push_escaped_wifi(out: &mut String, s: &str) {
544 for c in s.chars() {
545 if matches!(c, ';' | ',' | '"' | '\\' | ':') {
546 out.push('\\');
547 }
548 out.push(c);
549 }
550}
551
552impl Index<(usize, usize)> for QrCode {
553 type Output = Color;
554
555 fn index(&self, (x, y): (usize, usize)) -> &Color {
556 let index = y * self.width + x;
557 &self.content[index]
558 }
559}
560
561#[derive(Clone, Debug)]
569pub struct QrCodeBuilder<D: AsRef<[u8]>> {
570 data: D,
571 ec_level: EcLevel,
572 version: Option<Version>,
573 micro: bool,
574 mode_hint: Option<Mode>,
575}
576
577impl<D: AsRef<[u8]>> QrCodeBuilder<D> {
578 fn new(data: D) -> Self {
579 Self { data, ec_level: EcLevel::M, version: None, micro: false, mode_hint: None }
580 }
581
582 #[must_use]
584 pub fn ec_level(mut self, ec_level: EcLevel) -> Self {
585 self.ec_level = ec_level;
586 self
587 }
588
589 #[must_use]
593 pub fn version(mut self, version: Version) -> Self {
594 self.version = Some(version);
595 self
596 }
597
598 #[must_use]
602 pub fn micro(mut self, yes: bool) -> Self {
603 self.micro = yes;
604 self
605 }
606
607 #[must_use]
617 pub fn encoding_mode(mut self, mode: Mode) -> Self {
618 self.mode_hint = Some(mode);
619 self
620 }
621
622 #[must_use]
626 pub fn force_mode(self, mode: Mode) -> Self {
627 self.encoding_mode(mode)
628 }
629
630 pub fn build(self) -> QrResult<QrCode> {
638 if let Some(version) = self.version {
639 if let Some(mode) = self.mode_hint {
640 return QrCode::with_mode(self.data, version, self.ec_level, mode);
641 }
642 return QrCode::with_version(self.data, version, self.ec_level);
643 }
644 if let Some(mode) = self.mode_hint {
645 return QrCode::with_mode_auto(self.data, self.ec_level, mode);
646 }
647 if self.micro {
648 return QrCode::micro_with_error_correction_level(self.data, self.ec_level);
649 }
650 QrCode::with_error_correction_level(self.data, self.ec_level)
651 }
652}
653
654#[derive(Clone, Copy, Debug, PartialEq, Eq)]
664pub struct Info {
665 version: Version,
666 ec_level: EcLevel,
667 width: usize,
668 module_count: usize,
669 max_allowed_errors: usize,
670 data_capacity_bytes: usize,
671}
672
673impl Info {
674 #[must_use]
676 pub const fn version(&self) -> Version {
677 self.version
678 }
679
680 #[must_use]
682 pub const fn ec_level(&self) -> EcLevel {
683 self.ec_level
684 }
685
686 #[must_use]
688 pub const fn width(&self) -> usize {
689 self.width
690 }
691
692 #[must_use]
694 pub const fn module_count(&self) -> usize {
695 self.module_count
696 }
697
698 #[must_use]
700 pub const fn max_allowed_errors(&self) -> usize {
701 self.max_allowed_errors
702 }
703
704 #[must_use]
706 pub const fn data_capacity_bytes(&self) -> usize {
707 self.data_capacity_bytes
708 }
709}
710
711pub struct Rows<'a> {
717 code: &'a QrCode,
718 y: usize,
719}
720
721impl<'a> Iterator for Rows<'a> {
722 type Item = Row<'a>;
723
724 fn next(&mut self) -> Option<Self::Item> {
725 let w = self.code.width;
726 if self.y < w {
727 let row = Row { code: self.code, y: self.y, x: 0 };
728 self.y += 1;
729 Some(row)
730 } else {
731 None
732 }
733 }
734
735 fn size_hint(&self) -> (usize, Option<usize>) {
736 let rem = self.code.width - self.y;
737 (rem, Some(rem))
738 }
739}
740
741impl<'a> ExactSizeIterator for Rows<'a> {
742 fn len(&self) -> usize {
743 self.code.width - self.y
744 }
745}
746
747impl<'a> FusedIterator for Rows<'a> {}
748
749pub struct Row<'a> {
752 code: &'a QrCode,
753 y: usize,
754 x: usize,
755}
756
757impl<'a> Row<'a> {
758 #[must_use]
760 pub fn len(&self) -> usize {
761 self.code.width
762 }
763
764 #[must_use]
766 pub fn is_empty(&self) -> bool {
767 self.code.width == 0
768 }
769}
770
771impl<'a> Iterator for Row<'a> {
772 type Item = Color;
773
774 fn next(&mut self) -> Option<Color> {
775 let w = self.code.width;
776 if self.x < w {
777 let color = self.code.content[self.y * w + self.x];
778 self.x += 1;
779 Some(color)
780 } else {
781 None
782 }
783 }
784
785 fn size_hint(&self) -> (usize, Option<usize>) {
786 let rem = self.code.width - self.x;
787 (rem, Some(rem))
788 }
789}
790
791impl<'a> ExactSizeIterator for Row<'a> {
792 fn len(&self) -> usize {
793 self.code.width - self.x
794 }
795}
796
797impl<'a> FusedIterator for Row<'a> {}
798
799pub struct DarkModules<'a> {
802 code: &'a QrCode,
803 idx: usize,
804}
805
806impl<'a> Iterator for DarkModules<'a> {
807 type Item = (usize, usize);
808
809 fn next(&mut self) -> Option<(usize, usize)> {
810 let w = self.code.width;
811 let content = &self.code.content;
812 while self.idx < content.len() {
813 let i = self.idx;
814 self.idx += 1;
815 if content[i] == Color::Dark {
816 return Some((i % w, i / w));
817 }
818 }
819 None
820 }
821}
822
823impl<'a> FusedIterator for DarkModules<'a> {}
824
825#[cfg(test)]
828mod tests {
829 use crate::{EcLevel, QrCode, Version};
830
831 #[test]
832 fn test_annex_i_qr() {
833 let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
835 assert_eq!(
836 &*code.to_debug_str('#', '.'),
837 "\
838 #######..#.##.#######\n\
839 #.....#..####.#.....#\n\
840 #.###.#.#.....#.###.#\n\
841 #.###.#.##....#.###.#\n\
842 #.###.#.#.###.#.###.#\n\
843 #.....#.#...#.#.....#\n\
844 #######.#.#.#.#######\n\
845 ........#..##........\n\
846 #.#####..#..#.#####..\n\
847 ...#.#.##.#.#..#.##..\n\
848 ..#...##.#.#.#..#####\n\
849 ....#....#.....####..\n\
850 ...######..#.#..#....\n\
851 ........#.#####..##..\n\
852 #######..##.#.##.....\n\
853 #.....#.#.#####...#.#\n\
854 #.###.#.#...#..#.##..\n\
855 #.###.#.##..#..#.....\n\
856 #.###.#.#.##.#..#.#..\n\
857 #.....#........##.##.\n\
858 #######.####.#..#.#.."
859 );
860 }
861
862 #[test]
863 fn test_annex_i_micro_qr() {
864 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
865 assert_eq!(
866 &*code.to_debug_str('#', '.'),
867 "\
868 #######.#.#.#\n\
869 #.....#.###.#\n\
870 #.###.#..##.#\n\
871 #.###.#..####\n\
872 #.###.#.###..\n\
873 #.....#.#...#\n\
874 #######..####\n\
875 .........##..\n\
876 ##.#....#...#\n\
877 .##.#.#.#.#.#\n\
878 ###..#######.\n\
879 ...#.#....##.\n\
880 ###.#..##.###"
881 );
882 }
883}
884
885#[cfg(test)]
886mod api_tests {
887 use crate::{Color, EcLevel, Mode, QrCode, Version};
888
889 fn colors(code: &QrCode) -> Vec<Color> {
890 code.to_colors()
891 }
892
893 #[test]
894 fn builder_matches_with_error_correction_level() {
895 let direct = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
896 let built = QrCode::builder(b"Some data").ec_level(EcLevel::H).build().unwrap();
897 assert_eq!(colors(&direct), colors(&built));
898 assert_eq!(direct.version(), built.version());
899 assert_eq!(direct.error_correction_level(), built.error_correction_level());
900 }
901
902 #[test]
903 fn builder_matches_with_version() {
904 let direct = QrCode::with_version(b"Some data", Version::Normal(1), EcLevel::M).unwrap();
905 let built = QrCode::builder(b"Some data").version(Version::Normal(1)).build().unwrap();
906 assert_eq!(colors(&direct), colors(&built));
907 }
908
909 #[test]
910 fn builder_micro_matches() {
911 let direct = QrCode::micro_with_error_correction_level(b"123", EcLevel::L).unwrap();
912 let built = QrCode::builder(b"123").ec_level(EcLevel::L).micro(true).build().unwrap();
913 assert_eq!(colors(&direct), colors(&built));
914 assert!(built.version().is_micro());
915 }
916
917 #[test]
918 fn builder_version_wins_over_micro() {
919 let built = QrCode::builder(b"01234567").version(Version::Micro(2)).micro(true).build().unwrap();
920 assert_eq!(built.version(), Version::Micro(2));
921 }
922
923 #[test]
924 fn builder_forces_byte_mode() {
925 let optimal = QrCode::builder(b"01234567").version(Version::Normal(2)).build().unwrap();
927 let byte = QrCode::builder(b"01234567").version(Version::Normal(2)).encoding_mode(Mode::Byte).build().unwrap();
928 assert_ne!(colors(&optimal), colors(&byte));
929 }
930
931 #[test]
932 fn rows_iterate_full_grid() {
933 let code = QrCode::new(b"hello").unwrap();
934 let w = code.width();
935 let rows: Vec<Vec<Color>> = code.rows().map(|r| r.collect()).collect();
936 assert_eq!(rows.len(), w);
937 assert!(rows.iter().all(|r| r.len() == w));
938 for y in 0..w {
939 for x in 0..w {
940 assert_eq!(rows[y][x], code[(x, y)]);
941 }
942 }
943 }
944
945 #[test]
946 fn rows_exact_size() {
947 let code = QrCode::new(b"hello").unwrap();
948 let mut rows = code.rows();
949 let total = rows.len();
950 let mut counted = 0;
951 while rows.next().is_some() {
952 counted += 1;
953 assert_eq!(rows.len(), total - counted);
954 }
955 }
956
957 #[test]
958 fn dark_modules_match_indexed_dark_cells() {
959 let code = QrCode::new(b"hello").unwrap();
960 let w = code.width();
961 let expected: Vec<(usize, usize)> =
962 (0..w).flat_map(|y| (0..w).map(move |x| (x, y))).filter(|&(x, y)| code[(x, y)] == Color::Dark).collect();
963 let actual: Vec<(usize, usize)> = code.dark_modules().collect();
964 assert_eq!(expected, actual);
966 }
967
968 #[test]
969 fn for_url_uses_high_ec() {
970 let code = QrCode::for_url(b"https://example.com").unwrap();
971 assert_eq!(code.error_correction_level(), EcLevel::H);
972 }
973
974 #[test]
975 fn wifi_escape_helper() {
976 let mut out = String::new();
977 super::push_escaped_wifi(&mut out, "a;b,c\"d\\e:f");
978 assert_eq!(out, "a\\;b\\,c\\\"d\\\\e\\:f");
979 }
980
981 #[test]
982 fn for_wifi_encodes_with_special_chars() {
983 let code = QrCode::for_wifi("My;Net", "a,b", "WPA").unwrap();
984 assert!(code.width() > 0);
985 }
986
987 #[test]
988 fn for_vcard_encodes() {
989 let code = QrCode::for_vcard("John Doe", "+1234567890", "john@example.com").unwrap();
990 assert!(code.width() > 0);
991 }
992
993 #[test]
994 fn for_gs1_encodes() {
995 let code = QrCode::for_gs1("010491234512345915970331301234561842").unwrap();
996 assert!(code.width() > 0);
997 assert!(!code.version().is_micro());
999 assert_eq!(code.error_correction_level(), crate::EcLevel::M);
1000 }
1001
1002 #[test]
1003 fn info_reports_metadata() {
1004 let code = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::M).unwrap();
1005 let info = code.info();
1006 assert_eq!(info.version(), Version::Normal(1));
1007 assert_eq!(info.ec_level(), crate::EcLevel::M);
1008 assert_eq!(info.width(), code.width());
1009 assert_eq!(info.module_count(), code.width() * code.width());
1010 assert!(info.data_capacity_bytes() > 0);
1011 let code_h = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::H).unwrap();
1013 assert!(info.data_capacity_bytes() > code_h.info().data_capacity_bytes());
1014 }
1015
1016 #[test]
1017 fn force_mode_without_version_auto_selects() {
1018 let auto = QrCode::new(b"0123456789").unwrap();
1020 let forced_byte = QrCode::builder(b"0123456789").force_mode(Mode::Byte).build().unwrap();
1021 assert_ne!(colors(&auto), colors(&forced_byte));
1022 let forced_num = QrCode::builder(b"0123456789").force_mode(Mode::Numeric).build().unwrap();
1024 assert_eq!(colors(&auto), colors(&forced_num));
1025 let err = QrCode::builder(b"\x93").force_mode(Mode::Kanji).build();
1027 assert!(matches!(err, Err(crate::QrError::InvalidCharacter { .. })));
1028 }
1029}
1030
1031#[cfg(all(test, feature = "image"))]
1032mod image_tests {
1033 use crate::{EcLevel, QrCode, Version};
1034 use image::{Luma, Rgb, load_from_memory};
1035
1036 #[test]
1037 fn test_annex_i_qr_as_image() {
1038 let code = QrCode::new(b"01234567").unwrap();
1039 let image = code.render::<Luma<u8>>().build();
1040 let expected =
1041 load_from_memory(include_bytes!("../docs/images/test_annex_i_qr_as_image.png")).unwrap().to_luma8();
1042 assert_eq!(image.dimensions(), expected.dimensions());
1043 assert_eq!(image.into_raw(), expected.into_raw());
1044 }
1045
1046 #[test]
1047 fn test_annex_i_micro_qr_as_image() {
1048 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1049 let image = code
1050 .render()
1051 .min_dimensions(200, 200)
1052 .dark_color(Rgb([128, 0, 0]))
1053 .light_color(Rgb([255, 255, 128]))
1054 .build();
1055 let expected =
1056 load_from_memory(include_bytes!("../docs/images/test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb8();
1057 assert_eq!(image.dimensions(), expected.dimensions());
1058 assert_eq!(image.into_raw(), expected.into_raw());
1059 }
1060}
1061
1062#[cfg(all(test, feature = "svg"))]
1063mod svg_tests {
1064 use crate::render::svg::Color as SvgColor;
1065 use crate::{EcLevel, QrCode, Version};
1066
1067 #[test]
1068 fn test_annex_i_qr_as_svg() {
1069 let code = QrCode::new(b"01234567").unwrap();
1070 let image = code.render::<SvgColor>().build();
1071 let expected = include_str!("../docs/images/test_annex_i_qr_as_svg.svg");
1072 assert_eq!(&image, expected);
1073 }
1074
1075 #[test]
1076 fn test_annex_i_micro_qr_as_svg() {
1077 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1078 let image = code
1079 .render()
1080 .min_dimensions(200, 200)
1081 .dark_color(SvgColor("#800000"))
1082 .light_color(SvgColor("#ffff80"))
1083 .build();
1084 let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_svg.svg");
1085 assert_eq!(&image, expected);
1086 }
1087}
1088
1089#[cfg(all(test, feature = "eps"))]
1090mod eps_tests {
1091 use crate::render::eps::Color as EpsColor;
1092 use crate::{EcLevel, QrCode, Version};
1093
1094 #[test]
1095 fn test_annex_i_qr_as_eps() {
1096 let code = QrCode::new(b"01234567").unwrap();
1097 let image = code.render::<EpsColor>().build();
1098 let expected = include_str!("../docs/images/test_annex_i_qr_as_eps.eps");
1099 assert_eq!(&image, expected);
1100 }
1101
1102 #[test]
1103 fn test_annex_i_micro_qr_as_eps() {
1104 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1105 let image = code
1106 .render()
1107 .min_dimensions(200, 200)
1108 .dark_color(EpsColor([0.5, 0.0, 0.0]))
1109 .light_color(EpsColor([1.0, 1.0, 0.5]))
1110 .build();
1111 let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_eps.eps");
1112 assert_eq!(&image, expected);
1113 }
1114}
1115
1116#[cfg(all(test, feature = "pic"))]
1117mod pic_tests {
1118 use crate::render::pic::Color as PicColor;
1119 use crate::{EcLevel, QrCode, Version};
1120
1121 #[test]
1122 fn test_annex_i_qr_as_pic() {
1123 let code = QrCode::new(b"01234567").unwrap();
1124 let image = code.render::<PicColor>().build();
1125 let expected = include_str!("../docs/images/test_annex_i_qr_as_pic.pic");
1126 assert_eq!(&image, expected);
1127 }
1128
1129 #[test]
1130 fn test_annex_i_micro_qr_as_pic() {
1131 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1132 let image = code.render::<PicColor>().min_dimensions(1, 1).build();
1133 let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_pic.pic");
1134 assert_eq!(&image, expected);
1135 }
1136}