1#![cfg_attr(feature = "image", doc = "```rust")]
6#![cfg_attr(not(feature = "image"), doc = "```ignore")]
7#![cfg_attr(feature = "bench", feature(test))] #![warn(clippy::pedantic)]
31#![allow(
32 clippy::must_use_candidate, )]
34#![cfg_attr(feature = "bench", doc = include_str!("../README.md"))]
35use std::ops::Index;
38
39pub mod bits;
40pub mod canvas;
41mod cast;
42pub mod ec;
43pub mod optimize;
44pub mod render;
45pub mod types;
46
47pub use crate::types::{Color, EcLevel, QrResult, Version};
48
49use crate::cast::As;
50use crate::render::{Pixel, Renderer};
51
52#[derive(Clone)]
54pub struct QrCode {
55 content: Vec<Color>,
56 version: Version,
57 ec_level: EcLevel,
58 width: usize,
59}
60
61impl QrCode {
62 pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
76 Self::with_error_correction_level(data, EcLevel::M)
77 }
78
79 pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
93 let bits = bits::encode_auto(data.as_ref(), ec_level)?;
94 Self::with_bits(bits, ec_level)
95 }
96
97 pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
116 let mut bits = bits::Bits::new(version);
117 bits.push_optimal_data(data.as_ref())?;
118 bits.push_terminator(ec_level)?;
119 Self::with_bits(bits, ec_level)
120 }
121
122 pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
150 let version = bits.version();
151 let data = bits.into_bytes();
152 let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?;
153 let mut canvas = canvas::Canvas::new(version, ec_level);
154 canvas.draw_all_functional_patterns();
155 canvas.draw_data(&encoded_data, &ec_data);
156 let canvas = canvas.apply_best_mask();
157 Ok(Self { content: canvas.into_colors(), version, ec_level, width: version.width().as_usize() })
158 }
159
160 pub fn version(&self) -> Version {
162 self.version
163 }
164
165 pub fn error_correction_level(&self) -> EcLevel {
167 self.ec_level
168 }
169
170 pub fn width(&self) -> usize {
174 self.width
175 }
176
177 #[allow(clippy::missing_panics_doc)] pub fn max_allowed_errors(&self) -> usize {
182 ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
183 }
184
185 pub fn is_functional(&self, x: usize, y: usize) -> bool {
192 let x = x.try_into().expect("coordinate is too large for QR code");
193 let y = y.try_into().expect("coordinate is too large for QR code");
194 canvas::is_functional(self.version, self.version.width(), x, y)
195 }
196
197 pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
200 self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build()
201 }
202
203 #[deprecated(since = "0.4.0", note = "use `to_colors()` instead")]
206 pub fn to_vec(&self) -> Vec<bool> {
207 self.content.iter().map(|c| *c != Color::Light).collect()
208 }
209
210 #[deprecated(since = "0.4.0", note = "use `into_colors()` instead")]
213 pub fn into_vec(self) -> Vec<bool> {
214 self.content.into_iter().map(|c| c != Color::Light).collect()
215 }
216
217 pub fn to_colors(&self) -> Vec<Color> {
219 self.content.clone()
220 }
221
222 pub fn into_colors(self) -> Vec<Color> {
224 self.content
225 }
226
227 #[cfg_attr(feature = "image", doc = " ```rust")]
234 #[cfg_attr(not(feature = "image"), doc = " ```ignore")]
235 pub fn render<P: Pixel>(&self) -> Renderer<P> {
250 let quiet_zone = if self.version.is_micro() { 2 } else { 4 };
251 Renderer::new(&self.content, self.width, quiet_zone)
252 }
253}
254
255impl Index<(usize, usize)> for QrCode {
256 type Output = Color;
257
258 fn index(&self, (x, y): (usize, usize)) -> &Color {
259 let index = y * self.width + x;
260 &self.content[index]
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use crate::{EcLevel, QrCode, Version};
267
268 #[test]
269 fn test_annex_i_qr() {
270 let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
272 assert_eq!(
273 &*code.to_debug_str('#', '.'),
274 "\
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 ...######..#.#..#....\n\
288 ........#.#####..##..\n\
289 #######..##.#.##.....\n\
290 #.....#.#.#####...#.#\n\
291 #.###.#.#...#..#.##..\n\
292 #.###.#.##..#..#.....\n\
293 #.###.#.#.##.#..#.#..\n\
294 #.....#........##.##.\n\
295 #######.####.#..#.#.."
296 );
297 }
298
299 #[test]
300 fn test_annex_i_micro_qr() {
301 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
302 assert_eq!(
303 &*code.to_debug_str('#', '.'),
304 "\
305 #######.#.#.#\n\
306 #.....#.###.#\n\
307 #.###.#..##.#\n\
308 #.###.#..####\n\
309 #.###.#.###..\n\
310 #.....#.#...#\n\
311 #######..####\n\
312 .........##..\n\
313 ##.#....#...#\n\
314 .##.#.#.#.#.#\n\
315 ###..#######.\n\
316 ...#.#....##.\n\
317 ###.#..##.###"
318 );
319 }
320}
321
322#[cfg(all(test, feature = "image"))]
323mod image_tests {
324 use crate::{EcLevel, QrCode, Version};
325 use image::{load_from_memory, Luma, Rgb};
326
327 #[test]
328 fn test_annex_i_qr_as_image() {
329 let code = QrCode::new(b"01234567").unwrap();
330 let image = code.render::<Luma<u8>>().build();
331 let expected = load_from_memory(include_bytes!("test_annex_i_qr_as_image.png")).unwrap().into_luma8();
332 assert_eq!(image.dimensions(), expected.dimensions());
333 assert_eq!(image.into_raw(), expected.into_raw());
334 }
335
336 #[test]
337 fn test_annex_i_micro_qr_as_image() {
338 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
339 let image = code
340 .render()
341 .min_dimensions(200, 200)
342 .dark_color(Rgb([128, 0, 0]))
343 .light_color(Rgb([255, 255, 128]))
344 .build();
345 let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png")).unwrap().into_rgb8();
346 assert_eq!(image.dimensions(), expected.dimensions());
347 assert_eq!(image.into_raw(), expected.into_raw());
348 }
349}
350
351#[cfg(all(test, feature = "svg"))]
352mod svg_tests {
353 use crate::render::svg::Color as SvgColor;
354 use crate::{EcLevel, QrCode, Version};
355
356 #[test]
357 fn test_annex_i_qr_as_svg() {
358 let code = QrCode::new(b"01234567").unwrap();
359 let image = code.render::<SvgColor>().build();
360 let expected = include_str!("test_annex_i_qr_as_svg.svg");
361 assert_eq!(&image, expected);
362 }
363
364 #[test]
365 fn test_annex_i_micro_qr_as_svg() {
366 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
367 let image = code
368 .render()
369 .min_dimensions(200, 200)
370 .dark_color(SvgColor("#800000"))
371 .light_color(SvgColor("#ffff80"))
372 .build();
373 let expected = include_str!("test_annex_i_micro_qr_as_svg.svg");
374 assert_eq!(&image, expected);
375 }
376}
377
378#[cfg(all(test, feature = "pic"))]
379mod pic_tests {
380 use crate::render::pic::Color as PicColor;
381 use crate::{EcLevel, QrCode, Version};
382
383 #[test]
384 fn test_annex_i_qr_as_pic() {
385 let code = QrCode::new(b"01234567").unwrap();
386 let image = code.render::<PicColor>().build();
387 let expected = include_str!("test_annex_i_qr_as_pic.pic");
388 assert_eq!(&image, expected);
389 }
390
391 #[test]
392 fn test_annex_i_micro_qr_as_pic() {
393 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
394 let image = code.render::<PicColor>().min_dimensions(1, 1).build();
395 let expected = include_str!("test_annex_i_micro_qr_as_pic.pic");
396 assert_eq!(&image, expected);
397 }
398}