1#![cfg_attr(feature = "image", doc = "```rust")]
6#![cfg_attr(not(feature = "image"), doc = "```ignore")]
7#![cfg_attr(feature = "bench", feature(test))] #![deny(clippy::uninlined_format_args, clippy::manual_range_contains, clippy::semicolon_if_nothing_returned)]
31#![allow(
32 clippy::must_use_candidate, )]
34#![cfg_attr(feature = "bench", doc = include_str!("../README.md"))]
35pub mod bits;
38pub mod canvas;
39mod cast;
40pub mod ec;
41pub mod optimize;
42pub mod render;
43pub mod types;
44pub use crate::types::{Color, EcLevel, QrResult, Version};
45
46use crate::cast::As;
47use crate::render::{Pixel, Renderer};
48use std::ops::Index;
49
50#[derive(Clone)]
52pub struct QrCode {
53 content: Vec<Color>,
54 version: Version,
55 ec_level: EcLevel,
56 width: usize,
57}
58
59impl QrCode {
60 pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
74 Self::with_error_correction_level(data, EcLevel::M)
75 }
76
77 pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
91 let bits = bits::encode_auto(data.as_ref(), ec_level)?;
92 Self::with_bits(bits, ec_level)
93 }
94
95 pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
114 let mut bits = bits::Bits::new(version);
115 bits.push_optimal_data(data.as_ref())?;
116 bits.push_terminator(ec_level)?;
117 Self::with_bits(bits, ec_level)
118 }
119
120 pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
148 let version = bits.version();
149 let data = bits.into_bytes();
150 let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?;
151 let mut canvas = canvas::Canvas::new(version, ec_level);
152 canvas.draw_all_functional_patterns();
153 canvas.draw_data(&encoded_data, &ec_data);
154 let canvas = canvas.apply_best_mask();
155 Ok(Self { content: canvas.into_colors(), version, ec_level, width: version.width().as_usize() })
156 }
157
158 pub fn version(&self) -> Version {
160 self.version
161 }
162
163 pub fn error_correction_level(&self) -> EcLevel {
165 self.ec_level
166 }
167
168 pub fn width(&self) -> usize {
172 self.width
173 }
174
175 pub fn max_allowed_errors(&self) -> usize {
179 ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
180 }
181
182 pub fn is_functional(&self, x: usize, y: usize) -> bool {
185 let x = x.try_into().expect("coordinate is too large for QR code");
186 let y = y.try_into().expect("coordinate is too large for QR code");
187 canvas::is_functional(self.version, self.version.width(), x, y)
188 }
189
190 pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
193 self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build()
194 }
195
196 #[deprecated(since = "0.4.0", note = "use `to_colors()` instead")]
199 pub fn to_vec(&self) -> Vec<bool> {
200 self.content.iter().map(|c| *c != Color::Light).collect()
201 }
202
203 #[deprecated(since = "0.4.0", note = "use `into_colors()` instead")]
206 pub fn into_vec(self) -> Vec<bool> {
207 self.content.into_iter().map(|c| c != Color::Light).collect()
208 }
209
210 pub fn to_colors(&self) -> Vec<Color> {
212 self.content.clone()
213 }
214
215 pub fn into_colors(self) -> Vec<Color> {
217 self.content
218 }
219
220 #[cfg_attr(feature = "image", doc = " ```rust")]
228 #[cfg_attr(not(feature = "image"), doc = " ```ignore")]
229 pub fn render<P: Pixel>(&self) -> Renderer<P> {
242 let quiet_zone = if self.version.is_micro() { 2 } else { 4 };
243 Renderer::new(&self.content, self.width, quiet_zone)
244 }
245}
246
247impl Index<(usize, usize)> for QrCode {
248 type Output = Color;
249
250 fn index(&self, (x, y): (usize, usize)) -> &Color {
251 let index = y * self.width + x;
252 &self.content[index]
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use crate::{EcLevel, QrCode, Version};
259
260 #[test]
261 fn test_annex_i_qr() {
262 let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
264 assert_eq!(
265 &*code.to_debug_str('#', '.'),
266 "\
267 #######..#.##.#######\n\
268 #.....#..####.#.....#\n\
269 #.###.#.#.....#.###.#\n\
270 #.###.#.##....#.###.#\n\
271 #.###.#.#.###.#.###.#\n\
272 #.....#.#...#.#.....#\n\
273 #######.#.#.#.#######\n\
274 ........#..##........\n\
275 #.#####..#..#.#####..\n\
276 ...#.#.##.#.#..#.##..\n\
277 ..#...##.#.#.#..#####\n\
278 ....#....#.....####..\n\
279 ...######..#.#..#....\n\
280 ........#.#####..##..\n\
281 #######..##.#.##.....\n\
282 #.....#.#.#####...#.#\n\
283 #.###.#.#...#..#.##..\n\
284 #.###.#.##..#..#.....\n\
285 #.###.#.#.##.#..#.#..\n\
286 #.....#........##.##.\n\
287 #######.####.#..#.#.."
288 );
289 }
290
291 #[test]
292 fn test_annex_i_micro_qr() {
293 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
294 assert_eq!(
295 &*code.to_debug_str('#', '.'),
296 "\
297 #######.#.#.#\n\
298 #.....#.###.#\n\
299 #.###.#..##.#\n\
300 #.###.#..####\n\
301 #.###.#.###..\n\
302 #.....#.#...#\n\
303 #######..####\n\
304 .........##..\n\
305 ##.#....#...#\n\
306 .##.#.#.#.#.#\n\
307 ###..#######.\n\
308 ...#.#....##.\n\
309 ###.#..##.###"
310 );
311 }
312}
313
314#[cfg(all(test, feature = "image"))]
315mod image_tests {
316 use crate::{EcLevel, QrCode, Version};
317 use image::{load_from_memory, Luma, Rgb};
318
319 #[test]
320 fn test_annex_i_qr_as_image() {
321 let code = QrCode::new(b"01234567").unwrap();
322 let image = code.render::<Luma<u8>>().build();
323 let expected =
324 load_from_memory(include_bytes!("../docs/images/test_annex_i_qr_as_image.png")).unwrap().to_luma8();
325 assert_eq!(image.dimensions(), expected.dimensions());
326 assert_eq!(image.into_raw(), expected.into_raw());
327 }
328
329 #[test]
330 fn test_annex_i_micro_qr_as_image() {
331 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
332 let image = code
333 .render()
334 .min_dimensions(200, 200)
335 .dark_color(Rgb([128, 0, 0]))
336 .light_color(Rgb([255, 255, 128]))
337 .build();
338 let expected =
339 load_from_memory(include_bytes!("../docs/images/test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb8();
340 assert_eq!(image.dimensions(), expected.dimensions());
341 assert_eq!(image.into_raw(), expected.into_raw());
342 }
343}
344
345#[cfg(all(test, feature = "svg"))]
346mod svg_tests {
347 use crate::render::svg::Color as SvgColor;
348 use crate::{EcLevel, QrCode, Version};
349
350 #[test]
351 fn test_annex_i_qr_as_svg() {
352 let code = QrCode::new(b"01234567").unwrap();
353 let image = code.render::<SvgColor>().build();
354 let expected = include_str!("../docs/images/test_annex_i_qr_as_svg.svg");
355 assert_eq!(&image, expected);
356 }
357
358 #[test]
359 fn test_annex_i_micro_qr_as_svg() {
360 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
361 let image = code
362 .render()
363 .min_dimensions(200, 200)
364 .dark_color(SvgColor("#800000"))
365 .light_color(SvgColor("#ffff80"))
366 .build();
367 let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_svg.svg");
368 assert_eq!(&image, expected);
369 }
370}