1#![cfg_attr(feature = "image", doc = "```rust")]
6#![cfg_attr(not(feature = "image"), doc = "```ignore")]
7#![cfg_attr(feature = "bench", feature(test, external_doc))] #![deny(warnings, clippy::pedantic)]
31#![allow(
32 clippy::must_use_candidate, clippy::use_self, )]
35#![cfg_attr(feature = "bench", doc(include = "../README.md"))]
36use std::ops::Index;
39
40pub mod bits;
41pub mod canvas;
42mod cast;
43pub mod ec;
44pub mod optimize;
45pub mod render;
46pub mod types;
47
48pub use crate::types::{Color, EcLevel, QrResult, Version};
49
50use crate::cast::As;
51use crate::render::{Pixel, Renderer};
52use checked_int_cast::CheckedIntCast;
53
54#[derive(Clone)]
56pub struct QrCode {
57 content: Vec<Color>,
58 version: Version,
59 ec_level: EcLevel,
60 width: usize,
61}
62
63impl QrCode {
64 pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
78 Self::with_error_correction_level(data, EcLevel::M)
79 }
80
81 pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
95 let bits = bits::encode_auto(data.as_ref(), ec_level)?;
96 Self::with_bits(bits, ec_level)
97 }
98
99 pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
118 let mut bits = bits::Bits::new(version);
119 bits.push_optimal_data(data.as_ref())?;
120 bits.push_terminator(ec_level)?;
121 Self::with_bits(bits, ec_level)
122 }
123
124 pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
152 let version = bits.version();
153 let data = bits.into_bytes();
154 let (encoded_data, ec_data) = ec::construct_codewords(&*data, version, ec_level)?;
155 let mut canvas = canvas::Canvas::new(version, ec_level);
156 canvas.draw_all_functional_patterns();
157 canvas.draw_data(&*encoded_data, &*ec_data);
158 let canvas = canvas.apply_best_mask();
159 Ok(Self { content: canvas.into_colors(), version, ec_level, width: version.width().as_usize() })
160 }
161
162 pub fn version(&self) -> Version {
164 self.version
165 }
166
167 pub fn error_correction_level(&self) -> EcLevel {
169 self.ec_level
170 }
171
172 pub fn width(&self) -> usize {
176 self.width
177 }
178
179 pub fn max_allowed_errors(&self) -> usize {
183 ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
184 }
185
186 pub fn is_functional(&self, x: usize, y: usize) -> bool {
189 let x = x.as_i16_checked().expect("coordinate is too large for QR code");
190 let y = y.as_i16_checked().expect("coordinate is too large for QR code");
191 canvas::is_functional(self.version, self.version.width(), x, y)
192 }
193
194 pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
197 self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build()
198 }
199
200 #[deprecated(since = "0.4.0", note = "use `to_colors()` instead")]
203 pub fn to_vec(&self) -> Vec<bool> {
204 self.content.iter().map(|c| *c != Color::Light).collect()
205 }
206
207 #[deprecated(since = "0.4.0", note = "use `into_colors()` instead")]
210 pub fn into_vec(self) -> Vec<bool> {
211 self.content.into_iter().map(|c| c != Color::Light).collect()
212 }
213
214 pub fn to_colors(&self) -> Vec<Color> {
216 self.content.clone()
217 }
218
219 pub fn into_colors(self) -> Vec<Color> {
221 self.content
222 }
223
224 #[cfg_attr(feature = "image", doc = " ```rust")]
231 #[cfg_attr(not(feature = "image"), doc = " ```ignore")]
232 pub fn render<P: Pixel>(&self) -> Renderer<P> {
247 let quiet_zone = if self.version.is_micro() { 2 } else { 4 };
248 Renderer::new(&self.content, self.width, quiet_zone)
249 }
250}
251
252impl Index<(usize, usize)> for QrCode {
253 type Output = Color;
254
255 fn index(&self, (x, y): (usize, usize)) -> &Color {
256 let index = y * self.width + x;
257 &self.content[index]
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use crate::{EcLevel, QrCode, Version};
264
265 #[test]
266 fn test_annex_i_qr() {
267 let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
269 assert_eq!(
270 &*code.to_debug_str('#', '.'),
271 "\
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 #.....#.#.#####...#.#\n\
288 #.###.#.#...#..#.##..\n\
289 #.###.#.##..#..#.....\n\
290 #.###.#.#.##.#..#.#..\n\
291 #.....#........##.##.\n\
292 #######.####.#..#.#.."
293 );
294 }
295
296 #[test]
297 fn test_annex_i_micro_qr() {
298 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
299 assert_eq!(
300 &*code.to_debug_str('#', '.'),
301 "\
302 #######.#.#.#\n\
303 #.....#.###.#\n\
304 #.###.#..##.#\n\
305 #.###.#..####\n\
306 #.###.#.###..\n\
307 #.....#.#...#\n\
308 #######..####\n\
309 .........##..\n\
310 ##.#....#...#\n\
311 .##.#.#.#.#.#\n\
312 ###..#######.\n\
313 ...#.#....##.\n\
314 ###.#..##.###"
315 );
316 }
317}
318
319#[cfg(all(test, feature = "image"))]
320mod image_tests {
321 use crate::{EcLevel, QrCode, Version};
322 use image::{load_from_memory, Luma, Rgb};
323
324 #[test]
325 fn test_annex_i_qr_as_image() {
326 let code = QrCode::new(b"01234567").unwrap();
327 let image = code.render::<Luma<u8>>().build();
328 let expected = load_from_memory(include_bytes!("test_annex_i_qr_as_image.png")).unwrap().to_luma();
329 assert_eq!(image.dimensions(), expected.dimensions());
330 assert_eq!(image.into_raw(), expected.into_raw());
331 }
332
333 #[test]
334 fn test_annex_i_micro_qr_as_image() {
335 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
336 let image = code
337 .render()
338 .min_dimensions(200, 200)
339 .dark_color(Rgb([128, 0, 0]))
340 .light_color(Rgb([255, 255, 128]))
341 .build();
342 let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb();
343 assert_eq!(image.dimensions(), expected.dimensions());
344 assert_eq!(image.into_raw(), expected.into_raw());
345 }
346}
347
348#[cfg(all(test, feature = "svg"))]
349mod svg_tests {
350 use crate::render::svg::Color as SvgColor;
351 use crate::{EcLevel, QrCode, Version};
352
353 #[test]
354 fn test_annex_i_qr_as_svg() {
355 let code = QrCode::new(b"01234567").unwrap();
356 let image = code.render::<SvgColor>().build();
357 let expected = include_str!("test_annex_i_qr_as_svg.svg");
358 assert_eq!(&image, expected);
359 }
360
361 #[test]
362 fn test_annex_i_micro_qr_as_svg() {
363 let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
364 let image = code
365 .render()
366 .min_dimensions(200, 200)
367 .dark_color(SvgColor("#800000"))
368 .light_color(SvgColor("#ffff80"))
369 .build();
370 let expected = include_str!("test_annex_i_micro_qr_as_svg.svg");
371 assert_eq!(&image, expected);
372 }
373}