From ce8063431e9c64a17f4d61fc0ceadd6b1d4d2248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Fri, 19 Jun 2026 18:56:28 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=BC=80=E6=BA=90=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E5=8C=96=20=E2=80=94=20doc=20comments=20+=20=E7=A4=BE=E5=8C=BA?= =?UTF-8?q?=E6=96=87=E4=BB=B6=20+=20=E7=A4=BA=E4=BE=8B=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=20+=20crates.io=20=E5=B0=B1=E7=BB=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 core 公开 API 添加完整 doc comments(rustdoc 可用) - 新增 .editorconfig / CONTRIBUTING / CODE_OF_CONDUCT / SECURITY - 新增 Issue 模板(bug + feature)+ PR 模板 - 新增 3 个代码示例(examples/) - 更新 Cargo.toml 元数据(description/repository/keywords/categories/MSRV) - 更新 README + CHANGELOG --- .editorconfig | 29 ++++++ .github/ISSUE_TEMPLATE/bug_report.md | 29 ++++++ .github/ISSUE_TEMPLATE/feature_request.md | 19 ++++ .github/PULL_REQUEST_TEMPLATE.md | 35 +++++++ CHANGELOG.md | 6 +- CODE_OF_CONDUCT.md | 29 ++++++ CONTRIBUTING.md | 105 +++++++++++++++++++ Cargo.toml | 10 +- README.md | 47 ++++++++- SECURITY.md | 36 +++++++ core/Cargo.toml | 20 ++++ core/src/qr.rs | 121 +++++++++++++++++++++- core/src/version.rs | 81 +++++++++++++-- examples/basic_qr.rs | 30 ++++++ examples/custom_config.rs | 27 +++++ examples/high_ecc.rs | 22 ++++ web/src/main.rs | 12 ++- 17 files changed, 640 insertions(+), 18 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 examples/basic_qr.rs create mode 100644 examples/custom_config.rs create mode 100644 examples/high_ecc.rs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4f70a64 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,29 @@ +# EditorConfig — 跨编辑器统一编码风格 +# https://editorconfig.org + +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +[*.rs] +indent_style = space +indent_size = 4 + +[*.{ts,tsx,js,jsx,json,css,html,md,yaml,yml}] +indent_style = space +indent_size = 2 + +[*.toml] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab +indent_size = 4 + +[*.{png,jpg,jpeg,gif,svg,ico,ttf,woff,woff2,eot}] +insert_final_newline = false diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dccef7c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug 报告 +about: 报告一个问题,帮助我们改进 +title: "[Bug] " +labels: bug +assignees: '' +--- + +**描述问题** +一句话描述你遇到什么 bug。 + +**复现步骤** +1. 执行 `...` +2. 输入 `...` +3. 看到错误 `...` + +**预期行为** +你期望发生什么? + +**截图或日志** +如果适用,粘贴错误日志或截图。 + +**环境** +- OS: [如 Windows 11, macOS 15] +- Rust 版本: [`rustc --version` 输出] +- QRGen 版本: [v0.1.0 或 commit hash] + +**补充信息** +其他相关内容。 diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..2bca2b9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: 功能请求 +about: 提出一个新功能或改进建议 +title: "[Feature] " +labels: enhancement +assignees: '' +--- + +**动机** +这个功能解决什么问题?你的使用场景是什么? + +**方案描述** +描述你期望的方案。如果有 API 设计,请写出伪代码。 + +**备选方案** +你考虑过哪些替代方案? + +**补充信息** +截图、参考链接等。 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..99b5ce0 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ +## 概述 + + + +## 变更类型 + +- [ ] Bug 修复 +- [ ] 新功能 +- [ ] 重构(无功能变更) +- [ ] 文档更新 +- [ ] 测试 +- [ ] 其他: + +## 测试 + + + +- [ ] `cargo test` 全部通过 +- [ ] `cargo clippy -- -D warnings` 无警告 +- [ ] `cargo fmt` 已运行 +- [ ] 新增测试: + +## 截图(如适用) + +## 关联 Issue + +Closes # + +## 检查清单 + +- [ ] 代码已自审 +- [ ] 注释清晰(中文) +- [ ] 文档已更新 +- [ ] 无硬编码密钥 +- [ ] 无破坏性变更(或已标注) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78897bc..e82a67b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.1.0 (2026-06-18) +## 0.1.0 (2026-06-19) ### Added @@ -59,4 +59,6 @@ - Web:axum 0.8 + tokio,编译期 HTML 嵌入 (include_str!) - 82 个测试(58 单元 + 24 集成) - NSIS Windows 安装包 + Docker Alpine 镜像 -- GitHub Actions 就绪(测试 + clippy + fmt) +- 文档:API doc comments(rustdoc 可用)+ 3 个代码示例 +- 社区:CONTRIBUTING / CODE_OF_CONDUCT / SECURITY / Issue & PR 模板 +- 工程:.editorconfig / MSRV=1.87 / crates.io 就绪 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3f565de --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,29 @@ +# 行为准则 + +## 承诺 + +为了营造一个开放和友好的环境,我们承诺让所有人无论年龄、体型、身体条件、种族、性别认同与表达、经验水平、教育程度、社会经济地位、国籍、个人外貌、宗教信仰或性取向,都能无骚扰地参与本项目。 + +## 标准 + +**鼓励的行为:** +- 使用友好和包容的语言 +- 尊重不同的观点和经验 +- 优雅地接受建设性批评 +- 关注对社区最有利的事 +- 对其他社区成员表达同理心 + +**不可接受的行为:** +- 使用带有性别色彩、性暗示的语言或图像 +- 人身攻击、政治抨击或网络暴力 +- 公开或私下骚扰 +- 未经明确许可发布他人私人信息 +- 在专业场合中不合理的其他行为 + +## 执行 + +项目维护者有责任澄清可接受行为的标准,并对任何不可接受的行为采取适当且公平的纠正措施。 + +如遇违规行为,请联系:**lhy3364451258@163.com** + +本行为准则改编自 [Contributor Covenant](https://www.contributor-covenant.org)。 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8703bc4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,105 @@ +# 贡献指南 + +感谢你对 QRGen 的关注!这份指南帮助你了解如何参与项目。 + +## 项目架构 + +QRGen 是 Rust workspace,四层架构: + +| crate | 用途 | 技术栈 | +|-------|------|--------| +| `qr-core` | 核心算法库 | Rust,纯算法无三方依赖 | +| `qrgen` | CLI 命令行 | clap + anyhow | +| `qrgen-gui` | 桌面应用 | Tauri 2.x + React 18 + TypeScript | +| `qrgen-web` | Web 服务 | axum 0.8 + tokio | + +详细架构见 [CLAUDE.md](CLAUDE.md)。 + +## 开发环境 + +- **Rust** 1.95+ (`stable-x86_64-pc-windows-gnu` 或 `stable-x86_64-unknown-linux-gnu`) +- **Node.js** 22+(仅 GUI 前端需要) +- **pnpm**(GUI 前端包管理) + +## 本地构建 + +```bash +# 克隆 +git clone git@lhy-git.liuhangyv.top:Serendipity/QRGen.git +cd QRGen + +# 安装 GUI 前端依赖 +cd gui/src-frontend && pnpm install && cd ../.. + +# 编译检查 +cargo check + +# CLI +cargo build -p qrgen + +# Web +cargo build -p qrgen-web + +# GUI(需要前端依赖) +cd gui/src-frontend && pnpm build && cd ../.. +cargo build -p qrgen-gui +``` + +## 提交规范 + +使用 Conventional Commits 格式: + +``` +: <简短描述> + +feat: 添加 Logo 图片支持 +fix: 修复 PNG margin 全黑问题 +refactor: 提取 fill_module 辅助函数 +docs: 更新 README 安装说明 +test: 添加格式信息 roundtrip 测试 +``` + +类型:`feat` `fix` `refactor` `docs` `test` `chore` `perf` `ci` + +## 代码风格 + +- **Rust**: `cargo fmt` + `cargo clippy -- -D warnings` 必须通过 +- **TypeScript**: `pnpm tsc --noEmit` 必须通过(strict 模式) +- 注释用中文,标识符用英文 + +## 测试 + +```bash +# 全量测试 +cargo test # 所有 crate + +# 仅核心库 +cargo test -p qr-core + +# 特定测试 +cargo test -p qr-core -- mask # 掩码相关测试 + +# 覆盖率 +cargo tarpaulin --out Html +``` + +测试覆盖率目标 ≥ 80%。 + +## 提交流程 + +1. Fork 仓库 +2. 创建功能分支 (`git checkout -b feat/my-feature`) +3. 编码 + 测试(遵循 TDD) +4. `cargo fmt` + `cargo clippy -- -D warnings` +5. `cargo test` 全部通过 +6. 提交并 push +7. 创建 Pull Request + +## Issue 规范 + +- Bug 报告:附重现步骤 + 预期/实际行为 +- 功能请求:描述使用场景和期望效果 + +## 感谢 + +所有贡献者将在 [README.md](README.md) 中致谢。 diff --git a/Cargo.toml b/Cargo.toml index 2882756..173df8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,12 @@ members = ["core", "cli", "gui", "web"] version = "0.1.0" edition = "2021" license = "MIT" -authors = ["刘航宇"] +authors = ["刘航宇 "] +description = "从零手写的 QR 码生成器 — ISO/IEC 18004 完整实现" +repository = "https://github.com/LHY0125/QRGen" +homepage = "https://github.com/LHY0125/QRGen" +documentation = "https://github.com/LHY0125/QRGen" +readme = "README.md" +keywords = ["qr", "qr-code", "qrcode", "encoding", "barcode"] +categories = ["encoding", "command-line-utilities", "gui", "web-programming"] +rust-version = "1.87" diff --git a/README.md b/README.md index 10e86f9..ba94e17 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,14 @@

version + rust tauri react axum - rust docker license - tests + tests + clippy

--- @@ -271,6 +272,48 @@ QRGen/ | 自动版本选择 | 根据数据长度 + 纠错级别 | | Docker 镜像 | ~18MB (alpine) | +## 代码示例 + +`examples/` 目录包含三个示例: + +```bash +# 基础编码(默认配置) +cargo run --example basic_qr + +# 高纠错级别(H 级,30% 容错) +cargo run --example high_ecc + +# 自定义配置(固定版本 + 大尺寸 PNG) +cargo run --example custom_config +``` + +作为程序库使用: + +```rust +use qr_core::qr::{QrCode, QrConfig}; + +let qr = QrCode::encode("Hello QR!", QrConfig::default())?; +println!("{}", qr.to_ascii(false)); // 终端预览 +qr.to_png_bytes(8)?; // PNG 字节 +let svg = qr.to_svg(); // SVG 字符串 +``` + +## 发布到 crates.io + +`qr-core` 可作为 Rust 库被其他项目引用: + +```bash +cargo add qr-core +``` + +## 社区 + +- [贡献指南](CONTRIBUTING.md) — 如何参与开发 +- [行为准则](CODE_OF_CONDUCT.md) — 社区规范 +- [安全策略](SECURITY.md) — 漏洞报告流程 +- [变更日志](CHANGELOG.md) — 版本历史 +- [Issue 模板](.github/ISSUE_TEMPLATE/) — Bug 报告 / 功能请求 + ## 许可证 MIT License diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..1c9de1e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,36 @@ +# 安全策略 + +## 报告漏洞 + +如果你发现 QRGen 的安全漏洞,**请勿公开提交 Issue**。 + +请发送邮件至:**lhy3364451258@163.com** + +在邮件中包含: +- 漏洞描述 +- 复现步骤 +- 影响范围评估 +- 建议修复方案(如有) + +我会在 **48 小时内回复**,并在确认后尽快发布修复。 + +## 安全考量 + +QRGen 是一个 QR 码生成工具,不处理用户隐私数据。关注的安全领域: + +- **输入验证**:恶意构造的超长文本可能导致内存问题 +- **文件路径**:CLI 输出路径防止目录遍历(已内置 `..` 拒绝) +- **Web 服务**:如果公网部署,需限制速率(建议通过反向代理实现) +- **依赖安全**:定期 `cargo audit` 检查 Rust 依赖漏洞 + +## 支持版本 + +| 版本 | 安全更新 | +|------|----------| +| 最新版 | ✅ | +| 旧版本 | ❌ 请升级到最新版 | + +## 披露策略 + +- 修复发布后,漏洞细节将在 CHANGELOG 中公开 +- 严重漏洞会优先发布补丁,延迟细节披露 diff --git a/core/Cargo.toml b/core/Cargo.toml index 1fdf9ad..22d1b95 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -4,9 +4,29 @@ version.workspace = true edition.workspace = true license.workspace = true authors.workspace = true +description.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +readme.workspace = true +keywords.workspace = true +categories.workspace = true +rust-version.workspace = true [dependencies] image = { version = "0.25", default-features = false, features = ["png"] } serde = { version = "1", features = ["derive"] } [dev-dependencies] + +[[example]] +name = "basic_qr" +path = "../examples/basic_qr.rs" + +[[example]] +name = "high_ecc" +path = "../examples/high_ecc.rs" + +[[example]] +name = "custom_config" +path = "../examples/custom_config.rs" diff --git a/core/src/qr.rs b/core/src/qr.rs index dbd1867..9f4169b 100644 --- a/core/src/qr.rs +++ b/core/src/qr.rs @@ -12,21 +12,61 @@ use crate::matrix::placement::place_data; use crate::version::{get_data_capacity, EcLevel, Version}; /// 版本选择模式 +/// +/// 控制 QR 码版本(尺寸)的确定方式: +/// - `Auto`:根据数据量和纠错级别自动选择最小可用版本 +/// - `Fixed(v)`:强制使用指定版本(1~40),数据超限时返回错误 +/// +/// ```rust +/// use qr_core::qr::{QrCode, QrConfig, VersionMode}; +/// use qr_core::version::EcLevel; +/// +/// // 自动选择版本 +/// let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap(); +/// assert_eq!(qr.version.0, 1); +/// +/// // 强制指定版本 +/// let config = QrConfig { +/// version: VersionMode::Fixed(5), +/// ..Default::default() +/// }; +/// let qr = QrCode::encode("VERSION 5", config).unwrap(); +/// assert_eq!(qr.version.0, 5); +/// ``` #[derive(Debug, Clone)] pub enum VersionMode { + /// 根据数据量自动选择最小可用版本 Auto, + /// 强制固定版本(1~40) Fixed(u8), } -/// QR 码配置 +/// QR 码编码配置 +/// +/// 控制纠错级别、版本选择策略和静区边距。 +/// +/// ```rust +/// use qr_core::qr::QrConfig; +/// use qr_core::version::EcLevel; +/// +/// let config = QrConfig { +/// level: EcLevel::H, // 30% 纠错能力 +/// margin: 8, // 8 模块静区 +/// ..Default::default() +/// }; +/// ``` #[derive(Debug, Clone)] pub struct QrConfig { + /// 纠错级别(L/M/Q/H) pub level: EcLevel, + /// 版本选择模式 pub version: VersionMode, + /// 静区边距(模块数),默认 4 pub margin: u8, } impl Default for QrConfig { + /// 默认配置:M 级纠错 + 自动版本 + 4 模块边距 fn default() -> Self { QrConfig { level: EcLevel::M, @@ -37,16 +77,61 @@ impl Default for QrConfig { } /// 生成的 QR 码 +/// +/// 包含编码后的矩阵数据和元信息,可通过方法导出为 SVG / PNG / ASCII。 +/// +/// ```rust +/// use qr_core::qr::{QrCode, QrConfig}; +/// +/// let qr = QrCode::encode("https://example.com", QrConfig::default()).unwrap(); +/// println!("版本: {}, 尺寸: {}×{}", qr.version.0, qr.size(), qr.size()); +/// +/// // 导出为 SVG +/// let svg = qr.to_svg(); +/// assert!(svg.starts_with(" 100); +/// +/// // 终端 ASCII 输出 +/// let ascii = qr.to_ascii(false); +/// assert!(!ascii.is_empty()); +/// ``` pub struct QrCode { + /// QR 码版本(1~40) pub version: Version, + /// 纠错级别 pub level: EcLevel, + /// 选中的掩码编号(0~7) pub mask: u8, matrix: Matrix, + /// 静区边距(模块数) pub margin: u8, } impl QrCode { - /// 编码字符串生成 QR 码 + /// 编码字符串为 QR 码 + /// + /// 执行完整的 9 步流水线:分段 → 版本选择 → 模式编码 → 纠错 → 矩阵布局 → + /// 蛇形排列 → 掩码评分 → 格式信息 → 版本信息。 + /// + /// # 参数 + /// - `text`:要编码的文本,支持汉字(Shift JIS)、数字、字母、字节 + /// - `config`:[`QrConfig`] 编码配置 + /// + /// # 错误 + /// - 输入为空时返回 `"输入为空"` + /// - 版本无效时返回 `"无效版本号 (1-40)"` + /// - 数据超出版本容量时返回 `"数据过长,超出 QR 码最大容量"` + /// + /// ```rust + /// use qr_core::qr::{QrCode, QrConfig}; + /// + /// let qr = QrCode::encode("Hello QR!", QrConfig::default()).unwrap(); + /// assert_eq!(qr.version.0, 1); + /// assert_eq!(qr.size(), 21); + /// ``` pub fn encode(text: &str, config: QrConfig) -> Result { // 1. 分段 let segments = segment_text(text); @@ -127,22 +212,54 @@ impl QrCode { }) } + /// 返回 QR 码的布尔矩阵(`true` = 深色模块) + /// + /// 矩阵尺寸为 `size() × size()`,包含功能图案、数据模块和静区。 pub fn modules(&self) -> &[Vec] { &self.matrix.modules } + /// 返回 QR 码的边长(模块数,含静区) + /// + /// 取值范围:21(版本 1 + 4×2 边距)到 185(版本 40 + 4×2 边距) pub fn size(&self) -> u8 { self.matrix.size } + /// 导出为 SVG 字符串 + /// + /// SVG 内含 `viewBox`、深色模块用 `#000` 填充。 + /// + /// ```rust + /// use qr_core::qr::{QrCode, QrConfig}; + /// + /// let qr = QrCode::encode("test", QrConfig::default()).unwrap(); + /// let svg = qr.to_svg(); + /// assert!(svg.starts_with(" String { crate::render::svg::render_svg(self) } + /// 导出为终端 ASCII 文本 + /// + /// 深色模块用 `██`,浅色模块用 ` `(双空格)。 + /// `invert=true` 时反转颜色(白底黑码 → 黑底白码)。 pub fn to_ascii(&self, invert: bool) -> String { crate::render::ascii::render_ascii(self, invert) } + /// 导出为 PNG 字节数据 + /// + /// `module_size` 控制每个模块的像素大小(2~20),越大文件越大。 + /// + /// ```rust + /// use qr_core::qr::{QrCode, QrConfig}; + /// + /// let qr = QrCode::encode("PNG test", QrConfig::default()).unwrap(); + /// let bytes = qr.to_png_bytes(4).unwrap(); + /// std::fs::write("test.png", &bytes).unwrap(); + /// ``` pub fn to_png_bytes(&self, module_size: u8) -> Result, image::ImageError> { crate::render::png::render_png(self, module_size) } diff --git a/core/src/version.rs b/core/src/version.rs index e0b6572..aab2534 100644 --- a/core/src/version.rs +++ b/core/src/version.rs @@ -1,17 +1,33 @@ use serde::Serialize; use std::sync::OnceLock; -/// 纠错级别 +/// QR 码纠错级别 +/// +/// 决定 QR 码被遮挡/损毁后仍可扫描的能力。纠错越强,可存储的数据越少。 +/// +/// ```rust +/// use qr_core::version::{EcLevel, Version}; +/// +/// let ver = Version::new(1).unwrap(); +/// let h = ver.ec_info(EcLevel::H); +/// let l = ver.ec_info(EcLevel::L); +/// // H 级纠错码字更多,数据码字更少 +/// assert!(h.ec_per_block > l.ec_per_block); +/// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] pub enum EcLevel { - L, // 约 7% - M, // 约 15% - Q, // 约 25% - H, // 约 30% + /// L 级 — 约 7% 纠错能力 + L, + /// M 级 — 约 15% 纠错能力(默认) + M, + /// Q 级 — 约 25% 纠错能力 + Q, + /// H 级 — 约 30% 纠错能力 + H, } impl EcLevel { - /// 格式信息中使用的指示位 + /// 格式信息中使用的 2-bit 指示位 pub fn indicator_bits(self) -> u8 { match self { EcLevel::L => 0b01, @@ -22,7 +38,23 @@ impl EcLevel { } } -/// 版本号 1~40 +/// QR 码版本号(1~40) +/// +/// 版本决定 QR 码的物理尺寸:`side = 17 + version × 4` 模块。 +/// 版本 1 是 21×21,版本 40 是 177×177。 +/// +/// ```rust +/// use qr_core::version::Version; +/// +/// let v1 = Version::new(1).unwrap(); +/// assert_eq!(v1.size(), 21); +/// +/// let v40 = Version::new(40).unwrap(); +/// assert_eq!(v40.size(), 177); +/// +/// assert!(Version::new(0).is_none()); +/// assert!(Version::new(41).is_none()); +/// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] pub struct Version(pub u8); @@ -104,16 +136,27 @@ impl Version { } } +/// 纠错分组信息 +/// +/// 一组 QR 数据被分为多个块,每个块独立计算 RS 纠错码。 #[derive(Debug, Clone)] pub struct EcInfo { + /// 数据码字总数 pub total_codewords: u16, + /// 每个块的纠错码字数 pub ec_per_block: u8, + /// 分组信息(1~2 组,不同数据码字数) pub blocks: Vec, } +/// 单个分组信息 +/// +/// 所有块的数据码字总数 + 纠错码字总数 = QR 码总容量。 #[derive(Debug, Clone)] pub struct BlockInfo { + /// 该组块数 pub count: u16, + /// 每个块的数据码字数 pub data_codewords: u16, } @@ -1290,6 +1333,16 @@ fn init_capacity_table() -> [[u16; 4]; 40] { table } +/// 查询指定版本+纠错级别的数据码字容量 +/// +/// 返回数据码字数(非比特数),乘以 8 得比特容量。 +/// +/// ```rust +/// use qr_core::version::{get_data_capacity, Version, EcLevel}; +/// +/// let cap = get_data_capacity(Version(1), EcLevel::M); +/// assert_eq!(cap, 16); // 版本 1-M 有 16 个数据码字 +/// ``` // SAFETY: version.0 ∈ [1,40] 由 Version::new() 保证; level 是 4 变体枚举 #[allow(clippy::indexing_slicing)] pub fn get_data_capacity(version: Version, level: EcLevel) -> u16 { @@ -1298,7 +1351,19 @@ pub fn get_data_capacity(version: Version, level: EcLevel) -> u16 { cap[version.0 as usize - 1][level as usize] } -/// 自动选择最小版本:返回能容纳 data_bits 比特的最小版本 +/// 自动选择能容纳 `data_bits` 比特的最小版本 +/// +/// 返回 `None` 表示数据量超出 QR 码最大容量(版本 40)。 +/// +/// ```rust +/// use qr_core::version::{pick_version, EcLevel}; +/// +/// let v = pick_version(100, EcLevel::M).unwrap(); +/// assert_eq!(v.0, 1); // 版本 1-M 可容纳 16×8=128 bit >= 100 +/// +/// let too_big = pick_version(50_000, EcLevel::H); +/// assert!(too_big.is_none()); +/// ``` pub fn pick_version(data_bits: u16, level: EcLevel) -> Option { for v in 1..=40 { let cap_bits = get_data_capacity(Version(v), level) * 8; diff --git a/examples/basic_qr.rs b/examples/basic_qr.rs new file mode 100644 index 0000000..5023984 --- /dev/null +++ b/examples/basic_qr.rs @@ -0,0 +1,30 @@ +//! QRGen 基础示例:生成 QR 码并导出为多种格式 +//! +//! 运行: `cargo run --example basic_qr` + +use qr_core::qr::{QrCode, QrConfig}; + +fn main() -> Result<(), Box> { + let text = "https://github.com/LHY0125/QRGen"; + + // 使用默认配置(M 级纠错,自动版本,4 模块边距) + let qr = QrCode::encode(text, QrConfig::default())?; + + println!("版本: {}", qr.version.0); + println!("尺寸: {}×{} 模块", qr.size(), qr.size()); + println!("掩码: {}", qr.mask); + + // 终端 ASCII 预览 + println!("\n--- ASCII 预览 ---"); + println!("{}", qr.to_ascii(false)); + + // 导出 PNG + qr.to_png_bytes(8)?; + println!("\nPNG 生成成功"); + + // 导出 SVG + let svg = qr.to_svg(); + println!("SVG 长度: {} 字节", svg.len()); + + Ok(()) +} diff --git a/examples/custom_config.rs b/examples/custom_config.rs new file mode 100644 index 0000000..fa62caf --- /dev/null +++ b/examples/custom_config.rs @@ -0,0 +1,27 @@ +//! QRGen 自定义配置:强制指定版本、模块大小 +//! +//! 运行: `cargo run --example custom_config` + +use qr_core::qr::{QrCode, QrConfig, VersionMode}; +use qr_core::version::EcLevel; + +fn main() -> Result<(), Box> { + // 强制使用版本 10(57×57 模块) + let config = QrConfig { + level: EcLevel::Q, + version: VersionMode::Fixed(10), + margin: 4, + }; + + let qr = QrCode::encode("固定版本 10 的 QR 码", config)?; + assert_eq!(qr.version.0, 10); + + // 导出大尺寸 PNG(每个模块 8 像素) + let png = qr.to_png_bytes(8)?; + println!("版本 10 QR 码 PNG: {} 字节", png.len()); + + // 反转色终端输出(白底黑码 → 黑底白码) + println!("\n{}", qr.to_ascii(true)); + + Ok(()) +} diff --git a/examples/high_ecc.rs b/examples/high_ecc.rs new file mode 100644 index 0000000..133b4df --- /dev/null +++ b/examples/high_ecc.rs @@ -0,0 +1,22 @@ +//! QRGen 高纠错示例:生成可抵抗 30% 损坏的 QR 码 +//! +//! 运行: `cargo run --example high_ecc` + +use qr_core::qr::{QrCode, QrConfig, VersionMode}; +use qr_core::version::EcLevel; + +fn main() -> Result<(), Box> { + let config = QrConfig { + level: EcLevel::H, // 30% 纠错能力 + version: VersionMode::Auto, + margin: 6, // 更大的静区 + }; + + let qr = QrCode::encode("重要数据 - High ECC", config)?; + println!("版本: {}, 纠错: {:?}, 尺寸: {}×{}", qr.version.0, qr.level, qr.size(), qr.size()); + + let svg = qr.to_svg(); + println!("SVG 生成成功: {} 字节", svg.len()); + + Ok(()) +} diff --git a/web/src/main.rs b/web/src/main.rs index 57ad21b..f9015e6 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -24,9 +24,15 @@ struct QrParams { fmt: String, } -fn default_level() -> String { "M".into() } -fn default_margin() -> u8 { 4 } -fn default_size() -> u8 { 8 } +fn default_level() -> String { + "M".into() +} +fn default_margin() -> u8 { + 4 +} +fn default_size() -> u8 { + 8 +} fn parse_level(s: &str) -> Result { match s.to_uppercase().as_str() {