From a210abca23d039ab194a0c1c34bd76eeaf4b8888 Mon Sep 17 00:00:00 2001 From: able Date: Fri, 29 Jul 2022 09:46:09 -0500 Subject: [PATCH] BSOD with qr code --- Cargo.lock | 5 + ableos/Cargo.toml | 2 + ableos/src/arch/x86_64/interrupts.rs | 77 + ableos/src/graphics/mod.rs | 3 +- ableos/src/kmain.rs | 49 +- ableos/src/relib/image/mono_bitmap.rs | 5 +- qrcode-rust/Cargo.lock | 338 +++ qrcode-rust/Cargo.toml | 42 + qrcode-rust/rustfmt.toml | 3 + qrcode-rust/src/bin/qrencode.rs | 8 + qrcode-rust/src/bits.rs | 997 ++++++++ qrcode-rust/src/canvas.rs | 2010 +++++++++++++++++ qrcode-rust/src/cast.rs | 86 + qrcode-rust/src/ec.rs | 488 ++++ qrcode-rust/src/lib.rs | 382 ++++ qrcode-rust/src/main.rs | 11 + qrcode-rust/src/optimize.rs | 686 ++++++ qrcode-rust/src/render/image.rs | 150 ++ qrcode-rust/src/render/mod.rs | 191 ++ qrcode-rust/src/render/string.rs | 125 + qrcode-rust/src/render/svg.rs | 76 + qrcode-rust/src/render/unicode.rs | 146 ++ .../src/test_annex_i_micro_qr_as_image.png | Bin 0 -> 186 bytes .../src/test_annex_i_micro_qr_as_svg.svg | 1 + qrcode-rust/src/test_annex_i_qr_as_image.png | Bin 0 -> 217 bytes qrcode-rust/src/test_annex_i_qr_as_svg.svg | 1 + qrcode-rust/src/types.rs | 326 +++ 27 files changed, 6197 insertions(+), 11 deletions(-) create mode 100644 qrcode-rust/Cargo.lock create mode 100644 qrcode-rust/Cargo.toml create mode 100644 qrcode-rust/rustfmt.toml create mode 100644 qrcode-rust/src/bin/qrencode.rs create mode 100644 qrcode-rust/src/bits.rs create mode 100644 qrcode-rust/src/canvas.rs create mode 100644 qrcode-rust/src/cast.rs create mode 100644 qrcode-rust/src/ec.rs create mode 100644 qrcode-rust/src/lib.rs create mode 100644 qrcode-rust/src/main.rs create mode 100644 qrcode-rust/src/optimize.rs create mode 100644 qrcode-rust/src/render/image.rs create mode 100644 qrcode-rust/src/render/mod.rs create mode 100644 qrcode-rust/src/render/string.rs create mode 100644 qrcode-rust/src/render/svg.rs create mode 100644 qrcode-rust/src/render/unicode.rs create mode 100644 qrcode-rust/src/test_annex_i_micro_qr_as_image.png create mode 100644 qrcode-rust/src/test_annex_i_micro_qr_as_svg.svg create mode 100644 qrcode-rust/src/test_annex_i_qr_as_image.png create mode 100644 qrcode-rust/src/test_annex_i_qr_as_svg.svg create mode 100644 qrcode-rust/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 11d2321..e2753ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,6 +49,7 @@ dependencies = [ "pic8259", "picorand", "pretty-hex", + "qrcode", "rdrand", "rhai", "riscv", @@ -698,6 +699,10 @@ dependencies = [ "syn", ] +[[package]] +name = "qrcode" +version = "0.12.0" + [[package]] name = "quote" version = "1.0.18" diff --git a/ableos/Cargo.toml b/ableos/Cargo.toml index 7c020de..aadb93b 100644 --- a/ableos/Cargo.toml +++ b/ableos/Cargo.toml @@ -43,6 +43,8 @@ test-args = [ [dependencies] lazy_static = { version = "*", features = ["spin_no_std"] } +qrcode = { path = "../qrcode-rust" } + linked_list_allocator = "0.9.0" lliw = "0.2.0" diff --git a/ableos/src/arch/x86_64/interrupts.rs b/ableos/src/arch/x86_64/interrupts.rs index 831f55a..7538c28 100644 --- a/ableos/src/arch/x86_64/interrupts.rs +++ b/ableos/src/arch/x86_64/interrupts.rs @@ -1,12 +1,19 @@ use crate::{ arch::{drivers::vga::WRITER, gdt}, + image::mono_bitmap::bruh, kernel_state::KERNEL_STATE, print, println, rhai_shell::KEYBUFF, + VgaBuffer, SCREEN_BUFFER, }; use cpuio::outb; use pic8259::ChainedPics; +use qrcode::{ + render::{string, unicode}, + QrCode, +}; use spin::Lazy; +use vga::colors::Color16; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; pub const PIC_1_OFFSET: u8 = 32; @@ -61,6 +68,8 @@ extern "x86-interrupt" fn double_fault_handler( stack_frame: InterruptStackFrame, error_code: u64, ) -> ! { + bsod(&stack_frame, error_code); + panic!( "EXCEPTION: Error code{}\nDOUBLE FAULT\n{:#?}", error_code, stack_frame @@ -185,3 +194,71 @@ pub fn reset_pit_for_cpu() { set_pit_2(1000); set_pit_3(1000); } + +fn bsod(stack_frame: &InterruptStackFrame, error_code: u64) { + let mut mode = SCREEN_BUFFER.lock(); + + mode.force_redraw(); + for y in 0..480 { + for x in 0..640 { + mode.set_pixel(x, y, 0x0000ff00); + } + } + + let mut x = 1; + let mut y = 1; + + let sf = format!("DF"); + + let code = QrCode::new("DOUBLE FAULT").unwrap(); + // let image = code + // .render::() + // .dark_color(unicode::Dense1x2::Light) + // .light_color(unicode::Dense1x2::Dark) + // .build(); + + let image = code + .render::() + .quiet_zone(false) + .module_dimensions(2, 1) + .build(); + + // let st = image; + let st = format!("We fucked up ඞ\n{:#?}", stack_frame); + + for current in image.chars() { + x += 1; + if current == '\n' { + trace!("{}", y); + y += 1; + x = 1; + } else { + if current == '█' { + mode.draw_filled_rect(x * 6, y * 6, (x * 6) + 6, (y * 6) + 6, 0xffff0000); + } + } + } + let mut x = 0; + let mut y = 6; + for current in st.chars() { + x += 1; + if current == '\n' { + trace!("{}", y); + y += 1; + x = 1; + } else { + mode.draw_char( + (x * 14).try_into().unwrap(), + (y * 22).try_into().unwrap(), + current, + 0xffff0000, + ); + + // if current == '█' { + // mode.draw_filled_rect(x * 4, y * 4, (x * 4) + 4, (y * 4) + 4, 0xffff0000); + // } + } + } + + mode.copy_to_buffer(); +} diff --git a/ableos/src/graphics/mod.rs b/ableos/src/graphics/mod.rs index 96f8214..74874ef 100644 --- a/ableos/src/graphics/mod.rs +++ b/ableos/src/graphics/mod.rs @@ -12,6 +12,7 @@ pub static SCREEN_BUFFER: Lazy> = Lazy::new(|| spin::Mutex::new(ScreenBuffer::new(640, 480))); const FONT_SCALE: f32 = 1.6; + const GLYPH_HEIGHT: f32 = 18.0; const GLYPH_WIDTH: f32 = 10.0; @@ -190,7 +191,7 @@ impl ScreenBuffer { if let Some(q) = plane.outline_glyph(q_glyph) { q.draw(|gx, gy, c| { - if c > 0.015 { + if c > 0.0015 { let corner = q.px_bounds().min; self.set_pixel( gx as usize + corner.x as usize + x as usize, diff --git a/ableos/src/kmain.rs b/ableos/src/kmain.rs index fa052be..bae6941 100644 --- a/ableos/src/kmain.rs +++ b/ableos/src/kmain.rs @@ -7,15 +7,18 @@ use crate::ipc::channel::ChannelMessage; use crate::ipc::{self, IPC}; use crate::scheduler::SCHEDULER; use crate::time::fetch_time; -use crate::SectionType; use crate::{ arch::{init, sloop}, relib::network::socket::{SimpleSock, Socket}, scratchpad, }; use crate::{boot_conf::KernelConfig, systeminfo::RELEASE_TYPE}; +use crate::{wasm_jumploader, SectionType}; +use genfs::{Fs, OpenOptions}; use kernel::{KERNEL_VERSION, TICK}; use libwasm::syscalls::time_calls::get_time; +use qrcode::render::unicode; +use qrcode::QrCode; use spin::Lazy; use x86_64::instructions::interrupts::{disable, enable}; @@ -36,13 +39,17 @@ pub fn kernel_main() -> ! { let mut ipc_service = IPC.lock(); // create some channels or whatever here then drop it let log_handle = ipc_service.create_channel("LOG", true); + let log_sock_handle = ipc_service.create_socket("LOG", true); + + for handle in ipc_service.examine_board() { + println!("Discovered: {}", handle); + } drop(ipc_service); x86_64::instructions::interrupts::without_interrupts(|| { let mut scheduler = SCHEDULER.lock(); - // TODO: This task never gets run scheduler.enqueue_spawn(traceloop); // @@ -53,14 +60,40 @@ pub fn kernel_main() -> ! { } pub fn traceloop() { - let mut last_time = 0.0; + + // let mut last_time = 0.0; + /* loop { - // let time = fetch_time(); - // if time > last_time { - // last_time = time; - // trace!("Timer"); - // } + // FIXME: the following double faults + /* + let time = fetch_time(); + if time > last_time { + last_time = time; + trace!("Timer"); + } + */ } + + */ + + /* TODO: This also double faults + let fs = &*crate::filesystem::FILE_SYSTEM.lock(); + let path = format!("/home/able/bins/aos_test.wasm"); + + let home_exec_file = fs.open(&path.as_bytes(), OpenOptions::new().read(true)); + + drop(fs); + let mut binary_prog: Vec = vec![]; + + match home_exec_file { + Ok(file) => { + let ret = file.read_to_end(&mut binary_prog).unwrap(); + } + _ => {} + } + + wasm_jumploader::run_program(&binary_prog); + */ } pub fn cpu_socket_startup() { diff --git a/ableos/src/relib/image/mono_bitmap.rs b/ableos/src/relib/image/mono_bitmap.rs index dea1e93..59e7386 100644 --- a/ableos/src/relib/image/mono_bitmap.rs +++ b/ableos/src/relib/image/mono_bitmap.rs @@ -15,8 +15,9 @@ pub fn bruh() { let mut screen_lock = SCREEN_BUFFER.lock(); - for y in 0..290 { - for x in 0..290 { + for y in 0..20 { + for x in 0..20 { + // trace!(""); // info!("x{} | y{}", x, y); // Stops at 419 diff --git a/qrcode-rust/Cargo.lock b/qrcode-rust/Cargo.lock new file mode 100644 index 0000000..fc33c58 --- /dev/null +++ b/qrcode-rust/Cargo.lock @@ -0,0 +1,338 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytemuck" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "checked_int_cast" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cc5e6b5ab06331c33589842070416baa137e8b0eb912b008cfd4a78ada7919" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" +dependencies = [ + "rayon", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "qrcode" +version = "0.12.0" +dependencies = [ + "checked_int_cast", + "image", +] + +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "tiff" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +dependencies = [ + "jpeg-decoder", + "miniz_oxide 0.4.4", + "weezl", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" diff --git a/qrcode-rust/Cargo.toml b/qrcode-rust/Cargo.toml new file mode 100644 index 0000000..4a33b82 --- /dev/null +++ b/qrcode-rust/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "qrcode" +description = "QR code encoder in Rust" +license = "MIT OR Apache-2.0" +version = "0.12.0" +edition = "2018" +authors = ["kennytm "] +keywords = ["qrcode"] +repository = "https://github.com/kennytm/qrcode-rust" +readme = "README.md" +documentation = "http://docs.rs/qrcode" +exclude = [".travis.yml", ".gitignore", "test-data/**"] + +[badges] +travis-ci = { repository = "kennytm/qrcode-rust" } +coveralls = { repository = "kennytm/qrcode-rust" } +is-it-maintained-issue-resolution = { repository = "kennytm/qrcode-rust" } +is-it-maintained-open-issues = { repository = "kennytm/qrcode-rust" } +maintenance = { status = "passively-maintained" } + +[dependencies] +# checked_int_cast = "1" + + +[features] +default = [] +bench = [] +svg = [] + +[[bin]] +name = "qrencode" + +[[example]] +name = "encode_image" +required-features = ["image"] + +[[example]] +name = "encode_string" + +[[example]] +name = "encode_svg" +required-features = ["svg"] diff --git a/qrcode-rust/rustfmt.toml b/qrcode-rust/rustfmt.toml new file mode 100644 index 0000000..acd2021 --- /dev/null +++ b/qrcode-rust/rustfmt.toml @@ -0,0 +1,3 @@ +max_width = 120 +use_small_heuristics = "Max" +use_field_init_shorthand = true diff --git a/qrcode-rust/src/bin/qrencode.rs b/qrcode-rust/src/bin/qrencode.rs new file mode 100644 index 0000000..4837828 --- /dev/null +++ b/qrcode-rust/src/bin/qrencode.rs @@ -0,0 +1,8 @@ +use std::env; + +pub fn main() { + let arg = env::args().nth(1).unwrap(); + let code = qrcode::QrCode::new(arg.as_bytes()).unwrap(); + + print!("{}", code.render().dark_color("\x1b[7m \x1b[0m").light_color("\x1b[49m \x1b[0m").build()); +} diff --git a/qrcode-rust/src/bits.rs b/qrcode-rust/src/bits.rs new file mode 100644 index 0000000..22471f3 --- /dev/null +++ b/qrcode-rust/src/bits.rs @@ -0,0 +1,997 @@ +//! The `bits` module encodes binary data into raw bits used in a QR code. + +use core::cmp::min; + +#[cfg(feature = "bench")] +extern crate test; + +use alloc::vec::Vec; + +use crate::cast::{As, Truncate}; +use crate::optimize::{total_encoded_len, Optimizer, Parser, Segment}; +use crate::types::{EcLevel, Mode, QrError, QrResult, Version}; + +//------------------------------------------------------------------------------ +//{{{ Bits + +/// The `Bits` structure stores the encoded data for a QR code. +pub struct Bits { + data: Vec, + bit_offset: usize, + version: Version, +} + +impl Bits { + /// Constructs a new, empty bits structure. + pub fn new(version: Version) -> Self { + Self { data: Vec::new(), bit_offset: 0, version } + } + + /// Pushes an N-bit big-endian integer to the end of the bits. + /// + /// Note: It is up to the developer to ensure that `number` really only is + /// `n` bit in size. Otherwise the excess bits may stomp on the existing + /// ones. + fn push_number(&mut self, n: usize, number: u16) { + debug_assert!(n == 16 || n < 16 && number < (1 << n), "{} is too big as a {}-bit number", number, n); + + let b = self.bit_offset + n; + let last_index = self.data.len().wrapping_sub(1); + match (self.bit_offset, b) { + (0, 0..=8) => { + self.data.push((number << (8 - b)).truncate_as_u8()); + } + (0, _) => { + self.data.push((number >> (b - 8)).truncate_as_u8()); + self.data.push((number << (16 - b)).truncate_as_u8()); + } + (_, 0..=8) => { + self.data[last_index] |= (number << (8 - b)).truncate_as_u8(); + } + (_, 9..=16) => { + self.data[last_index] |= (number >> (b - 8)).truncate_as_u8(); + self.data.push((number << (16 - b)).truncate_as_u8()); + } + _ => { + self.data[last_index] |= (number >> (b - 8)).truncate_as_u8(); + self.data.push((number >> (b - 16)).truncate_as_u8()); + self.data.push((number << (24 - b)).truncate_as_u8()); + } + } + self.bit_offset = b & 7; + } + + /// Pushes an N-bit big-endian integer to the end of the bits, and check + /// that the number does not overflow the bits. + /// + /// Returns `Err(QrError::DataTooLong)` on overflow. + fn push_number_checked(&mut self, n: usize, number: usize) -> QrResult<()> { + if n > 16 || number >= (1 << n) { + Err(QrError::DataTooLong) + } else { + self.push_number(n, number.as_u16()); + Ok(()) + } + } + + /// Reserves `n` extra bits of space for pushing. + fn reserve(&mut self, n: usize) { + let extra_bytes = (n + (8 - self.bit_offset) % 8) / 8; + self.data.reserve(extra_bytes); + } + + /// Convert the bits into a bytes vector. + pub fn into_bytes(self) -> Vec { + self.data + } + + /// Total number of bits currently pushed. + pub fn len(&self) -> usize { + if self.bit_offset == 0 { + self.data.len() * 8 + } else { + (self.data.len() - 1) * 8 + self.bit_offset + } + } + + /// Whether there are any bits pushed. + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + /// The maximum number of bits allowed by the provided QR code version and + /// error correction level. + /// + /// # Errors + /// + /// Returns `Err(QrError::InvalidVersion)` if it is not valid to use the + /// `ec_level` for the given version (e.g. `Version::Micro(1)` with + /// `EcLevel::H`). + pub fn max_len(&self, ec_level: EcLevel) -> QrResult { + self.version.fetch(ec_level, &DATA_LENGTHS) + } + + /// Version of the QR code. + pub fn version(&self) -> Version { + self.version + } +} + +#[test] +fn test_push_number() { + let mut bits = Bits::new(Version::Normal(1)); + + bits.push_number(3, 0b010); // 0:0 .. 0:3 + bits.push_number(3, 0b110); // 0:3 .. 0:6 + bits.push_number(3, 0b101); // 0:6 .. 1:1 + bits.push_number(7, 0b001_1010); // 1:1 .. 2:0 + bits.push_number(4, 0b1100); // 2:0 .. 2:4 + bits.push_number(12, 0b1011_0110_1101); // 2:4 .. 4:0 + bits.push_number(10, 0b01_1001_0001); // 4:0 .. 5:2 + bits.push_number(15, 0b111_0010_1110_0011); // 5:2 .. 7:1 + + let bytes = bits.into_bytes(); + + assert_eq!( + bytes, + vec![ + 0b010__110__10, // 90 + 0b1__001_1010, // 154 + 0b1100__1011, // 203 + 0b0110_1101, // 109 + 0b01_1001_00, // 100 + 0b01__111_001, // 121 + 0b0_1110_001, // 113 + 0b1__0000000, // 128 + ] + ); +} + +#[cfg(feature = "bench")] +#[bench] +fn bench_push_splitted_bytes(bencher: &mut test::Bencher) { + bencher.iter(|| { + let mut bits = Bits::new(Version::Normal(40)); + bits.push_number(4, 0b0101); + for _ in 0..1024 { + bits.push_number(8, 0b10101010); + } + bits.into_bytes() + }); +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Mode indicator + +/// An "extended" mode indicator, includes all indicators supported by QR code +/// beyond those bearing data. +#[derive(Copy, Clone)] +pub enum ExtendedMode { + /// ECI mode indicator, to introduce an ECI designator. + Eci, + + /// The normal mode to introduce data. + Data(Mode), + + /// FNC-1 mode in the first position. + Fnc1First, + + /// FNC-1 mode in the second position. + Fnc1Second, + + /// Structured append. + StructuredAppend, +} + +impl Bits { + /// Push the mode indicator to the end of the bits. + /// + /// # Errors + /// + /// If the mode is not supported in the provided version, this method + /// returns `Err(QrError::UnsupportedCharacterSet)`. + pub fn push_mode_indicator(&mut self, mode: ExtendedMode) -> QrResult<()> { + #[allow(clippy::match_same_arms)] + let number = match (self.version, mode) { + (Version::Micro(1), ExtendedMode::Data(Mode::Numeric)) => return Ok(()), + (Version::Micro(_), ExtendedMode::Data(Mode::Numeric)) => 0, + (Version::Micro(_), ExtendedMode::Data(Mode::Alphanumeric)) => 1, + (Version::Micro(_), ExtendedMode::Data(Mode::Byte)) => 0b10, + (Version::Micro(_), ExtendedMode::Data(Mode::Kanji)) => 0b11, + (Version::Micro(_), _) => return Err(QrError::UnsupportedCharacterSet), + (_, ExtendedMode::Data(Mode::Numeric)) => 0b0001, + (_, ExtendedMode::Data(Mode::Alphanumeric)) => 0b0010, + (_, ExtendedMode::Data(Mode::Byte)) => 0b0100, + (_, ExtendedMode::Data(Mode::Kanji)) => 0b1000, + (_, ExtendedMode::Eci) => 0b0111, + (_, ExtendedMode::Fnc1First) => 0b0101, + (_, ExtendedMode::Fnc1Second) => 0b1001, + (_, ExtendedMode::StructuredAppend) => 0b0011, + }; + let bits = self.version.mode_bits_count(); + self.push_number_checked(bits, number).or(Err(QrError::UnsupportedCharacterSet)) + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ ECI + +impl Bits { + /// Push an ECI (Extended Channel Interpretation) designator to the bits. + /// + /// An ECI designator is a 6-digit number to specify the character set of + /// the following binary data. After calling this method, one could call + /// `.push_byte_data()` or similar methods to insert the actual data, e.g. + /// + /// #![allow(unused_must_use)] + /// + /// use qrcode::bits::Bits; + /// use qrcode::types::Version; + /// + /// let mut bits = Bits::new(Version::Normal(1)); + /// bits.push_eci_designator(9); // 9 = ISO-8859-7 (Greek). + /// bits.push_byte_data(b"\xa1\xa2\xa3\xa4\xa5"); // ΑΒΓΔΕ + /// + /// + /// The full list of ECI designator values can be found from + /// . Some example values are: + /// + /// ECI # | Character set + /// ------|------------------------------------- + /// 3 | ISO-8859-1 (Western European) + /// 20 | Shift JIS (Japanese) + /// 23 | Windows 1252 (Latin 1) (Western European) + /// 25 | UTF-16 Big Endian + /// 26 | UTF-8 + /// 28 | Big 5 (Traditional Chinese) + /// 29 | GB-18030 (Simplified Chinese) + /// 30 | EUC-KR (Korean) + /// + /// # Errors + /// + /// If the QR code version does not support ECI, this method will return + /// `Err(QrError::UnsupportedCharacterSet)`. + /// + /// If the designator is outside of the expected range, this method will + /// return `Err(QrError::InvalidECIDesignator)`. + pub fn push_eci_designator(&mut self, eci_designator: u32) -> QrResult<()> { + self.reserve(12); // assume the common case that eci_designator <= 127. + self.push_mode_indicator(ExtendedMode::Eci)?; + match eci_designator { + 0..=127 => { + self.push_number(8, eci_designator.as_u16()); + } + 128..=16383 => { + self.push_number(2, 0b10); + self.push_number(14, eci_designator.as_u16()); + } + 16384..=999_999 => { + self.push_number(3, 0b110); + self.push_number(5, (eci_designator >> 16).as_u16()); + self.push_number(16, (eci_designator & 0xffff).as_u16()); + } + _ => return Err(QrError::InvalidEciDesignator), + } + Ok(()) + } +} + +#[cfg(test)] +mod eci_tests { + use crate::bits::Bits; + use crate::types::{QrError, Version}; + + #[test] + fn test_9() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_eci_designator(9), Ok(())); + assert_eq!(bits.into_bytes(), vec![0b0111__0000, 0b1001__0000]); + } + + #[test] + fn test_899() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_eci_designator(899), Ok(())); + assert_eq!(bits.into_bytes(), vec![0b0111__10_00, 0b00111000, 0b0011__0000]); + } + + #[test] + fn test_999999() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_eci_designator(999999), Ok(())); + assert_eq!(bits.into_bytes(), vec![0b0111__110_0, 0b11110100, 0b00100011, 0b1111__0000]); + } + + #[test] + fn test_invalid_designator() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_eci_designator(1000000), Err(QrError::InvalidEciDesignator)); + } + + #[test] + fn test_unsupported_character_set() { + let mut bits = Bits::new(Version::Micro(4)); + assert_eq!(bits.push_eci_designator(9), Err(QrError::UnsupportedCharacterSet)); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Mode::Numeric mode + +impl Bits { + fn push_header(&mut self, mode: Mode, raw_data_len: usize) -> QrResult<()> { + let length_bits = mode.length_bits_count(self.version); + self.reserve(length_bits + 4 + mode.data_bits_count(raw_data_len)); + self.push_mode_indicator(ExtendedMode::Data(mode))?; + self.push_number_checked(length_bits, raw_data_len)?; + Ok(()) + } + + /// Encodes a numeric string to the bits. + /// + /// The data should only contain the characters 0 to 9. + /// + /// # Errors + /// + /// Returns `Err(QrError::DataTooLong)` on overflow. + pub fn push_numeric_data(&mut self, data: &[u8]) -> QrResult<()> { + self.push_header(Mode::Numeric, data.len())?; + for chunk in data.chunks(3) { + let number = chunk.iter().map(|b| u16::from(*b - b'0')).fold(0, |a, b| a * 10 + b); + let length = chunk.len() * 3 + 1; + self.push_number(length, number); + } + Ok(()) + } +} + +#[cfg(test)] +mod numeric_tests { + use crate::bits::Bits; + use crate::types::{QrError, Version}; + + #[test] + fn test_iso_18004_2006_example_1() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_numeric_data(b"01234567"), Ok(())); + assert_eq!( + bits.into_bytes(), + vec![0b0001_0000, 0b001000_00, 0b00001100, 0b01010110, 0b01_100001, 0b1__0000000] + ); + } + + #[test] + fn test_iso_18004_2000_example_2() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(())); + assert_eq!( + bits.into_bytes(), + vec![ + 0b0001_0000, + 0b010000_00, + 0b00001100, + 0b01010110, + 0b01_101010, + 0b0110_1110, + 0b000101_00, + 0b11101010, + 0b0101__0000, + ] + ); + } + + #[test] + fn test_iso_18004_2006_example_2() { + let mut bits = Bits::new(Version::Micro(3)); + assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(())); + assert_eq!( + bits.into_bytes(), + vec![ + 0b00_10000_0, + 0b00000110, + 0b0_0101011, + 0b001_10101, + 0b00110_111, + 0b0000101_0, + 0b01110101, + 0b00101__000, + ] + ); + } + + #[test] + fn test_data_too_long_error() { + let mut bits = Bits::new(Version::Micro(1)); + assert_eq!(bits.push_numeric_data(b"12345678"), Err(QrError::DataTooLong)); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Mode::Alphanumeric mode + +/// In QR code `Mode::Alphanumeric` mode, a pair of alphanumeric characters will +/// be encoded as a base-45 integer. `alphanumeric_digit` converts each +/// character into its corresponding base-45 digit. +/// +/// The conversion is specified in ISO/IEC 18004:2006, §8.4.3, Table 5. +#[inline] +fn alphanumeric_digit(character: u8) -> u16 { + match character { + b'0'..=b'9' => u16::from(character - b'0'), + b'A'..=b'Z' => u16::from(character - b'A') + 10, + b' ' => 36, + b'$' => 37, + b'%' => 38, + b'*' => 39, + b'+' => 40, + b'-' => 41, + b'.' => 42, + b'/' => 43, + b':' => 44, + _ => 0, + } +} + +impl Bits { + /// Encodes an alphanumeric string to the bits. + /// + /// The data should only contain the charaters A to Z (excluding lowercase), + /// 0 to 9, space, `$`, `%`, `*`, `+`, `-`, `.`, `/` or `:`. + /// + /// # Errors + /// + /// Returns `Err(QrError::DataTooLong)` on overflow. + pub fn push_alphanumeric_data(&mut self, data: &[u8]) -> QrResult<()> { + self.push_header(Mode::Alphanumeric, data.len())?; + for chunk in data.chunks(2) { + let number = chunk.iter().map(|b| alphanumeric_digit(*b)).fold(0, |a, b| a * 45 + b); + let length = chunk.len() * 5 + 1; + self.push_number(length, number); + } + Ok(()) + } +} + +#[cfg(test)] +mod alphanumeric_tests { + use crate::bits::Bits; + use crate::types::{QrError, Version}; + + #[test] + fn test_iso_18004_2006_example() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_alphanumeric_data(b"AC-42"), Ok(())); + assert_eq!( + bits.into_bytes(), + vec![0b0010_0000, 0b00101_001, 0b11001110, 0b11100111, 0b001_00001, 0b0__0000000] + ); + } + + #[test] + fn test_micro_qr_unsupported() { + let mut bits = Bits::new(Version::Micro(1)); + assert_eq!(bits.push_alphanumeric_data(b"A"), Err(QrError::UnsupportedCharacterSet)); + } + + #[test] + fn test_data_too_long() { + let mut bits = Bits::new(Version::Micro(2)); + assert_eq!(bits.push_alphanumeric_data(b"ABCDEFGH"), Err(QrError::DataTooLong)); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Mode::Byte mode + +impl Bits { + /// Encodes 8-bit byte data to the bits. + /// + /// # Errors + /// + /// Returns `Err(QrError::DataTooLong)` on overflow. + pub fn push_byte_data(&mut self, data: &[u8]) -> QrResult<()> { + self.push_header(Mode::Byte, data.len())?; + for b in data { + self.push_number(8, u16::from(*b)); + } + Ok(()) + } +} + +#[cfg(test)] +mod byte_tests { + use crate::bits::Bits; + use crate::types::{QrError, Version}; + + #[test] + fn test() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_byte_data(b"\x12\x34\x56\x78\x9a\xbc\xde\xf0"), Ok(())); + assert_eq!( + bits.into_bytes(), + vec![ + 0b0100_0000, + 0b1000_0001, + 0b0010_0011, + 0b0100_0101, + 0b0110_0111, + 0b1000_1001, + 0b1010_1011, + 0b1100_1101, + 0b1110_1111, + 0b0000__0000, + ] + ); + } + + #[test] + fn test_micro_qr_unsupported() { + let mut bits = Bits::new(Version::Micro(2)); + assert_eq!(bits.push_byte_data(b"?"), Err(QrError::UnsupportedCharacterSet)); + } + + #[test] + fn test_data_too_long() { + let mut bits = Bits::new(Version::Micro(3)); + assert_eq!(bits.push_byte_data(b"0123456701234567"), Err(QrError::DataTooLong)); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Mode::Kanji mode + +impl Bits { + /// Encodes Shift JIS double-byte data to the bits. + /// + /// # Errors + /// + /// Returns `Err(QrError::DataTooLong)` on overflow. + /// + /// Returns `Err(QrError::InvalidCharacter)` if the data is not Shift JIS + /// double-byte data (e.g. if the length of data is not an even number). + pub fn push_kanji_data(&mut self, data: &[u8]) -> QrResult<()> { + self.push_header(Mode::Kanji, data.len() / 2)?; + for kanji in data.chunks(2) { + if kanji.len() != 2 { + return Err(QrError::InvalidCharacter); + } + let cp = u16::from(kanji[0]) * 256 + u16::from(kanji[1]); + let bytes = if cp < 0xe040 { cp - 0x8140 } else { cp - 0xc140 }; + let number = (bytes >> 8) * 0xc0 + (bytes & 0xff); + self.push_number(13, number); + } + Ok(()) + } +} + +#[cfg(test)] +mod kanji_tests { + use crate::bits::Bits; + use crate::types::{QrError, Version}; + + #[test] + fn test_iso_18004_example() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_kanji_data(b"\x93\x5f\xe4\xaa"), Ok(())); + assert_eq!(bits.into_bytes(), vec![0b1000_0000, 0b0010_0110, 0b11001111, 0b1_1101010, 0b101010__00]); + } + + #[test] + fn test_micro_qr_unsupported() { + let mut bits = Bits::new(Version::Micro(2)); + assert_eq!(bits.push_kanji_data(b"?"), Err(QrError::UnsupportedCharacterSet)); + } + + #[test] + fn test_data_too_long() { + let mut bits = Bits::new(Version::Micro(3)); + assert_eq!(bits.push_kanji_data(b"\x93_\x93_\x93_\x93_\x93_\x93_\x93_\x93_"), Err(QrError::DataTooLong)); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ FNC1 mode + +impl Bits { + /// Encodes an indicator that the following data are formatted according to + /// the UCC/EAN Application Identifiers standard. + /// + /// #![allow(unused_must_use)] + /// + /// use qrcode::bits::Bits; + /// use qrcode::types::Version; + /// + /// let mut bits = Bits::new(Version::Normal(1)); + /// bits.push_fnc1_first_position(); + /// bits.push_numeric_data(b"01049123451234591597033130128"); + /// bits.push_alphanumeric_data(b"%10ABC123"); + /// + /// In QR code, the character `%` is used as the data field separator (0x1D). + /// + /// # Errors + /// + /// If the mode is not supported in the provided version, this method + /// returns `Err(QrError::UnsupportedCharacterSet)`. + pub fn push_fnc1_first_position(&mut self) -> QrResult<()> { + self.push_mode_indicator(ExtendedMode::Fnc1First) + } + + /// Encodes an indicator that the following data are formatted in accordance + /// with specific industry or application specifications previously agreed + /// with AIM International. + /// + /// #![allow(unused_must_use)] + /// + /// use qrcode::bits::Bits; + /// use qrcode::types::Version; + /// + /// let mut bits = Bits::new(Version::Normal(1)); + /// bits.push_fnc1_second_position(37); + /// bits.push_alphanumeric_data(b"AA1234BBB112"); + /// bits.push_byte_data(b"text text text text\r"); + /// + /// If the application indicator is a single Latin alphabet (a–z / A–Z), + /// please pass in its ASCII value + 100: + /// + /// ```ignore + /// bits.push_fnc1_second_position(b'A' + 100); + /// ``` + /// + /// # Errors + /// + /// If the mode is not supported in the provided version, this method + /// returns `Err(QrError::UnsupportedCharacterSet)`. + pub fn push_fnc1_second_position(&mut self, application_indicator: u8) -> QrResult<()> { + self.push_mode_indicator(ExtendedMode::Fnc1Second)?; + self.push_number(8, u16::from(application_indicator)); + Ok(()) + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Finish + +// This table is copied from ISO/IEC 18004:2006 §6.4.10, Table 7. +static DATA_LENGTHS: [[usize; 4]; 44] = [ + // Normal versions + [152, 128, 104, 72], + [272, 224, 176, 128], + [440, 352, 272, 208], + [640, 512, 384, 288], + [864, 688, 496, 368], + [1088, 864, 608, 480], + [1248, 992, 704, 528], + [1552, 1232, 880, 688], + [1856, 1456, 1056, 800], + [2192, 1728, 1232, 976], + [2592, 2032, 1440, 1120], + [2960, 2320, 1648, 1264], + [3424, 2672, 1952, 1440], + [3688, 2920, 2088, 1576], + [4184, 3320, 2360, 1784], + [4712, 3624, 2600, 2024], + [5176, 4056, 2936, 2264], + [5768, 4504, 3176, 2504], + [6360, 5016, 3560, 2728], + [6888, 5352, 3880, 3080], + [7456, 5712, 4096, 3248], + [8048, 6256, 4544, 3536], + [8752, 6880, 4912, 3712], + [9392, 7312, 5312, 4112], + [10208, 8000, 5744, 4304], + [10960, 8496, 6032, 4768], + [11744, 9024, 6464, 5024], + [12248, 9544, 6968, 5288], + [13048, 10136, 7288, 5608], + [13880, 10984, 7880, 5960], + [14744, 11640, 8264, 6344], + [15640, 12328, 8920, 6760], + [16568, 13048, 9368, 7208], + [17528, 13800, 9848, 7688], + [18448, 14496, 10288, 7888], + [19472, 15312, 10832, 8432], + [20528, 15936, 11408, 8768], + [21616, 16816, 12016, 9136], + [22496, 17728, 12656, 9776], + [23648, 18672, 13328, 10208], + // Micro versions + [20, 0, 0, 0], + [40, 32, 0, 0], + [84, 68, 0, 0], + [128, 112, 80, 0], +]; + +impl Bits { + /// Pushes the ending bits to indicate no more data. + /// + /// # Errors + /// + /// Returns `Err(QrError::DataTooLong)` on overflow. + /// + /// Returns `Err(QrError::InvalidVersion)` if it is not valid to use the + /// `ec_level` for the given version (e.g. `Version::Micro(1)` with + /// `EcLevel::H`). + pub fn push_terminator(&mut self, ec_level: EcLevel) -> QrResult<()> { + let terminator_size = match self.version { + Version::Micro(a) => a.as_usize() * 2 + 1, + _ => 4, + }; + + let cur_length = self.len(); + let data_length = self.max_len(ec_level)?; + if cur_length > data_length { + return Err(QrError::DataTooLong); + } + + let terminator_size = min(terminator_size, data_length - cur_length); + if terminator_size > 0 { + self.push_number(terminator_size, 0); + } + + if self.len() < data_length { + const PADDING_BYTES: &[u8] = &[0b1110_1100, 0b0001_0001]; + + self.bit_offset = 0; + let data_bytes_length = data_length / 8; + let padding_bytes_count = data_bytes_length - self.data.len(); + let padding = PADDING_BYTES.iter().cloned().cycle().take(padding_bytes_count); + self.data.extend(padding); + } + + if self.len() < data_length { + self.data.push(0); + } + + Ok(()) + } +} + +#[cfg(test)] +mod finish_tests { + use crate::bits::Bits; + use crate::types::{EcLevel, QrError, Version}; + + #[test] + fn test_hello_world() { + let mut bits = Bits::new(Version::Normal(1)); + assert_eq!(bits.push_alphanumeric_data(b"HELLO WORLD"), Ok(())); + assert_eq!(bits.push_terminator(EcLevel::Q), Ok(())); + assert_eq!( + bits.into_bytes(), + vec![ + 0b00100000, 0b01011011, 0b00001011, 0b01111000, 0b11010001, 0b01110010, 0b11011100, 0b01001101, + 0b01000011, 0b01000000, 0b11101100, 0b00010001, 0b11101100, + ] + ); + } + + #[test] + fn test_too_long() { + let mut bits = Bits::new(Version::Micro(1)); + assert_eq!(bits.push_numeric_data(b"9999999"), Ok(())); + assert_eq!(bits.push_terminator(EcLevel::L), Err(QrError::DataTooLong)); + } + + #[test] + fn test_no_terminator() { + let mut bits = Bits::new(Version::Micro(1)); + assert_eq!(bits.push_numeric_data(b"99999"), Ok(())); + assert_eq!(bits.push_terminator(EcLevel::L), Ok(())); + assert_eq!(bits.into_bytes(), vec![0b101_11111, 0b00111_110, 0b0011__0000]); + } + + #[test] + fn test_no_padding() { + let mut bits = Bits::new(Version::Micro(1)); + assert_eq!(bits.push_numeric_data(b"9999"), Ok(())); + assert_eq!(bits.push_terminator(EcLevel::L), Ok(())); + assert_eq!(bits.into_bytes(), vec![0b100_11111, 0b00111_100, 0b1_000__0000]); + } + + #[test] + fn test_micro_version_1_half_byte_padding() { + let mut bits = Bits::new(Version::Micro(1)); + assert_eq!(bits.push_numeric_data(b"999"), Ok(())); + assert_eq!(bits.push_terminator(EcLevel::L), Ok(())); + assert_eq!(bits.into_bytes(), vec![0b011_11111, 0b00111_000, 0b0000__0000]); + } + + #[test] + fn test_micro_version_1_full_byte_padding() { + let mut bits = Bits::new(Version::Micro(1)); + assert_eq!(bits.push_numeric_data(b""), Ok(())); + assert_eq!(bits.push_terminator(EcLevel::L), Ok(())); + assert_eq!(bits.into_bytes(), vec![0b000_000_00, 0b11101100, 0]); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Front end. + +impl Bits { + /// Push a segmented data to the bits, and then terminate it. + /// + /// # Errors + /// + /// Returns `Err(QrError::DataTooLong)` on overflow. + /// + /// Returns `Err(QrError::InvalidData)` if the segment refers to incorrectly + /// encoded byte sequence. + pub fn push_segments(&mut self, data: &[u8], segments_iter: I) -> QrResult<()> + where + I: Iterator, + { + for segment in segments_iter { + let slice = &data[segment.begin..segment.end]; + match segment.mode { + Mode::Numeric => self.push_numeric_data(slice), + Mode::Alphanumeric => self.push_alphanumeric_data(slice), + Mode::Byte => self.push_byte_data(slice), + Mode::Kanji => self.push_kanji_data(slice), + }?; + } + Ok(()) + } + + /// Pushes the data the bits, using the optimal encoding. + /// + /// # Errors + /// + /// Returns `Err(QrError::DataTooLong)` on overflow. + pub fn push_optimal_data(&mut self, data: &[u8]) -> QrResult<()> { + let segments = Parser::new(data).optimize(self.version); + self.push_segments(data, segments) + } +} + +#[cfg(test)] +mod encode_tests { + use alloc::vec::Vec; + + use crate::bits::Bits; + use crate::types::{EcLevel, QrError, QrResult, Version}; + + fn encode(data: &[u8], version: Version, ec_level: EcLevel) -> QrResult> { + let mut bits = Bits::new(version); + bits.push_optimal_data(data)?; + bits.push_terminator(ec_level)?; + Ok(bits.into_bytes()) + } + + #[test] + fn test_alphanumeric() { + let res = encode(b"HELLO WORLD", Version::Normal(1), EcLevel::Q); + assert_eq!( + res, + Ok(vec![ + 0b00100000, 0b01011011, 0b00001011, 0b01111000, 0b11010001, 0b01110010, 0b11011100, 0b01001101, + 0b01000011, 0b01000000, 0b11101100, 0b00010001, 0b11101100, + ]) + ); + } + + #[test] + fn test_auto_mode_switch() { + let res = encode(b"123A", Version::Micro(2), EcLevel::L); + assert_eq!(res, Ok(vec![0b0_0011_000, 0b1111011_1, 0b001_00101, 0b0_00000__00, 0b11101100])); + } + + #[test] + fn test_too_long() { + let res = encode(b">>>>>>>>", Version::Normal(1), EcLevel::H); + assert_eq!(res, Err(QrError::DataTooLong)); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Auto version minimization + +/// Automatically determines the minimum version to store the data, and encode +/// the result. +/// +/// This method will not consider any Micro QR code versions. +/// +/// # Errors +/// +/// Returns `Err(QrError::DataTooLong)` if the data is too long to fit even the +/// highest QR code version. +pub fn encode_auto(data: &[u8], ec_level: EcLevel) -> QrResult { + let segments = Parser::new(data).collect::>(); + for version in &[Version::Normal(9), Version::Normal(26), Version::Normal(40)] { + let opt_segments = Optimizer::new(segments.iter().cloned(), *version).collect::>(); + let total_len = total_encoded_len(&*opt_segments, *version); + let data_capacity = version.fetch(ec_level, &DATA_LENGTHS).expect("invalid DATA_LENGTHS"); + if total_len <= data_capacity { + let min_version = find_min_version(total_len, ec_level); + let mut bits = Bits::new(min_version); + bits.reserve(total_len); + bits.push_segments(data, opt_segments.into_iter())?; + bits.push_terminator(ec_level)?; + return Ok(bits); + } + } + Err(QrError::DataTooLong) +} + +/// Finds the smallest version (QR code only) that can store N bits of data +/// in the given error correction level. +fn find_min_version(length: usize, ec_level: EcLevel) -> Version { + let mut base = 0_usize; + let mut size = 39; + while size > 1 { + let half = size / 2; + let mid = base + half; + // mid is always in [0, size). + // mid >= 0: by definition + // mid < size: mid = size / 2 + size / 4 + size / 8 ... + base = if DATA_LENGTHS[mid][ec_level as usize] > length { base } else { mid }; + size -= half; + } + // base is always in [0, mid) because base <= mid. + base = if DATA_LENGTHS[base][ec_level as usize] >= length { base } else { base + 1 }; + Version::Normal((base + 1).as_i16()) +} + +#[cfg(test)] +mod encode_auto_tests { + use crate::bits::{encode_auto, find_min_version}; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_find_min_version() { + assert_eq!(find_min_version(60, EcLevel::L), Version::Normal(1)); + assert_eq!(find_min_version(200, EcLevel::L), Version::Normal(2)); + assert_eq!(find_min_version(200, EcLevel::H), Version::Normal(3)); + assert_eq!(find_min_version(20000, EcLevel::L), Version::Normal(37)); + assert_eq!(find_min_version(640, EcLevel::L), Version::Normal(4)); + assert_eq!(find_min_version(641, EcLevel::L), Version::Normal(5)); + assert_eq!(find_min_version(999999, EcLevel::H), Version::Normal(40)); + } + + #[test] + fn test_alpha_q() { + let bits = encode_auto(b"HELLO WORLD", EcLevel::Q).unwrap(); + assert_eq!(bits.version(), Version::Normal(1)); + } + + #[test] + fn test_alpha_h() { + let bits = encode_auto(b"HELLO WORLD", EcLevel::H).unwrap(); + assert_eq!(bits.version(), Version::Normal(2)); + } + + #[test] + fn test_mixed() { + let bits = encode_auto(b"This is a mixed data test. 1234567890", EcLevel::H).unwrap(); + assert_eq!(bits.version(), Version::Normal(4)); + } +} + +#[cfg(feature = "bench")] +#[bench] +fn bench_find_min_version(bencher: &mut test::Bencher) { + use test::black_box; + + bencher.iter(|| { + black_box(find_min_version(60, EcLevel::L)); + black_box(find_min_version(200, EcLevel::L)); + black_box(find_min_version(200, EcLevel::H)); + black_box(find_min_version(20000, EcLevel::L)); + black_box(find_min_version(640, EcLevel::L)); + black_box(find_min_version(641, EcLevel::L)); + black_box(find_min_version(999999, EcLevel::H)); + }) +} + +//}}} +//------------------------------------------------------------------------------ diff --git a/qrcode-rust/src/canvas.rs b/qrcode-rust/src/canvas.rs new file mode 100644 index 0000000..f30d75a --- /dev/null +++ b/qrcode-rust/src/canvas.rs @@ -0,0 +1,2010 @@ +//! The `canvas` module puts raw bits into the QR code canvas. +//! +//! use qrcode::types::{Version, EcLevel}; +//! use qrcode::canvas::{Canvas, MaskPattern}; +//! +//! let mut c = Canvas::new(Version::Normal(1), EcLevel::L); +//! c.draw_all_functional_patterns(); +//! c.draw_data(b"data_here", b"ec_code_here"); +//! c.apply_mask(MaskPattern::Checkerboard); +//! let bools = c.to_bools(); + +use core::cmp::max; + +use alloc::boxed::Box; +use alloc::vec::Vec; + +use crate::cast::As; +use crate::types::{Color, EcLevel, Version}; + +//------------------------------------------------------------------------------ +//{{{ Modules + +/// The color of a module (pixel) in the QR code. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum Module { + /// The module is empty. + Empty, + + /// The module is of functional patterns which cannot be masked, or pixels + /// which have been masked. + Masked(Color), + + /// The module is of data and error correction bits before masking. + Unmasked(Color), +} + +impl From for Color { + fn from(module: Module) -> Self { + match module { + Module::Empty => Color::Light, + Module::Masked(c) | Module::Unmasked(c) => c, + } + } +} + +impl Module { + /// Checks whether a module is dark. + pub fn is_dark(self) -> bool { + Color::from(self) == Color::Dark + } + + /// Apply a mask to the unmasked modules. + /// + /// use qrcode::canvas::Module; + /// use qrcode::types::Color; + /// + /// assert_eq!(Module::Unmasked(Color::Light).mask(true), Module::Masked(Color::Dark)); + /// assert_eq!(Module::Unmasked(Color::Dark).mask(true), Module::Masked(Color::Light)); + /// assert_eq!(Module::Unmasked(Color::Light).mask(false), Module::Masked(Color::Light)); + /// assert_eq!(Module::Masked(Color::Dark).mask(true), Module::Masked(Color::Dark)); + /// assert_eq!(Module::Masked(Color::Dark).mask(false), Module::Masked(Color::Dark)); + /// + pub fn mask(self, should_invert: bool) -> Self { + match (self, should_invert) { + (Module::Empty, true) => Module::Masked(Color::Dark), + (Module::Empty, false) => Module::Masked(Color::Light), + (Module::Unmasked(c), true) => Module::Masked(!c), + (Module::Unmasked(c), false) | (Module::Masked(c), _) => Module::Masked(c), + } + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Canvas + +/// `Canvas` is an intermediate helper structure to render error-corrected data +/// into a QR code. +#[derive(Clone)] +pub struct Canvas { + /// The width and height of the canvas (cached as it is needed frequently). + width: i16, + + /// The version of the QR code. + version: Version, + + /// The error correction level of the QR code. + ec_level: EcLevel, + + /// The modules of the QR code. Modules are arranged in left-to-right, then + /// top-to-bottom order. + modules: Vec, +} + +#[cfg(test)] +use alloc::string::String; + +impl Canvas { + /// Constructs a new canvas big enough for a QR code of the given version. + pub fn new(version: Version, ec_level: EcLevel) -> Self { + let width = version.width(); + Self { width, version, ec_level, modules: vec![Module::Empty; (width * width).as_usize()] } + } + + /// Converts the canvas into a human-readable string. + #[cfg(test)] + + fn to_debug_str(&self) -> String { + let width = self.width; + let mut res = String::with_capacity((width * (width + 1)) as usize); + for y in 0..width { + res.push('\n'); + for x in 0..width { + res.push(match self.get(x, y) { + Module::Empty => '?', + Module::Masked(Color::Light) => '.', + Module::Masked(Color::Dark) => '#', + Module::Unmasked(Color::Light) => '-', + Module::Unmasked(Color::Dark) => '*', + }); + } + } + res + } + + fn coords_to_index(&self, x: i16, y: i16) -> usize { + let x = if x < 0 { x + self.width } else { x }.as_usize(); + let y = if y < 0 { y + self.width } else { y }.as_usize(); + y * self.width.as_usize() + x + } + + /// Obtains a module at the given coordinates. For convenience, negative + /// coordinates will wrap around. + pub fn get(&self, x: i16, y: i16) -> Module { + self.modules[self.coords_to_index(x, y)] + } + + /// Obtains a mutable module at the given coordinates. For convenience, + /// negative coordinates will wrap around. + pub fn get_mut(&mut self, x: i16, y: i16) -> &mut Module { + let index = self.coords_to_index(x, y); + &mut self.modules[index] + } + + /// Sets the color of a functional module at the given coordinates. For + /// convenience, negative coordinates will wrap around. + pub fn put(&mut self, x: i16, y: i16, color: Color) { + *self.get_mut(x, y) = Module::Masked(color); + } +} + +#[cfg(test)] +mod basic_canvas_tests { + use crate::canvas::{Canvas, Module}; + use crate::types::{Color, EcLevel, Version}; + + #[test] + fn test_index() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + + assert_eq!(c.get(0, 4), Module::Empty); + assert_eq!(c.get(-1, -7), Module::Empty); + assert_eq!(c.get(21 - 1, 21 - 7), Module::Empty); + + c.put(0, 0, Color::Dark); + c.put(-1, -7, Color::Light); + assert_eq!(c.get(0, 0), Module::Masked(Color::Dark)); + assert_eq!(c.get(21 - 1, -7), Module::Masked(Color::Light)); + assert_eq!(c.get(-1, 21 - 7), Module::Masked(Color::Light)); + } + + #[test] + fn test_debug_str() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + + for i in 3_i16..20 { + for j in 3_i16..20 { + *c.get_mut(i, j) = match ((i * 3) ^ j) % 5 { + 0 => Module::Empty, + 1 => Module::Masked(Color::Light), + 2 => Module::Masked(Color::Dark), + 3 => Module::Unmasked(Color::Light), + 4 => Module::Unmasked(Color::Dark), + _ => unreachable!(), + }; + } + } + + assert_eq!( + &*c.to_debug_str(), + "\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????####****....---?\n\ + ???--.##-..##?..#??.?\n\ + ???#*?-.*?#.-*#?-*.??\n\ + ?????*?*?****-*-*---?\n\ + ???*.-.-.-?-?#?#?#*#?\n\ + ???.*#.*.*#.*#*#.*#*?\n\ + ?????.#-#--??.?.#---?\n\ + ???-.?*.-#?-.?#*-#?.?\n\ + ???##*??*..##*--*..??\n\ + ?????-???--??---?---?\n\ + ???*.#.*.#**.#*#.#*#?\n\ + ???##.-##..##..?#..??\n\ + ???.-?*.-?#.-?#*-?#*?\n\ + ????-.#?-.**#?-.#?-.?\n\ + ???**?-**??--**?-**??\n\ + ???#?*?#?*#.*-.-*-.-?\n\ + ???..-...--??###?###?\n\ + ?????????????????????" + ); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Finder patterns + +impl Canvas { + /// Draws a single finder pattern with the center at (x, y). + fn draw_finder_pattern_at(&mut self, x: i16, y: i16) { + let (dx_left, dx_right) = if x >= 0 { (-3, 4) } else { (-4, 3) }; + let (dy_top, dy_bottom) = if y >= 0 { (-3, 4) } else { (-4, 3) }; + for j in dy_top..=dy_bottom { + for i in dx_left..=dx_right { + self.put( + x + i, + y + j, + #[allow(clippy::match_same_arms)] + match (i, j) { + (4, _) | (_, 4) | (-4, _) | (_, -4) => Color::Light, + (3, _) | (_, 3) | (-3, _) | (_, -3) => Color::Dark, + (2, _) | (_, 2) | (-2, _) | (_, -2) => Color::Light, + _ => Color::Dark, + }, + ); + } + } + } + + /// Draws the finder patterns. + /// + /// The finder patterns is are 7×7 square patterns appearing at the three + /// corners of a QR code. They allows scanner to locate the QR code and + /// determine the orientation. + fn draw_finder_patterns(&mut self) { + self.draw_finder_pattern_at(3, 3); + + match self.version { + Version::Micro(_) => {} + Version::Normal(_) => { + self.draw_finder_pattern_at(-4, 3); + self.draw_finder_pattern_at(3, -4); + } + } + } +} + +#[cfg(test)] +mod finder_pattern_tests { + use crate::canvas::Canvas; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_qr() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + c.draw_finder_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######.?????.#######\n\ + #.....#.?????.#.....#\n\ + #.###.#.?????.#.###.#\n\ + #.###.#.?????.#.###.#\n\ + #.###.#.?????.#.###.#\n\ + #.....#.?????.#.....#\n\ + #######.?????.#######\n\ + ........?????........\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ........?????????????\n\ + #######.?????????????\n\ + #.....#.?????????????\n\ + #.###.#.?????????????\n\ + #.###.#.?????????????\n\ + #.###.#.?????????????\n\ + #.....#.?????????????\n\ + #######.?????????????" + ); + } + + #[test] + fn test_micro_qr() { + let mut c = Canvas::new(Version::Micro(1), EcLevel::L); + c.draw_finder_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######.???\n\ + #.....#.???\n\ + #.###.#.???\n\ + #.###.#.???\n\ + #.###.#.???\n\ + #.....#.???\n\ + #######.???\n\ + ........???\n\ + ???????????\n\ + ???????????\n\ + ???????????" + ); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Alignment patterns + +impl Canvas { + /// Draws a alignment pattern with the center at (x, y). + fn draw_alignment_pattern_at(&mut self, x: i16, y: i16) { + if self.get(x, y) != Module::Empty { + return; + } + for j in -2..=2 { + for i in -2..=2 { + self.put( + x + i, + y + j, + match (i, j) { + (2, _) | (_, 2) | (-2, _) | (_, -2) | (0, 0) => Color::Dark, + _ => Color::Light, + }, + ); + } + } + } + + /// Draws the alignment patterns. + /// + /// The alignment patterns are 5×5 square patterns inside the QR code symbol + /// to help the scanner create the square grid. + fn draw_alignment_patterns(&mut self) { + match self.version { + Version::Micro(_) | Version::Normal(1) => {} + Version::Normal(2..=6) => self.draw_alignment_pattern_at(-7, -7), + Version::Normal(a) => { + let positions = ALIGNMENT_PATTERN_POSITIONS[(a - 7).as_usize()]; + for x in positions.iter() { + for y in positions.iter() { + self.draw_alignment_pattern_at(*x, *y); + } + } + } + } + } +} + +#[cfg(test)] +mod alignment_pattern_tests { + use crate::canvas::Canvas; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_draw_alignment_patterns_1() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + c.draw_finder_patterns(); + c.draw_alignment_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######.?????.#######\n\ + #.....#.?????.#.....#\n\ + #.###.#.?????.#.###.#\n\ + #.###.#.?????.#.###.#\n\ + #.###.#.?????.#.###.#\n\ + #.....#.?????.#.....#\n\ + #######.?????.#######\n\ + ........?????........\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ........?????????????\n\ + #######.?????????????\n\ + #.....#.?????????????\n\ + #.###.#.?????????????\n\ + #.###.#.?????????????\n\ + #.###.#.?????????????\n\ + #.....#.?????????????\n\ + #######.?????????????" + ); + } + + #[test] + fn test_draw_alignment_patterns_3() { + let mut c = Canvas::new(Version::Normal(3), EcLevel::L); + c.draw_finder_patterns(); + c.draw_alignment_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######.?????????????.#######\n\ + #.....#.?????????????.#.....#\n\ + #.###.#.?????????????.#.###.#\n\ + #.###.#.?????????????.#.###.#\n\ + #.###.#.?????????????.#.###.#\n\ + #.....#.?????????????.#.....#\n\ + #######.?????????????.#######\n\ + ........?????????????........\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ?????????????????????????????\n\ + ????????????????????#####????\n\ + ........????????????#...#????\n\ + #######.????????????#.#.#????\n\ + #.....#.????????????#...#????\n\ + #.###.#.????????????#####????\n\ + #.###.#.?????????????????????\n\ + #.###.#.?????????????????????\n\ + #.....#.?????????????????????\n\ + #######.?????????????????????" + ); + } + + #[test] + fn test_draw_alignment_patterns_7() { + let mut c = Canvas::new(Version::Normal(7), EcLevel::L); + c.draw_finder_patterns(); + c.draw_alignment_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######.?????????????????????????????.#######\n\ + #.....#.?????????????????????????????.#.....#\n\ + #.###.#.?????????????????????????????.#.###.#\n\ + #.###.#.?????????????????????????????.#.###.#\n\ + #.###.#.????????????#####????????????.#.###.#\n\ + #.....#.????????????#...#????????????.#.....#\n\ + #######.????????????#.#.#????????????.#######\n\ + ........????????????#...#????????????........\n\ + ????????????????????#####????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ????#####???????????#####???????????#####????\n\ + ????#...#???????????#...#???????????#...#????\n\ + ????#.#.#???????????#.#.#???????????#.#.#????\n\ + ????#...#???????????#...#???????????#...#????\n\ + ????#####???????????#####???????????#####????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ????????????????????#####???????????#####????\n\ + ........????????????#...#???????????#...#????\n\ + #######.????????????#.#.#???????????#.#.#????\n\ + #.....#.????????????#...#???????????#...#????\n\ + #.###.#.????????????#####???????????#####????\n\ + #.###.#.?????????????????????????????????????\n\ + #.###.#.?????????????????????????????????????\n\ + #.....#.?????????????????????????????????????\n\ + #######.?????????????????????????????????????" + ); + } +} + +/// `ALIGNMENT_PATTERN_POSITIONS` describes the x- and y-coordinates of the +/// center of the alignment patterns. Since the QR code is symmetric, only one +/// coordinate is needed. +static ALIGNMENT_PATTERN_POSITIONS: [&[i16]; 34] = [ + &[6, 22, 38], + &[6, 24, 42], + &[6, 26, 46], + &[6, 28, 50], + &[6, 30, 54], + &[6, 32, 58], + &[6, 34, 62], + &[6, 26, 46, 66], + &[6, 26, 48, 70], + &[6, 26, 50, 74], + &[6, 30, 54, 78], + &[6, 30, 56, 82], + &[6, 30, 58, 86], + &[6, 34, 62, 90], + &[6, 28, 50, 72, 94], + &[6, 26, 50, 74, 98], + &[6, 30, 54, 78, 102], + &[6, 28, 54, 80, 106], + &[6, 32, 58, 84, 110], + &[6, 30, 58, 86, 114], + &[6, 34, 62, 90, 118], + &[6, 26, 50, 74, 98, 122], + &[6, 30, 54, 78, 102, 126], + &[6, 26, 52, 78, 104, 130], + &[6, 30, 56, 82, 108, 134], + &[6, 34, 60, 86, 112, 138], + &[6, 30, 58, 86, 114, 142], + &[6, 34, 62, 90, 118, 146], + &[6, 30, 54, 78, 102, 126, 150], + &[6, 24, 50, 76, 102, 128, 154], + &[6, 28, 54, 80, 106, 132, 158], + &[6, 32, 58, 84, 110, 136, 162], + &[6, 26, 54, 82, 110, 138, 166], + &[6, 30, 58, 86, 114, 142, 170], +]; + +//}}} +//------------------------------------------------------------------------------ +//{{{ Timing patterns + +impl Canvas { + /// Draws a line from (x1, y1) to (x2, y2), inclusively. + /// + /// The line must be either horizontal or vertical, i.e. + /// `x1 == x2 || y1 == y2`. Additionally, the first coordinates must be less + /// then the second ones. + /// + /// On even coordinates, `color_even` will be plotted; on odd coordinates, + /// `color_odd` will be plotted instead. Thus the timing pattern can be + /// drawn using this method. + /// + fn draw_line(&mut self, x1: i16, y1: i16, x2: i16, y2: i16, color_even: Color, color_odd: Color) { + debug_assert!(x1 == x2 || y1 == y2); + + if y1 == y2 { + // Horizontal line. + for x in x1..=x2 { + self.put(x, y1, if x % 2 == 0 { color_even } else { color_odd }); + } + } else { + // Vertical line. + for y in y1..=y2 { + self.put(x1, y, if y % 2 == 0 { color_even } else { color_odd }); + } + } + } + + /// Draws the timing patterns. + /// + /// The timing patterns are checkboard-colored lines near the edge of the QR + /// code symbol, to establish the fine-grained module coordinates when + /// scanning. + fn draw_timing_patterns(&mut self) { + let width = self.width; + let (y, x1, x2) = match self.version { + Version::Micro(_) => (0, 8, width - 1), + Version::Normal(_) => (6, 8, width - 9), + }; + self.draw_line(x1, y, x2, y, Color::Dark, Color::Light); + self.draw_line(y, x1, y, x2, Color::Dark, Color::Light); + } +} + +#[cfg(test)] +mod timing_pattern_tests { + use crate::canvas::Canvas; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_draw_timing_patterns_qr() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + c.draw_timing_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ????????#.#.#????????\n\ + ?????????????????????\n\ + ??????#??????????????\n\ + ??????.??????????????\n\ + ??????#??????????????\n\ + ??????.??????????????\n\ + ??????#??????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????" + ); + } + + #[test] + fn test_draw_timing_patterns_micro_qr() { + let mut c = Canvas::new(Version::Micro(1), EcLevel::L); + c.draw_timing_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + ????????#.#\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + #??????????\n\ + .??????????\n\ + #??????????" + ); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Format info & Version info + +impl Canvas { + /// Draws a big-endian integer onto the canvas with the given coordinates. + /// + /// The 1 bits will be plotted with `on_color` and the 0 bits with + /// `off_color`. The coordinates will be extracted from the `coords` + /// iterator. It will start from the most significant bits first, so + /// *trailing* zeros will be ignored. + fn draw_number(&mut self, number: u32, bits: u32, on_color: Color, off_color: Color, coords: &[(i16, i16)]) { + let mut mask = 1 << (bits - 1); + for &(x, y) in coords { + let color = if (mask & number) == 0 { off_color } else { on_color }; + self.put(x, y, color); + mask >>= 1; + } + } + + /// Draws the format info patterns for an encoded number. + fn draw_format_info_patterns_with_number(&mut self, format_info: u16) { + let format_info = u32::from(format_info); + match self.version { + Version::Micro(_) => { + self.draw_number(format_info, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_MICRO_QR); + } + Version::Normal(_) => { + self.draw_number(format_info, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_QR_MAIN); + self.draw_number(format_info, 15, Color::Dark, Color::Light, &FORMAT_INFO_COORDS_QR_SIDE); + self.put(8, -8, Color::Dark); // Dark module. + } + } + } + + /// Reserves area to put in the format information. + fn draw_reserved_format_info_patterns(&mut self) { + self.draw_format_info_patterns_with_number(0); + } + + /// Draws the version information patterns. + fn draw_version_info_patterns(&mut self) { + match self.version { + Version::Micro(_) | Version::Normal(1..=6) => {} + Version::Normal(a) => { + let version_info = VERSION_INFOS[(a - 7).as_usize()]; + self.draw_number(version_info, 18, Color::Dark, Color::Light, &VERSION_INFO_COORDS_BL); + self.draw_number(version_info, 18, Color::Dark, Color::Light, &VERSION_INFO_COORDS_TR); + } + } + } +} + +#[cfg(test)] +mod draw_version_info_tests { + use crate::canvas::Canvas; + use crate::types::{Color, EcLevel, Version}; + + #[test] + fn test_draw_number() { + let mut c = Canvas::new(Version::Micro(1), EcLevel::L); + c.draw_number(0b10101101, 8, Color::Dark, Color::Light, &[(0, 0), (0, -1), (-2, -2), (-2, 0)]); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #????????.?\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ???????????\n\ + ?????????#?\n\ + .??????????" + ); + } + + #[test] + fn test_draw_version_info_1() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + c.draw_version_info_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????" + ); + } + + #[test] + fn test_draw_version_info_7() { + let mut c = Canvas::new(Version::Normal(7), EcLevel::L); + c.draw_version_info_patterns(); + + assert_eq!( + &*c.to_debug_str(), + "\n\ + ??????????????????????????????????..#????????\n\ + ??????????????????????????????????.#.????????\n\ + ??????????????????????????????????.#.????????\n\ + ??????????????????????????????????.##????????\n\ + ??????????????????????????????????###????????\n\ + ??????????????????????????????????...????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ....#.???????????????????????????????????????\n\ + .####.???????????????????????????????????????\n\ + #..##.???????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????\n\ + ?????????????????????????????????????????????" + ); + } + + #[test] + fn test_draw_reserved_format_info_patterns_qr() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + c.draw_reserved_format_info_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ?????????????????????\n\ + ????????.????????????\n\ + ......?..????........\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ????????#????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????.????????????" + ); + } + + #[test] + fn test_draw_reserved_format_info_patterns_micro_qr() { + let mut c = Canvas::new(Version::Micro(1), EcLevel::L); + c.draw_reserved_format_info_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + ???????????\n\ + ????????.??\n\ + ????????.??\n\ + ????????.??\n\ + ????????.??\n\ + ????????.??\n\ + ????????.??\n\ + ????????.??\n\ + ?........??\n\ + ???????????\n\ + ???????????" + ); + } +} + +static VERSION_INFO_COORDS_BL: [(i16, i16); 18] = [ + (5, -9), + (5, -10), + (5, -11), + (4, -9), + (4, -10), + (4, -11), + (3, -9), + (3, -10), + (3, -11), + (2, -9), + (2, -10), + (2, -11), + (1, -9), + (1, -10), + (1, -11), + (0, -9), + (0, -10), + (0, -11), +]; + +static VERSION_INFO_COORDS_TR: [(i16, i16); 18] = [ + (-9, 5), + (-10, 5), + (-11, 5), + (-9, 4), + (-10, 4), + (-11, 4), + (-9, 3), + (-10, 3), + (-11, 3), + (-9, 2), + (-10, 2), + (-11, 2), + (-9, 1), + (-10, 1), + (-11, 1), + (-9, 0), + (-10, 0), + (-11, 0), +]; + +static FORMAT_INFO_COORDS_QR_MAIN: [(i16, i16); 15] = [ + (0, 8), + (1, 8), + (2, 8), + (3, 8), + (4, 8), + (5, 8), + (7, 8), + (8, 8), + (8, 7), + (8, 5), + (8, 4), + (8, 3), + (8, 2), + (8, 1), + (8, 0), +]; + +static FORMAT_INFO_COORDS_QR_SIDE: [(i16, i16); 15] = [ + (8, -1), + (8, -2), + (8, -3), + (8, -4), + (8, -5), + (8, -6), + (8, -7), + (-8, 8), + (-7, 8), + (-6, 8), + (-5, 8), + (-4, 8), + (-3, 8), + (-2, 8), + (-1, 8), +]; + +static FORMAT_INFO_COORDS_MICRO_QR: [(i16, i16); 15] = [ + (1, 8), + (2, 8), + (3, 8), + (4, 8), + (5, 8), + (6, 8), + (7, 8), + (8, 8), + (8, 7), + (8, 6), + (8, 5), + (8, 4), + (8, 3), + (8, 2), + (8, 1), +]; + +static VERSION_INFOS: [u32; 34] = [ + 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78, 0x1145d, 0x12a17, + 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, + 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69, +]; + +//}}} +//------------------------------------------------------------------------------ +//{{{ All functional patterns before data placement + +impl Canvas { + /// Draw all functional patterns, before data placement. + /// + /// All functional patterns (e.g. the finder pattern) *except* the format + /// info pattern will be filled in. The format info pattern will be filled + /// with light modules instead. Data bits can then put in the empty modules. + /// with `.draw_data()`. + pub fn draw_all_functional_patterns(&mut self) { + self.draw_finder_patterns(); + self.draw_alignment_patterns(); + self.draw_reserved_format_info_patterns(); + self.draw_timing_patterns(); + self.draw_version_info_patterns(); + } +} + +/// Gets whether the module at the given coordinates represents a functional +/// module. +pub fn is_functional(version: Version, width: i16, x: i16, y: i16) -> bool { + debug_assert!(width == version.width()); + + let x = if x < 0 { x + width } else { x }; + let y = if y < 0 { y + width } else { y }; + + match version { + Version::Micro(_) => x == 0 || y == 0 || (x < 9 && y < 9), + Version::Normal(a) => { + let non_alignment_test = x == 6 || y == 6 || // Timing patterns + (x < 9 && y < 9) || // Top-left finder pattern + (x < 9 && y >= width-8) || // Bottom-left finder pattern + (x >= width-8 && y < 9); // Top-right finder pattern + if non_alignment_test { + true + } else if a == 1 { + false + } else if 2 <= a && a <= 6 { + (width - 7 - x).abs() <= 2 && (width - 7 - y).abs() <= 2 + } else { + let positions = ALIGNMENT_PATTERN_POSITIONS[(a - 7).as_usize()]; + let last = positions.len() - 1; + for (i, align_x) in positions.iter().enumerate() { + for (j, align_y) in positions.iter().enumerate() { + if i == 0 && (j == 0 || j == last) || (i == last && j == 0) { + continue; + } + if (*align_x - x).abs() <= 2 && (*align_y - y).abs() <= 2 { + return true; + } + } + } + false + } + } + } +} + +#[cfg(test)] +mod all_functional_patterns_tests { + use crate::canvas::{is_functional, Canvas}; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_all_functional_patterns_qr() { + let mut c = Canvas::new(Version::Normal(2), EcLevel::L); + c.draw_all_functional_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######..????????.#######\n\ + #.....#..????????.#.....#\n\ + #.###.#..????????.#.###.#\n\ + #.###.#..????????.#.###.#\n\ + #.###.#..????????.#.###.#\n\ + #.....#..????????.#.....#\n\ + #######.#.#.#.#.#.#######\n\ + .........????????........\n\ + ......#..????????........\n\ + ??????.??????????????????\n\ + ??????#??????????????????\n\ + ??????.??????????????????\n\ + ??????#??????????????????\n\ + ??????.??????????????????\n\ + ??????#??????????????????\n\ + ??????.??????????????????\n\ + ??????#?????????#####????\n\ + ........#???????#...#????\n\ + #######..???????#.#.#????\n\ + #.....#..???????#...#????\n\ + #.###.#..???????#####????\n\ + #.###.#..????????????????\n\ + #.###.#..????????????????\n\ + #.....#..????????????????\n\ + #######..????????????????" + ); + } + + #[test] + fn test_all_functional_patterns_micro_qr() { + let mut c = Canvas::new(Version::Micro(1), EcLevel::L); + c.draw_all_functional_patterns(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######.#.#\n\ + #.....#..??\n\ + #.###.#..??\n\ + #.###.#..??\n\ + #.###.#..??\n\ + #.....#..??\n\ + #######..??\n\ + .........??\n\ + #........??\n\ + .??????????\n\ + #??????????" + ); + } + + #[test] + fn test_is_functional_qr_1() { + let version = Version::Normal(1); + assert!(is_functional(version, version.width(), 0, 0)); + assert!(is_functional(version, version.width(), 10, 6)); + assert!(!is_functional(version, version.width(), 10, 5)); + assert!(!is_functional(version, version.width(), 14, 14)); + assert!(is_functional(version, version.width(), 6, 11)); + assert!(!is_functional(version, version.width(), 4, 11)); + assert!(is_functional(version, version.width(), 4, 13)); + assert!(is_functional(version, version.width(), 17, 7)); + assert!(!is_functional(version, version.width(), 17, 17)); + } + + #[test] + fn test_is_functional_qr_3() { + let version = Version::Normal(3); + assert!(is_functional(version, version.width(), 0, 0)); + assert!(!is_functional(version, version.width(), 25, 24)); + assert!(is_functional(version, version.width(), 24, 24)); + assert!(!is_functional(version, version.width(), 9, 25)); + assert!(!is_functional(version, version.width(), 20, 0)); + assert!(is_functional(version, version.width(), 21, 0)); + } + + #[test] + fn test_is_functional_qr_7() { + let version = Version::Normal(7); + assert!(is_functional(version, version.width(), 21, 4)); + assert!(is_functional(version, version.width(), 7, 21)); + assert!(is_functional(version, version.width(), 22, 22)); + assert!(is_functional(version, version.width(), 8, 8)); + assert!(!is_functional(version, version.width(), 19, 5)); + assert!(!is_functional(version, version.width(), 36, 3)); + assert!(!is_functional(version, version.width(), 4, 36)); + assert!(is_functional(version, version.width(), 38, 38)); + } + + #[test] + fn test_is_functional_micro() { + let version = Version::Micro(1); + assert!(is_functional(version, version.width(), 8, 0)); + assert!(is_functional(version, version.width(), 10, 0)); + assert!(!is_functional(version, version.width(), 10, 1)); + assert!(is_functional(version, version.width(), 8, 8)); + assert!(is_functional(version, version.width(), 0, 9)); + assert!(!is_functional(version, version.width(), 1, 9)); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Data placement iterator + +struct DataModuleIter { + x: i16, + y: i16, + width: i16, + timing_pattern_column: i16, +} + +impl DataModuleIter { + fn new(version: Version) -> Self { + let width = version.width(); + Self { + x: width - 1, + y: width - 1, + width, + timing_pattern_column: match version { + Version::Micro(_) => 0, + Version::Normal(_) => 6, + }, + } + } +} + +impl Iterator for DataModuleIter { + type Item = (i16, i16); + + fn next(&mut self) -> Option<(i16, i16)> { + let adjusted_ref_col = if self.x <= self.timing_pattern_column { self.x + 1 } else { self.x }; + if adjusted_ref_col <= 0 { + return None; + } + + let res = (self.x, self.y); + let column_type = (self.width - adjusted_ref_col) % 4; + + match column_type { + 2 if self.y > 0 => { + self.y -= 1; + self.x += 1; + } + 0 if self.y < self.width - 1 => { + self.y += 1; + self.x += 1; + } + 0 | 2 if self.x == self.timing_pattern_column + 1 => { + self.x -= 2; + } + _ => { + self.x -= 1; + } + } + + Some(res) + } +} + +#[cfg(test)] +#[rustfmt::skip] // skip to prevent file becoming too long. +mod data_iter_tests { + use alloc::vec::{*,Vec}; + + use crate::canvas::DataModuleIter; + use crate::types::Version; + + #[test] + fn test_qr() { + let res = DataModuleIter::new(Version::Normal(1)).collect::>(); + assert_eq!(res, vec![ + (20, 20), (19, 20), (20, 19), (19, 19), (20, 18), (19, 18), + (20, 17), (19, 17), (20, 16), (19, 16), (20, 15), (19, 15), + (20, 14), (19, 14), (20, 13), (19, 13), (20, 12), (19, 12), + (20, 11), (19, 11), (20, 10), (19, 10), (20, 9), (19, 9), + (20, 8), (19, 8), (20, 7), (19, 7), (20, 6), (19, 6), + (20, 5), (19, 5), (20, 4), (19, 4), (20, 3), (19, 3), + (20, 2), (19, 2), (20, 1), (19, 1), (20, 0), (19, 0), + + (18, 0), (17, 0), (18, 1), (17, 1), (18, 2), (17, 2), + (18, 3), (17, 3), (18, 4), (17, 4), (18, 5), (17, 5), + (18, 6), (17, 6), (18, 7), (17, 7), (18, 8), (17, 8), + (18, 9), (17, 9), (18, 10), (17, 10), (18, 11), (17, 11), + (18, 12), (17, 12), (18, 13), (17, 13), (18, 14), (17, 14), + (18, 15), (17, 15), (18, 16), (17, 16), (18, 17), (17, 17), + (18, 18), (17, 18), (18, 19), (17, 19), (18, 20), (17, 20), + + (16, 20), (15, 20), (16, 19), (15, 19), (16, 18), (15, 18), + (16, 17), (15, 17), (16, 16), (15, 16), (16, 15), (15, 15), + (16, 14), (15, 14), (16, 13), (15, 13), (16, 12), (15, 12), + (16, 11), (15, 11), (16, 10), (15, 10), (16, 9), (15, 9), + (16, 8), (15, 8), (16, 7), (15, 7), (16, 6), (15, 6), + (16, 5), (15, 5), (16, 4), (15, 4), (16, 3), (15, 3), + (16, 2), (15, 2), (16, 1), (15, 1), (16, 0), (15, 0), + + (14, 0), (13, 0), (14, 1), (13, 1), (14, 2), (13, 2), + (14, 3), (13, 3), (14, 4), (13, 4), (14, 5), (13, 5), + (14, 6), (13, 6), (14, 7), (13, 7), (14, 8), (13, 8), + (14, 9), (13, 9), (14, 10), (13, 10), (14, 11), (13, 11), + (14, 12), (13, 12), (14, 13), (13, 13), (14, 14), (13, 14), + (14, 15), (13, 15), (14, 16), (13, 16), (14, 17), (13, 17), + (14, 18), (13, 18), (14, 19), (13, 19), (14, 20), (13, 20), + + (12, 20), (11, 20), (12, 19), (11, 19), (12, 18), (11, 18), + (12, 17), (11, 17), (12, 16), (11, 16), (12, 15), (11, 15), + (12, 14), (11, 14), (12, 13), (11, 13), (12, 12), (11, 12), + (12, 11), (11, 11), (12, 10), (11, 10), (12, 9), (11, 9), + (12, 8), (11, 8), (12, 7), (11, 7), (12, 6), (11, 6), + (12, 5), (11, 5), (12, 4), (11, 4), (12, 3), (11, 3), + (12, 2), (11, 2), (12, 1), (11, 1), (12, 0), (11, 0), + + (10, 0), (9, 0), (10, 1), (9, 1), (10, 2), (9, 2), + (10, 3), (9, 3), (10, 4), (9, 4), (10, 5), (9, 5), + (10, 6), (9, 6), (10, 7), (9, 7), (10, 8), (9, 8), + (10, 9), (9, 9), (10, 10), (9, 10), (10, 11), (9, 11), + (10, 12), (9, 12), (10, 13), (9, 13), (10, 14), (9, 14), + (10, 15), (9, 15), (10, 16), (9, 16), (10, 17), (9, 17), + (10, 18), (9, 18), (10, 19), (9, 19), (10, 20), (9, 20), + + (8, 20), (7, 20), (8, 19), (7, 19), (8, 18), (7, 18), + (8, 17), (7, 17), (8, 16), (7, 16), (8, 15), (7, 15), + (8, 14), (7, 14), (8, 13), (7, 13), (8, 12), (7, 12), + (8, 11), (7, 11), (8, 10), (7, 10), (8, 9), (7, 9), + (8, 8), (7, 8), (8, 7), (7, 7), (8, 6), (7, 6), + (8, 5), (7, 5), (8, 4), (7, 4), (8, 3), (7, 3), + (8, 2), (7, 2), (8, 1), (7, 1), (8, 0), (7, 0), + + (5, 0), (4, 0), (5, 1), (4, 1), (5, 2), (4, 2), + (5, 3), (4, 3), (5, 4), (4, 4), (5, 5), (4, 5), + (5, 6), (4, 6), (5, 7), (4, 7), (5, 8), (4, 8), + (5, 9), (4, 9), (5, 10), (4, 10), (5, 11), (4, 11), + (5, 12), (4, 12), (5, 13), (4, 13), (5, 14), (4, 14), + (5, 15), (4, 15), (5, 16), (4, 16), (5, 17), (4, 17), + (5, 18), (4, 18), (5, 19), (4, 19), (5, 20), (4, 20), + + (3, 20), (2, 20), (3, 19), (2, 19), (3, 18), (2, 18), + (3, 17), (2, 17), (3, 16), (2, 16), (3, 15), (2, 15), + (3, 14), (2, 14), (3, 13), (2, 13), (3, 12), (2, 12), + (3, 11), (2, 11), (3, 10), (2, 10), (3, 9), (2, 9), + (3, 8), (2, 8), (3, 7), (2, 7), (3, 6), (2, 6), + (3, 5), (2, 5), (3, 4), (2, 4), (3, 3), (2, 3), + (3, 2), (2, 2), (3, 1), (2, 1), (3, 0), (2, 0), + + (1, 0), (0, 0), (1, 1), (0, 1), (1, 2), (0, 2), + (1, 3), (0, 3), (1, 4), (0, 4), (1, 5), (0, 5), + (1, 6), (0, 6), (1, 7), (0, 7), (1, 8), (0, 8), + (1, 9), (0, 9), (1, 10), (0, 10), (1, 11), (0, 11), + (1, 12), (0, 12), (1, 13), (0, 13), (1, 14), (0, 14), + (1, 15), (0, 15), (1, 16), (0, 16), (1, 17), (0, 17), + (1, 18), (0, 18), (1, 19), (0, 19), (1, 20), (0, 20), + ]); + } + + #[test] + fn test_micro_qr() { + let res = DataModuleIter::new(Version::Micro(1)).collect::>(); + assert_eq!(res, vec![ + (10, 10), (9, 10), (10, 9), (9, 9), (10, 8), (9, 8), + (10, 7), (9, 7), (10, 6), (9, 6), (10, 5), (9, 5), + (10, 4), (9, 4), (10, 3), (9, 3), (10, 2), (9, 2), + (10, 1), (9, 1), (10, 0), (9, 0), + + (8, 0), (7, 0), (8, 1), (7, 1), (8, 2), (7, 2), + (8, 3), (7, 3), (8, 4), (7, 4), (8, 5), (7, 5), + (8, 6), (7, 6), (8, 7), (7, 7), (8, 8), (7, 8), + (8, 9), (7, 9), (8, 10), (7, 10), + + (6, 10), (5, 10), (6, 9), (5, 9), (6, 8), (5, 8), + (6, 7), (5, 7), (6, 6), (5, 6), (6, 5), (5, 5), + (6, 4), (5, 4), (6, 3), (5, 3), (6, 2), (5, 2), + (6, 1), (5, 1), (6, 0), (5, 0), + + (4, 0), (3, 0), (4, 1), (3, 1), (4, 2), (3, 2), + (4, 3), (3, 3), (4, 4), (3, 4), (4, 5), (3, 5), + (4, 6), (3, 6), (4, 7), (3, 7), (4, 8), (3, 8), + (4, 9), (3, 9), (4, 10), (3, 10), + + (2, 10), (1, 10), (2, 9), (1, 9), (2, 8), (1, 8), + (2, 7), (1, 7), (2, 6), (1, 6), (2, 5), (1, 5), + (2, 4), (1, 4), (2, 3), (1, 3), (2, 2), (1, 2), + (2, 1), (1, 1), (2, 0), (1, 0), + ]); + } + + #[test] + fn test_micro_qr_2() { + let res = DataModuleIter::new(Version::Micro(2)).collect::>(); + assert_eq!(res, vec![ + (12, 12), (11, 12), (12, 11), (11, 11), (12, 10), (11, 10), + (12, 9), (11, 9), (12, 8), (11, 8), (12, 7), (11, 7), + (12, 6), (11, 6), (12, 5), (11, 5), (12, 4), (11, 4), + (12, 3), (11, 3), (12, 2), (11, 2), (12, 1), (11, 1), + (12, 0), (11, 0), + + (10, 0), (9, 0), (10, 1), (9, 1), (10, 2), (9, 2), + (10, 3), (9, 3), (10, 4), (9, 4), (10, 5), (9, 5), + (10, 6), (9, 6), (10, 7), (9, 7), (10, 8), (9, 8), + (10, 9), (9, 9), (10, 10), (9, 10), (10, 11), (9, 11), + (10, 12), (9, 12), + + (8, 12), (7, 12), (8, 11), (7, 11), (8, 10), (7, 10), + (8, 9), (7, 9), (8, 8), (7, 8), (8, 7), (7, 7), + (8, 6), (7, 6), (8, 5), (7, 5), (8, 4), (7, 4), + (8, 3), (7, 3), (8, 2), (7, 2), (8, 1), (7, 1), + (8, 0), (7, 0), + + (6, 0), (5, 0), (6, 1), (5, 1), (6, 2), (5, 2), + (6, 3), (5, 3), (6, 4), (5, 4), (6, 5), (5, 5), + (6, 6), (5, 6), (6, 7), (5, 7), (6, 8), (5, 8), + (6, 9), (5, 9), (6, 10), (5, 10), (6, 11), (5, 11), + (6, 12), (5, 12), + + (4, 12), (3, 12), (4, 11), (3, 11), (4, 10), (3, 10), + (4, 9), (3, 9), (4, 8), (3, 8), (4, 7), (3, 7), + (4, 6), (3, 6), (4, 5), (3, 5), (4, 4), (3, 4), + (4, 3), (3, 3), (4, 2), (3, 2), (4, 1), (3, 1), + (4, 0), (3, 0), + + (2, 0), (1, 0), (2, 1), (1, 1), (2, 2), (1, 2), + (2, 3), (1, 3), (2, 4), (1, 4), (2, 5), (1, 5), + (2, 6), (1, 6), (2, 7), (1, 7), (2, 8), (1, 8), + (2, 9), (1, 9), (2, 10), (1, 10), (2, 11), (1, 11), + (2, 12), (1, 12), + ]); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Data placement + +impl Canvas { + fn draw_codewords(&mut self, codewords: &[u8], is_half_codeword_at_end: bool, coords: &mut I) + where + I: Iterator, + { + let length = codewords.len(); + let last_word = if is_half_codeword_at_end { length - 1 } else { length }; + for (i, b) in codewords.iter().enumerate() { + let bits_end = if i == last_word { 4 } else { 0 }; + 'outside: for j in (bits_end..=7).rev() { + let color = if (*b & (1 << j)) == 0 { Color::Light } else { Color::Dark }; + while let Some((x, y)) = coords.next() { + let r = self.get_mut(x, y); + if *r == Module::Empty { + *r = Module::Unmasked(color); + continue 'outside; + } + } + return; + } + } + } + + /// Draws the encoded data and error correction codes to the empty modules. + pub fn draw_data(&mut self, data: &[u8], ec: &[u8]) { + let is_half_codeword_at_end = match (self.version, self.ec_level) { + (Version::Micro(1), EcLevel::L) | (Version::Micro(3), EcLevel::M) => true, + _ => false, + }; + + let mut coords = DataModuleIter::new(self.version); + self.draw_codewords(data, is_half_codeword_at_end, &mut coords); + self.draw_codewords(ec, false, &mut coords); + } +} + +#[cfg(test)] +mod draw_codewords_test { + use crate::canvas::Canvas; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_micro_qr_1() { + let mut c = Canvas::new(Version::Micro(1), EcLevel::L); + c.draw_all_functional_patterns(); + c.draw_data(b"\x6e\x5d\xe2", b"\x2b\x63"); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######.#.#\n\ + #.....#..-*\n\ + #.###.#..**\n\ + #.###.#..*-\n\ + #.###.#..**\n\ + #.....#..*-\n\ + #######..*-\n\ + .........-*\n\ + #........**\n\ + .***-**---*\n\ + #---*-*-**-" + ); + } + + #[test] + fn test_qr_2() { + let mut c = Canvas::new(Version::Normal(2), EcLevel::L); + c.draw_all_functional_patterns(); + c.draw_data( + b"\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\ + \x92I$\x92I$\x92I$\x92I$\x92I$\x92I$\x92I$", + b"", + ); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######..--*---*-.#######\n\ + #.....#..-*-*-*-*.#.....#\n\ + #.###.#..*---*---.#.###.#\n\ + #.###.#..--*---*-.#.###.#\n\ + #.###.#..-*-*-*-*.#.###.#\n\ + #.....#..*---*---.#.....#\n\ + #######.#.#.#.#.#.#######\n\ + .........--*---*-........\n\ + ......#..-*-*-*-*........\n\ + --*-*-.-**---*---*--**--*\n\ + -*-*--#----*---*---------\n\ + *----*.*--*-*-*-*-**--**-\n\ + --*-*-#-**---*---*--**--*\n\ + -*-*--.----*---*---------\n\ + *----*#*--*-*-*-*-**--**-\n\ + --*-*-.-**---*---*--**--*\n\ + -*-*--#----*---*#####----\n\ + ........#-*-*-*-#...#-**-\n\ + #######..*---*--#.#.#*--*\n\ + #.....#..--*---*#...#----\n\ + #.###.#..-*-*-*-#####-**-\n\ + #.###.#..*---*--*----*--*\n\ + #.###.#..--*------**-----\n\ + #.....#..-*-*-**-*--*-**-\n\ + #######..*---*--*----*--*" + ); + } +} +//}}} +//------------------------------------------------------------------------------ +//{{{ Masking + +/// The mask patterns. Since QR code and Micro QR code do not use the same +/// pattern number, we name them according to their shape instead of the number. +#[derive(Debug, Copy, Clone)] +pub enum MaskPattern { + /// QR code pattern 000: `(x + y) % 2 == 0`. + Checkerboard = 0b000, + + /// QR code pattern 001: `y % 2 == 0`. + HorizontalLines = 0b001, + + /// QR code pattern 010: `x % 3 == 0`. + VerticalLines = 0b010, + + /// QR code pattern 011: `(x + y) % 3 == 0`. + DiagonalLines = 0b011, + + /// QR code pattern 100: `((x/3) + (y/2)) % 2 == 0`. + LargeCheckerboard = 0b100, + + /// QR code pattern 101: `(x*y)%2 + (x*y)%3 == 0`. + Fields = 0b101, + + /// QR code pattern 110: `((x*y)%2 + (x*y)%3) % 2 == 0`. + Diamonds = 0b110, + + /// QR code pattern 111: `((x+y)%2 + (x*y)%3) % 2 == 0`. + Meadow = 0b111, +} + +mod mask_functions { + pub fn checkerboard(x: i16, y: i16) -> bool { + (x + y) % 2 == 0 + } + pub fn horizontal_lines(_: i16, y: i16) -> bool { + y % 2 == 0 + } + pub fn vertical_lines(x: i16, _: i16) -> bool { + x % 3 == 0 + } + pub fn diagonal_lines(x: i16, y: i16) -> bool { + (x + y) % 3 == 0 + } + pub fn large_checkerboard(x: i16, y: i16) -> bool { + ((y / 2) + (x / 3)) % 2 == 0 + } + pub fn fields(x: i16, y: i16) -> bool { + (x * y) % 2 + (x * y) % 3 == 0 + } + pub fn diamonds(x: i16, y: i16) -> bool { + ((x * y) % 2 + (x * y) % 3) % 2 == 0 + } + pub fn meadow(x: i16, y: i16) -> bool { + ((x + y) % 2 + (x * y) % 3) % 2 == 0 + } +} + +fn get_mask_function(pattern: MaskPattern) -> fn(i16, i16) -> bool { + match pattern { + MaskPattern::Checkerboard => mask_functions::checkerboard, + MaskPattern::HorizontalLines => mask_functions::horizontal_lines, + MaskPattern::VerticalLines => mask_functions::vertical_lines, + MaskPattern::DiagonalLines => mask_functions::diagonal_lines, + MaskPattern::LargeCheckerboard => mask_functions::large_checkerboard, + MaskPattern::Fields => mask_functions::fields, + MaskPattern::Diamonds => mask_functions::diamonds, + MaskPattern::Meadow => mask_functions::meadow, + } +} + +impl Canvas { + /// Applies a mask to the canvas. This method will also draw the format info + /// patterns. + pub fn apply_mask(&mut self, pattern: MaskPattern) { + let mask_fn = get_mask_function(pattern); + for x in 0..self.width { + for y in 0..self.width { + let module = self.get_mut(x, y); + *module = module.mask(mask_fn(x, y)); + } + } + + self.draw_format_info_patterns(pattern); + } + + /// Draws the format information to encode the error correction level and + /// mask pattern. + /// + /// If the error correction level or mask pattern is not supported in the + /// current QR code version, this method will fail. + fn draw_format_info_patterns(&mut self, pattern: MaskPattern) { + let format_number = match self.version { + Version::Normal(_) => { + let simple_format_number = ((self.ec_level as usize) ^ 1) << 3 | (pattern as usize); + FORMAT_INFOS_QR[simple_format_number] + } + Version::Micro(a) => { + let micro_pattern_number = match pattern { + MaskPattern::HorizontalLines => 0b00, + MaskPattern::LargeCheckerboard => 0b01, + MaskPattern::Diamonds => 0b10, + MaskPattern::Meadow => 0b11, + _ => panic!("Unsupported mask pattern in Micro QR code"), + }; + let symbol_number = match (a, self.ec_level) { + (1, EcLevel::L) => 0b000, + (2, EcLevel::L) => 0b001, + (2, EcLevel::M) => 0b010, + (3, EcLevel::L) => 0b011, + (3, EcLevel::M) => 0b100, + (4, EcLevel::L) => 0b101, + (4, EcLevel::M) => 0b110, + (4, EcLevel::Q) => 0b111, + _ => panic!("Unsupported version/ec_level combination in Micro QR code"), + }; + let simple_format_number = symbol_number << 2 | micro_pattern_number; + FORMAT_INFOS_MICRO_QR[simple_format_number] + } + }; + self.draw_format_info_patterns_with_number(format_number); + } +} + +#[cfg(test)] +mod mask_tests { + use crate::canvas::{Canvas, MaskPattern}; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_apply_mask_qr() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + c.draw_all_functional_patterns(); + c.apply_mask(MaskPattern::Checkerboard); + + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######...#.#.#######\n\ + #.....#..#.#..#.....#\n\ + #.###.#.#.#.#.#.###.#\n\ + #.###.#..#.#..#.###.#\n\ + #.###.#...#.#.#.###.#\n\ + #.....#..#.#..#.....#\n\ + #######.#.#.#.#######\n\ + ........##.#.........\n\ + ###.#####.#.###...#..\n\ + .#.#.#.#.#.#.#.#.#.#.\n\ + #.#.#.#.#.#.#.#.#.#.#\n\ + .#.#.#.#.#.#.#.#.#.#.\n\ + #.#.#.#.#.#.#.#.#.#.#\n\ + ........##.#.#.#.#.#.\n\ + #######.#.#.#.#.#.#.#\n\ + #.....#.##.#.#.#.#.#.\n\ + #.###.#.#.#.#.#.#.#.#\n\ + #.###.#..#.#.#.#.#.#.\n\ + #.###.#.#.#.#.#.#.#.#\n\ + #.....#.##.#.#.#.#.#.\n\ + #######.#.#.#.#.#.#.#" + ); + } + + #[test] + fn test_draw_format_info_patterns_qr() { + let mut c = Canvas::new(Version::Normal(1), EcLevel::L); + c.draw_format_info_patterns(MaskPattern::LargeCheckerboard); + assert_eq!( + &*c.to_debug_str(), + "\n\ + ????????#????????????\n\ + ????????#????????????\n\ + ????????#????????????\n\ + ????????#????????????\n\ + ????????.????????????\n\ + ????????#????????????\n\ + ?????????????????????\n\ + ????????.????????????\n\ + ##..##?..????..#.####\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ?????????????????????\n\ + ????????#????????????\n\ + ????????.????????????\n\ + ????????#????????????\n\ + ????????#????????????\n\ + ????????.????????????\n\ + ????????.????????????\n\ + ????????#????????????\n\ + ????????#????????????" + ); + } + + #[test] + fn test_draw_format_info_patterns_micro_qr() { + let mut c = Canvas::new(Version::Micro(2), EcLevel::L); + c.draw_format_info_patterns(MaskPattern::LargeCheckerboard); + assert_eq!( + &*c.to_debug_str(), + "\n\ + ?????????????\n\ + ????????#????\n\ + ????????.????\n\ + ????????.????\n\ + ????????#????\n\ + ????????#????\n\ + ????????.????\n\ + ????????.????\n\ + ?#.#....#????\n\ + ?????????????\n\ + ?????????????\n\ + ?????????????\n\ + ?????????????" + ); + } +} + +static FORMAT_INFOS_QR: [u16; 32] = [ + 0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, + 0x6c41, 0x6976, 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b, 0x355f, 0x3068, 0x3f31, 0x3a06, + 0x24b4, 0x2183, 0x2eda, 0x2bed, +]; + +static FORMAT_INFOS_MICRO_QR: [u16; 32] = [ + 0x4445, 0x4172, 0x4e2b, 0x4b1c, 0x55ae, 0x5099, 0x5fc0, 0x5af7, 0x6793, 0x62a4, 0x6dfd, 0x68ca, 0x7678, 0x734f, + 0x7c16, 0x7921, 0x06de, 0x03e9, 0x0cb0, 0x0987, 0x1735, 0x1202, 0x1d5b, 0x186c, 0x2508, 0x203f, 0x2f66, 0x2a51, + 0x34e3, 0x31d4, 0x3e8d, 0x3bba, +]; + +//}}} +//------------------------------------------------------------------------------ +//{{{ Penalty score + +impl Canvas { + /// Compute the penalty score for having too many adjacent modules with the + /// same color. + /// + /// Every 5+N adjacent modules in the same column/row having the same color + /// will contribute 3+N points. + fn compute_adjacent_penalty_score(&self, is_horizontal: bool) -> u16 { + let mut total_score = 0; + + for i in 0..self.width { + let map_fn = |j| if is_horizontal { self.get(j, i) } else { self.get(i, j) }; + + let colors = (0..self.width).map(map_fn).chain(Some(Module::Empty).into_iter()); + let mut last_color = Module::Empty; + let mut consecutive_len = 1_u16; + + for color in colors { + if color == last_color { + consecutive_len += 1; + } else { + last_color = color; + if consecutive_len >= 5 { + total_score += consecutive_len - 2; + } + consecutive_len = 1; + } + } + } + + total_score + } + + /// Compute the penalty score for having too many rectangles with the same + /// color. + /// + /// Every 2×2 blocks (with overlapping counted) having the same color will + /// contribute 3 points. + fn compute_block_penalty_score(&self) -> u16 { + let mut total_score = 0; + + for i in 0..self.width - 1 { + for j in 0..self.width - 1 { + let this = self.get(i, j); + let right = self.get(i + 1, j); + let bottom = self.get(i, j + 1); + let bottom_right = self.get(i + 1, j + 1); + if this == right && right == bottom && bottom == bottom_right { + total_score += 3; + } + } + } + + total_score + } + + /// Compute the penalty score for having a pattern similar to the finder + /// pattern in the wrong place. + /// + /// Every pattern that looks like `#.###.#....` in any orientation will add + /// 40 points. + fn compute_finder_penalty_score(&self, is_horizontal: bool) -> u16 { + static PATTERN: [Color; 7] = + [Color::Dark, Color::Light, Color::Dark, Color::Dark, Color::Dark, Color::Light, Color::Dark]; + + let mut total_score = 0; + + for i in 0..self.width { + for j in 0..self.width - 6 { + // TODO a ref to a closure should be enough? + let get: Box Color> = if is_horizontal { + Box::new(|k| self.get(k, i).into()) + } else { + Box::new(|k| self.get(i, k).into()) + }; + + if (j..(j + 7)).map(&*get).ne(PATTERN.iter().cloned()) { + continue; + } + + let check = |k| 0 <= k && k < self.width && get(k) != Color::Light; + if !((j - 4)..j).any(&check) || !((j + 7)..(j + 11)).any(&check) { + total_score += 40; + } + } + } + + total_score - 360 + } + + /// Compute the penalty score for having an unbalanced dark/light ratio. + /// + /// The score is given linearly by the deviation from a 50% ratio of dark + /// modules. The highest possible score is 100. + /// + /// Note that this algorithm differs slightly from the standard we do not + /// round the result every 5%, but the difference should be negligible and + /// should not affect which mask is chosen. + fn compute_balance_penalty_score(&self) -> u16 { + let dark_modules = self.modules.iter().filter(|m| m.is_dark()).count(); + let total_modules = self.modules.len(); + let ratio = dark_modules * 200 / total_modules; + if ratio >= 100 { ratio - 100 } else { 100 - ratio }.as_u16() + } + + /// Compute the penalty score for having too many light modules on the sides. + /// + /// This penalty score is exclusive to Micro QR code. + /// + /// Note that the standard gives the formula for *efficiency* score, which + /// has the inverse meaning of this method, but it is very easy to convert + /// between the two (this score is (16×width − standard-score)). + fn compute_light_side_penalty_score(&self) -> u16 { + let h = (1..self.width).filter(|j| !self.get(*j, -1).is_dark()).count(); + let v = (1..self.width).filter(|j| !self.get(-1, *j).is_dark()).count(); + + (h + v + 15 * max(h, v)).as_u16() + } + + /// Compute the total penalty scores. A QR code having higher points is less + /// desirable. + fn compute_total_penalty_scores(&self) -> u16 { + match self.version { + Version::Normal(_) => { + let s1_a = self.compute_adjacent_penalty_score(true); + let s1_b = self.compute_adjacent_penalty_score(false); + let s2 = self.compute_block_penalty_score(); + let s3_a = self.compute_finder_penalty_score(true); + let s3_b = self.compute_finder_penalty_score(false); + let s4 = self.compute_balance_penalty_score(); + s1_a + s1_b + s2 + s3_a + s3_b + s4 + } + Version::Micro(_) => self.compute_light_side_penalty_score(), + } + } +} + +#[cfg(test)] +mod penalty_tests { + use crate::canvas::{Canvas, MaskPattern}; + use crate::types::{Color, EcLevel, Version}; + + fn create_test_canvas() -> Canvas { + let mut c = Canvas::new(Version::Normal(1), EcLevel::Q); + c.draw_all_functional_patterns(); + c.draw_data( + b"\x20\x5b\x0b\x78\xd1\x72\xdc\x4d\x43\x40\xec\x11\x00", + b"\xa8\x48\x16\x52\xd9\x36\x9c\x00\x2e\x0f\xb4\x7a\x10", + ); + c.apply_mask(MaskPattern::Checkerboard); + c + } + + #[test] + fn check_penalty_canvas() { + let c = create_test_canvas(); + assert_eq!( + &*c.to_debug_str(), + "\n\ + #######.##....#######\n\ + #.....#.#..#..#.....#\n\ + #.###.#.#..##.#.###.#\n\ + #.###.#.#.....#.###.#\n\ + #.###.#.#.#...#.###.#\n\ + #.....#...#...#.....#\n\ + #######.#.#.#.#######\n\ + ........#............\n\ + .##.#.##....#.#.#####\n\ + .#......####....#...#\n\ + ..##.###.##...#.##...\n\ + .##.##.#..##.#.#.###.\n\ + #...#.#.#.###.###.#.#\n\ + ........##.#..#...#.#\n\ + #######.#.#....#.##..\n\ + #.....#..#.##.##.#...\n\ + #.###.#.#.#...#######\n\ + #.###.#..#.#.#.#...#.\n\ + #.###.#.#...####.#..#\n\ + #.....#.#.##.#...#.##\n\ + #######.....####....#" + ); + } + + #[test] + fn test_penalty_score_adjacent() { + let c = create_test_canvas(); + assert_eq!(c.compute_adjacent_penalty_score(true), 88); + assert_eq!(c.compute_adjacent_penalty_score(false), 92); + } + + #[test] + fn test_penalty_score_block() { + let c = create_test_canvas(); + assert_eq!(c.compute_block_penalty_score(), 90); + } + + #[test] + fn test_penalty_score_finder() { + let c = create_test_canvas(); + assert_eq!(c.compute_finder_penalty_score(true), 0); + assert_eq!(c.compute_finder_penalty_score(false), 40); + } + + #[test] + fn test_penalty_score_balance() { + let c = create_test_canvas(); + assert_eq!(c.compute_balance_penalty_score(), 2); + } + + #[test] + fn test_penalty_score_light_sides() { + static HORIZONTAL_SIDE: [Color; 17] = [ + Color::Dark, + Color::Light, + Color::Light, + Color::Dark, + Color::Dark, + Color::Dark, + Color::Light, + Color::Light, + Color::Dark, + Color::Light, + Color::Dark, + Color::Light, + Color::Light, + Color::Dark, + Color::Light, + Color::Light, + Color::Light, + ]; + static VERTICAL_SIDE: [Color; 17] = [ + Color::Dark, + Color::Dark, + Color::Dark, + Color::Light, + Color::Light, + Color::Dark, + Color::Dark, + Color::Light, + Color::Dark, + Color::Light, + Color::Dark, + Color::Light, + Color::Dark, + Color::Light, + Color::Light, + Color::Dark, + Color::Light, + ]; + + let mut c = Canvas::new(Version::Micro(4), EcLevel::Q); + for i in 0_i16..17 { + c.put(i, -1, HORIZONTAL_SIDE[i as usize]); + c.put(-1, i, VERTICAL_SIDE[i as usize]); + } + + assert_eq!(c.compute_light_side_penalty_score(), 168); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Select mask with lowest penalty score + +static ALL_PATTERNS_QR: [MaskPattern; 8] = [ + MaskPattern::Checkerboard, + MaskPattern::HorizontalLines, + MaskPattern::VerticalLines, + MaskPattern::DiagonalLines, + MaskPattern::LargeCheckerboard, + MaskPattern::Fields, + MaskPattern::Diamonds, + MaskPattern::Meadow, +]; + +static ALL_PATTERNS_MICRO_QR: [MaskPattern; 4] = + [MaskPattern::HorizontalLines, MaskPattern::LargeCheckerboard, MaskPattern::Diamonds, MaskPattern::Meadow]; + +impl Canvas { + /// Construct a new canvas and apply the best masking that gives the lowest + /// penalty score. + pub fn apply_best_mask(&self) -> Self { + match self.version { + Version::Normal(_) => ALL_PATTERNS_QR.iter(), + Version::Micro(_) => ALL_PATTERNS_MICRO_QR.iter(), + } + .map(|ptn| { + let mut c = self.clone(); + c.apply_mask(*ptn); + c + }) + .min_by_key(Self::compute_total_penalty_scores) + .expect("at least one pattern") + } + + /// Convert the modules into a vector of booleans. + #[deprecated(since = "0.4.0", note = "use `into_colors()` instead")] + pub fn to_bools(&self) -> Vec { + self.modules.iter().map(|m| m.is_dark()).collect() + } + + /// Convert the modules into a vector of colors. + pub fn into_colors(self) -> Vec { + self.modules.into_iter().map(Color::from).collect() + } +} + +//}}} +//------------------------------------------------------------------------------ diff --git a/qrcode-rust/src/cast.rs b/qrcode-rust/src/cast.rs new file mode 100644 index 0000000..91634b8 --- /dev/null +++ b/qrcode-rust/src/cast.rs @@ -0,0 +1,86 @@ +use core::fmt::Display; + +pub trait Truncate { + fn truncate_as_u8(self) -> u8; +} + +impl Truncate for u16 { + #[allow(clippy::cast_possible_truncation)] + fn truncate_as_u8(self) -> u8 { + (self & 0xff) as u8 + } +} + +pub trait As { + fn as_u16(self) -> u16; + fn as_i16(self) -> i16; + fn as_u32(self) -> u32; + fn as_usize(self) -> usize; + fn as_isize(self) -> isize; +} + +trait ExpectOrOverflow { + type Output; + fn expect_or_overflow(self, value: D, ty: &str) -> Self::Output; +} + +impl ExpectOrOverflow for Option { + type Output = T; + fn expect_or_overflow(self, value: D, ty: &str) -> Self::Output { + match self { + Some(v) => v, + None => panic!("{} overflows {}", value, ty), + } + } +} + +macro_rules! impl_as { + ($ty:ty) => { + #[cfg(debug_assertions)] + impl As for $ty { + fn as_u16(self) -> u16 { + self.as_u16() + } + + fn as_i16(self) -> i16 { + self.as_i16() + } + + fn as_u32(self) -> u32 { + self.as_u32() + } + + fn as_usize(self) -> usize { + self.as_usize() + } + + fn as_isize(self) -> isize { + self.as_isize() + } + } + + #[cfg(not(debug_assertions))] + impl As for $ty { + fn as_u16(self) -> u16 { + self as u16 + } + fn as_i16(self) -> i16 { + self as i16 + } + fn as_u32(self) -> u32 { + self as u32 + } + fn as_usize(self) -> usize { + self as usize + } + fn as_isize(self) -> isize { + self as isize + } + } + }; +} + +impl_as!(i16); +impl_as!(u32); +impl_as!(usize); +impl_as!(isize); diff --git a/qrcode-rust/src/ec.rs b/qrcode-rust/src/ec.rs new file mode 100644 index 0000000..b37b6f1 --- /dev/null +++ b/qrcode-rust/src/ec.rs @@ -0,0 +1,488 @@ +//! The `ec` module applies the Reed-Solomon error correction codes. + +use core::ops::Deref; + +use alloc::vec::Vec; + +use crate::types::{EcLevel, QrResult, Version}; + +//------------------------------------------------------------------------------ +//{{{ Error correction primitive + +/// Creates the error correction code in N bytes. +/// +/// This method only supports computing the error-correction code up to +/// 69 bytes. Longer blocks will result in task panic. +/// +/// This method treats the data as a polynomial of the form +/// (a\[0\] xm+n + a\[1\] xm+n-1 + … + a\[m\] xn) +/// in GF(28), and then computes the polynomial modulus with a +/// generator polynomial of degree N. +pub fn create_error_correction_code(data: &[u8], ec_code_size: usize) -> Vec { + let data_len = data.len(); + let log_den = GENERATOR_POLYNOMIALS[ec_code_size]; + + let mut res = data.to_vec(); + res.resize(ec_code_size + data_len, 0); + + // rust-lang-nursery/rust-clippy#2213 + for i in 0..data_len { + let lead_coeff = res[i] as usize; + if lead_coeff == 0 { + continue; + } + + let log_lead_coeff = usize::from(LOG_TABLE[lead_coeff]); + for (u, v) in res[i + 1..].iter_mut().zip(log_den.iter()) { + *u ^= EXP_TABLE[(usize::from(*v) + log_lead_coeff) % 255]; + } + } + + res.split_off(data_len) +} + +#[cfg(test)] +mod ec_tests { + use crate::ec::create_error_correction_code; + + #[test] + fn test_poly_mod_1() { + let res = create_error_correction_code(b" [\x0bx\xd1r\xdcMC@\xec\x11\xec\x11\xec\x11", 10); + assert_eq!(&*res, b"\xc4#'w\xeb\xd7\xe7\xe2]\x17"); + } + + #[test] + fn test_poly_mod_2() { + let res = create_error_correction_code(b" [\x0bx\xd1r\xdcMC@\xec\x11\xec", 13); + assert_eq!(&*res, b"\xa8H\x16R\xd96\x9c\x00.\x0f\xb4z\x10"); + } + + #[test] + fn test_poly_mod_3() { + let res = create_error_correction_code(b"CUF\x86W&U\xc2w2\x06\x12\x06g&", 18); + assert_eq!(&*res, b"\xd5\xc7\x0b-s\xf7\xf1\xdf\xe5\xf8\x9au\x9aoV\xa1o'"); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Interleave support + +/// This method interleaves a vector of slices into a single vector. +/// +/// It will first insert all the first elements of the slices in `blocks`, then +/// all the second elements, then all the third elements, and so on. +/// +/// The longest slice must be at the last of `blocks`, and `blocks` must not be +/// empty. +fn interleave>(blocks: &[V]) -> Vec { + let last_block_len = blocks.last().expect("non-empty blocks").len(); + let mut res = Vec::with_capacity(last_block_len * blocks.len()); + for i in 0..last_block_len { + for t in blocks { + if i < t.len() { + res.push(t[i]); + } + } + } + res +} + +#[test] +fn test_interleave() { + let res = interleave(&[&b"1234"[..], b"5678", b"abcdef", b"ghijkl"]); + assert_eq!(&*res, b"15ag26bh37ci48djekfl"); +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ QR code error correction + +/// Constructs data and error correction codewords ready to be put in the QR +/// code matrix. +/// +/// # Errors +/// +/// Returns `Err(QrError::InvalidVersion)` if it is not valid to use the +/// `ec_level` for the given version (e.g. `Version::Micro(1)` with +/// `EcLevel::H`). +pub fn construct_codewords(rawbits: &[u8], version: Version, ec_level: EcLevel) -> QrResult<(Vec, Vec)> { + let (block_1_size, block_1_count, block_2_size, block_2_count) = version.fetch(ec_level, &DATA_BYTES_PER_BLOCK)?; + + let blocks_count = block_1_count + block_2_count; + let block_1_end = block_1_size * block_1_count; + let total_size = block_1_end + block_2_size * block_2_count; + + debug_assert_eq!(rawbits.len(), total_size); + + // Divide the data into blocks. + let mut blocks = Vec::with_capacity(blocks_count); + blocks.extend(rawbits[..block_1_end].chunks(block_1_size)); + if block_2_size > 0 { + blocks.extend(rawbits[block_1_end..].chunks(block_2_size)); + } + + // Generate EC codes. + let ec_bytes = version.fetch(ec_level, &EC_BYTES_PER_BLOCK)?; + let ec_codes = blocks.iter().map(|block| create_error_correction_code(*block, ec_bytes)).collect::>>(); + + let blocks_vec = interleave(&blocks); + let ec_vec = interleave(&ec_codes); + + Ok((blocks_vec, ec_vec)) +} + +#[cfg(test)] +mod construct_codewords_test { + use crate::ec::construct_codewords; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_add_ec_simple() { + let msg = b" [\x0bx\xd1r\xdcMC@\xec\x11\xec\x11\xec\x11"; + let (blocks_vec, ec_vec) = construct_codewords(msg, Version::Normal(1), EcLevel::M).unwrap(); + assert_eq!(&*blocks_vec, msg); + assert_eq!(&*ec_vec, b"\xc4#'w\xeb\xd7\xe7\xe2]\x17"); + } + + #[test] + fn test_add_ec_complex() { + let msg = b"CUF\x86W&U\xc2w2\x06\x12\x06g&\xf6\xf6B\x07v\x86\xf2\x07&V\x16\xc6\xc7\x92\x06\ + \xb6\xe6\xf7w2\x07v\x86W&R\x06\x86\x972\x07F\xf7vV\xc2\x06\x972\x10\xec\x11\xec\ + \x11\xec\x11\xec"; + let expected_blocks = b"C\xf6\xb6FU\xf6\xe6\xf7FB\xf7v\x86\x07wVWv2\xc2&\x86\x07\x06U\xf2v\ + \x97\xc2\x07\x862w&W\x102V&\xec\x06\x16R\x11\x12\xc6\x06\xec\x06\ + \xc7\x86\x11g\x92\x97\xec&\x062\x11\x07\xec"; + let expected_ec = b"\xd5W\x94\xeb\xc7\xcct\x9f\x0b`\xb1\x05-<\xd4\xads\xcaL\x18\xf7\xb6\x85\ + \x93\xf1|K;\xdf\x9d\xf2!\xe5\xc8\xeej\xf8\x86L(\x9a\x1b\xc3\xffu\x81\ + \xe6\xac\x9a\xd1\xbdRo\x11\n\x02V\xa3l\x83\xa1\xa3\xf0 ox\xc0\xb2'\x85\ + \x8d\xec"; + + let (blocks_vec, ec_vec) = construct_codewords(msg, Version::Normal(5), EcLevel::Q).unwrap(); + assert_eq!(&*blocks_vec, &expected_blocks[..]); + assert_eq!(&*ec_vec, &expected_ec[..]); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Number of allowed errors + +/// Computes the maximum allowed number of erratic modules can be introduced to +/// the QR code, before the data becomes truly corrupted. +/// +/// # Errors +/// +/// Returns `Err(QrError::InvalidVersion)` if it is not valid to use the +/// `ec_level` for the given version (e.g. `Version::Micro(1)` with +/// `EcLevel::H`). +pub fn max_allowed_errors(version: Version, ec_level: EcLevel) -> QrResult { + use crate::EcLevel::{L, M}; + use crate::Version::{Micro, Normal}; + + let p = match (version, ec_level) { + (Micro(2), L) | (Normal(1), L) => 3, + (Micro(_), L) | (Normal(2), L) | (Micro(2), M) | (Normal(1), M) => 2, + (Normal(1), _) | (Normal(3), L) => 1, + _ => 0, + }; + + let ec_bytes_per_block = version.fetch(ec_level, &EC_BYTES_PER_BLOCK)?; + let (_, count1, _, count2) = version.fetch(ec_level, &DATA_BYTES_PER_BLOCK)?; + let ec_bytes = (count1 + count2) * ec_bytes_per_block; + + Ok((ec_bytes - p) / 2) +} + +#[cfg(test)] +mod max_allowed_errors_test { + use crate::ec::max_allowed_errors; + use crate::types::{EcLevel, Version}; + + #[test] + fn test_low_versions() { + assert_eq!(Ok(0), max_allowed_errors(Version::Micro(1), EcLevel::L)); + + assert_eq!(Ok(1), max_allowed_errors(Version::Micro(2), EcLevel::L)); + assert_eq!(Ok(2), max_allowed_errors(Version::Micro(2), EcLevel::M)); + + assert_eq!(Ok(2), max_allowed_errors(Version::Micro(3), EcLevel::L)); + assert_eq!(Ok(4), max_allowed_errors(Version::Micro(3), EcLevel::M)); + + assert_eq!(Ok(3), max_allowed_errors(Version::Micro(4), EcLevel::L)); + assert_eq!(Ok(5), max_allowed_errors(Version::Micro(4), EcLevel::M)); + assert_eq!(Ok(7), max_allowed_errors(Version::Micro(4), EcLevel::Q)); + + assert_eq!(Ok(2), max_allowed_errors(Version::Normal(1), EcLevel::L)); + assert_eq!(Ok(4), max_allowed_errors(Version::Normal(1), EcLevel::M)); + assert_eq!(Ok(6), max_allowed_errors(Version::Normal(1), EcLevel::Q)); + assert_eq!(Ok(8), max_allowed_errors(Version::Normal(1), EcLevel::H)); + + assert_eq!(Ok(4), max_allowed_errors(Version::Normal(2), EcLevel::L)); + assert_eq!(Ok(8), max_allowed_errors(Version::Normal(2), EcLevel::M)); + assert_eq!(Ok(11), max_allowed_errors(Version::Normal(2), EcLevel::Q)); + assert_eq!(Ok(14), max_allowed_errors(Version::Normal(2), EcLevel::H)); + + assert_eq!(Ok(7), max_allowed_errors(Version::Normal(3), EcLevel::L)); + assert_eq!(Ok(13), max_allowed_errors(Version::Normal(3), EcLevel::M)); + assert_eq!(Ok(18), max_allowed_errors(Version::Normal(3), EcLevel::Q)); + assert_eq!(Ok(22), max_allowed_errors(Version::Normal(3), EcLevel::H)); + + assert_eq!(Ok(10), max_allowed_errors(Version::Normal(4), EcLevel::L)); + assert_eq!(Ok(18), max_allowed_errors(Version::Normal(4), EcLevel::M)); + assert_eq!(Ok(26), max_allowed_errors(Version::Normal(4), EcLevel::Q)); + assert_eq!(Ok(32), max_allowed_errors(Version::Normal(4), EcLevel::H)); + } + + #[test] + fn test_high_versions() { + assert_eq!(Ok(375), max_allowed_errors(Version::Normal(40), EcLevel::L)); + assert_eq!(Ok(686), max_allowed_errors(Version::Normal(40), EcLevel::M)); + assert_eq!(Ok(1020), max_allowed_errors(Version::Normal(40), EcLevel::Q)); + assert_eq!(Ok(1215), max_allowed_errors(Version::Normal(40), EcLevel::H)); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Precomputed tables for GF(256). + +/// `EXP_TABLE` encodes the value of 2n in the Galois Field GF(256). +static EXP_TABLE: &[u8] = b"\ +\x01\x02\x04\x08\x10\x20\x40\x80\x1d\x3a\x74\xe8\xcd\x87\x13\x26\ +\x4c\x98\x2d\x5a\xb4\x75\xea\xc9\x8f\x03\x06\x0c\x18\x30\x60\xc0\ +\x9d\x27\x4e\x9c\x25\x4a\x94\x35\x6a\xd4\xb5\x77\xee\xc1\x9f\x23\ +\x46\x8c\x05\x0a\x14\x28\x50\xa0\x5d\xba\x69\xd2\xb9\x6f\xde\xa1\ +\x5f\xbe\x61\xc2\x99\x2f\x5e\xbc\x65\xca\x89\x0f\x1e\x3c\x78\xf0\ +\xfd\xe7\xd3\xbb\x6b\xd6\xb1\x7f\xfe\xe1\xdf\xa3\x5b\xb6\x71\xe2\ +\xd9\xaf\x43\x86\x11\x22\x44\x88\x0d\x1a\x34\x68\xd0\xbd\x67\xce\ +\x81\x1f\x3e\x7c\xf8\xed\xc7\x93\x3b\x76\xec\xc5\x97\x33\x66\xcc\ +\x85\x17\x2e\x5c\xb8\x6d\xda\xa9\x4f\x9e\x21\x42\x84\x15\x2a\x54\ +\xa8\x4d\x9a\x29\x52\xa4\x55\xaa\x49\x92\x39\x72\xe4\xd5\xb7\x73\ +\xe6\xd1\xbf\x63\xc6\x91\x3f\x7e\xfc\xe5\xd7\xb3\x7b\xf6\xf1\xff\ +\xe3\xdb\xab\x4b\x96\x31\x62\xc4\x95\x37\x6e\xdc\xa5\x57\xae\x41\ +\x82\x19\x32\x64\xc8\x8d\x07\x0e\x1c\x38\x70\xe0\xdd\xa7\x53\xa6\ +\x51\xa2\x59\xb2\x79\xf2\xf9\xef\xc3\x9b\x2b\x56\xac\x45\x8a\x09\ +\x12\x24\x48\x90\x3d\x7a\xf4\xf5\xf7\xf3\xfb\xeb\xcb\x8b\x0b\x16\ +\x2c\x58\xb0\x7d\xfa\xe9\xcf\x83\x1b\x36\x6c\xd8\xad\x47\x8e\x01"; + +/// `LOG_TABLE` is the inverse function of `EXP_TABLE`. +static LOG_TABLE: &[u8] = b"\ +\xff\x00\x01\x19\x02\x32\x1a\xc6\x03\xdf\x33\xee\x1b\x68\xc7\x4b\ +\x04\x64\xe0\x0e\x34\x8d\xef\x81\x1c\xc1\x69\xf8\xc8\x08\x4c\x71\ +\x05\x8a\x65\x2f\xe1\x24\x0f\x21\x35\x93\x8e\xda\xf0\x12\x82\x45\ +\x1d\xb5\xc2\x7d\x6a\x27\xf9\xb9\xc9\x9a\x09\x78\x4d\xe4\x72\xa6\ +\x06\xbf\x8b\x62\x66\xdd\x30\xfd\xe2\x98\x25\xb3\x10\x91\x22\x88\ +\x36\xd0\x94\xce\x8f\x96\xdb\xbd\xf1\xd2\x13\x5c\x83\x38\x46\x40\ +\x1e\x42\xb6\xa3\xc3\x48\x7e\x6e\x6b\x3a\x28\x54\xfa\x85\xba\x3d\ +\xca\x5e\x9b\x9f\x0a\x15\x79\x2b\x4e\xd4\xe5\xac\x73\xf3\xa7\x57\ +\x07\x70\xc0\xf7\x8c\x80\x63\x0d\x67\x4a\xde\xed\x31\xc5\xfe\x18\ +\xe3\xa5\x99\x77\x26\xb8\xb4\x7c\x11\x44\x92\xd9\x23\x20\x89\x2e\ +\x37\x3f\xd1\x5b\x95\xbc\xcf\xcd\x90\x87\x97\xb2\xdc\xfc\xbe\x61\ +\xf2\x56\xd3\xab\x14\x2a\x5d\x9e\x84\x3c\x39\x53\x47\x6d\x41\xa2\ +\x1f\x2d\x43\xd8\xb7\x7b\xa4\x76\xc4\x17\x49\xec\x7f\x0c\x6f\xf6\ +\x6c\xa1\x3b\x52\x29\x9d\x55\xaa\xfb\x60\x86\xb1\xbb\xcc\x3e\x5a\ +\xcb\x59\x5f\xb0\x9c\xa9\xa0\x51\x0b\xf5\x16\xeb\x7a\x75\x2c\xd7\ +\x4f\xae\xd5\xe9\xe6\xe7\xad\xe8\x74\xd6\xf4\xea\xa8\x50\x58\xaf"; + +/// The generator polynomial list. +/// +/// `GENERATOR_POLYNOMIALS[i]` is the polynomial for `i` error correction code +/// words. Each entry encodes the log coefficients of the expanded polynomial +/// (x − 20)(x − 21)…(x − 2i-1). Each entry is +/// used as the denominator for polynomial division to obtain the modulus which +/// is the Reed-Solomon error correction code. +/// +/// A partial list can be found from ISO/IEC 18004:2006 Annex A. +#[rustfmt::skip] +// ^ this attribute is currently useless, see rust-lang-nursery/rustfmt#1080 and 1298 +static GENERATOR_POLYNOMIALS: [&[u8]; 70] = [ + b"", + b"\x00", + b"\x19\x01", + b"\xc6\xc7\x03", + b"\x4b\xf9\x4e\x06", + b"\x71\xa4\xa6\x77\x0a", + b"\xa6\x00\x86\x05\xb0\x0f", + b"\x57\xe5\x92\x95\xee\x66\x15", + b"\xaf\xee\xd0\xf9\xd7\xfc\xc4\x1c", + b"\x5f\xf6\x89\xe7\xeb\x95\x0b\x7b\x24", + b"\xfb\x43\x2e\x3d\x76\x46\x40\x5e\x20\x2d", + b"\xdc\xc0\x5b\xc2\xac\xb1\xd1\x74\xe3\x0a\x37", + b"\x66\x2b\x62\x79\xbb\x71\xc6\x8f\x83\x57\x9d\x42", + b"\x4a\x98\xb0\x64\x56\x64\x6a\x68\x82\xda\xce\x8c\x4e", + b"\xc7\xf9\x9b\x30\xbe\x7c\xda\x89\xd8\x57\xcf\x3b\x16\x5b", + b"\x08\xb7\x3d\x5b\xca\x25\x33\x3a\x3a\xed\x8c\x7c\x05\x63\x69", + b"\x78\x68\x6b\x6d\x66\xa1\x4c\x03\x5b\xbf\x93\xa9\xb6\xc2\xe1\x78", + b"\x2b\x8b\xce\x4e\x2b\xef\x7b\xce\xd6\x93\x18\x63\x96\x27\xf3\xa3\x88", + b"\xd7\xea\x9e\x5e\xb8\x61\x76\xaa\x4f\xbb\x98\x94\xfc\xb3\x05\x62\x60\x99", + b"\x43\x03\x69\x99\x34\x5a\x53\x11\x96\x9f\x2c\x80\x99\x85\xfc\xde\x8a\xdc\xab", + b"\x11\x3c\x4f\x32\x3d\xa3\x1a\xbb\xca\xb4\xdd\xe1\x53\xef\x9c\xa4\xd4\xd4\xbc\xbe", + b"\xf0\xe9\x68\xf7\xb5\x8c\x43\x62\x55\xc8\xd2\x73\x94\x89\xe6\x24\x7a\xfe\x94\xaf\xd2", + b"\xd2\xab\xf7\xf2\x5d\xe6\x0e\x6d\xdd\x35\xc8\x4a\x08\xac\x62\x50\xdb\x86\xa0\x69\xa5\xe7", + b"\xab\x66\x92\x5b\x31\x67\x41\x11\xc1\x96\x0e\x19\xb7\xf8\x5e\xa4\xe0\xc0\x01\x4e\x38\x93\xfd", + b"\xe5\x79\x87\x30\xd3\x75\xfb\x7e\x9f\xb4\xa9\x98\xc0\xe2\xe4\xda\x6f\x00\x75\xe8\x57\x60\xe3\x15", + b"\xe7\xb5\x9c\x27\xaa\x1a\x0c\x3b\x0f\x94\xc9\x36\x42\xed\xd0\x63\xa7\x90\xb6\x5f\xf3\x81\xb2\xfc\x2d", + b"\xad\x7d\x9e\x02\x67\xb6\x76\x11\x91\xc9\x6f\x1c\xa5\x35\xa1\x15\xf5\x8e\x0d\x66\x30\xe3\x99\x91\xda\x46", + b"\x4f\xe4\x08\xa5\xe3\x15\xb4\x1d\x09\xed\x46\x63\x2d\x3a\x8a\x87\x49\x7e\xac\x5e\xd8\xc1\x9d\x1a\x11\x95\x60", + b"\xa8\xdf\xc8\x68\xe0\xea\x6c\xb4\x6e\xbe\xc3\x93\xcd\x1b\xe8\xc9\x15\x2b\xf5\x57\x2a\xc3\xd4\x77\xf2\x25\x09\x7b", + b"\x9c\x2d\xb7\x1d\x97\xdb\x36\x60\xf9\x18\x88\x05\xf1\xaf\xbd\x1c\x4b\xea\x96\x94\x17\x09\xca\xa2\x44\xfa\x8c\x18\x97", + b"\x29\xad\x91\x98\xd8\x1f\xb3\xb6\x32\x30\x6e\x56\xef\x60\xde\x7d\x2a\xad\xe2\xc1\xe0\x82\x9c\x25\xfb\xd8\xee\x28\xc0\xb4", + b"\x14\x25\xfc\x5d\x3f\x4b\xe1\x1f\x73\x53\x71\x27\x2c\x49\x7a\x89\x76\x77\x90\xf8\xf8\x37\x01\xe1\x69\x7b\xb7\x75\xbb\xc8\xd2", + b"\x0a\x06\x6a\xbe\xf9\xa7\x04\x43\xd1\x8a\x8a\x20\xf2\x7b\x59\x1b\x78\xb9\x50\x9c\x26\x45\xab\x3c\x1c\xde\x50\x34\xfe\xb9\xdc\xf1", + b"\xf5\xe7\x37\x18\x47\x4e\x4c\x51\xe1\xd4\xad\x25\xd7\x2e\x77\xe5\xf5\xa7\x7e\x48\xb5\x5e\xa5\xd2\x62\x7d\x9f\xb8\xa9\xe8\xb9\xe7\x12", + b"\x6f\x4d\x92\x5e\x1a\x15\x6c\x13\x69\x5e\x71\xc1\x56\x8c\xa3\x7d\x3a\x9e\xe5\xef\xda\x67\x38\x46\x72\x3d\xb7\x81\xa7\x0d\x62\x3e\x81\x33", + b"\x07\x5e\x8f\x51\xf7\x7f\xca\xca\xc2\x7d\x92\x1d\x8a\xa2\x99\x41\x69\x7a\x74\xee\x1a\x24\xd8\x70\x7d\xe4\x0f\x31\x08\xa2\x1e\x7e\x6f\x3a\x55", + b"\xc8\xb7\x62\x10\xac\x1f\xf6\xea\x3c\x98\x73\x00\xa7\x98\x71\xf8\xee\x6b\x12\x3f\xda\x25\x57\xd2\x69\xb1\x78\x4a\x79\xc4\x75\xfb\x71\xe9\x1e\x78", + b"\x9a\x4b\x8d\xb4\x3d\xa5\x68\xe8\x2e\xe3\x60\xb2\x5c\x87\x39\xa2\x78\xc2\xd4\xae\xfc\xb7\x2a\x23\x9d\x6f\x17\x85\x64\x08\x69\x25\xc0\xbd\x9f\x13\x9c", + b"\x9f\x22\x26\xe4\xe6\x3b\xf3\x5f\x31\xda\xb0\xa4\x14\x41\x2d\x6f\x27\x51\x31\x76\x71\xde\xc1\xfa\xf2\xa8\xd9\x29\xa4\xf7\xb1\x1e\xee\x12\x78\x99\x3c\xc1", + b"\x51\xd8\xae\x2f\xc8\x96\x3b\x9c\x59\x8f\x59\xa6\xb7\xaa\x98\x15\xa5\xb1\x71\x84\xea\x05\x9a\x44\x7c\xaf\xc4\x9d\xf9\xe9\x53\x18\x99\xf1\x7e\x24\x74\x13\xe7", + b"\x3b\x74\x4f\xa1\xfc\x62\x80\xcd\x80\xa1\xf7\x39\xa3\x38\xeb\x6a\x35\x1a\xbb\xae\xe2\x68\xaa\x07\xaf\x23\xb5\x72\x58\x29\x2f\xa3\x7d\x86\x48\x14\xe8\x35\x23\x0f", + b"\x84\xa7\x34\x8b\xb8\xdf\x95\x5c\xfa\x12\x53\x21\x7f\x6d\xc2\x07\xd3\xf2\x6d\x42\x56\xa9\x57\x60\xbb\x9f\x72\xac\x76\xd0\xb7\xc8\x52\xb3\x26\x27\x22\xf2\x8e\x93\x37", + b"\xfa\x67\xdd\xe6\x19\x12\x89\xe7\x00\x03\x3a\xf2\xdd\xbf\x6e\x54\xe6\x08\xbc\x6a\x60\x93\x0f\x83\x8b\x22\x65\xdf\x27\x65\xd5\xc7\xed\xfe\xc9\x7b\xab\xa2\xc2\x75\x32\x60", + b"\x60\x43\x03\xf5\xd9\xd7\x21\x41\xf0\x6d\x90\x3f\x15\x83\x26\x65\x99\x80\x37\x1f\xed\x03\x5e\xa0\x14\x57\x4d\x38\xbf\x7b\xcf\x4b\x52\x00\x7a\x84\x65\x91\xd7\x0f\x79\xc0\x8a", + b"\xbe\x07\x3d\x79\x47\xf6\x45\x37\xa8\xbc\x59\xf3\xbf\x19\x48\x7b\x09\x91\x0e\xf7\x01\xee\x2c\x4e\x8f\x3e\xe0\x7e\x76\x72\x44\xa3\x34\xc2\xd9\x93\xcc\xa9\x25\x82\x71\x66\x49\xb5", + b"\x06\xac\x48\xfa\x12\xab\xab\xa2\xe5\xbb\xef\x04\xbb\x0b\x25\xe4\x66\x48\x66\x16\x21\x49\x5f\x63\x84\x01\x0f\x59\x04\x70\x82\x5f\xd3\xeb\xe3\x3a\x23\x58\x84\x17\x2c\xa5\x36\xbb\xe1", + b"\x70\x5e\x58\x70\xfd\xe0\xca\x73\xbb\x63\x59\x05\x36\x71\x81\x2c\x3a\x10\x87\xd8\xa9\xd3\x24\x01\x04\x60\x3c\xf1\x49\x68\xea\x08\xf9\xf5\x77\xae\x34\x19\x9d\xe0\x2b\xca\xdf\x13\x52\x0f", + b"\x4c\xa4\xe5\x5c\x4f\xa8\xdb\x6e\x68\x15\xdc\x4a\x13\xc7\xc3\x64\x5d\xbf\x2b\xd5\x48\x38\x8a\xa1\x7d\xbb\x77\xfa\xbd\x89\xbe\x4c\x7e\xf7\x5d\x1e\x84\x06\x3a\xd5\xd0\xa5\xe0\x98\x85\x5b\x3d", + b"\xe4\x19\xc4\x82\xd3\x92\x3c\x18\xfb\x5a\x27\x66\xf0\x3d\xb2\x3f\x2e\x7b\x73\x12\xdd\x6f\x87\xa0\xb6\xcd\x6b\xce\x5f\x96\x78\xb8\x5b\x15\xf7\x9c\x8c\xee\xbf\x0b\x5e\xe3\x54\x32\xa3\x27\x22\x6c", + b"\xac\x79\x01\x29\xc1\xde\xed\x40\x6d\xb5\x34\x78\xd4\xe2\xef\xf5\xd0\x14\xf6\x22\xe1\xcc\x86\x65\x7d\xce\x45\x8a\xfa\x00\x4d\x3a\x8f\xb9\xdc\xfe\xd2\xbe\x70\x58\x5b\x39\x5a\x6d\x05\x0d\xb5\x19\x9c", + b"\xe8\x7d\x9d\xa1\xa4\x09\x76\x2e\xd1\x63\xcb\xc1\x23\x03\xd1\x6f\xc3\xf2\xcb\xe1\x2e\x0d\x20\xa0\x7e\xd1\x82\xa0\xf2\xd7\xf2\x4b\x4d\x2a\xbd\x20\x71\x41\x7c\x45\xe4\x72\xeb\xaf\x7c\xaa\xd7\xe8\x85\xcd", + b"\xd5\xa6\x8e\x2b\x0a\xd8\x8d\xa3\xac\xb4\x66\x46\x59\x3e\xde\x3e\x2a\xd2\x97\xa3\xda\x46\x4d\x27\xa6\xbf\x72\xca\xf5\xbc\xb7\xdd\x4b\xd4\x1b\xed\x7f\xcc\xeb\x3e\xbe\xe8\x12\x2e\xab\x0f\x62\xf7\x42\xa3\x00", + b"\x74\x32\x56\xba\x32\xdc\xfb\x59\xc0\x2e\x56\x7f\x7c\x13\xb8\xe9\x97\xd7\x16\x0e\x3b\x91\x25\xf2\xcb\x86\xfe\x59\xbe\x5e\x3b\x41\x7c\x71\x64\xe9\xeb\x79\x16\x4c\x56\x61\x27\xf2\xc8\xdc\x65\x21\xef\xfe\x74\x33", + b"\x7a\xd6\xe7\x88\xc7\x0b\x06\xcd\x7c\x48\xd5\x75\xbb\x3c\x93\xc9\x49\x4b\x21\x92\xab\xf7\x76\xd0\x9d\xb1\xcb\xeb\x53\x2d\xe2\xca\xe5\xa8\x07\x39\xed\xeb\xc8\x7c\x6a\xfe\xa5\x0e\x93\x00\x39\x2a\x1f\xb2\xd5\xad\x67", + b"\xb7\x1a\xc9\x57\xd2\xdd\x71\x15\x2e\x41\x2d\x32\xee\xb8\xf9\xe1\x66\x3a\xd1\xda\x6d\xa5\x1a\x5f\xb8\xc0\x34\xf5\x23\xfe\xee\xaf\xac\x4f\x7b\x19\x7a\x2b\x78\x6c\xd7\x50\x80\xc9\xeb\x08\x99\x3b\x65\x1f\xc6\x4c\x1f\x9c", + b"\x26\xc5\x7b\xa7\x10\x57\xb2\xee\xe3\x61\x94\xf7\x1a\x5a\xe4\xb6\xec\xc5\x2f\xf9\x24\xd5\x36\x71\xb5\x4a\xb1\xcc\x9b\x3d\x2f\x2a\x00\x84\x90\xfb\xc8\x26\x26\x8a\x36\x2c\x40\x13\x16\xce\x10\x0a\xe4\xd3\xa1\xab\x2c\xc2\xd2", + b"\x6a\x78\x6b\x9d\xa4\xd8\x70\x74\x02\x5b\xf8\xa3\x24\xc9\xca\xe5\x06\x90\xfe\x9b\x87\xd0\xaa\xd1\x0c\x8b\x7f\x8e\xb6\xf9\xb1\xae\xbe\x1c\x0a\x55\xef\xb8\x65\x7c\x98\xce\x60\x17\xa3\x3d\x1b\xc4\xf7\x97\x9a\xca\xcf\x14\x3d\x0a", + b"\x3a\x8c\xed\x5d\x6a\x3d\xc1\x02\x57\x49\xc2\xd7\x9f\xa3\x0a\x9b\x05\x79\x99\x3b\xf8\x04\x75\x16\x3c\xb1\x90\x2c\x48\xe4\x3e\x01\x13\xaa\x71\x9e\x19\xaf\xc7\x8b\x5a\x01\xd2\x07\x77\x9a\x59\x9f\x82\x7a\x2e\x93\xbe\x87\x5e\x44\x42", + b"\x52\x74\x1a\xf7\x42\x1b\x3e\x6b\xfc\xb6\xc8\xb9\xeb\x37\xfb\xf2\xd2\x90\x9a\xed\xb0\x8d\xc0\xf8\x98\xf9\xce\x55\xfd\x8e\x41\xa5\x7d\x17\x18\x1e\x7a\xf0\xd6\x06\x81\xda\x1d\x91\x7f\x86\xce\xf5\x75\x1d\x29\x3f\x9f\x8e\xe9\x7d\x94\x7b", + b"\x39\x73\xe8\x0b\xc3\xd9\x03\xce\x4d\x43\x1d\xa6\xb4\x6a\x76\xcb\x11\x45\x98\xd5\x4a\x2c\x31\x2b\x62\x3d\xfd\x7a\x0e\x2b\xd1\x8f\x09\x68\x6b\xab\xe0\x39\xfe\xfb\xe2\xe8\xdd\xc2\xf0\x75\xa1\x52\xb2\xf6\xb2\x21\x32\x56\xd7\xef\xb4\xb4\xb5", + b"\x6b\x8c\x1a\x0c\x09\x8d\xf3\xc5\xe2\xc5\xdb\x2d\xd3\x65\xdb\x78\x1c\xb5\x7f\x06\x64\xf7\x02\xcd\xc6\x39\x73\xdb\x65\x6d\xa0\x52\x25\x26\xee\x31\xa0\xd1\x79\x56\x0b\x7c\x1e\xb5\x54\x19\xc2\x57\x41\x66\xbe\xdc\x46\x1b\xd1\x10\x59\x07\x21\xf0", + b"\xa1\xf4\x69\x73\x40\x09\xdd\xec\x10\x91\x94\x22\x90\xba\x0d\x14\xfe\xf6\x26\x23\xca\x48\x04\xd4\x9f\xd3\xa5\x87\xfc\xfa\x19\x57\x1e\x78\xe2\xea\x5c\xc7\x48\x07\x9b\xda\xe7\x2c\x7d\xb2\x9c\xae\x7c\x2b\x64\x1f\x38\x65\xcc\x40\xaf\xe1\xa9\x92\x2d", + b"\x41\xca\x71\x62\x47\xdf\xf8\x76\xd6\x5e\x00\x7a\x25\x17\x02\xe4\x3a\x79\x07\x69\x87\x4e\xf3\x76\x46\x4c\xdf\x59\x48\x32\x46\x6f\xc2\x11\xd4\x7e\xb5\x23\xdd\x75\xeb\x0b\xe5\x95\x93\x7b\xd5\x28\x73\x06\xc8\x64\x1a\xf6\xb6\xda\x7f\xd7\x24\xba\x6e\x6a", + b"\x1e\x47\x24\x47\x13\xc3\xac\x6e\x3d\x02\xa9\xc2\x5a\x88\x3b\xb6\xe7\x91\x66\x27\xaa\xe7\xd6\x43\xc4\xcf\x35\x70\xf6\x5a\x5a\x79\xb7\x92\x4a\x4d\x26\x59\x16\xe7\x37\x38\xf2\x70\xd9\x6e\x7b\x3e\xc9\xd9\x80\xa5\x3c\xb5\x25\xa1\xf6\x84\xf6\x12\x73\x88\xa8", + b"\x2d\x33\xaf\x09\x07\x9e\x9f\x31\x44\x77\x5c\x7b\xb1\xcc\xbb\xfe\xc8\x4e\x8d\x95\x77\x1a\x7f\x35\xa0\x5d\xc7\xd4\x1d\x18\x91\x9c\xd0\x96\xda\xd1\x04\xd8\x5b\x2f\xb8\x92\x2f\x8c\xc3\xc3\x7d\xf2\xee\x3f\x63\x6c\x8c\xe6\xf2\x1f\xcc\x0b\xb2\xf3\xd9\x9c\xd5\xe7", + b"\x89\x9e\xf7\xf0\x25\xee\xd6\x80\x63\xda\x2e\x8a\xc6\x80\x5c\xdb\x6d\x8b\xa6\x19\x42\x43\x0e\x3a\xee\x95\xb1\xc3\xdd\x9a\xab\x30\x50\x0c\x3b\xbe\xe4\x13\x37\xd0\x5c\x70\xe5\x25\x3c\x0a\x2f\x51\x00\xc0\x25\xab\xaf\x93\x80\x49\xa6\x3d\x95\x0c\x18\x5f\x46\x71\x28", + b"\x05\x76\xde\xb4\x88\x88\xa2\x33\x2e\x75\x0d\xd7\x51\x11\x8b\xf7\xc5\xab\x5f\xad\x41\x89\xb2\x44\x6f\x5f\x65\x29\x48\xd6\xa9\xc5\x5f\x07\x2c\x9a\x4d\x6f\xec\x28\x79\x8f\x3f\x57\x50\xfd\xf0\x7e\xd9\x4d\x22\xe8\x6a\x32\xa8\x52\x4c\x92\x43\x6a\xab\x19\x84\x5d\x2d\x69", + b"\xbf\xac\x71\x56\x07\xa6\xf6\xb9\x9b\xfa\x62\x71\x59\x56\xd6\xe1\x9c\xbe\x3a\x21\x90\x43\xb3\xa3\x34\x9a\xe9\x97\x68\xfb\xa0\x7e\xaf\xd0\xe1\x46\xe3\x92\x04\x98\x8b\x67\x19\x6b\x3d\xcc\x9f\xfa\xc1\xe1\x69\xa0\x62\xa7\x02\x35\x10\xf2\x53\xd2\xc4\x67\xf8\x56\xd3\x29\xab", + b"\xf7\x9f\xdf\x21\xe0\x5d\x4d\x46\x5a\xa0\x20\xfe\x2b\x96\x54\x65\xbe\xcd\x85\x34\x3c\xca\xa5\xdc\xcb\x97\x5d\x54\x0f\x54\xfd\xad\xa0\x59\xe3\x34\xc7\x61\x5f\xe7\x34\xb1\x29\x7d\x89\xf1\xa6\xe1\x76\x02\x36\x20\x52\xd7\xaf\xc6\x2b\xee\xeb\x1b\x65\xb8\x7f\x03\x05\x08\xa3\xee", + b"\x69\x49\x44\x01\x1d\xa8\x75\x0e\x58\xd0\x37\x2e\x2a\xd9\x06\x54\xb3\x61\x06\xf0\xc0\xe7\x9e\x40\x76\xa0\xcb\x39\x3d\x6c\xc7\x7c\x41\xbb\xdd\xa7\x27\xb6\x9f\xb4\xf4\xcb\xe4\xfe\x0d\xaf\x3d\x5a\xce\x28\xc7\x5e\x43\x39\x51\xe5\x2e\x7b\x59\x25\x1f\xca\x42\xfa\x23\xaa\xf3\x58\x33", +]; + +//}}} +//------------------------------------------------------------------------------ +//{{{ Tables for error correction sizes + +/// `EC_BYTES_PER_BLOCK` provides the number of codewords (bytes) used for error +/// correction per block in each version. +/// +/// This is a copy of ISO/IEC 18004:2006, §6.5.1, Table 9 (The 4th column divide +/// by the sum of the 6th column). +static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [ + // Normal versions. + [7, 10, 13, 17], // 1 + [10, 16, 22, 28], // 2 + [15, 26, 18, 22], // 3 + [20, 18, 26, 16], // 4 + [26, 24, 18, 22], // 5 + [18, 16, 24, 28], // 6 + [20, 18, 18, 26], // 7 + [24, 22, 22, 26], // 8 + [30, 22, 20, 24], // 9 + [18, 26, 24, 28], // 10 + [20, 30, 28, 24], // 11 + [24, 22, 26, 28], // 12 + [26, 22, 24, 22], // 13 + [30, 24, 20, 24], // 14 + [22, 24, 30, 24], // 15 + [24, 28, 24, 30], // 16 + [28, 28, 28, 28], // 17 + [30, 26, 28, 28], // 18 + [28, 26, 26, 26], // 19 + [28, 26, 30, 28], // 20 + [28, 26, 28, 30], // 21 + [28, 28, 30, 24], // 22 + [30, 28, 30, 30], // 23 + [30, 28, 30, 30], // 24 + [26, 28, 30, 30], // 25 + [28, 28, 28, 30], // 26 + [30, 28, 30, 30], // 27 + [30, 28, 30, 30], // 28 + [30, 28, 30, 30], // 29 + [30, 28, 30, 30], // 30 + [30, 28, 30, 30], // 31 + [30, 28, 30, 30], // 32 + [30, 28, 30, 30], // 33 + [30, 28, 30, 30], // 34 + [30, 28, 30, 30], // 35 + [30, 28, 30, 30], // 36 + [30, 28, 30, 30], // 37 + [30, 28, 30, 30], // 38 + [30, 28, 30, 30], // 39 + [30, 28, 30, 30], // 40 + // Micro versions. + [2, 0, 0, 0], // M1 + [5, 6, 0, 0], // M2 + [6, 8, 0, 0], // M3 + [8, 10, 14, 0], // M4 +]; + +/// `DATA_BYTES_PER_BLOCK` provides the number of codewords (bytes) used for +/// real data per block in each version. +/// +/// This is a copy of ISO/IEC 18004:2006, §6.5.1, Table 9 (The value "k" of the +/// 7th column, followed by the 6th column). +/// +/// Every entry is a 4-tuple. Take `DATA_BYTES_PER_BLOCK[39][3] == (15, 20, 16, 61)` +/// as an example, this means in version 40 with correction level H, there are +/// 20 blocks with 15 bytes in size, and 61 blocks with 16 bytes in size. +static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [ + // Normal versions. + [(19, 1, 0, 0), (16, 1, 0, 0), (13, 1, 0, 0), (9, 1, 0, 0)], // 1 + [(34, 1, 0, 0), (28, 1, 0, 0), (22, 1, 0, 0), (16, 1, 0, 0)], // 2 + [(55, 1, 0, 0), (44, 1, 0, 0), (17, 2, 0, 0), (13, 2, 0, 0)], // 3 + [(80, 1, 0, 0), (32, 2, 0, 0), (24, 2, 0, 0), (9, 4, 0, 0)], // 4 + [(108, 1, 0, 0), (43, 2, 0, 0), (15, 2, 16, 2), (11, 2, 12, 2)], // 5 + [(68, 2, 0, 0), (27, 4, 0, 0), (19, 4, 0, 0), (15, 4, 0, 0)], // 6 + [(78, 2, 0, 0), (31, 4, 0, 0), (14, 2, 15, 4), (13, 4, 14, 1)], // 7 + [(97, 2, 0, 0), (38, 2, 39, 2), (18, 4, 19, 2), (14, 4, 15, 2)], // 8 + [(116, 2, 0, 0), (36, 3, 37, 2), (16, 4, 17, 4), (12, 4, 13, 4)], // 9 + [(68, 2, 69, 2), (43, 4, 44, 1), (19, 6, 20, 2), (15, 6, 16, 2)], // 10 + [(81, 4, 0, 0), (50, 1, 51, 4), (22, 4, 23, 4), (12, 3, 13, 8)], // 11 + [(92, 2, 93, 2), (36, 6, 37, 2), (20, 4, 21, 6), (14, 7, 15, 4)], // 12 + [(107, 4, 0, 0), (37, 8, 38, 1), (20, 8, 21, 4), (11, 12, 12, 4)], // 13 + [(115, 3, 116, 1), (40, 4, 41, 5), (16, 11, 17, 5), (12, 11, 13, 5)], // 14 + [(87, 5, 88, 1), (41, 5, 42, 5), (24, 5, 25, 7), (12, 11, 13, 7)], // 15 + [(98, 5, 99, 1), (45, 7, 46, 3), (19, 15, 20, 2), (15, 3, 16, 13)], // 16 + [(107, 1, 108, 5), (46, 10, 47, 1), (22, 1, 23, 15), (14, 2, 15, 17)], // 17 + [(120, 5, 121, 1), (43, 9, 44, 4), (22, 17, 23, 1), (14, 2, 15, 19)], // 18 + [(113, 3, 114, 4), (44, 3, 45, 11), (21, 17, 22, 4), (13, 9, 14, 16)], // 19 + [(107, 3, 108, 5), (41, 3, 42, 13), (24, 15, 25, 5), (15, 15, 16, 10)], // 20 + [(116, 4, 117, 4), (42, 17, 0, 0), (22, 17, 23, 6), (16, 19, 17, 6)], // 21 + [(111, 2, 112, 7), (46, 17, 0, 0), (24, 7, 25, 16), (13, 34, 0, 0)], // 22 + [(121, 4, 122, 5), (47, 4, 48, 14), (24, 11, 25, 14), (15, 16, 16, 14)], // 23 + [(117, 6, 118, 4), (45, 6, 46, 14), (24, 11, 25, 16), (16, 30, 17, 2)], // 24 + [(106, 8, 107, 4), (47, 8, 48, 13), (24, 7, 25, 22), (15, 22, 16, 13)], // 25 + [(114, 10, 115, 2), (46, 19, 47, 4), (22, 28, 23, 6), (16, 33, 17, 4)], // 26 + [(122, 8, 123, 4), (45, 22, 46, 3), (23, 8, 24, 26), (15, 12, 16, 28)], // 27 + [(117, 3, 118, 10), (45, 3, 46, 23), (24, 4, 25, 31), (15, 11, 16, 31)], // 28 + [(116, 7, 117, 7), (45, 21, 46, 7), (23, 1, 24, 37), (15, 19, 16, 26)], // 29 + [(115, 5, 116, 10), (47, 19, 48, 10), (24, 15, 25, 25), (15, 23, 16, 25)], // 30 + [(115, 13, 116, 3), (46, 2, 47, 29), (24, 42, 25, 1), (15, 23, 16, 28)], // 31 + [(115, 17, 0, 0), (46, 10, 47, 23), (24, 10, 25, 35), (15, 19, 16, 35)], // 32 + [(115, 17, 116, 1), (46, 14, 47, 21), (24, 29, 25, 19), (15, 11, 16, 46)], // 33 + [(115, 13, 116, 6), (46, 14, 47, 23), (24, 44, 25, 7), (16, 59, 17, 1)], // 34 + [(121, 12, 122, 7), (47, 12, 48, 26), (24, 39, 25, 14), (15, 22, 16, 41)], // 35 + [(121, 6, 122, 14), (47, 6, 48, 34), (24, 46, 25, 10), (15, 2, 16, 64)], // 36 + [(122, 17, 123, 4), (46, 29, 47, 14), (24, 49, 25, 10), (15, 24, 16, 46)], // 37 + [(122, 4, 123, 18), (46, 13, 47, 32), (24, 48, 25, 14), (15, 42, 16, 32)], // 38 + [(117, 20, 118, 4), (47, 40, 48, 7), (24, 43, 25, 22), (15, 10, 16, 67)], // 39 + [(118, 19, 119, 6), (47, 18, 48, 31), (24, 34, 25, 34), (15, 20, 16, 61)], // 40 + // Micro versions. + [(3, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M1 + [(5, 1, 0, 0), (4, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M2 + [(11, 1, 0, 0), (9, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M3 + [(16, 1, 0, 0), (14, 1, 0, 0), (10, 1, 0, 0), (0, 0, 0, 0)], // M4 +]; + +//}}} diff --git a/qrcode-rust/src/lib.rs b/qrcode-rust/src/lib.rs new file mode 100644 index 0000000..e188d70 --- /dev/null +++ b/qrcode-rust/src/lib.rs @@ -0,0 +1,382 @@ +//! QRCode encoder +//! +//! This crate provides a QR code and Micro QR code encoder for binary data. +//! +//! +//! + +#![no_std] +#![cfg_attr(feature = "image", doc = "```rust")] +#![cfg_attr(not(feature = "image"), doc = "```ignore")] +//! use qrcode::QrCode; +//! use image::Luma; +//! +//! // Encode some data into bits. +//! let code = QrCode::new(b"01234567").unwrap(); +//! +//! // Render the bits into an image. +//! let image = code.render::>().build(); +//! +//! // Save the image. +//! # if cfg!(unix) { +//! image.save("/tmp/qrcode.png").unwrap(); +//! # } +//! +//! // You can also render it into a string. +//! let string = code.render() +//! .light_color(' ') +//! .dark_color('#') +//! .build(); +//! println!("{}", string); +//! ``` + +#![cfg_attr(feature = "bench", feature(test, external_doc))] // Unstable libraries +// #![deny(warnings, clippy::pedantic)] +#![allow( + clippy::must_use_candidate, // This is just annoying. + clippy::use_self, // Rust 1.33 doesn't support Self::EnumVariant, let's try again in 1.37. +)] +#![cfg_attr(feature = "bench", doc(include = "../README.md"))] +// ^ make sure we can test our README.md. + +#[macro_use] +extern crate alloc; + +use core::ops::Index; + +pub mod bits; +pub mod canvas; +mod cast; +pub mod ec; +pub mod optimize; +pub mod render; +pub mod types; + +pub use crate::types::{Color, EcLevel, QrResult, Version}; + +use crate::cast::As; +use crate::render::{Pixel, Renderer}; +use alloc::string::String; +use alloc::vec::Vec; +// use checked_int_cast::CheckedIntCast; + +/// The encoded QR code symbol. +#[derive(Clone)] +pub struct QrCode { + content: Vec, + version: Version, + ec_level: EcLevel, + width: usize, +} + +impl QrCode { + /// Constructs a new QR code which automatically encodes the given data. + /// + /// This method uses the "medium" error correction level and automatically + /// chooses the smallest QR code. + /// + /// use qrcode::QrCode; + /// + /// let code = QrCode::new(b"Some data").unwrap(); + /// + /// # Errors + /// + /// Returns error if the QR code cannot be constructed, e.g. when the data + /// is too long. + pub fn new>(data: D) -> QrResult { + Self::with_error_correction_level(data, EcLevel::M) + } + + /// Constructs a new QR code which automatically encodes the given data at a + /// specific error correction level. + /// + /// This method automatically chooses the smallest QR code. + /// + /// use qrcode::{QrCode, EcLevel}; + /// + /// let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap(); + /// + /// # Errors + /// + /// Returns error if the QR code cannot be constructed, e.g. when the data + /// is too long. + pub fn with_error_correction_level>(data: D, ec_level: EcLevel) -> QrResult { + let bits = bits::encode_auto(data.as_ref(), ec_level)?; + Self::with_bits(bits, ec_level) + } + + /// Constructs a new QR code for the given version and error correction + /// level. + /// + /// use qrcode::{QrCode, Version, EcLevel}; + /// + /// let code = QrCode::with_version(b"Some data", Version::Normal(5), EcLevel::M).unwrap(); + /// + /// This method can also be used to generate Micro QR code. + /// + /// use qrcode::{QrCode, Version, EcLevel}; + /// + /// let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap(); + /// + /// # Errors + /// + /// Returns error if the QR code cannot be constructed, e.g. when the data + /// is too long, or when the version and error correction level are + /// incompatible. + pub fn with_version>(data: D, version: Version, ec_level: EcLevel) -> QrResult { + let mut bits = bits::Bits::new(version); + bits.push_optimal_data(data.as_ref())?; + bits.push_terminator(ec_level)?; + Self::with_bits(bits, ec_level) + } + + /// Constructs a new QR code with encoded bits. + /// + /// Use this method only if there are very special need to manipulate the + /// raw bits before encoding. Some examples are: + /// + /// * Encode data using specific character set with ECI + /// * Use the FNC1 modes + /// * Avoid the optimal segmentation algorithm + /// + /// See the `Bits` structure for detail. + /// + /// #![allow(unused_must_use)] + /// + /// use qrcode::{QrCode, Version, EcLevel}; + /// use qrcode::bits::Bits; + /// + /// let mut bits = Bits::new(Version::Normal(1)); + /// bits.push_eci_designator(9); + /// bits.push_byte_data(b"\xca\xfe\xe4\xe9\xea\xe1\xf2 QR"); + /// bits.push_terminator(EcLevel::L); + /// let qrcode = QrCode::with_bits(bits, EcLevel::L); + /// + /// # Errors + /// + /// Returns error if the QR code cannot be constructed, e.g. when the bits + /// are too long, or when the version and error correction level are + /// incompatible. + pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult { + let version = bits.version(); + let data = bits.into_bytes(); + let (encoded_data, ec_data) = ec::construct_codewords(&*data, version, ec_level)?; + let mut canvas = canvas::Canvas::new(version, ec_level); + canvas.draw_all_functional_patterns(); + canvas.draw_data(&*encoded_data, &*ec_data); + let canvas = canvas.apply_best_mask(); + Ok(Self { content: canvas.into_colors(), version, ec_level, width: version.width().as_usize() }) + } + + /// Gets the version of this QR code. + pub fn version(&self) -> Version { + self.version + } + + /// Gets the error correction level of this QR code. + pub fn error_correction_level(&self) -> EcLevel { + self.ec_level + } + + /// Gets the number of modules per side, i.e. the width of this QR code. + /// + /// The width here does not contain the quiet zone paddings. + pub fn width(&self) -> usize { + self.width + } + + /// Gets the maximum number of allowed erratic modules can be introduced + /// before the data becomes corrupted. Note that errors should not be + /// introduced to functional modules. + pub fn max_allowed_errors(&self) -> usize { + ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level") + } + + /// Checks whether a module at coordinate (x, y) is a functional module or + /// not. + pub fn is_functional(&self, x: usize, y: usize) -> bool { + let x: i16 = x.as_i16(); + let y: i16 = y.as_i16(); + canvas::is_functional(self.version, self.version.width(), x, y) + } + + /// Converts the QR code into a human-readable string. This is mainly for + /// debugging only. + pub fn to_debug_str(&self, on_char: char, off_char: char) -> String { + self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build() + } + + /// Converts the QR code to a vector of booleans. Each entry represents the + /// color of the module, with "true" means dark and "false" means light. + #[deprecated(since = "0.4.0", note = "use `to_colors()` instead")] + pub fn to_vec(&self) -> Vec { + self.content.iter().map(|c| *c != Color::Light).collect() + } + + /// Converts the QR code to a vector of booleans. Each entry represents the + /// color of the module, with "true" means dark and "false" means light. + #[deprecated(since = "0.4.0", note = "use `into_colors()` instead")] + pub fn into_vec(self) -> Vec { + self.content.into_iter().map(|c| c != Color::Light).collect() + } + + /// Converts the QR code to a vector of colors. + pub fn to_colors(&self) -> Vec { + self.content.clone() + } + + /// Converts the QR code to a vector of colors. + pub fn into_colors(self) -> Vec { + self.content + } + + /// Renders the QR code into an image. The result is an image builder, which + /// you may do some additional configuration before copying it into a + /// concrete image. + /// + /// # Examples + /// + #[cfg_attr(feature = "image", doc = " ```rust")] + #[cfg_attr(not(feature = "image"), doc = " ```ignore")] + /// # use qrcode::QrCode; + /// # use image::Rgb; + /// + /// let image = QrCode::new(b"hello").unwrap() + /// .render() + /// .dark_color(Rgb([0, 0, 128])) + /// .light_color(Rgb([224, 224, 224])) // adjust colors + /// .quiet_zone(false) // disable quiet zone (white border) + /// .min_dimensions(300, 300) // sets minimum image size + /// .build(); + /// ``` + /// + /// Note: the `image` crate itself also provides method to rotate the image, + /// or overlay a logo on top of the QR code. + pub fn render(&self) -> Renderer

{ + let quiet_zone = if self.version.is_micro() { 2 } else { 4 }; + Renderer::new(&self.content, self.width, quiet_zone) + } +} + +impl Index<(usize, usize)> for QrCode { + type Output = Color; + + fn index(&self, (x, y): (usize, usize)) -> &Color { + let index = y * self.width + x; + &self.content[index] + } +} + +#[cfg(test)] +mod tests { + use crate::{EcLevel, QrCode, Version}; + + #[test] + fn test_annex_i_qr() { + // This uses the ISO Annex I as test vector. + let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap(); + assert_eq!( + &*code.to_debug_str('#', '.'), + "\ + #######..#.##.#######\n\ + #.....#..####.#.....#\n\ + #.###.#.#.....#.###.#\n\ + #.###.#.##....#.###.#\n\ + #.###.#.#.###.#.###.#\n\ + #.....#.#...#.#.....#\n\ + #######.#.#.#.#######\n\ + ........#..##........\n\ + #.#####..#..#.#####..\n\ + ...#.#.##.#.#..#.##..\n\ + ..#...##.#.#.#..#####\n\ + ....#....#.....####..\n\ + ...######..#.#..#....\n\ + ........#.#####..##..\n\ + #######..##.#.##.....\n\ + #.....#.#.#####...#.#\n\ + #.###.#.#...#..#.##..\n\ + #.###.#.##..#..#.....\n\ + #.###.#.#.##.#..#.#..\n\ + #.....#........##.##.\n\ + #######.####.#..#.#.." + ); + } + + #[test] + fn test_annex_i_micro_qr() { + let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap(); + assert_eq!( + &*code.to_debug_str('#', '.'), + "\ + #######.#.#.#\n\ + #.....#.###.#\n\ + #.###.#..##.#\n\ + #.###.#..####\n\ + #.###.#.###..\n\ + #.....#.#...#\n\ + #######..####\n\ + .........##..\n\ + ##.#....#...#\n\ + .##.#.#.#.#.#\n\ + ###..#######.\n\ + ...#.#....##.\n\ + ###.#..##.###" + ); + } +} + +#[cfg(all(test, feature = "image"))] +mod image_tests { + use crate::{EcLevel, QrCode, Version}; + use image::{load_from_memory, Luma, Rgb}; + + #[test] + fn test_annex_i_qr_as_image() { + let code = QrCode::new(b"01234567").unwrap(); + let image = code.render::>().build(); + let expected = load_from_memory(include_bytes!("test_annex_i_qr_as_image.png")).unwrap().to_luma(); + assert_eq!(image.dimensions(), expected.dimensions()); + assert_eq!(image.into_raw(), expected.into_raw()); + } + + #[test] + fn test_annex_i_micro_qr_as_image() { + let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap(); + let image = code + .render() + .min_dimensions(200, 200) + .dark_color(Rgb([128, 0, 0])) + .light_color(Rgb([255, 255, 128])) + .build(); + let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb(); + assert_eq!(image.dimensions(), expected.dimensions()); + assert_eq!(image.into_raw(), expected.into_raw()); + } +} + +#[cfg(all(test, feature = "svg"))] +mod svg_tests { + use crate::render::svg::Color as SvgColor; + use crate::{EcLevel, QrCode, Version}; + + #[test] + fn test_annex_i_qr_as_svg() { + let code = QrCode::new(b"01234567").unwrap(); + let image = code.render::().build(); + let expected = include_str!("test_annex_i_qr_as_svg.svg"); + assert_eq!(&image, expected); + } + + #[test] + fn test_annex_i_micro_qr_as_svg() { + let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap(); + let image = code + .render() + .min_dimensions(200, 200) + .dark_color(SvgColor("#800000")) + .light_color(SvgColor("#ffff80")) + .build(); + let expected = include_str!("test_annex_i_micro_qr_as_svg.svg"); + assert_eq!(&image, expected); + } +} diff --git a/qrcode-rust/src/main.rs b/qrcode-rust/src/main.rs new file mode 100644 index 0000000..67946c8 --- /dev/null +++ b/qrcode-rust/src/main.rs @@ -0,0 +1,11 @@ +use qrcode::{render::unicode, QrCode}; + +fn main() { + let code = QrCode::new("https://www.youtube.com/watch?v=p7YXXieghto").unwrap(); + let image = code + .render::() + .dark_color(unicode::Dense1x2::Light) + .light_color(unicode::Dense1x2::Dark) + .build(); + println!("{}", image); +} diff --git a/qrcode-rust/src/optimize.rs b/qrcode-rust/src/optimize.rs new file mode 100644 index 0000000..2ba8cd6 --- /dev/null +++ b/qrcode-rust/src/optimize.rs @@ -0,0 +1,686 @@ +//! Find the optimal data mode sequence to encode a piece of data. +use crate::types::{Mode, Version}; +#[cfg(test)] +use alloc::vec::{self, Vec, *}; + +use core::slice::Iter; + +#[cfg(feature = "bench")] +extern crate test; + +//------------------------------------------------------------------------------ +//{{{ Segment + +/// A segment of data committed to an encoding mode. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub struct Segment { + /// The encoding mode of the segment of data. + pub mode: Mode, + + /// The start index of the segment. + pub begin: usize, + + /// The end index (exclusive) of the segment. + pub end: usize, +} + +impl Segment { + /// Compute the number of bits (including the size of the mode indicator and + /// length bits) when this segment is encoded. + pub fn encoded_len(&self, version: Version) -> usize { + let byte_size = self.end - self.begin; + let chars_count = if self.mode == Mode::Kanji { byte_size / 2 } else { byte_size }; + + let mode_bits_count = version.mode_bits_count(); + let length_bits_count = self.mode.length_bits_count(version); + let data_bits_count = self.mode.data_bits_count(chars_count); + + mode_bits_count + length_bits_count + data_bits_count + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Parser + +/// This iterator is basically equivalent to +/// +/// ```ignore +/// data.map(|c| ExclCharSet::from_u8(*c)) +/// .chain(Some(ExclCharSet::End).move_iter()) +/// .enumerate() +/// ``` +/// +/// But the type is too hard to write, thus the new type. +/// +struct EcsIter { + base: I, + index: usize, + ended: bool, +} + +impl<'a, I: Iterator> Iterator for EcsIter { + type Item = (usize, ExclCharSet); + + fn next(&mut self) -> Option<(usize, ExclCharSet)> { + if self.ended { + return None; + } + + match self.base.next() { + None => { + self.ended = true; + Some((self.index, ExclCharSet::End)) + } + Some(c) => { + let old_index = self.index; + self.index += 1; + Some((old_index, ExclCharSet::from_u8(*c))) + } + } + } +} + +/// QR code data parser to classify the input into distinct segments. +pub struct Parser<'a> { + ecs_iter: EcsIter>, + state: State, + begin: usize, + pending_single_byte: bool, +} + +impl<'a> Parser<'a> { + /// Creates a new iterator which parse the data into segments that only + /// contains their exclusive subsets. No optimization is done at this point. + /// + /// use qrcode::optimize::{Parser, Segment}; + /// use qrcode::types::Mode::{Alphanumeric, Numeric, Byte}; + /// + /// let parse_res = Parser::new(b"ABC123abcd").collect::>(); + /// assert_eq!(parse_res, vec![Segment { mode: Alphanumeric, begin: 0, end: 3 }, + /// Segment { mode: Numeric, begin: 3, end: 6 }, + /// Segment { mode: Byte, begin: 6, end: 10 }]); + /// + pub fn new(data: &[u8]) -> Parser { + Parser { + ecs_iter: EcsIter { base: data.iter(), index: 0, ended: false }, + state: State::Init, + begin: 0, + pending_single_byte: false, + } + } +} + +impl<'a> Iterator for Parser<'a> { + type Item = Segment; + + fn next(&mut self) -> Option { + if self.pending_single_byte { + self.pending_single_byte = false; + self.begin += 1; + return Some(Segment { mode: Mode::Byte, begin: self.begin - 1, end: self.begin }); + } + + loop { + let (i, ecs) = match self.ecs_iter.next() { + None => return None, + Some(a) => a, + }; + let (next_state, action) = STATE_TRANSITION[self.state as usize + ecs as usize]; + self.state = next_state; + + let old_begin = self.begin; + let push_mode = match action { + Action::Idle => continue, + Action::Numeric => Mode::Numeric, + Action::Alpha => Mode::Alphanumeric, + Action::Byte => Mode::Byte, + Action::Kanji => Mode::Kanji, + Action::KanjiAndSingleByte => { + let next_begin = i - 1; + if self.begin == next_begin { + Mode::Byte + } else { + self.pending_single_byte = true; + self.begin = next_begin; + return Some(Segment { mode: Mode::Kanji, begin: old_begin, end: next_begin }); + } + } + }; + + self.begin = i; + return Some(Segment { mode: push_mode, begin: old_begin, end: i }); + } + } +} + +#[cfg(test)] +mod parse_tests { + use alloc::vec::{self, Vec}; + + use crate::optimize::{Parser, Segment}; + use crate::types::Mode; + + fn parse(data: &[u8]) -> Vec { + Parser::new(data).collect() + } + + #[test] + fn test_parse_1() { + let segs = parse(b"01049123451234591597033130128%10ABC123"); + assert_eq!( + segs, + vec![ + Segment { mode: Mode::Numeric, begin: 0, end: 29 }, + Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 }, + Segment { mode: Mode::Numeric, begin: 30, end: 32 }, + Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 }, + Segment { mode: Mode::Numeric, begin: 35, end: 38 }, + ] + ); + } + + #[test] + fn test_parse_shift_jis_example_1() { + let segs = parse(b"\x82\xa0\x81\x41\x41\xb1\x81\xf0"); // "あ、AアÅ" + assert_eq!( + segs, + vec![ + Segment { mode: Mode::Kanji, begin: 0, end: 4 }, + Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 }, + Segment { mode: Mode::Byte, begin: 5, end: 6 }, + Segment { mode: Mode::Kanji, begin: 6, end: 8 }, + ] + ); + } + + #[test] + fn test_parse_utf_8() { + // Mojibake? + let segs = parse(b"\xe3\x81\x82\xe3\x80\x81A\xef\xbd\xb1\xe2\x84\xab"); + assert_eq!( + segs, + vec![ + Segment { mode: Mode::Kanji, begin: 0, end: 4 }, + Segment { mode: Mode::Byte, begin: 4, end: 5 }, + Segment { mode: Mode::Kanji, begin: 5, end: 7 }, + Segment { mode: Mode::Byte, begin: 7, end: 10 }, + Segment { mode: Mode::Kanji, begin: 10, end: 12 }, + Segment { mode: Mode::Byte, begin: 12, end: 13 }, + ] + ); + } + + #[test] + fn test_not_kanji_1() { + let segs = parse(b"\x81\x30"); + assert_eq!( + segs, + vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Numeric, begin: 1, end: 2 },] + ); + } + + #[test] + fn test_not_kanji_2() { + // Note that it's implementation detail that the byte seq is split into + // two. Perhaps adjust the test to check for this. + let segs = parse(b"\xeb\xc0"); + assert_eq!( + segs, + vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Byte, begin: 1, end: 2 },] + ); + } + + #[test] + fn test_not_kanji_3() { + let segs = parse(b"\x81\x7f"); + assert_eq!( + segs, + vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Byte, begin: 1, end: 2 },] + ); + } + + #[test] + fn test_not_kanji_4() { + let segs = parse(b"\x81\x40\x81"); + assert_eq!( + segs, + vec![Segment { mode: Mode::Kanji, begin: 0, end: 2 }, Segment { mode: Mode::Byte, begin: 2, end: 3 },] + ); + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Optimizer + +pub struct Optimizer { + parser: I, + last_segment: Segment, + last_segment_size: usize, + version: Version, + ended: bool, +} + +impl> Optimizer { + /// Optimize the segments by combining adjacent segments when possible. + /// + /// Currently this method uses a greedy algorithm by combining segments from + /// left to right until the new segment is longer than before. This method + /// does *not* use Annex J from the ISO standard. + /// + pub fn new(mut segments: I, version: Version) -> Self { + match segments.next() { + None => Self { + parser: segments, + last_segment: Segment { mode: Mode::Numeric, begin: 0, end: 0 }, + last_segment_size: 0, + version, + ended: true, + }, + Some(segment) => Self { + parser: segments, + last_segment: segment, + last_segment_size: segment.encoded_len(version), + version, + ended: false, + }, + } + } +} + +impl<'a> Parser<'a> { + pub fn optimize(self, version: Version) -> Optimizer> { + Optimizer::new(self, version) + } +} + +impl> Iterator for Optimizer { + type Item = Segment; + + fn next(&mut self) -> Option { + if self.ended { + return None; + } + + loop { + match self.parser.next() { + None => { + self.ended = true; + return Some(self.last_segment); + } + Some(segment) => { + let seg_size = segment.encoded_len(self.version); + + let new_segment = Segment { + mode: self.last_segment.mode.max(segment.mode), + begin: self.last_segment.begin, + end: segment.end, + }; + let new_size = new_segment.encoded_len(self.version); + + if self.last_segment_size + seg_size >= new_size { + self.last_segment = new_segment; + self.last_segment_size = new_size; + } else { + let old_segment = self.last_segment; + self.last_segment = segment; + self.last_segment_size = seg_size; + return Some(old_segment); + } + } + } + } + } +} + +/// Computes the total encoded length of all segments. +pub fn total_encoded_len(segments: &[Segment], version: Version) -> usize { + segments.iter().map(|seg| seg.encoded_len(version)).sum() +} + +#[cfg(test)] +mod optimize_tests { + use alloc::vec::Vec; + + use crate::optimize::{total_encoded_len, Optimizer, Segment}; + use crate::types::{Mode, Version}; + + fn test_optimization_result(given: Vec, expected: Vec, version: Version) { + let prev_len = total_encoded_len(&*given, version); + let opt_segs = Optimizer::new(given.iter().map(|seg| *seg), version).collect::>(); + let new_len = total_encoded_len(&*opt_segs, version); + if given != opt_segs { + assert!(prev_len > new_len, "{} > {}", prev_len, new_len); + } + assert!( + opt_segs == expected, + "Optimization gave something better: {} < {} ({:?})", + new_len, + total_encoded_len(&*expected, version), + opt_segs + ); + } + + #[test] + fn test_example_1() { + test_optimization_result( + vec![ + Segment { mode: Mode::Alphanumeric, begin: 0, end: 3 }, + Segment { mode: Mode::Numeric, begin: 3, end: 6 }, + Segment { mode: Mode::Byte, begin: 6, end: 10 }, + ], + vec![ + Segment { mode: Mode::Alphanumeric, begin: 0, end: 6 }, + Segment { mode: Mode::Byte, begin: 6, end: 10 }, + ], + Version::Normal(1), + ); + } + + #[test] + fn test_example_2() { + test_optimization_result( + vec![ + Segment { mode: Mode::Numeric, begin: 0, end: 29 }, + Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 }, + Segment { mode: Mode::Numeric, begin: 30, end: 32 }, + Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 }, + Segment { mode: Mode::Numeric, begin: 35, end: 38 }, + ], + vec![ + Segment { mode: Mode::Numeric, begin: 0, end: 29 }, + Segment { mode: Mode::Alphanumeric, begin: 29, end: 38 }, + ], + Version::Normal(9), + ); + } + + #[test] + fn test_example_3() { + test_optimization_result( + vec![ + Segment { mode: Mode::Kanji, begin: 0, end: 4 }, + Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 }, + Segment { mode: Mode::Byte, begin: 5, end: 6 }, + Segment { mode: Mode::Kanji, begin: 6, end: 8 }, + ], + vec![Segment { mode: Mode::Byte, begin: 0, end: 8 }], + Version::Normal(1), + ); + } + + #[test] + fn test_example_4() { + test_optimization_result( + vec![Segment { mode: Mode::Kanji, begin: 0, end: 10 }, Segment { mode: Mode::Byte, begin: 10, end: 11 }], + vec![Segment { mode: Mode::Kanji, begin: 0, end: 10 }, Segment { mode: Mode::Byte, begin: 10, end: 11 }], + Version::Normal(1), + ); + } + + #[test] + fn test_annex_j_guideline_1a() { + test_optimization_result( + vec![ + Segment { mode: Mode::Numeric, begin: 0, end: 3 }, + Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }, + ], + vec![ + Segment { mode: Mode::Numeric, begin: 0, end: 3 }, + Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }, + ], + Version::Micro(2), + ); + } + + #[test] + fn test_annex_j_guideline_1b() { + test_optimization_result( + vec![ + Segment { mode: Mode::Numeric, begin: 0, end: 2 }, + Segment { mode: Mode::Alphanumeric, begin: 2, end: 4 }, + ], + vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }], + Version::Micro(2), + ); + } + + #[test] + fn test_annex_j_guideline_1c() { + test_optimization_result( + vec![ + Segment { mode: Mode::Numeric, begin: 0, end: 3 }, + Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }, + ], + vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }], + Version::Micro(3), + ); + } +} + +#[cfg(feature = "bench")] +#[bench] +fn bench_optimize(bencher: &mut test::Bencher) { + use crate::types::Version; + + let data = b"QR\x83R\x81[\x83h\x81i\x83L\x83\x85\x81[\x83A\x81[\x83\x8b\x83R\x81[\x83h\x81j\ + \x82\xc6\x82\xcd\x81A1994\x94N\x82\xc9\x83f\x83\x93\x83\\\x81[\x82\xcc\x8aJ\ + \x94\xad\x95\x94\x96\xe5\x81i\x8c\xbb\x8d\xdd\x82\xcd\x95\xaa\x97\xa3\x82\xb5\x83f\ + \x83\x93\x83\\\x81[\x83E\x83F\x81[\x83u\x81j\x82\xaa\x8aJ\x94\xad\x82\xb5\x82\xbd\ + \x83}\x83g\x83\x8a\x83b\x83N\x83X\x8c^\x93\xf1\x8e\x9f\x8c\xb3\x83R\x81[\x83h\ + \x82\xc5\x82\xa0\x82\xe9\x81B\x82\xc8\x82\xa8\x81AQR\x83R\x81[\x83h\x82\xc6\ + \x82\xa2\x82\xa4\x96\xbc\x8f\xcc\x81i\x82\xa8\x82\xe6\x82\xd1\x92P\x8c\xea\x81j\ + \x82\xcd\x83f\x83\x93\x83\\\x81[\x83E\x83F\x81[\x83u\x82\xcc\x93o\x98^\x8f\xa4\ + \x95W\x81i\x91\xe64075066\x8d\x86\x81j\x82\xc5\x82\xa0\x82\xe9\x81BQR\x82\xcd\ + Quick Response\x82\xc9\x97R\x97\x88\x82\xb5\x81A\x8d\x82\x91\xac\x93\xc7\x82\xdd\ + \x8e\xe6\x82\xe8\x82\xaa\x82\xc5\x82\xab\x82\xe9\x82\xe6\x82\xa4\x82\xc9\x8aJ\ + \x94\xad\x82\xb3\x82\xea\x82\xbd\x81B\x93\x96\x8f\x89\x82\xcd\x8e\xa9\x93\xae\ + \x8e\xd4\x95\x94\x95i\x8dH\x8f\xea\x82\xe2\x94z\x91\x97\x83Z\x83\x93\x83^\x81[\ + \x82\xc8\x82\xc7\x82\xc5\x82\xcc\x8eg\x97p\x82\xf0\x94O\x93\xaa\x82\xc9\x8aJ\ + \x94\xad\x82\xb3\x82\xea\x82\xbd\x82\xaa\x81A\x8c\xbb\x8d\xdd\x82\xc5\x82\xcd\x83X\ + \x83}\x81[\x83g\x83t\x83H\x83\x93\x82\xcc\x95\x81\x8by\x82\xc8\x82\xc7\x82\xc9\ + \x82\xe6\x82\xe8\x93\xfa\x96{\x82\xc9\x8c\xc0\x82\xe7\x82\xb8\x90\xa2\x8aE\x93I\ + \x82\xc9\x95\x81\x8by\x82\xb5\x82\xc4\x82\xa2\x82\xe9\x81B"; + bencher.iter(|| Parser::new(data).optimize(Version::Normal(15))); +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Internal types and data for parsing + +/// All values of `u8` can be split into 9 different character sets when +/// determining which encoding to use. This enum represents these groupings for +/// parsing purpose. +#[derive(Copy, Clone)] +enum ExclCharSet { + /// The end of string. + End = 0, + + /// All symbols supported by the Alphanumeric encoding, i.e. space, `$`, `%`, + /// `*`, `+`, `-`, `.`, `/` and `:`. + Symbol = 1, + + /// All numbers (0–9). + Numeric = 2, + + /// All uppercase letters (A–Z). These characters may also appear in the + /// second byte of a Shift JIS 2-byte encoding. + Alpha = 3, + + /// The first byte of a Shift JIS 2-byte encoding, in the range 0x81–0x9f. + KanjiHi1 = 4, + + /// The first byte of a Shift JIS 2-byte encoding, in the range 0xe0–0xea. + KanjiHi2 = 5, + + /// The first byte of a Shift JIS 2-byte encoding, of value 0xeb. This is + /// different from the other two range that the second byte has a smaller + /// range. + KanjiHi3 = 6, + + /// The second byte of a Shift JIS 2-byte encoding, in the range 0x40–0xbf, + /// excluding letters (covered by `Alpha`), 0x81–0x9f (covered by `KanjiHi1`), + /// and the invalid byte 0x7f. + KanjiLo1 = 7, + + /// The second byte of a Shift JIS 2-byte encoding, in the range 0xc0–0xfc, + /// excluding the range 0xe0–0xeb (covered by `KanjiHi2` and `KanjiHi3`). + /// This half of byte-pair cannot appear as the second byte leaded by + /// `KanjiHi3`. + KanjiLo2 = 8, + + /// Any other values not covered by the above character sets. + Byte = 9, +} + +impl ExclCharSet { + /// Determines which character set a byte is in. + fn from_u8(c: u8) -> Self { + match c { + 0x20 | 0x24 | 0x25 | 0x2a | 0x2b | 0x2d..=0x2f | 0x3a => ExclCharSet::Symbol, + 0x30..=0x39 => ExclCharSet::Numeric, + 0x41..=0x5a => ExclCharSet::Alpha, + 0x81..=0x9f => ExclCharSet::KanjiHi1, + 0xe0..=0xea => ExclCharSet::KanjiHi2, + 0xeb => ExclCharSet::KanjiHi3, + 0x40 | 0x5b..=0x7e | 0x80 | 0xa0..=0xbf => ExclCharSet::KanjiLo1, + 0xc0..=0xdf | 0xec..=0xfc => ExclCharSet::KanjiLo2, + _ => ExclCharSet::Byte, + } + } +} + +/// The current parsing state. +#[derive(Copy, Clone)] +enum State { + /// Just initialized. + Init = 0, + + /// Inside a string that can be exclusively encoded as Numeric. + Numeric = 10, + + /// Inside a string that can be exclusively encoded as Alphanumeric. + Alpha = 20, + + /// Inside a string that can be exclusively encoded as 8-Bit Byte. + Byte = 30, + + /// Just encountered the first byte of a Shift JIS 2-byte sequence of the + /// set `KanjiHi1` or `KanjiHi2`. + KanjiHi12 = 40, + + /// Just encountered the first byte of a Shift JIS 2-byte sequence of the + /// set `KanjiHi3`. + KanjiHi3 = 50, + + /// Inside a string that can be exclusively encoded as Kanji. + Kanji = 60, +} + +/// What should the parser do after a state transition. +#[derive(Copy, Clone)] +enum Action { + /// The parser should do nothing. + Idle, + + /// Push the current segment as a Numeric string, and reset the marks. + Numeric, + + /// Push the current segment as an Alphanumeric string, and reset the marks. + Alpha, + + /// Push the current segment as a 8-Bit Byte string, and reset the marks. + Byte, + + /// Push the current segment as a Kanji string, and reset the marks. + Kanji, + + /// Push the current segment excluding the last byte as a Kanji string, then + /// push the remaining single byte as a Byte string, and reset the marks. + KanjiAndSingleByte, +} + +static STATE_TRANSITION: [(State, Action); 70] = [ + // STATE_TRANSITION[current_state + next_character] == (next_state, what_to_do) + + // Init state: + (State::Init, Action::Idle), // End + (State::Alpha, Action::Idle), // Symbol + (State::Numeric, Action::Idle), // Numeric + (State::Alpha, Action::Idle), // Alpha + (State::KanjiHi12, Action::Idle), // KanjiHi1 + (State::KanjiHi12, Action::Idle), // KanjiHi2 + (State::KanjiHi3, Action::Idle), // KanjiHi3 + (State::Byte, Action::Idle), // KanjiLo1 + (State::Byte, Action::Idle), // KanjiLo2 + (State::Byte, Action::Idle), // Byte + // Numeric state: + (State::Init, Action::Numeric), // End + (State::Alpha, Action::Numeric), // Symbol + (State::Numeric, Action::Idle), // Numeric + (State::Alpha, Action::Numeric), // Alpha + (State::KanjiHi12, Action::Numeric), // KanjiHi1 + (State::KanjiHi12, Action::Numeric), // KanjiHi2 + (State::KanjiHi3, Action::Numeric), // KanjiHi3 + (State::Byte, Action::Numeric), // KanjiLo1 + (State::Byte, Action::Numeric), // KanjiLo2 + (State::Byte, Action::Numeric), // Byte + // Alpha state: + (State::Init, Action::Alpha), // End + (State::Alpha, Action::Idle), // Symbol + (State::Numeric, Action::Alpha), // Numeric + (State::Alpha, Action::Idle), // Alpha + (State::KanjiHi12, Action::Alpha), // KanjiHi1 + (State::KanjiHi12, Action::Alpha), // KanjiHi2 + (State::KanjiHi3, Action::Alpha), // KanjiHi3 + (State::Byte, Action::Alpha), // KanjiLo1 + (State::Byte, Action::Alpha), // KanjiLo2 + (State::Byte, Action::Alpha), // Byte + // Byte state: + (State::Init, Action::Byte), // End + (State::Alpha, Action::Byte), // Symbol + (State::Numeric, Action::Byte), // Numeric + (State::Alpha, Action::Byte), // Alpha + (State::KanjiHi12, Action::Byte), // KanjiHi1 + (State::KanjiHi12, Action::Byte), // KanjiHi2 + (State::KanjiHi3, Action::Byte), // KanjiHi3 + (State::Byte, Action::Idle), // KanjiLo1 + (State::Byte, Action::Idle), // KanjiLo2 + (State::Byte, Action::Idle), // Byte + // KanjiHi12 state: + (State::Init, Action::KanjiAndSingleByte), // End + (State::Alpha, Action::KanjiAndSingleByte), // Symbol + (State::Numeric, Action::KanjiAndSingleByte), // Numeric + (State::Kanji, Action::Idle), // Alpha + (State::Kanji, Action::Idle), // KanjiHi1 + (State::Kanji, Action::Idle), // KanjiHi2 + (State::Kanji, Action::Idle), // KanjiHi3 + (State::Kanji, Action::Idle), // KanjiLo1 + (State::Kanji, Action::Idle), // KanjiLo2 + (State::Byte, Action::KanjiAndSingleByte), // Byte + // KanjiHi3 state: + (State::Init, Action::KanjiAndSingleByte), // End + (State::Alpha, Action::KanjiAndSingleByte), // Symbol + (State::Numeric, Action::KanjiAndSingleByte), // Numeric + (State::Kanji, Action::Idle), // Alpha + (State::Kanji, Action::Idle), // KanjiHi1 + (State::KanjiHi12, Action::KanjiAndSingleByte), // KanjiHi2 + (State::KanjiHi3, Action::KanjiAndSingleByte), // KanjiHi3 + (State::Kanji, Action::Idle), // KanjiLo1 + (State::Byte, Action::KanjiAndSingleByte), // KanjiLo2 + (State::Byte, Action::KanjiAndSingleByte), // Byte + // Kanji state: + (State::Init, Action::Kanji), // End + (State::Alpha, Action::Kanji), // Symbol + (State::Numeric, Action::Kanji), // Numeric + (State::Alpha, Action::Kanji), // Alpha + (State::KanjiHi12, Action::Idle), // KanjiHi1 + (State::KanjiHi12, Action::Idle), // KanjiHi2 + (State::KanjiHi3, Action::Idle), // KanjiHi3 + (State::Byte, Action::Kanji), // KanjiLo1 + (State::Byte, Action::Kanji), // KanjiLo2 + (State::Byte, Action::Kanji), // Byte +]; + +//}}} diff --git a/qrcode-rust/src/render/image.rs b/qrcode-rust/src/render/image.rs new file mode 100644 index 0000000..d42776b --- /dev/null +++ b/qrcode-rust/src/render/image.rs @@ -0,0 +1,150 @@ +#![cfg(feature="image")] + +use crate::render::{Canvas, Pixel}; +use crate::types::Color; + +use image::{ImageBuffer, Luma, LumaA, Pixel as ImagePixel, Primitive, Rgb, Rgba}; + +macro_rules! impl_pixel_for_image_pixel { + ($p:ident<$s:ident>: $c:pat => $d:expr) => { + impl<$s: Primitive + 'static> Pixel for $p<$s> { + type Image = ImageBuffer>; + type Canvas = (Self, Self::Image); + + fn default_color(color: Color) -> Self { + match color.select($s::zero(), $s::max_value()) { + $c => $p($d), + } + } + } + }; +} + +impl_pixel_for_image_pixel! { Luma: p => [p] } +impl_pixel_for_image_pixel! { LumaA: p => [p, S::max_value()] } +impl_pixel_for_image_pixel! { Rgb: p => [p, p, p] } +impl_pixel_for_image_pixel! { Rgba: p => [p, p, p, S::max_value()] } + +impl Canvas for (P, ImageBuffer>) { + type Pixel = P; + type Image = ImageBuffer>; + + fn new(width: u32, height: u32, dark_pixel: P, light_pixel: P) -> Self { + (dark_pixel, ImageBuffer::from_pixel(width, height, light_pixel)) + } + + fn draw_dark_pixel(&mut self, x: u32, y: u32) { + self.1.put_pixel(x, y, self.0); + } + + fn into_image(self) -> ImageBuffer> { + self.1 + } +} + +#[cfg(test)] +mod render_tests { + use crate::render::Renderer; + use crate::types::Color; + use image::{Luma, Rgba}; + + #[test] + fn test_render_luma8_unsized() { + let image = Renderer::>::new( + &[ + Color::Light, + Color::Dark, + Color::Dark, + // + Color::Dark, + Color::Light, + Color::Light, + // + Color::Light, + Color::Dark, + Color::Light, + ], + 3, + 1, + ) + .module_dimensions(1, 1) + .build(); + + #[rustfmt::skip] + let expected = [ + 255, 255, 255, 255, 255, + 255, 255, 0, 0, 255, + 255, 0, 255, 255, 255, + 255, 255, 0, 255, 255, + 255, 255, 255, 255, 255, + ]; + assert_eq!(image.into_raw(), expected); + } + + #[test] + fn test_render_rgba_unsized() { + let image = Renderer::>::new(&[Color::Light, Color::Dark, Color::Dark, Color::Dark], 2, 1) + .module_dimensions(1, 1) + .build(); + + #[rustfmt::skip] + let expected: &[u8] = &[ + 255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255, + 255,255,255,255, 255,255,255,255, 0, 0, 0,255, 255,255,255,255, + 255,255,255,255, 0, 0, 0,255, 0, 0, 0,255, 255,255,255,255, + 255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255, + ]; + + assert_eq!(image.into_raw(), expected); + } + + #[test] + fn test_render_resized_min() { + let image = Renderer::>::new(&[Color::Dark, Color::Light, Color::Light, Color::Dark], 2, 1) + .min_dimensions(10, 10) + .build(); + + #[rustfmt::skip] + let expected: &[u8] = &[ + 255,255,255, 255,255,255, 255,255,255, 255,255,255, + 255,255,255, 255,255,255, 255,255,255, 255,255,255, + 255,255,255, 255,255,255, 255,255,255, 255,255,255, + + 255,255,255, 0, 0, 0, 255,255,255, 255,255,255, + 255,255,255, 0, 0, 0, 255,255,255, 255,255,255, + 255,255,255, 0, 0, 0, 255,255,255, 255,255,255, + + 255,255,255, 255,255,255, 0, 0, 0, 255,255,255, + 255,255,255, 255,255,255, 0, 0, 0, 255,255,255, + 255,255,255, 255,255,255, 0, 0, 0, 255,255,255, + + 255,255,255, 255,255,255, 255,255,255, 255,255,255, + 255,255,255, 255,255,255, 255,255,255, 255,255,255, + 255,255,255, 255,255,255, 255,255,255, 255,255,255, + ]; + + assert_eq!(image.dimensions(), (12, 12)); + assert_eq!(image.into_raw(), expected); + } + + #[test] + fn test_render_resized_max() { + let image = Renderer::>::new(&[Color::Dark, Color::Light, Color::Light, Color::Dark], 2, 1) + .max_dimensions(10, 5) + .build(); + + #[rustfmt::skip] + let expected: &[u8] = &[ + 255,255, 255,255, 255,255, 255,255, + + 255,255, 0, 0, 255,255, 255,255, + + 255,255, 255,255, 0, 0, 255,255, + + 255,255, 255,255, 255,255, 255,255, + ]; + + assert_eq!(image.dimensions(), (8, 4)); + assert_eq!(image.into_raw(), expected); + } +} diff --git a/qrcode-rust/src/render/mod.rs b/qrcode-rust/src/render/mod.rs new file mode 100644 index 0000000..71f4306 --- /dev/null +++ b/qrcode-rust/src/render/mod.rs @@ -0,0 +1,191 @@ +//! Render a QR code into image. + +use crate::cast::As; +use crate::types::Color; +use core::cmp::max; + +pub mod image; +pub mod string; +pub mod svg; +pub mod unicode; + +//------------------------------------------------------------------------------ +//{{{ Pixel trait + +/// Abstraction of an image pixel. +pub trait Pixel: Copy + Sized { + /// Type of the finalized image. + type Image: Sized + 'static; + + /// The type that stores an intermediate buffer before finalizing to a + /// concrete image + type Canvas: Canvas; + + /// Obtains the default module size. The result must be at least 1×1. + fn default_unit_size() -> (u32, u32) { + (8, 8) + } + + /// Obtains the default pixel color when a module is dark or light. + fn default_color(color: Color) -> Self; +} + +/// Rendering canvas of a QR code image. +pub trait Canvas: Sized { + type Pixel: Sized; + type Image: Sized; + + /// Constructs a new canvas of the given dimensions. + fn new(width: u32, height: u32, dark_pixel: Self::Pixel, light_pixel: Self::Pixel) -> Self; + + /// Draws a single dark pixel at the (x, y) coordinate. + fn draw_dark_pixel(&mut self, x: u32, y: u32); + + fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) { + for y in top..(top + height) { + for x in left..(left + width) { + self.draw_dark_pixel(x, y); + } + } + } + + /// Finalize the canvas to a real image. + fn into_image(self) -> Self::Image; +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Renderer + +/// A QR code renderer. This is a builder type which converts a bool-vector into +/// an image. +pub struct Renderer<'a, P: Pixel> { + content: &'a [Color], + modules_count: u32, // <- we call it `modules_count` here to avoid ambiguity of `width`. + quiet_zone: u32, + module_size: (u32, u32), + + dark_color: P, + light_color: P, + has_quiet_zone: bool, +} + +impl<'a, P: Pixel> Renderer<'a, P> { + /// Creates a new renderer. + pub fn new(content: &'a [Color], modules_count: usize, quiet_zone: u32) -> Renderer<'a, P> { + assert!(modules_count * modules_count == content.len()); + Renderer { + content, + modules_count: modules_count.as_u32(), + quiet_zone, + module_size: P::default_unit_size(), + dark_color: P::default_color(Color::Dark), + light_color: P::default_color(Color::Light), + has_quiet_zone: true, + } + } + + /// Sets color of a dark module. Default is opaque black. + pub fn dark_color(&mut self, color: P) -> &mut Self { + self.dark_color = color; + self + } + + /// Sets color of a light module. Default is opaque white. + pub fn light_color(&mut self, color: P) -> &mut Self { + self.light_color = color; + self + } + + /// Whether to include the quiet zone in the generated image. + pub fn quiet_zone(&mut self, has_quiet_zone: bool) -> &mut Self { + self.has_quiet_zone = has_quiet_zone; + self + } + + /// Sets the size of each module in pixels. Default is 8px. + #[deprecated(since = "0.4.0", note = "use `.module_dimensions(width, width)` instead")] + pub fn module_size(&mut self, width: u32) -> &mut Self { + self.module_dimensions(width, width) + } + + /// Sets the size of each module in pixels. Default is 8×8. + pub fn module_dimensions(&mut self, width: u32, height: u32) -> &mut Self { + self.module_size = (max(width, 1), max(height, 1)); + self + } + + #[deprecated(since = "0.4.0", note = "use `.min_dimensions(width, width)` instead")] + pub fn min_width(&mut self, width: u32) -> &mut Self { + self.min_dimensions(width, width) + } + + /// Sets the minimum total image size in pixels, including the quiet zone if + /// applicable. The renderer will try to find the dimension as small as + /// possible, such that each module in the QR code has uniform size (no + /// distortion). + /// + /// For instance, a version 1 QR code has 19 modules across including the + /// quiet zone. If we request an image of size ≥200×200, we get that each + /// module's size should be 11×11, so the actual image size will be 209×209. + pub fn min_dimensions(&mut self, width: u32, height: u32) -> &mut Self { + let quiet_zone = if self.has_quiet_zone { 2 } else { 0 } * self.quiet_zone; + let width_in_modules = self.modules_count + quiet_zone; + let unit_width = (width + width_in_modules - 1) / width_in_modules; + let unit_height = (height + width_in_modules - 1) / width_in_modules; + self.module_dimensions(unit_width, unit_height) + } + + /// Sets the maximum total image size in pixels, including the quiet zone if + /// applicable. The renderer will try to find the dimension as large as + /// possible, such that each module in the QR code has uniform size (no + /// distortion). + /// + /// For instance, a version 1 QR code has 19 modules across including the + /// quiet zone. If we request an image of size ≤200×200, we get that each + /// module's size should be 10×10, so the actual image size will be 190×190. + /// + /// The module size is at least 1×1, so if the restriction is too small, the + /// final image *can* be larger than the input. + pub fn max_dimensions(&mut self, width: u32, height: u32) -> &mut Self { + let quiet_zone = if self.has_quiet_zone { 2 } else { 0 } * self.quiet_zone; + let width_in_modules = self.modules_count + quiet_zone; + let unit_width = width / width_in_modules; + let unit_height = height / width_in_modules; + self.module_dimensions(unit_width, unit_height) + } + + /// Renders the QR code into an image. + #[deprecated(since = "0.4.0", note = "renamed to `.build()` to de-emphasize the image connection")] + pub fn to_image(&self) -> P::Image { + self.build() + } + + /// Renders the QR code into an image. + pub fn build(&self) -> P::Image { + let w = self.modules_count; + let qz = if self.has_quiet_zone { self.quiet_zone } else { 0 }; + let width = w + 2 * qz; + + let (mw, mh) = self.module_size; + let real_width = width * mw; + let real_height = width * mh; + + let mut canvas = P::Canvas::new(real_width, real_height, self.dark_color, self.light_color); + let mut i = 0; + for y in 0..width { + for x in 0..width { + if qz <= x && x < w + qz && qz <= y && y < w + qz { + if self.content[i] != Color::Light { + canvas.draw_dark_rect(x * mw, y * mh, mw, mh); + } + i += 1; + } + } + } + + canvas.into_image() + } +} + +//}}} diff --git a/qrcode-rust/src/render/string.rs b/qrcode-rust/src/render/string.rs new file mode 100644 index 0000000..a7ae42c --- /dev/null +++ b/qrcode-rust/src/render/string.rs @@ -0,0 +1,125 @@ +//! String rendering support. + +use alloc::string::String; +use alloc::vec::Vec; + +use crate::cast::As; +use crate::render::{Canvas as RenderCanvas, Pixel}; +use crate::types::Color; + +pub trait Element: Copy { + fn default_color(color: Color) -> Self; + fn strlen(self) -> usize; + fn append_to_string(self, string: &mut String); +} + +impl Element for char { + fn default_color(color: Color) -> Self { + color.select('█', ' ') + } + + fn strlen(self) -> usize { + self.len_utf8() + } + + fn append_to_string(self, string: &mut String) { + string.push(self) + } +} + +impl<'a> Element for &'a str { + fn default_color(color: Color) -> Self { + color.select("\u{2588}", " ") + } + + fn strlen(self) -> usize { + self.len() + } + + fn append_to_string(self, string: &mut String) { + string.push_str(self) + } +} + +#[doc(hidden)] +pub struct Canvas { + buffer: Vec

, + width: usize, + dark_pixel: P, + dark_cap_inc: isize, + capacity: isize, +} + +impl Pixel for P { + type Canvas = Canvas; + type Image = String; + + fn default_unit_size() -> (u32, u32) { + (1, 1) + } + + fn default_color(color: Color) -> Self { + ::default_color(color) + } +} + +impl RenderCanvas for Canvas

{ + type Pixel = P; + type Image = String; + + fn new(width: u32, height: u32, dark_pixel: P, light_pixel: P) -> Self { + let width = width.as_usize(); + let height = height.as_isize(); + let dark_cap = dark_pixel.strlen().as_isize(); + let light_cap = light_pixel.strlen().as_isize(); + + Self { + buffer: vec![light_pixel; width * height.as_usize()], + width, + dark_pixel, + dark_cap_inc: dark_cap - light_cap, + capacity: light_cap * width.as_isize() * height + (height - 1), + } + } + + fn draw_dark_pixel(&mut self, x: u32, y: u32) { + let x = x.as_usize(); + let y = y.as_usize(); + self.capacity += self.dark_cap_inc; + self.buffer[x + y * self.width] = self.dark_pixel; + } + + fn into_image(self) -> String { + let mut result = String::with_capacity(self.capacity.as_usize()); + for (i, pixel) in self.buffer.into_iter().enumerate() { + if i != 0 && i % self.width == 0 { + result.push('\n'); + } + pixel.append_to_string(&mut result); + } + result + } +} + +#[test] +fn test_render_to_string() { + use crate::render::Renderer; + + let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark]; + let image: String = Renderer::::new(colors, 2, 1).build(); + assert_eq!(&image, " \n \u{2588} \n \u{2588} \n "); + + let image2 = Renderer::new(colors, 2, 1).light_color("A").dark_color("!B!").module_dimensions(2, 2).build(); + + assert_eq!( + &image2, + "AAAAAAAA\n\ + AAAAAAAA\n\ + AA!B!!B!AAAA\n\ + AA!B!!B!AAAA\n\ + AAAA!B!!B!AA\n\ + AAAA!B!!B!AA\n\ + AAAAAAAA\n\ + AAAAAAAA" + ); +} diff --git a/qrcode-rust/src/render/svg.rs b/qrcode-rust/src/render/svg.rs new file mode 100644 index 0000000..de4dedf --- /dev/null +++ b/qrcode-rust/src/render/svg.rs @@ -0,0 +1,76 @@ +//! SVG rendering support. +//! +//! # Example +//! +//! ``` +//! use qrcode::QrCode; +//! use qrcode::render::svg; +//! +//! let code = QrCode::new(b"Hello").unwrap(); +//! let svg_xml = code.render::().build(); +//! println!("{}", svg_xml); + +#![cfg(feature="svg")] + +use std::fmt::Write; +use std::marker::PhantomData; + +use crate::render::{Canvas as RenderCanvas, Pixel}; +use crate::types::Color as ModuleColor; + +/// An SVG color. +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Color<'a>(pub &'a str); + +impl<'a> Pixel for Color<'a> { + type Canvas = Canvas<'a>; + type Image = String; + + fn default_color(color: ModuleColor) -> Self { + Color(color.select("#000", "#fff")) + } +} + +#[doc(hidden)] +pub struct Canvas<'a> { + svg: String, + marker: PhantomData>, +} + +impl<'a> RenderCanvas for Canvas<'a> { + type Pixel = Color<'a>; + type Image = String; + + fn new(width: u32, height: u32, dark_pixel: Color<'a>, light_pixel: Color<'a>) -> Self { + Canvas { + svg: format!( + concat!( + r#""#, + r#""#, + r#""#, + r#" String { + self.svg.push_str(r#""/>"#); + self.svg + } +} diff --git a/qrcode-rust/src/render/unicode.rs b/qrcode-rust/src/render/unicode.rs new file mode 100644 index 0000000..e7bd4bf --- /dev/null +++ b/qrcode-rust/src/render/unicode.rs @@ -0,0 +1,146 @@ +//! UTF-8 rendering, with 2 pixels per symbol. + +use alloc::{ + string::String, + vec::{self, Vec}, +}; + +use crate::render::{Canvas as RenderCanvas, Color, Pixel}; + +const CODEPAGE: [&str; 4] = [" ", "\u{2584}", "\u{2580}", "\u{2588}"]; + +#[derive(Copy, Clone, PartialEq)] +pub enum Dense1x2 { + Dark, + Light, +} + +impl Pixel for Dense1x2 { + type Image = String; + type Canvas = Canvas1x2; + fn default_color(color: Color) -> Dense1x2 { + color.select(Dense1x2::Dark, Dense1x2::Light) + } + fn default_unit_size() -> (u32, u32) { + (1, 1) + } +} + +impl Dense1x2 { + fn value(self) -> u8 { + match self { + Dense1x2::Dark => 1, + Dense1x2::Light => 0, + } + } + fn parse_2_bits(sym: u8) -> &'static str { + CODEPAGE[usize::from(sym)] + } +} + +pub struct Canvas1x2 { + canvas: Vec, + width: u32, + dark_pixel: u8, +} + +impl RenderCanvas for Canvas1x2 { + type Pixel = Dense1x2; + type Image = String; + + fn new(width: u32, height: u32, dark_pixel: Dense1x2, light_pixel: Dense1x2) -> Self { + let a = vec![light_pixel.value(); (width * height) as usize]; + Canvas1x2 { width, canvas: a, dark_pixel: dark_pixel.value() } + } + + fn draw_dark_pixel(&mut self, x: u32, y: u32) { + self.canvas[(x + y * self.width) as usize] = self.dark_pixel; + } + + fn into_image(self) -> String { + self.canvas + // Chopping array into 1-line sized fragments + .chunks_exact(self.width as usize) + .collect::>() + // And then glueing every 2 lines. + .chunks(2) + .map(|rows| { + { + // Then zipping those 2 lines together into a single 2-bit number list. + if rows.len() == 2 { + rows[0].iter().zip(rows[1]).map(|(top, bot)| (top * 2 + bot)).collect::>() + } else { + rows[0].iter().map(|top| (top * 2)).collect::>() + } + } + .into_iter() + // Mapping those 2-bit numbers to corresponding pixels. + .map(Dense1x2::parse_2_bits) + .collect::>() + .concat() + }) + .collect::>() + .join("\n") + } +} + +#[test] +fn test_render_to_utf8_string() { + use crate::render::Renderer; + let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark]; + let image: String = Renderer::::new(colors, 2, 1).build(); + + assert_eq!(&image, " ▄ \n ▀ "); + + let image2 = Renderer::::new(colors, 2, 1).module_dimensions(2, 2).build(); + + assert_eq!(&image2, " \n ██ \n ██ \n "); +} + +#[test] +fn integration_render_utf8_1x2() { + use crate::render::unicode::Dense1x2; + use crate::{EcLevel, QrCode, Version}; + + let code = QrCode::with_version(b"09876542", Version::Micro(2), EcLevel::L).unwrap(); + let image = code.render::().module_dimensions(1, 1).build(); + assert_eq!( + image, + String::new() + + " \n" + + " █▀▀▀▀▀█ ▀ █ ▀ \n" + + " █ ███ █ ▀ █ \n" + + " █ ▀▀▀ █ ▀█ █ \n" + + " ▀▀▀▀▀▀▀ ▄▀▀ █ \n" + + " ▀█ ▀▀▀▀▀██▀▀▄ \n" + + " ▀███▄ ▀▀ █ ██ \n" + + " ▀▀▀ ▀ ▀▀ ▀ ▀ \n" + + " " + ) +} + +#[test] +fn integration_render_utf8_1x2_inverted() { + use crate::render::unicode::Dense1x2; + use crate::{EcLevel, QrCode, Version}; + + let code = QrCode::with_version(b"12345678", Version::Micro(2), EcLevel::L).unwrap(); + let image = code + .render::() + .dark_color(Dense1x2::Light) + .light_color(Dense1x2::Dark) + .module_dimensions(1, 1) + .build(); + assert_eq!( + image, + "█████████████████\n\ + ██ ▄▄▄▄▄ █▄▀▄█▄██\n\ + ██ █ █ █ █ ██\n\ + ██ █▄▄▄█ █▄▄██▀██\n\ + ██▄▄▄▄▄▄▄█▄▄▄▀ ██\n\ + ██▄ ▀ ▀ ▀▄▄ ████\n\ + ██▄▄▀▄█ ▀▀▀ ▀▄▄██\n\ + ██▄▄▄█▄▄█▄██▄█▄██\n\ + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀" + ); +} diff --git a/qrcode-rust/src/test_annex_i_micro_qr_as_image.png b/qrcode-rust/src/test_annex_i_micro_qr_as_image.png new file mode 100644 index 0000000000000000000000000000000000000000..43cb1f7afdf9ede217e5d9d5e8eec0de3952e8d6 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^XF!;d8A!6exwjEWu?6^qxc>j&(7?dZzPiE$D3b5# z;uum9_x742*8v3{*1-G+fAe4R-a4SZa>CW+th;9%bUk_7ldEsOOG#+<`&C z{Yt5G2>8;rV9sq8&6R3$j*)9uI0ocrcmjTFv0;>gTe~DWM4f4P!^b literal 0 HcmV?d00001 diff --git a/qrcode-rust/src/test_annex_i_micro_qr_as_svg.svg b/qrcode-rust/src/test_annex_i_micro_qr_as_svg.svg new file mode 100644 index 0000000..efac918 --- /dev/null +++ b/qrcode-rust/src/test_annex_i_micro_qr_as_svg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/qrcode-rust/src/test_annex_i_qr_as_image.png b/qrcode-rust/src/test_annex_i_qr_as_image.png new file mode 100644 index 0000000000000000000000000000000000000000..4d5e5d6924b13b7ba5911d97b94b3ae922f80d5f GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^FF=@)8A#qwx3UIOYymzYu0Z<#|Nl#G&c6#}^?ABD zhE&A8z2eAsM1kjM!2SQ1Bzt=jt}{#2Ep2&HdX(vzHsjjYmzU4@&(f^`=(9f(cV)@k=-yz&aAMx+tn`Er?b~j-NGhz|mAYkHlQrA)T+`>g z3Tq3td1RJ`Fr3e1y))G)78&qol`;+0843Jod5s; literal 0 HcmV?d00001 diff --git a/qrcode-rust/src/test_annex_i_qr_as_svg.svg b/qrcode-rust/src/test_annex_i_qr_as_svg.svg new file mode 100644 index 0000000..6f37446 --- /dev/null +++ b/qrcode-rust/src/test_annex_i_qr_as_svg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/qrcode-rust/src/types.rs b/qrcode-rust/src/types.rs new file mode 100644 index 0000000..0bca045 --- /dev/null +++ b/qrcode-rust/src/types.rs @@ -0,0 +1,326 @@ +use crate::cast::As; +use core::cmp::{Ordering, PartialOrd}; +use core::default::Default; +use core::fmt::{Display, Error, Formatter}; +use core::ops::Not; + +//------------------------------------------------------------------------------ +//{{{ QrResult + +/// `QrError` encodes the error encountered when generating a QR code. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum QrError { + /// The data is too long to encode into a QR code for the given version. + DataTooLong, + + /// The provided version / error correction level combination is invalid. + InvalidVersion, + + /// Some characters in the data cannot be supported by the provided QR code + /// version. + UnsupportedCharacterSet, + + /// The provided ECI designator is invalid. A valid designator should be + /// between 0 and 999999. + InvalidEciDesignator, + + /// A character not belonging to the character set is found. + InvalidCharacter, +} + +impl Display for QrError { + fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { + let msg = match *self { + QrError::DataTooLong => "data too long", + QrError::InvalidVersion => "invalid version", + QrError::UnsupportedCharacterSet => "unsupported character set", + QrError::InvalidEciDesignator => "invalid ECI designator", + QrError::InvalidCharacter => "invalid character", + }; + fmt.write_str(msg) + } +} + +/// `QrResult` is a convenient alias for a QR code generation result. +pub type QrResult = Result; + +//}}} +//------------------------------------------------------------------------------ +//{{{ Color + +/// The color of a module. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum Color { + /// The module is light colored. + Light, + /// The module is dark colored. + Dark, +} + +impl Color { + /// Selects a value according to color of the module. Equivalent to + /// `if self != Color::Light { dark } else { light }`. + /// + /// # Examples + /// + /// ```rust + /// # use qrcode::types::Color; + /// assert_eq!(Color::Light.select(1, 0), 0); + /// assert_eq!(Color::Dark.select("black", "white"), "black"); + /// ``` + pub fn select(self, dark: T, light: T) -> T { + match self { + Color::Light => light, + Color::Dark => dark, + } + } +} + +impl Not for Color { + type Output = Self; + fn not(self) -> Self { + match self { + Color::Light => Color::Dark, + Color::Dark => Color::Light, + } + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Error correction level + +/// The error correction level. It allows the original information be recovered +/// even if parts of the code is damaged. +#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] +pub enum EcLevel { + /// Low error correction. Allows up to 7% of wrong blocks. + L = 0, + + /// Medium error correction (default). Allows up to 15% of wrong blocks. + M = 1, + + /// "Quartile" error correction. Allows up to 25% of wrong blocks. + Q = 2, + + /// High error correction. Allows up to 30% of wrong blocks. + H = 3, +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Version + +/// In QR code terminology, `Version` means the size of the generated image. +/// Larger version means the size of code is larger, and therefore can carry +/// more information. +/// +/// The smallest version is `Version::Normal(1)` of size 21×21, and the largest +/// is `Version::Normal(40)` of size 177×177. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum Version { + /// A normal QR code version. The parameter should be between 1 and 40. + Normal(i16), + + /// A Micro QR code version. The parameter should be between 1 and 4. + Micro(i16), +} + +impl Version { + /// Get the number of "modules" on each size of the QR code, i.e. the width + /// and height of the code. + pub fn width(self) -> i16 { + match self { + Version::Normal(v) => v * 4 + 17, + Version::Micro(v) => v * 2 + 9, + } + } + + /// Obtains an object from a hard-coded table. + /// + /// The table must be a 44×4 array. The outer array represents the content + /// for each version. The first 40 entry corresponds to QR code versions 1 + /// to 40, and the last 4 corresponds to Micro QR code version 1 to 4. The + /// inner array represents the content in each error correction level, in + /// the order [L, M, Q, H]. + /// + /// # Errors + /// + /// If the entry compares equal to the default value of `T`, this method + /// returns `Err(QrError::InvalidVersion)`. + pub fn fetch(self, ec_level: EcLevel, table: &[[T; 4]]) -> QrResult + where + T: PartialEq + Default + Copy, + { + match self { + Version::Normal(v @ 1..=40) => { + return Ok(table[(v - 1).as_usize()][ec_level as usize]); + } + Version::Micro(v @ 1..=4) => { + let obj = table[(v + 39).as_usize()][ec_level as usize]; + if obj != T::default() { + return Ok(obj); + } + } + _ => {} + } + Err(QrError::InvalidVersion) + } + + /// The number of bits needed to encode the mode indicator. + pub fn mode_bits_count(self) -> usize { + match self { + Version::Micro(a) => (a - 1).as_usize(), + _ => 4, + } + } + + /// Checks whether is version refers to a Micro QR code. + pub fn is_micro(self) -> bool { + match self { + Version::Normal(_) => false, + Version::Micro(_) => true, + } + } +} + +//}}} +//------------------------------------------------------------------------------ +//{{{ Mode indicator + +/// The mode indicator, which specifies the character set of the encoded data. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum Mode { + /// The data contains only characters 0 to 9. + Numeric, + + /// The data contains only uppercase letters (A–Z), numbers (0–9) and a few + /// punctuations marks (space, `$`, `%`, `*`, `+`, `-`, `.`, `/`, `:`). + Alphanumeric, + + /// The data contains arbitrary binary data. + Byte, + + /// The data contains Shift-JIS-encoded double-byte text. + Kanji, +} + +impl Mode { + /// Computes the number of bits needed to encode the data length. + /// + /// use qrcode::types::{Version, Mode}; + /// + /// assert_eq!(Mode::Numeric.length_bits_count(Version::Normal(1)), 10); + /// + /// This method will return `Err(QrError::UnsupportedCharacterSet)` if the + /// mode is not supported in the given version. + pub fn length_bits_count(self, version: Version) -> usize { + match version { + Version::Micro(a) => { + let a = a.as_usize(); + match self { + Mode::Numeric => 2 + a, + Mode::Alphanumeric | Mode::Byte => 1 + a, + Mode::Kanji => a, + } + } + Version::Normal(1..=9) => match self { + Mode::Numeric => 10, + Mode::Alphanumeric => 9, + Mode::Byte | Mode::Kanji => 8, + }, + Version::Normal(10..=26) => match self { + Mode::Numeric => 12, + Mode::Alphanumeric => 11, + Mode::Byte => 16, + Mode::Kanji => 10, + }, + Version::Normal(_) => match self { + Mode::Numeric => 14, + Mode::Alphanumeric => 13, + Mode::Byte => 16, + Mode::Kanji => 12, + }, + } + } + + /// Computes the number of bits needed to some data of a given raw length. + /// + /// use qrcode::types::Mode; + /// + /// assert_eq!(Mode::Numeric.data_bits_count(7), 24); + /// + /// Note that in Kanji mode, the `raw_data_len` is the number of Kanjis, + /// i.e. half the total size of bytes. + pub fn data_bits_count(self, raw_data_len: usize) -> usize { + match self { + Mode::Numeric => (raw_data_len * 10 + 2) / 3, + Mode::Alphanumeric => (raw_data_len * 11 + 1) / 2, + Mode::Byte => raw_data_len * 8, + Mode::Kanji => raw_data_len * 13, + } + } + + /// Find the lowest common mode which both modes are compatible with. + /// + /// use qrcode::types::Mode; + /// + /// let a = Mode::Numeric; + /// let b = Mode::Kanji; + /// let c = a.max(b); + /// assert!(a <= c); + /// assert!(b <= c); + /// + pub fn max(self, other: Self) -> Self { + match self.partial_cmp(&other) { + Some(Ordering::Less) | Some(Ordering::Equal) => other, + Some(Ordering::Greater) => self, + None => Mode::Byte, + } + } +} + +impl PartialOrd for Mode { + /// Defines a partial ordering between modes. If `a <= b`, then `b` contains + /// a superset of all characters supported by `a`. + fn partial_cmp(&self, other: &Self) -> Option { + match (*self, *other) { + (Mode::Numeric, Mode::Alphanumeric) + | (Mode::Numeric, Mode::Byte) + | (Mode::Alphanumeric, Mode::Byte) + | (Mode::Kanji, Mode::Byte) => Some(Ordering::Less), + (Mode::Alphanumeric, Mode::Numeric) + | (Mode::Byte, Mode::Numeric) + | (Mode::Byte, Mode::Alphanumeric) + | (Mode::Byte, Mode::Kanji) => Some(Ordering::Greater), + (a, b) if a == b => Some(Ordering::Equal), + _ => None, + } + } +} + +#[cfg(test)] +mod mode_tests { + use crate::types::Mode::{Alphanumeric, Byte, Kanji, Numeric}; + + #[test] + fn test_mode_order() { + assert!(Numeric < Alphanumeric); + assert!(Byte > Kanji); + assert!(!(Numeric < Kanji)); + assert!(!(Numeric >= Kanji)); + } + + #[test] + fn test_max() { + assert_eq!(Byte.max(Kanji), Byte); + assert_eq!(Numeric.max(Alphanumeric), Alphanumeric); + assert_eq!(Alphanumeric.max(Alphanumeric), Alphanumeric); + assert_eq!(Numeric.max(Kanji), Byte); + assert_eq!(Kanji.max(Numeric), Byte); + assert_eq!(Alphanumeric.max(Numeric), Alphanumeric); + assert_eq!(Kanji.max(Kanji), Kanji); + } +} + +//}}}