feat: QR 码解码器 — 从零手写的完整解码流水线
新增 core/src/decoder/ 模块(9 文件,~1500 行): - bch.rs: BCH(15,5)+BCH(18,6) 查表解码(32+64 有效码字,t≤3) - format.rs: 从矩阵读取格式信息(EC+掩码)+版本信息(2 副本容错) - extract.rs: 逆向蛇形排列提取数据码字 - deinterleave.rs: 逆向 RS 数据交错 - rs_decode.rs: RS 纠错流水线(伴随式→BM→Chien→Forney) - mode_decode.rs: 逆向 4 种编码模式(数字/字母/字节/汉字 Shift JIS) - detect.rs: 定位图案检测(1:1:3:1:1 比例+交叉验证+聚类) - image.rs: 图像加载+灰度二值化(PNG/JPEG/WebP) - mod.rs: 顶层 API(decode_image + decode_matrix) 修改已有文件: - core: galois.rs 表 pub(crate), 新增 poly_eval(); reed_solomon 公开内部函数 - cli: 新增 --decode <file> 解码模式 - web: 新增 POST /api/decode(multipart file upload) 测试: 72 passed (58 原有 + 14 新增 decoder 测试)
This commit is contained in:
+49
-18
@@ -4,10 +4,17 @@ use qr_core::version::EcLevel;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "qrgen", about = "QR 码生成器 — 从零手搓的 ISO/IEC 18004 实现")]
|
||||
#[command(
|
||||
name = "qrgen",
|
||||
about = "QR 码生成/解码工具 — 从零手搓的 ISO/IEC 18004 实现"
|
||||
)]
|
||||
struct Args {
|
||||
/// 要编码的内容
|
||||
content: String,
|
||||
/// 要编码的内容(编码模式)
|
||||
content: Option<String>,
|
||||
|
||||
/// 解码图片文件 (PNG/JPEG/WebP),与编码模式互斥
|
||||
#[arg(short = 'd', long)]
|
||||
decode: Option<String>,
|
||||
|
||||
/// 输出文件 (.png 或 .svg),不指定则输出终端 ASCII
|
||||
#[arg(short = 'o', long)]
|
||||
@@ -37,6 +44,20 @@ struct Args {
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
// 解码模式
|
||||
if let Some(path) = args.decode {
|
||||
return do_decode(&path);
|
||||
}
|
||||
|
||||
// 编码模式
|
||||
let content = args
|
||||
.content
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow::anyhow!("请提供编码内容,或使用 --decode <文件> 解码图片"))?;
|
||||
do_encode(content, &args)
|
||||
}
|
||||
|
||||
fn do_encode(content: &str, args: &Args) -> anyhow::Result<()> {
|
||||
let level = match args.level.to_uppercase().as_str() {
|
||||
"L" => EcLevel::L,
|
||||
"M" => EcLevel::M,
|
||||
@@ -61,13 +82,11 @@ fn main() -> anyhow::Result<()> {
|
||||
margin: args.margin,
|
||||
};
|
||||
|
||||
let qr =
|
||||
QrCode::encode(&args.content, config).map_err(|e| anyhow::anyhow!("编码失败: {}", e))?;
|
||||
let qr = QrCode::encode(content, config).map_err(|e| anyhow::anyhow!("编码失败: {}", e))?;
|
||||
|
||||
match args.output {
|
||||
match &args.output {
|
||||
Some(path) => {
|
||||
// 防止路径遍历攻击,拒绝包含 ".." 的路径
|
||||
let path_obj = Path::new(&path);
|
||||
let path_obj = Path::new(path);
|
||||
if path_obj
|
||||
.components()
|
||||
.any(|c| matches!(c, std::path::Component::ParentDir))
|
||||
@@ -84,34 +103,46 @@ fn main() -> anyhow::Result<()> {
|
||||
match ext.as_str() {
|
||||
"png" => {
|
||||
let bytes = qr.to_png_bytes(args.size)?;
|
||||
std::fs::write(&path, bytes)?;
|
||||
std::fs::write(path, bytes)?;
|
||||
println!(
|
||||
"已生成: {} (版本 {}, {}×{} 模块, {} 级纠错)",
|
||||
"已生成: {} (版本 {}, {}×{} 模块, {:?} 级纠错)",
|
||||
path,
|
||||
qr.version.0,
|
||||
qr.size(),
|
||||
qr.size(),
|
||||
match qr.level {
|
||||
EcLevel::L => "L",
|
||||
EcLevel::M => "M",
|
||||
EcLevel::Q => "Q",
|
||||
EcLevel::H => "H",
|
||||
}
|
||||
qr.level
|
||||
);
|
||||
}
|
||||
"svg" => {
|
||||
let svg = qr.to_svg();
|
||||
std::fs::write(&path, svg)?;
|
||||
std::fs::write(path, svg)?;
|
||||
println!("已生成: {} (版本 {}, SVG 格式)", path, qr.version.0);
|
||||
}
|
||||
_ => anyhow::bail!("不支持的文件格式: .{}。支持 .png / .svg", ext),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// 终端 ASCII 输出
|
||||
println!("{}", qr.to_ascii(args.invert));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_decode(path: &str) -> anyhow::Result<()> {
|
||||
let bytes =
|
||||
std::fs::read(path).map_err(|e| anyhow::anyhow!("无法读取文件 '{}': {}", path, e))?;
|
||||
|
||||
let result = qr_core::decoder::decode_image(&bytes).map_err(|e| anyhow::anyhow!("{e}"))?;
|
||||
|
||||
println!("解码成功:");
|
||||
println!(" 文本: {}", result.text);
|
||||
println!(" 版本: {}", result.version);
|
||||
println!(" 纠错级别: {:?}", result.level);
|
||||
println!(" 掩码: {}", result.mask);
|
||||
if result.errors_corrected > 0 {
|
||||
println!(" 纠正错误: {} 码字", result.errors_corrected);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user