BSOD with qr code
This commit is contained in:
parent
ad5a7f388b
commit
a210abca23
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -49,6 +49,7 @@ dependencies = [
|
||||||
"pic8259",
|
"pic8259",
|
||||||
"picorand",
|
"picorand",
|
||||||
"pretty-hex",
|
"pretty-hex",
|
||||||
|
"qrcode",
|
||||||
"rdrand",
|
"rdrand",
|
||||||
"rhai",
|
"rhai",
|
||||||
"riscv",
|
"riscv",
|
||||||
|
@ -698,6 +699,10 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "qrcode"
|
||||||
|
version = "0.12.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
|
|
@ -43,6 +43,8 @@ test-args = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = { version = "*", features = ["spin_no_std"] }
|
lazy_static = { version = "*", features = ["spin_no_std"] }
|
||||||
|
qrcode = { path = "../qrcode-rust" }
|
||||||
|
|
||||||
|
|
||||||
linked_list_allocator = "0.9.0"
|
linked_list_allocator = "0.9.0"
|
||||||
lliw = "0.2.0"
|
lliw = "0.2.0"
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{drivers::vga::WRITER, gdt},
|
arch::{drivers::vga::WRITER, gdt},
|
||||||
|
image::mono_bitmap::bruh,
|
||||||
kernel_state::KERNEL_STATE,
|
kernel_state::KERNEL_STATE,
|
||||||
print, println,
|
print, println,
|
||||||
rhai_shell::KEYBUFF,
|
rhai_shell::KEYBUFF,
|
||||||
|
VgaBuffer, SCREEN_BUFFER,
|
||||||
};
|
};
|
||||||
use cpuio::outb;
|
use cpuio::outb;
|
||||||
use pic8259::ChainedPics;
|
use pic8259::ChainedPics;
|
||||||
|
use qrcode::{
|
||||||
|
render::{string, unicode},
|
||||||
|
QrCode,
|
||||||
|
};
|
||||||
use spin::Lazy;
|
use spin::Lazy;
|
||||||
|
use vga::colors::Color16;
|
||||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
||||||
|
|
||||||
pub const PIC_1_OFFSET: u8 = 32;
|
pub const PIC_1_OFFSET: u8 = 32;
|
||||||
|
@ -61,6 +68,8 @@ extern "x86-interrupt" fn double_fault_handler(
|
||||||
stack_frame: InterruptStackFrame,
|
stack_frame: InterruptStackFrame,
|
||||||
error_code: u64,
|
error_code: u64,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
|
bsod(&stack_frame, error_code);
|
||||||
|
|
||||||
panic!(
|
panic!(
|
||||||
"EXCEPTION: Error code{}\nDOUBLE FAULT\n{:#?}",
|
"EXCEPTION: Error code{}\nDOUBLE FAULT\n{:#?}",
|
||||||
error_code, stack_frame
|
error_code, stack_frame
|
||||||
|
@ -185,3 +194,71 @@ pub fn reset_pit_for_cpu() {
|
||||||
set_pit_2(1000);
|
set_pit_2(1000);
|
||||||
set_pit_3(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::<unicode::Dense1x2>()
|
||||||
|
// .dark_color(unicode::Dense1x2::Light)
|
||||||
|
// .light_color(unicode::Dense1x2::Dark)
|
||||||
|
// .build();
|
||||||
|
|
||||||
|
let image = code
|
||||||
|
.render::<char>()
|
||||||
|
.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();
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub static SCREEN_BUFFER: Lazy<spin::Mutex<ScreenBuffer>> =
|
||||||
Lazy::new(|| spin::Mutex::new(ScreenBuffer::new(640, 480)));
|
Lazy::new(|| spin::Mutex::new(ScreenBuffer::new(640, 480)));
|
||||||
|
|
||||||
const FONT_SCALE: f32 = 1.6;
|
const FONT_SCALE: f32 = 1.6;
|
||||||
|
|
||||||
const GLYPH_HEIGHT: f32 = 18.0;
|
const GLYPH_HEIGHT: f32 = 18.0;
|
||||||
const GLYPH_WIDTH: f32 = 10.0;
|
const GLYPH_WIDTH: f32 = 10.0;
|
||||||
|
|
||||||
|
@ -190,7 +191,7 @@ impl ScreenBuffer {
|
||||||
|
|
||||||
if let Some(q) = plane.outline_glyph(q_glyph) {
|
if let Some(q) = plane.outline_glyph(q_glyph) {
|
||||||
q.draw(|gx, gy, c| {
|
q.draw(|gx, gy, c| {
|
||||||
if c > 0.015 {
|
if c > 0.0015 {
|
||||||
let corner = q.px_bounds().min;
|
let corner = q.px_bounds().min;
|
||||||
self.set_pixel(
|
self.set_pixel(
|
||||||
gx as usize + corner.x as usize + x as usize,
|
gx as usize + corner.x as usize + x as usize,
|
||||||
|
|
|
@ -7,15 +7,18 @@ use crate::ipc::channel::ChannelMessage;
|
||||||
use crate::ipc::{self, IPC};
|
use crate::ipc::{self, IPC};
|
||||||
use crate::scheduler::SCHEDULER;
|
use crate::scheduler::SCHEDULER;
|
||||||
use crate::time::fetch_time;
|
use crate::time::fetch_time;
|
||||||
use crate::SectionType;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{init, sloop},
|
arch::{init, sloop},
|
||||||
relib::network::socket::{SimpleSock, Socket},
|
relib::network::socket::{SimpleSock, Socket},
|
||||||
scratchpad,
|
scratchpad,
|
||||||
};
|
};
|
||||||
use crate::{boot_conf::KernelConfig, systeminfo::RELEASE_TYPE};
|
use crate::{boot_conf::KernelConfig, systeminfo::RELEASE_TYPE};
|
||||||
|
use crate::{wasm_jumploader, SectionType};
|
||||||
|
use genfs::{Fs, OpenOptions};
|
||||||
use kernel::{KERNEL_VERSION, TICK};
|
use kernel::{KERNEL_VERSION, TICK};
|
||||||
use libwasm::syscalls::time_calls::get_time;
|
use libwasm::syscalls::time_calls::get_time;
|
||||||
|
use qrcode::render::unicode;
|
||||||
|
use qrcode::QrCode;
|
||||||
use spin::Lazy;
|
use spin::Lazy;
|
||||||
use x86_64::instructions::interrupts::{disable, enable};
|
use x86_64::instructions::interrupts::{disable, enable};
|
||||||
|
|
||||||
|
@ -36,13 +39,17 @@ pub fn kernel_main() -> ! {
|
||||||
let mut ipc_service = IPC.lock();
|
let mut ipc_service = IPC.lock();
|
||||||
// create some channels or whatever here then drop it
|
// create some channels or whatever here then drop it
|
||||||
let log_handle = ipc_service.create_channel("LOG", true);
|
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);
|
drop(ipc_service);
|
||||||
|
|
||||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||||
let mut scheduler = SCHEDULER.lock();
|
let mut scheduler = SCHEDULER.lock();
|
||||||
|
|
||||||
// TODO: This task never gets run
|
|
||||||
scheduler.enqueue_spawn(traceloop);
|
scheduler.enqueue_spawn(traceloop);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -53,14 +60,40 @@ pub fn kernel_main() -> ! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn traceloop() {
|
pub fn traceloop() {
|
||||||
let mut last_time = 0.0;
|
|
||||||
|
// let mut last_time = 0.0;
|
||||||
|
/*
|
||||||
loop {
|
loop {
|
||||||
// let time = fetch_time();
|
// FIXME: the following double faults
|
||||||
// if time > last_time {
|
/*
|
||||||
// last_time = time;
|
let time = fetch_time();
|
||||||
// trace!("Timer");
|
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<u8> = 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() {
|
pub fn cpu_socket_startup() {
|
||||||
|
|
|
@ -15,8 +15,9 @@ pub fn bruh() {
|
||||||
|
|
||||||
let mut screen_lock = SCREEN_BUFFER.lock();
|
let mut screen_lock = SCREEN_BUFFER.lock();
|
||||||
|
|
||||||
for y in 0..290 {
|
for y in 0..20 {
|
||||||
for x in 0..290 {
|
for x in 0..20 {
|
||||||
|
// trace!("");
|
||||||
// info!("x{} | y{}", x, y);
|
// info!("x{} | y{}", x, y);
|
||||||
|
|
||||||
// Stops at 419
|
// Stops at 419
|
||||||
|
|
338
qrcode-rust/Cargo.lock
generated
Normal file
338
qrcode-rust/Cargo.lock
generated
Normal file
|
@ -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"
|
42
qrcode-rust/Cargo.toml
Normal file
42
qrcode-rust/Cargo.toml
Normal file
|
@ -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 <kennytm@gmail.com>"]
|
||||||
|
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"]
|
3
qrcode-rust/rustfmt.toml
Normal file
3
qrcode-rust/rustfmt.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
max_width = 120
|
||||||
|
use_small_heuristics = "Max"
|
||||||
|
use_field_init_shorthand = true
|
8
qrcode-rust/src/bin/qrencode.rs
Normal file
8
qrcode-rust/src/bin/qrencode.rs
Normal file
|
@ -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());
|
||||||
|
}
|
997
qrcode-rust/src/bits.rs
Normal file
997
qrcode-rust/src/bits.rs
Normal file
|
@ -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<u8>,
|
||||||
|
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<u8> {
|
||||||
|
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<usize> {
|
||||||
|
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
|
||||||
|
/// <http://strokescribe.com/en/ECI.html>. 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<I>(&mut self, data: &[u8], segments_iter: I) -> QrResult<()>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = Segment>,
|
||||||
|
{
|
||||||
|
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<Vec<u8>> {
|
||||||
|
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<Bits> {
|
||||||
|
let segments = Parser::new(data).collect::<Vec<Segment>>();
|
||||||
|
for version in &[Version::Normal(9), Version::Normal(26), Version::Normal(40)] {
|
||||||
|
let opt_segments = Optimizer::new(segments.iter().cloned(), *version).collect::<Vec<_>>();
|
||||||
|
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));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
2010
qrcode-rust/src/canvas.rs
Normal file
2010
qrcode-rust/src/canvas.rs
Normal file
File diff suppressed because it is too large
Load diff
86
qrcode-rust/src/cast.rs
Normal file
86
qrcode-rust/src/cast.rs
Normal file
|
@ -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<D: Display>(self, value: D, ty: &str) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ExpectOrOverflow for Option<T> {
|
||||||
|
type Output = T;
|
||||||
|
fn expect_or_overflow<D: Display>(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);
|
488
qrcode-rust/src/ec.rs
Normal file
488
qrcode-rust/src/ec.rs
Normal file
|
@ -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\] x<sup>m+n</sup> + a\[1\] x<sup>m+n-1</sup> + … + a\[m\] x<sup>n</sup>)
|
||||||
|
/// in GF(2<sup>8</sup>), 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<u8> {
|
||||||
|
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<T: Copy, V: Deref<Target = [T]>>(blocks: &[V]) -> Vec<T> {
|
||||||
|
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<u8>, Vec<u8>)> {
|
||||||
|
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::<Vec<Vec<u8>>>();
|
||||||
|
|
||||||
|
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<usize> {
|
||||||
|
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 2<sup>n</sup> 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 − 2<sup>0</sup>)(x − 2<sup>1</sup>)…(x − 2<sup>i-1</sup>). 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
|
||||||
|
];
|
||||||
|
|
||||||
|
//}}}
|
382
qrcode-rust/src/lib.rs
Normal file
382
qrcode-rust/src/lib.rs
Normal file
|
@ -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::<Luma<u8>>().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<Color>,
|
||||||
|
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<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
|
||||||
|
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<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
|
||||||
|
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<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
|
||||||
|
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<Self> {
|
||||||
|
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<bool> {
|
||||||
|
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<bool> {
|
||||||
|
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<Color> {
|
||||||
|
self.content.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the QR code to a vector of colors.
|
||||||
|
pub fn into_colors(self) -> Vec<Color> {
|
||||||
|
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<P: Pixel>(&self) -> Renderer<P> {
|
||||||
|
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::<Luma<u8>>().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::<SvgColor>().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);
|
||||||
|
}
|
||||||
|
}
|
11
qrcode-rust/src/main.rs
Normal file
11
qrcode-rust/src/main.rs
Normal file
|
@ -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::<unicode::Dense1x2>()
|
||||||
|
.dark_color(unicode::Dense1x2::Light)
|
||||||
|
.light_color(unicode::Dense1x2::Dark)
|
||||||
|
.build();
|
||||||
|
println!("{}", image);
|
||||||
|
}
|
686
qrcode-rust/src/optimize.rs
Normal file
686
qrcode-rust/src/optimize.rs
Normal file
|
@ -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<I> {
|
||||||
|
base: I,
|
||||||
|
index: usize,
|
||||||
|
ended: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I: Iterator<Item = &'a u8>> Iterator for EcsIter<I> {
|
||||||
|
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<Iter<'a, u8>>,
|
||||||
|
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::<Vec<Segment>>();
|
||||||
|
/// 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<Segment> {
|
||||||
|
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<Segment> {
|
||||||
|
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<I> {
|
||||||
|
parser: I,
|
||||||
|
last_segment: Segment,
|
||||||
|
last_segment_size: usize,
|
||||||
|
version: Version,
|
||||||
|
ended: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Segment>> Optimizer<I> {
|
||||||
|
/// 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<Parser<'a>> {
|
||||||
|
Optimizer::new(self, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Item = Segment>> Iterator for Optimizer<I> {
|
||||||
|
type Item = Segment;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Segment> {
|
||||||
|
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<Segment>, expected: Vec<Segment>, version: Version) {
|
||||||
|
let prev_len = total_encoded_len(&*given, version);
|
||||||
|
let opt_segs = Optimizer::new(given.iter().map(|seg| *seg), version).collect::<Vec<_>>();
|
||||||
|
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
|
||||||
|
];
|
||||||
|
|
||||||
|
//}}}
|
150
qrcode-rust/src/render/image.rs
Normal file
150
qrcode-rust/src/render/image.rs
Normal file
|
@ -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<Self, Vec<S>>;
|
||||||
|
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<S>: p => [p] }
|
||||||
|
impl_pixel_for_image_pixel! { LumaA<S>: p => [p, S::max_value()] }
|
||||||
|
impl_pixel_for_image_pixel! { Rgb<S>: p => [p, p, p] }
|
||||||
|
impl_pixel_for_image_pixel! { Rgba<S>: p => [p, p, p, S::max_value()] }
|
||||||
|
|
||||||
|
impl<P: ImagePixel + 'static> Canvas for (P, ImageBuffer<P, Vec<P::Subpixel>>) {
|
||||||
|
type Pixel = P;
|
||||||
|
type Image = ImageBuffer<P, Vec<P::Subpixel>>;
|
||||||
|
|
||||||
|
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<P, Vec<P::Subpixel>> {
|
||||||
|
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::<Luma<u8>>::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::<Rgba<u8>>::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::<Luma<u8>>::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::<Luma<u8>>::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);
|
||||||
|
}
|
||||||
|
}
|
191
qrcode-rust/src/render/mod.rs
Normal file
191
qrcode-rust/src/render/mod.rs
Normal file
|
@ -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<Pixel = Self, Image = Self::Image>;
|
||||||
|
|
||||||
|
/// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
125
qrcode-rust/src/render/string.rs
Normal file
125
qrcode-rust/src/render/string.rs
Normal file
|
@ -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<P: Element> {
|
||||||
|
buffer: Vec<P>,
|
||||||
|
width: usize,
|
||||||
|
dark_pixel: P,
|
||||||
|
dark_cap_inc: isize,
|
||||||
|
capacity: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Element> Pixel for P {
|
||||||
|
type Canvas = Canvas<Self>;
|
||||||
|
type Image = String;
|
||||||
|
|
||||||
|
fn default_unit_size() -> (u32, u32) {
|
||||||
|
(1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_color(color: Color) -> Self {
|
||||||
|
<Self as Element>::default_color(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Element> RenderCanvas for Canvas<P> {
|
||||||
|
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::<char>::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"
|
||||||
|
);
|
||||||
|
}
|
76
qrcode-rust/src/render/svg.rs
Normal file
76
qrcode-rust/src/render/svg.rs
Normal file
|
@ -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::<svg::Color>().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<Color<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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#"<?xml version="1.0" standalone="yes"?>"#,
|
||||||
|
r#"<svg xmlns="http://www.w3.org/2000/svg""#,
|
||||||
|
r#" version="1.1" width="{w}" height="{h}""#,
|
||||||
|
r#" viewBox="0 0 {w} {h}" shape-rendering="crispEdges">"#,
|
||||||
|
r#"<rect x="0" y="0" width="{w}" height="{h}" fill="{bg}"/>"#,
|
||||||
|
r#"<path fill="{fg}" d=""#,
|
||||||
|
),
|
||||||
|
w = width,
|
||||||
|
h = height,
|
||||||
|
fg = dark_pixel.0,
|
||||||
|
bg = light_pixel.0
|
||||||
|
),
|
||||||
|
marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_dark_pixel(&mut self, x: u32, y: u32) {
|
||||||
|
self.draw_dark_rect(x, y, 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) {
|
||||||
|
write!(self.svg, "M{l} {t}h{w}v{h}H{l}V{t}", l = left, t = top, w = width, h = height).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_image(mut self) -> String {
|
||||||
|
self.svg.push_str(r#""/></svg>"#);
|
||||||
|
self.svg
|
||||||
|
}
|
||||||
|
}
|
146
qrcode-rust/src/render/unicode.rs
Normal file
146
qrcode-rust/src/render/unicode.rs
Normal file
|
@ -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<u8>,
|
||||||
|
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::<Vec<&[u8]>>()
|
||||||
|
// 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::<Vec<u8>>()
|
||||||
|
} else {
|
||||||
|
rows[0].iter().map(|top| (top * 2)).collect::<Vec<u8>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into_iter()
|
||||||
|
// Mapping those 2-bit numbers to corresponding pixels.
|
||||||
|
.map(Dense1x2::parse_2_bits)
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.concat()
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.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::<Dense1x2>::new(colors, 2, 1).build();
|
||||||
|
|
||||||
|
assert_eq!(&image, " ▄ \n ▀ ");
|
||||||
|
|
||||||
|
let image2 = Renderer::<Dense1x2>::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::<Dense1x2>().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::<Dense1x2>()
|
||||||
|
.dark_color(Dense1x2::Light)
|
||||||
|
.light_color(Dense1x2::Dark)
|
||||||
|
.module_dimensions(1, 1)
|
||||||
|
.build();
|
||||||
|
assert_eq!(
|
||||||
|
image,
|
||||||
|
"█████████████████\n\
|
||||||
|
██ ▄▄▄▄▄ █▄▀▄█▄██\n\
|
||||||
|
██ █ █ █ █ ██\n\
|
||||||
|
██ █▄▄▄█ █▄▄██▀██\n\
|
||||||
|
██▄▄▄▄▄▄▄█▄▄▄▀ ██\n\
|
||||||
|
██▄ ▀ ▀ ▀▄▄ ████\n\
|
||||||
|
██▄▄▀▄█ ▀▀▀ ▀▄▄██\n\
|
||||||
|
██▄▄▄█▄▄█▄██▄█▄██\n\
|
||||||
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"
|
||||||
|
);
|
||||||
|
}
|
BIN
qrcode-rust/src/test_annex_i_micro_qr_as_image.png
Normal file
BIN
qrcode-rust/src/test_annex_i_micro_qr_as_image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 186 B |
1
qrcode-rust/src/test_annex_i_micro_qr_as_svg.svg
Normal file
1
qrcode-rust/src/test_annex_i_micro_qr_as_svg.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="yes"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="204" height="204" viewBox="0 0 204 204" shape-rendering="crispEdges"><rect x="0" y="0" width="204" height="204" fill="#ffff80"/><path fill="#800000" d="M24 24h12v12H24V24M36 24h12v12H36V24M48 24h12v12H48V24M60 24h12v12H60V24M72 24h12v12H72V24M84 24h12v12H84V24M96 24h12v12H96V24M120 24h12v12H120V24M144 24h12v12H144V24M168 24h12v12H168V24M24 36h12v12H24V36M96 36h12v12H96V36M120 36h12v12H120V36M132 36h12v12H132V36M144 36h12v12H144V36M168 36h12v12H168V36M24 48h12v12H24V48M48 48h12v12H48V48M60 48h12v12H60V48M72 48h12v12H72V48M96 48h12v12H96V48M132 48h12v12H132V48M144 48h12v12H144V48M168 48h12v12H168V48M24 60h12v12H24V60M48 60h12v12H48V60M60 60h12v12H60V60M72 60h12v12H72V60M96 60h12v12H96V60M132 60h12v12H132V60M144 60h12v12H144V60M156 60h12v12H156V60M168 60h12v12H168V60M24 72h12v12H24V72M48 72h12v12H48V72M60 72h12v12H60V72M72 72h12v12H72V72M96 72h12v12H96V72M120 72h12v12H120V72M132 72h12v12H132V72M144 72h12v12H144V72M24 84h12v12H24V84M96 84h12v12H96V84M120 84h12v12H120V84M168 84h12v12H168V84M24 96h12v12H24V96M36 96h12v12H36V96M48 96h12v12H48V96M60 96h12v12H60V96M72 96h12v12H72V96M84 96h12v12H84V96M96 96h12v12H96V96M132 96h12v12H132V96M144 96h12v12H144V96M156 96h12v12H156V96M168 96h12v12H168V96M132 108h12v12H132V108M144 108h12v12H144V108M24 120h12v12H24V120M36 120h12v12H36V120M60 120h12v12H60V120M120 120h12v12H120V120M168 120h12v12H168V120M36 132h12v12H36V132M48 132h12v12H48V132M72 132h12v12H72V132M96 132h12v12H96V132M120 132h12v12H120V132M144 132h12v12H144V132M168 132h12v12H168V132M24 144h12v12H24V144M36 144h12v12H36V144M48 144h12v12H48V144M84 144h12v12H84V144M96 144h12v12H96V144M108 144h12v12H108V144M120 144h12v12H120V144M132 144h12v12H132V144M144 144h12v12H144V144M156 144h12v12H156V144M60 156h12v12H60V156M84 156h12v12H84V156M144 156h12v12H144V156M156 156h12v12H156V156M24 168h12v12H24V168M36 168h12v12H36V168M48 168h12v12H48V168M72 168h12v12H72V168M108 168h12v12H108V168M120 168h12v12H120V168M144 168h12v12H144V168M156 168h12v12H156V168M168 168h12v12H168V168"/></svg>
|
After Width: | Height: | Size: 2 KiB |
BIN
qrcode-rust/src/test_annex_i_qr_as_image.png
Normal file
BIN
qrcode-rust/src/test_annex_i_qr_as_image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 217 B |
1
qrcode-rust/src/test_annex_i_qr_as_svg.svg
Normal file
1
qrcode-rust/src/test_annex_i_qr_as_svg.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="yes"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="232" height="232" viewBox="0 0 232 232" shape-rendering="crispEdges"><rect x="0" y="0" width="232" height="232" fill="#fff"/><path fill="#000" d="M32 32h8v8H32V32M40 32h8v8H40V32M48 32h8v8H48V32M56 32h8v8H56V32M64 32h8v8H64V32M72 32h8v8H72V32M80 32h8v8H80V32M104 32h8v8H104V32M120 32h8v8H120V32M128 32h8v8H128V32M144 32h8v8H144V32M152 32h8v8H152V32M160 32h8v8H160V32M168 32h8v8H168V32M176 32h8v8H176V32M184 32h8v8H184V32M192 32h8v8H192V32M32 40h8v8H32V40M80 40h8v8H80V40M104 40h8v8H104V40M112 40h8v8H112V40M120 40h8v8H120V40M128 40h8v8H128V40M144 40h8v8H144V40M192 40h8v8H192V40M32 48h8v8H32V48M48 48h8v8H48V48M56 48h8v8H56V48M64 48h8v8H64V48M80 48h8v8H80V48M96 48h8v8H96V48M144 48h8v8H144V48M160 48h8v8H160V48M168 48h8v8H168V48M176 48h8v8H176V48M192 48h8v8H192V48M32 56h8v8H32V56M48 56h8v8H48V56M56 56h8v8H56V56M64 56h8v8H64V56M80 56h8v8H80V56M96 56h8v8H96V56M104 56h8v8H104V56M144 56h8v8H144V56M160 56h8v8H160V56M168 56h8v8H168V56M176 56h8v8H176V56M192 56h8v8H192V56M32 64h8v8H32V64M48 64h8v8H48V64M56 64h8v8H56V64M64 64h8v8H64V64M80 64h8v8H80V64M96 64h8v8H96V64M112 64h8v8H112V64M120 64h8v8H120V64M128 64h8v8H128V64M144 64h8v8H144V64M160 64h8v8H160V64M168 64h8v8H168V64M176 64h8v8H176V64M192 64h8v8H192V64M32 72h8v8H32V72M80 72h8v8H80V72M96 72h8v8H96V72M128 72h8v8H128V72M144 72h8v8H144V72M192 72h8v8H192V72M32 80h8v8H32V80M40 80h8v8H40V80M48 80h8v8H48V80M56 80h8v8H56V80M64 80h8v8H64V80M72 80h8v8H72V80M80 80h8v8H80V80M96 80h8v8H96V80M112 80h8v8H112V80M128 80h8v8H128V80M144 80h8v8H144V80M152 80h8v8H152V80M160 80h8v8H160V80M168 80h8v8H168V80M176 80h8v8H176V80M184 80h8v8H184V80M192 80h8v8H192V80M96 88h8v8H96V88M120 88h8v8H120V88M128 88h8v8H128V88M32 96h8v8H32V96M48 96h8v8H48V96M56 96h8v8H56V96M64 96h8v8H64V96M72 96h8v8H72V96M80 96h8v8H80V96M104 96h8v8H104V96M128 96h8v8H128V96M144 96h8v8H144V96M152 96h8v8H152V96M160 96h8v8H160V96M168 96h8v8H168V96M176 96h8v8H176V96M56 104h8v8H56V104M72 104h8v8H72V104M88 104h8v8H88V104M96 104h8v8H96V104M112 104h8v8H112V104M128 104h8v8H128V104M152 104h8v8H152V104M168 104h8v8H168V104M176 104h8v8H176V104M48 112h8v8H48V112M80 112h8v8H80V112M88 112h8v8H88V112M104 112h8v8H104V112M120 112h8v8H120V112M136 112h8v8H136V112M160 112h8v8H160V112M168 112h8v8H168V112M176 112h8v8H176V112M184 112h8v8H184V112M192 112h8v8H192V112M64 120h8v8H64V120M104 120h8v8H104V120M152 120h8v8H152V120M160 120h8v8H160V120M168 120h8v8H168V120M176 120h8v8H176V120M56 128h8v8H56V128M64 128h8v8H64V128M72 128h8v8H72V128M80 128h8v8H80V128M88 128h8v8H88V128M96 128h8v8H96V128M120 128h8v8H120V128M136 128h8v8H136V128M160 128h8v8H160V128M96 136h8v8H96V136M112 136h8v8H112V136M120 136h8v8H120V136M128 136h8v8H128V136M136 136h8v8H136V136M144 136h8v8H144V136M168 136h8v8H168V136M176 136h8v8H176V136M32 144h8v8H32V144M40 144h8v8H40V144M48 144h8v8H48V144M56 144h8v8H56V144M64 144h8v8H64V144M72 144h8v8H72V144M80 144h8v8H80V144M104 144h8v8H104V144M112 144h8v8H112V144M128 144h8v8H128V144M144 144h8v8H144V144M152 144h8v8H152V144M32 152h8v8H32V152M80 152h8v8H80V152M96 152h8v8H96V152M112 152h8v8H112V152M120 152h8v8H120V152M128 152h8v8H128V152M136 152h8v8H136V152M144 152h8v8H144V152M176 152h8v8H176V152M192 152h8v8H192V152M32 160h8v8H32V160M48 160h8v8H48V160M56 160h8v8H56V160M64 160h8v8H64V160M80 160h8v8H80V160M96 160h8v8H96V160M128 160h8v8H128V160M152 160h8v8H152V160M168 160h8v8H168V160M176 160h8v8H176V160M32 168h8v8H32V168M48 168h8v8H48V168M56 168h8v8H56V168M64 168h8v8H64V168M80 168h8v8H80V168M96 168h8v8H96V168M104 168h8v8H104V168M128 168h8v8H128V168M152 168h8v8H152V168M32 176h8v8H32V176M48 176h8v8H48V176M56 176h8v8H56V176M64 176h8v8H64V176M80 176h8v8H80V176M96 176h8v8H96V176M112 176h8v8H112V176M120 176h8v8H120V176M136 176h8v8H136V176M160 176h8v8H160V176M176 176h8v8H176V176M32 184h8v8H32V184M80 184h8v8H80V184M152 184h8v8H152V184M160 184h8v8H160V184M176 184h8v8H176V184M184 184h8v8H184V184M32 192h8v8H32V192M40 192h8v8H40V192M48 192h8v8H48V192M56 192h8v8H56V192M64 192h8v8H64V192M72 192h8v8H72V192M80 192h8v8H80V192M96 192h8v8H96V192M104 192h8v8H104V192M112 192h8v8H112V192M120 192h8v8H120V192M136 192h8v8H136V192M160 192h8v8H160V192M176 192h8v8H176V192"/></svg>
|
After Width: | Height: | Size: 4.1 KiB |
326
qrcode-rust/src/types.rs
Normal file
326
qrcode-rust/src/types.rs
Normal file
|
@ -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<T> = Result<T, QrError>;
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ 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<T>(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<T>(self, ec_level: EcLevel, table: &[[T; 4]]) -> QrResult<T>
|
||||||
|
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<Ordering> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
Loading…
Reference in a new issue