├── .editorconfig ├── .github └── workflows │ └── ghpages.yml ├── .gitignore ├── Cargo.toml ├── License.md ├── Readme.md ├── package.json ├── projects ├── qr-image-cli │ ├── Cargo.toml │ ├── Readme.md │ ├── src │ │ └── main.rs │ └── tests │ │ ├── main.rs │ │ └── wolfram-wolf.png ├── qr-image-core │ ├── Cargo.toml │ ├── package.json │ ├── src │ │ ├── drawer │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── renderer │ │ │ └── mod.rs │ └── tests │ │ ├── main.rs │ │ ├── wolfram-wolf.png │ │ └── wolfram-wolf.qr.png ├── qr-image-web │ ├── Cargo.toml │ ├── Readme.md │ ├── Trunk.toml │ ├── assets │ │ ├── logo.svg │ │ └── main.scss │ ├── i18n │ │ ├── zh-Hans │ │ │ └── main.yaml │ │ └── zh-Hant │ │ │ └── main.yaml │ ├── index.html │ ├── package.json │ ├── src │ │ ├── form.rs │ │ ├── github.png │ │ └── main.rs │ └── tests │ │ └── main.rs └── qr-image-wolfram │ └── Readme.md └── rustfmt.toml /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | 4 | 5 | [*.{css, scss}] 6 | indent_style = space 7 | indent_size = 4 8 | 9 | [*.html] 10 | indent_style = space 11 | indent_size = 4 -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: Publish Examples 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | persist-credentials: false 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | toolchain: nightly 18 | target: wasm32-unknown-unknown 19 | override: true 20 | profile: minimal 21 | - uses: jetli/wasm-bindgen-action@v0.1.0 22 | - name: Install trunk 🗝 23 | run: | 24 | cargo install trunk 25 | - name: Build website 💣 26 | run: | 27 | cd projects/qr-image-web 28 | wasm-bindgen -V 29 | trunk -V 30 | trunk build --release 31 | - name: Deploy 🚀 32 | uses: JamesIves/github-pages-deploy-action@3.7.1 33 | with: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | BRANCH: gh-pages 36 | FOLDER: projects/qr-image-web/dist 37 | CLEAN: true 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store/ 3 | 4 | # IDE 5 | .vscode/ 6 | .vs/ 7 | .idea/ 8 | *.iml 9 | 10 | # Rust 11 | dist/ 12 | target/ 13 | Cargo.lock 14 | *.nb 15 | *.ps1 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["projects/*"] 3 | default-members = [ 4 | "projects/qr-image-core", 5 | "projects/qr-image-cli", 6 | "projects/qr-image-web", 7 | ] 8 | 9 | exclude = [ 10 | "projects/.DS_Store", 11 | "projects/qr-image-wolfram", 12 | ] 13 | 14 | [profile.release] 15 | lto = true 16 | panic = "abort" -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | ### 1. Definitions 5 | 6 | **1.1. “Contributor”** 7 | means each individual or legal entity that creates, contributes to 8 | the creation of, or owns Covered Software. 9 | 10 | **1.2. “Contributor Version”** 11 | means the combination of the Contributions of others (if any) used 12 | by a Contributor and that particular Contributor's Contribution. 13 | 14 | **1.3. “Contribution”** 15 | means Covered Software of a particular Contributor. 16 | 17 | **1.4. “Covered Software”** 18 | means Source Code Form to which the initial Contributor has attached 19 | the notice in Exhibit A, the Executable Form of such Source Code 20 | Form, and Modifications of such Source Code Form, in each case 21 | including portions thereof. 22 | 23 | **1.5. “Incompatible With Secondary Licenses”** 24 | means 25 | 26 | * **(a)** that the initial Contributor has attached the notice described 27 | in Exhibit B to the Covered Software; or 28 | * **(b)** that the Covered Software was made available under the terms of 29 | version 1.1 or earlier of the License, but not also under the 30 | terms of a Secondary License. 31 | 32 | **1.6. “Executable Form”** 33 | means any form of the work other than Source Code Form. 34 | 35 | **1.7. “Larger Work”** 36 | means a work that combines Covered Software with other material, in 37 | a separate file or files, that is not Covered Software. 38 | 39 | **1.8. “License”** 40 | means this document. 41 | 42 | **1.9. “Licensable”** 43 | means having the right to grant, to the maximum extent possible, 44 | whether at the time of the initial grant or subsequently, any and 45 | all of the rights conveyed by this License. 46 | 47 | **1.10. “Modifications”** 48 | means any of the following: 49 | 50 | * **(a)** any file in Source Code Form that results from an addition to, 51 | deletion from, or modification of the contents of Covered 52 | Software; or 53 | * **(b)** any new file in Source Code Form that contains any Covered 54 | Software. 55 | 56 | **1.11. “Patent Claims” of a Contributor** 57 | means any patent claim(s), including without limitation, method, 58 | process, and apparatus claims, in any patent Licensable by such 59 | Contributor that would be infringed, but for the grant of the 60 | License, by the making, using, selling, offering for sale, having 61 | made, import, or transfer of either its Contributions or its 62 | Contributor Version. 63 | 64 | **1.12. “Secondary License”** 65 | means either the GNU General Public License, Version 2.0, the GNU 66 | Lesser General Public License, Version 2.1, the GNU Affero General 67 | Public License, Version 3.0, or any later versions of those 68 | licenses. 69 | 70 | **1.13. “Source Code Form”** 71 | means the form of the work preferred for making modifications. 72 | 73 | **1.14. “You” (or “Your”)** 74 | means an individual or a legal entity exercising rights under this 75 | License. For legal entities, “You” includes any entity that 76 | controls, is controlled by, or is under common control with You. For 77 | purposes of this definition, “control” means **(a)** the power, direct 78 | or indirect, to cause the direction or management of such entity, 79 | whether by contract or otherwise, or **(b)** ownership of more than 80 | fifty percent (50%) of the outstanding shares or beneficial 81 | ownership of such entity. 82 | 83 | 84 | ### 2. License Grants and Conditions 85 | 86 | #### 2.1. Grants 87 | 88 | Each Contributor hereby grants You a world-wide, royalty-free, 89 | non-exclusive license: 90 | 91 | * **(a)** under intellectual property rights (other than patent or trademark) 92 | Licensable by such Contributor to use, reproduce, make available, 93 | modify, display, perform, distribute, and otherwise exploit its 94 | Contributions, either on an unmodified basis, with Modifications, or 95 | as part of a Larger Work; and 96 | * **(b)** under Patent Claims of such Contributor to make, use, sell, offer 97 | for sale, have made, import, and otherwise transfer either its 98 | Contributions or its Contributor Version. 99 | 100 | #### 2.2. Effective Date 101 | 102 | The licenses granted in Section 2.1 with respect to any Contribution 103 | become effective for each Contribution on the date the Contributor first 104 | distributes such Contribution. 105 | 106 | #### 2.3. Limitations on Grant Scope 107 | 108 | The licenses granted in this Section 2 are the only rights granted under 109 | this License. No additional rights or licenses will be implied from the 110 | distribution or licensing of Covered Software under this License. 111 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 112 | Contributor: 113 | 114 | * **(a)** for any code that a Contributor has removed from Covered Software; 115 | or 116 | * **(b)** for infringements caused by: **(i)** Your and any other third party's 117 | modifications of Covered Software, or **(ii)** the combination of its 118 | Contributions with other software (except as part of its Contributor 119 | Version); or 120 | * **(c)** under Patent Claims infringed by Covered Software in the absence of 121 | its Contributions. 122 | 123 | This License does not grant any rights in the trademarks, service marks, 124 | or logos of any Contributor (except as may be necessary to comply with 125 | the notice requirements in Section 3.4). 126 | 127 | #### 2.4. Subsequent Licenses 128 | 129 | No Contributor makes additional grants as a result of Your choice to 130 | distribute the Covered Software under a subsequent version of this 131 | License (see Section 10.2) or under the terms of a Secondary License (if 132 | permitted under the terms of Section 3.3). 133 | 134 | #### 2.5. Representation 135 | 136 | Each Contributor represents that the Contributor believes its 137 | Contributions are its original creation(s) or it has sufficient rights 138 | to grant the rights to its Contributions conveyed by this License. 139 | 140 | #### 2.6. Fair Use 141 | 142 | This License is not intended to limit any rights You have under 143 | applicable copyright doctrines of fair use, fair dealing, or other 144 | equivalents. 145 | 146 | #### 2.7. Conditions 147 | 148 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 149 | in Section 2.1. 150 | 151 | 152 | ### 3. Responsibilities 153 | 154 | #### 3.1. Distribution of Source Form 155 | 156 | All distribution of Covered Software in Source Code Form, including any 157 | Modifications that You create or to which You contribute, must be under 158 | the terms of this License. You must inform recipients that the Source 159 | Code Form of the Covered Software is governed by the terms of this 160 | License, and how they can obtain a copy of this License. You may not 161 | attempt to alter or restrict the recipients' rights in the Source Code 162 | Form. 163 | 164 | #### 3.2. Distribution of Executable Form 165 | 166 | If You distribute Covered Software in Executable Form then: 167 | 168 | * **(a)** such Covered Software must also be made available in Source Code 169 | Form, as described in Section 3.1, and You must inform recipients of 170 | the Executable Form how they can obtain a copy of such Source Code 171 | Form by reasonable means in a timely manner, at a charge no more 172 | than the cost of distribution to the recipient; and 173 | 174 | * **(b)** You may distribute such Executable Form under the terms of this 175 | License, or sublicense it under different terms, provided that the 176 | license for the Executable Form does not attempt to limit or alter 177 | the recipients' rights in the Source Code Form under this License. 178 | 179 | #### 3.3. Distribution of a Larger Work 180 | 181 | You may create and distribute a Larger Work under terms of Your choice, 182 | provided that You also comply with the requirements of this License for 183 | the Covered Software. If the Larger Work is a combination of Covered 184 | Software with a work governed by one or more Secondary Licenses, and the 185 | Covered Software is not Incompatible With Secondary Licenses, this 186 | License permits You to additionally distribute such Covered Software 187 | under the terms of such Secondary License(s), so that the recipient of 188 | the Larger Work may, at their option, further distribute the Covered 189 | Software under the terms of either this License or such Secondary 190 | License(s). 191 | 192 | #### 3.4. Notices 193 | 194 | You may not remove or alter the substance of any license notices 195 | (including copyright notices, patent notices, disclaimers of warranty, 196 | or limitations of liability) contained within the Source Code Form of 197 | the Covered Software, except that You may alter any license notices to 198 | the extent required to remedy known factual inaccuracies. 199 | 200 | #### 3.5. Application of Additional Terms 201 | 202 | You may choose to offer, and to charge a fee for, warranty, support, 203 | indemnity or liability obligations to one or more recipients of Covered 204 | Software. However, You may do so only on Your own behalf, and not on 205 | behalf of any Contributor. You must make it absolutely clear that any 206 | such warranty, support, indemnity, or liability obligation is offered by 207 | You alone, and You hereby agree to indemnify every Contributor for any 208 | liability incurred by such Contributor as a result of warranty, support, 209 | indemnity or liability terms You offer. You may include additional 210 | disclaimers of warranty and limitations of liability specific to any 211 | jurisdiction. 212 | 213 | 214 | ### 4. Inability to Comply Due to Statute or Regulation 215 | 216 | If it is impossible for You to comply with any of the terms of this 217 | License with respect to some or all of the Covered Software due to 218 | statute, judicial order, or regulation then You must: **(a)** comply with 219 | the terms of this License to the maximum extent possible; and **(b)** 220 | describe the limitations and the code they affect. Such description must 221 | be placed in a text file included with all distributions of the Covered 222 | Software under this License. Except to the extent prohibited by statute 223 | or regulation, such description must be sufficiently detailed for a 224 | recipient of ordinary skill to be able to understand it. 225 | 226 | 227 | ### 5. Termination 228 | 229 | **5.1.** The rights granted under this License will terminate automatically 230 | if You fail to comply with any of its terms. However, if You become 231 | compliant, then the rights granted under this License from a particular 232 | Contributor are reinstated **(a)** provisionally, unless and until such 233 | Contributor explicitly and finally terminates Your grants, and **(b)** on an 234 | ongoing basis, if such Contributor fails to notify You of the 235 | non-compliance by some reasonable means prior to 60 days after You have 236 | come back into compliance. Moreover, Your grants from a particular 237 | Contributor are reinstated on an ongoing basis if such Contributor 238 | notifies You of the non-compliance by some reasonable means, this is the 239 | first time You have received notice of non-compliance with this License 240 | from such Contributor, and You become compliant prior to 30 days after 241 | Your receipt of the notice. 242 | 243 | **5.2.** If You initiate litigation against any entity by asserting a patent 244 | infringement claim (excluding declaratory judgment actions, 245 | counter-claims, and cross-claims) alleging that a Contributor Version 246 | directly or indirectly infringes any patent, then the rights granted to 247 | You by any and all Contributors for the Covered Software under Section 248 | 2.1 of this License shall terminate. 249 | 250 | **5.3.** In the event of termination under Sections 5.1 or 5.2 above, all 251 | end user license agreements (excluding distributors and resellers) which 252 | have been validly granted by You or Your distributors under this License 253 | prior to termination shall survive termination. 254 | 255 | 256 | ### 6. Disclaimer of Warranty 257 | 258 | > Covered Software is provided under this License on an “as is” 259 | > basis, without warranty of any kind, either expressed, implied, or 260 | > statutory, including, without limitation, warranties that the 261 | > Covered Software is free of defects, merchantable, fit for a 262 | > particular purpose or non-infringing. The entire risk as to the 263 | > quality and performance of the Covered Software is with You. 264 | > Should any Covered Software prove defective in any respect, You 265 | > (not any Contributor) assume the cost of any necessary servicing, 266 | > repair, or correction. This disclaimer of warranty constitutes an 267 | > essential part of this License. No use of any Covered Software is 268 | > authorized under this License except under this disclaimer. 269 | 270 | ### 7. Limitation of Liability 271 | 272 | > Under no circumstances and under no legal theory, whether tort 273 | > (including negligence), contract, or otherwise, shall any 274 | > Contributor, or anyone who distributes Covered Software as 275 | > permitted above, be liable to You for any direct, indirect, 276 | > special, incidental, or consequential damages of any character 277 | > including, without limitation, damages for lost profits, loss of 278 | > goodwill, work stoppage, computer failure or malfunction, or any 279 | > and all other commercial damages or losses, even if such party 280 | > shall have been informed of the possibility of such damages. This 281 | > limitation of liability shall not apply to liability for death or 282 | > personal injury resulting from such party's negligence to the 283 | > extent applicable law prohibits such limitation. Some 284 | > jurisdictions do not allow the exclusion or limitation of 285 | > incidental or consequential damages, so this exclusion and 286 | > limitation may not apply to You. 287 | 288 | 289 | ### 8. Litigation 290 | 291 | Any litigation relating to this License may be brought only in the 292 | courts of a jurisdiction where the defendant maintains its principal 293 | place of business and such litigation shall be governed by laws of that 294 | jurisdiction, without reference to its conflict-of-law provisions. 295 | Nothing in this Section shall prevent a party's ability to bring 296 | cross-claims or counter-claims. 297 | 298 | 299 | ### 9. Miscellaneous 300 | 301 | This License represents the complete agreement concerning the subject 302 | matter hereof. If any provision of this License is held to be 303 | unenforceable, such provision shall be reformed only to the extent 304 | necessary to make it enforceable. Any law or regulation which provides 305 | that the language of a contract shall be construed against the drafter 306 | shall not be used to construe this License against a Contributor. 307 | 308 | 309 | ### 10. Versions of the License 310 | 311 | #### 10.1. New Versions 312 | 313 | Mozilla Foundation is the license steward. Except as provided in Section 314 | 10.3, no one other than the license steward has the right to modify or 315 | publish new versions of this License. Each version will be given a 316 | distinguishing version number. 317 | 318 | #### 10.2. Effect of New Versions 319 | 320 | You may distribute the Covered Software under the terms of the version 321 | of the License under which You originally received the Covered Software, 322 | or under the terms of any subsequent version published by the license 323 | steward. 324 | 325 | #### 10.3. Modified Versions 326 | 327 | If you create software not governed by this License, and you want to 328 | create a new license for such software, you may create and use a 329 | modified version of this License if you rename the license and remove 330 | any references to the name of the license steward (except to note that 331 | such modified license differs from this License). 332 | 333 | #### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 334 | 335 | If You choose to distribute Source Code Form that is Incompatible With 336 | Secondary Licenses under the terms of this version of the License, the 337 | notice described in Exhibit B of this License must be attached. 338 | 339 | ## Exhibit A - Source Code Form License Notice 340 | 341 | This Source Code Form is subject to the terms of the Mozilla Public 342 | License, v. 2.0. If a copy of the MPL was not distributed with this 343 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 344 | 345 | If it is not possible or desirable to put the notice in a particular 346 | file, then You may include the notice in a location (such as a LICENSE 347 | file in a relevant directory) where a recipient would be likely to look 348 | for such a notice. 349 | 350 | You may add additional accurate notices of copyright ownership. 351 | 352 | ## Exhibit B - “Incompatible With Secondary Licenses” Notice 353 | 354 | This Source Code Form is "Incompatible With Secondary Licenses", as 355 | defined by the Mozilla Public License, v. 2.0. 356 | 357 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | QR Image Embed 2 | ============== 3 | 4 | ![](https://i.loli.net/2020/11/24/cUrPKjnv7upsFZq.png) 5 | 6 | ## Web 7 | 8 | - Online preview: https://galaster.github.io/qr-image 9 | 10 | ## CLI 11 | 12 | ```yaml 13 | QR Image Embed 0.1.0 14 | 15 | USAGE: 16 | qr-image.exe [OPTIONS] 17 | 18 | FLAGS: 19 | -h, --help Prints help information 20 | -V, --version Prints version information 21 | 22 | OPTIONS: 23 | --ec Set EC level 24 | -e, --enhance Set enhanced mode 25 | --qr Set QR Version 26 | -s, --size Set output image size 27 | 28 | ARGS: 29 | Sets the input image file path 30 | Sets the qr text for encoding 31 | ``` 32 | 33 | ## Algorithm 34 | 35 | - [Halftone QR Codes](http://vecg.cs.ucl.ac.uk/Projects/SmartGeometry/halftone_QR/paper_docs/halftoneQR_sigga13.pdf) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "cargo build --release", 5 | "test": "cargo test --release", 6 | "fmt": "cargo fmt --all", 7 | "push": "wee test && git push", 8 | "reset": "git reset Head~ --soft", 9 | "u": "cargo upgrade --offline --incompatible" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/qr-image-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qr-image" 3 | publish = false 4 | version = "0.1.0" 5 | authors = ["Aster <192607617@qq.com>"] 6 | description = "..." 7 | repository = "https://github.com/GalAster/qr-image" 8 | readme = "../../readme.md" 9 | license = "MPL-2.0" 10 | edition = "2018" 11 | 12 | [dependencies] 13 | qr-image-core = { version = "0.2", path = "../qr-image-core" } 14 | clap = "4.2" 15 | 16 | [dev-dependencies] 17 | 18 | [features] 19 | default = [] 20 | -------------------------------------------------------------------------------- /projects/qr-image-cli/Readme.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oovm/qr-image/07a2586b612d127ae65988d0e919426d92f8f575/projects/qr-image-cli/Readme.md -------------------------------------------------------------------------------- /projects/qr-image-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{App, Arg}; 2 | use qr_image_core::{EcLevel, QrError, QrImage, Version}; 3 | use std::{path::PathBuf, str::FromStr}; 4 | 5 | fn main() -> Result<(), QrError> { 6 | let app = App::new("QR Image Embed") 7 | .version(env!("CARGO_PKG_VERSION")) 8 | .author(env!("CARGO_PKG_AUTHORS")) 9 | .about(env!("CARGO_PKG_DESCRIPTION")) 10 | .arg(Arg::with_name("INPUT").help("Sets the input image file path").required(true).index(1)) 11 | .arg(Arg::with_name("Text").help("Sets the qr text for encoding").required(true).index(2)) 12 | .arg(Arg::with_name("Size").help("Set output image size").short("s").long("size").takes_value(true).value_name("size")) 13 | .arg( 14 | // flag, ture if turn on 15 | Arg::with_name("Enhance") 16 | .help("Set enhanced mode") 17 | .short("e") 18 | .long("enhance") 19 | .takes_value(true) 20 | .value_name("enhance"), 21 | ) 22 | .arg( 23 | Arg::with_name("EC Level") 24 | .help("Set EC level") 25 | //.short("e") 26 | .long("ec") 27 | .takes_value(true) 28 | .value_name("ec"), 29 | ) 30 | .arg( 31 | Arg::with_name("QR Version") 32 | .help("Set QR Version") 33 | //.short("e") 34 | .long("qr") 35 | .takes_value(true) 36 | .value_name("qr"), 37 | ) 38 | .get_matches(); 39 | let input = app.value_of("INPUT").and_then(|p| PathBuf::from_str(p).ok()).unwrap(); 40 | let text = app.value_of("TEXT").unwrap(); 41 | 42 | println!("{:?}", input); 43 | println!("{}", text); 44 | 45 | let output_size = app.value_of("size").and_then(|o| u32::from_str(o).ok()); 46 | println!("{:?}", output_size); 47 | 48 | let mut render = QrImage::default(); 49 | match app.value_of("enhance").and_then(|o| o.chars().next()) { 50 | // yes | on | true 51 | Some('y') | Some('o') | Some('t') => render.enhanced = true, 52 | _ => render.enhanced = false, 53 | } 54 | match app.value_of("ec").and_then(|o| o.to_uppercase().chars().next()) { 55 | Some('L') => render.ec_level = EcLevel::L, 56 | Some('M') => render.ec_level = EcLevel::M, 57 | Some('H') => render.ec_level = EcLevel::H, 58 | Some('Q') => render.ec_level = EcLevel::Q, 59 | _ => (), 60 | } 61 | if let Some(i) = app.value_of("qr").and_then(|o| i16::from_str(o).ok()) { 62 | render.qr_version = Version::Normal(i) 63 | } 64 | println!("{:?}", render); 65 | 66 | Ok(()) 67 | } 68 | -------------------------------------------------------------------------------- /projects/qr-image-cli/tests/main.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn ready() { 3 | println!("it works!") 4 | } 5 | -------------------------------------------------------------------------------- /projects/qr-image-cli/tests/wolfram-wolf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oovm/qr-image/07a2586b612d127ae65988d0e919426d92f8f575/projects/qr-image-cli/tests/wolfram-wolf.png -------------------------------------------------------------------------------- /projects/qr-image-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qr-image-core" 3 | version = "0.2.1" 4 | authors = ["Aster <192607617@qq.com>"] 5 | description = "Create QR code from a image." 6 | repository = "https://github.com/oovm/qr-image" 7 | documentation = "https://docs.rs/qr-image" 8 | readme = "../../readme.md" 9 | license = "MPL-2.0" 10 | edition = "2018" 11 | 12 | [dependencies] 13 | image = "0.25.1" 14 | #pix = "0.13.2" 15 | # qrcode-generator = "4.0" 16 | qrcode = "0.14.0" 17 | 18 | [dev-dependencies] 19 | 20 | [features] 21 | default = [] 22 | -------------------------------------------------------------------------------- /projects/qr-image-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "p": "cargo publish --allow-dirty" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /projects/qr-image-core/src/drawer/mod.rs: -------------------------------------------------------------------------------- 1 | pub struct Canvas {} 2 | 3 | impl Canvas { 4 | pub fn draw_svg(&self) {} 5 | pub fn draw_image(&self) {} 6 | pub fn draw_gray(&self) {} 7 | } 8 | 9 | impl Canvas { 10 | pub fn save_svg(&self) {} 11 | pub fn save_image(&self) {} 12 | pub fn save_gray(&self) {} 13 | } 14 | -------------------------------------------------------------------------------- /projects/qr-image-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod drawer; 2 | mod renderer; 3 | 4 | pub use crate::drawer::Canvas; 5 | pub use image::{Luma, Rgb}; 6 | pub use qrcode::{types::QrError, EcLevel, QrCode, Version}; 7 | 8 | pub type QrResult = Result; 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct QrImage { 12 | pub qr_version: Version, 13 | pub ec_level: EcLevel, 14 | pub dark_color: Rgb, 15 | pub light_color: Rgb, 16 | pub enhanced: bool, 17 | pub auto_size: bool, 18 | } 19 | 20 | impl Default for QrImage { 21 | fn default() -> Self { 22 | Self { 23 | qr_version: Version::Normal(2), 24 | ec_level: EcLevel::L, 25 | dark_color: Rgb([0, 0, 0]), 26 | light_color: Rgb([255, 255, 255]), 27 | enhanced: true, 28 | auto_size: false, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /projects/qr-image-core/src/renderer/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{QrCode, QrImage, QrResult, Version}; 2 | use image::{ 3 | imageops::{resize, FilterType}, 4 | DynamicImage, GenericImage, GenericImageView, Rgb, RgbImage, 5 | }; 6 | 7 | impl QrImage { 8 | fn target_qr(&self, data: &[u8]) -> QrResult { 9 | if self.auto_size { 10 | match QrCode::with_version(data, self.qr_version, self.ec_level) { 11 | Ok(o) => Ok(o), 12 | Err(_) => match QrCode::with_error_correction_level(data, self.ec_level) { 13 | Ok(o) => Ok(o), 14 | Err(_) => QrCode::new(data), 15 | }, 16 | } 17 | } 18 | else { 19 | QrCode::with_version(data, self.qr_version, self.ec_level) 20 | } 21 | } 22 | pub fn render(&self, data: &[u8], img: &DynamicImage) -> QrResult { 23 | let qr = self.target_qr(data)?; 24 | let size = qr.width() as u32; 25 | let out = resize(img, 3 * size, 3 * size, FilterType::Triangle); 26 | let rgb = unsafe { 27 | redraw_locations( 28 | &qr, 29 | DynamicImage::ImageRgba8(out).into_rgb8(), 30 | self.dark_color, 31 | self.light_color, 32 | self.enhanced, 33 | !self.enhanced, 34 | ) 35 | }; 36 | return Ok(DynamicImage::ImageRgb8(rgb)); 37 | } 38 | pub fn render_frames(&self) { 39 | unimplemented!() 40 | } 41 | } 42 | 43 | pub unsafe fn get_align_locations(qr: &QrCode) -> Vec<(usize, usize)> { 44 | let mut aligns = vec![]; 45 | match qr.version() { 46 | Version::Normal(ver) => { 47 | let align_location: &[Vec; 40] = &[ 48 | vec![], 49 | vec![6, 18], 50 | vec![6, 22], 51 | vec![6, 26], 52 | vec![6, 30], 53 | vec![6, 34], 54 | vec![6, 22, 38], 55 | vec![6, 24, 42], 56 | vec![6, 26, 46], 57 | vec![6, 28, 50], 58 | vec![6, 30, 54], 59 | vec![6, 32, 58], 60 | vec![6, 34, 62], 61 | vec![6, 26, 46, 66], 62 | vec![6, 26, 48, 70], 63 | vec![6, 26, 50, 74], 64 | vec![6, 30, 54, 78], 65 | vec![6, 30, 56, 82], 66 | vec![6, 30, 58, 86], 67 | vec![6, 34, 62, 90], 68 | vec![6, 28, 50, 72, 94], 69 | vec![6, 26, 50, 74, 98], 70 | vec![6, 30, 54, 78, 102], 71 | vec![6, 28, 54, 80, 106], 72 | vec![6, 32, 58, 84, 110], 73 | vec![6, 30, 58, 86, 114], 74 | vec![6, 34, 62, 90, 118], 75 | vec![6, 26, 50, 74, 98, 122], 76 | vec![6, 30, 54, 78, 102, 126], 77 | vec![6, 26, 52, 78, 104, 130], 78 | vec![6, 30, 56, 82, 108, 134], 79 | vec![6, 34, 60, 86, 112, 138], 80 | vec![6, 30, 58, 86, 114, 142], 81 | vec![6, 34, 62, 90, 118, 146], 82 | vec![6, 30, 54, 78, 102, 126, 150], 83 | vec![6, 24, 50, 76, 102, 128, 154], 84 | vec![6, 28, 54, 80, 106, 132, 158], 85 | vec![6, 32, 58, 84, 110, 136, 162], 86 | vec![6, 26, 54, 82, 110, 138, 166], 87 | vec![6, 30, 58, 86, 114, 142, 170], 88 | ]; 89 | let loc = align_location.get_unchecked(ver as usize - 1); 90 | for a in 0..loc.len() { 91 | for b in 0..loc.len() { 92 | if !((a == 0 || b == 0) || (a == loc.len() - 1 && b == 0) || (a == 0 && b == loc.len() - 1)) { 93 | for i in (loc.get_unchecked(a) * 3 - 6)..(loc.get_unchecked(a) * 3 + 9) { 94 | for j in (loc.get_unchecked(b) * 3 - 6)..(loc.get_unchecked(b) * 3 + 9) { 95 | aligns.push((i, j)) 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } 102 | Version::Micro(ver) => { 103 | let _ = ver; 104 | unimplemented!() 105 | } 106 | } 107 | return aligns; 108 | } 109 | 110 | #[rustfmt::skip] 111 | pub unsafe fn redraw_locations(qr: &QrCode, bg: RgbImage, dark: Rgb, light: Rgb, enhanced: bool, skip_bg: bool) -> RgbImage { 112 | let aligns = get_align_locations(qr); 113 | let mut qr_img = qr_render_rgb(qr, dark, light); 114 | // FIXME: 115 | // Too slow, maybe the target image should be modified 116 | for i in 0..qr_img.width() - 0 { 117 | for j in 0..qr_img.width() - 0 { 118 | let _ = skip_bg; 119 | if (i < 21 && j < 21) 120 | || (i < 21 && j > qr_img.width() - 22) 121 | || (i > qr_img.width() - 22 && j < 21) 122 | || (enhanced && [18, 19, 20].contains(&i)) 123 | || (enhanced && [18, 19, 20].contains(&j)) 124 | || (enhanced && aligns.contains(&(i as usize + 0, j as usize + 0))) 125 | || (i % 3 == 1 && j % 3 == 1) 126 | //|| (!skip_bg && bg.unsafe_get_pixel(i, j) == dark) 127 | { 128 | continue; 129 | } 130 | else { 131 | qr_img.unsafe_put_pixel(i, j, bg.unsafe_get_pixel(i, j)) 132 | } 133 | } 134 | } 135 | return qr_img; 136 | } 137 | 138 | pub fn qr_render_rgb(qr: &QrCode, dark: Rgb, light: Rgb) -> RgbImage { 139 | qr.render().quiet_zone(false).module_dimensions(3, 3).dark_color(dark).light_color(light).build() 140 | } 141 | -------------------------------------------------------------------------------- /projects/qr-image-core/tests/main.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn ready() { 3 | println!("it works!") 4 | } 5 | 6 | use image::{open, DynamicImage}; 7 | use qr_image_core::QrImage; 8 | 9 | #[test] 10 | fn test() { 11 | let cfg = QrImage::default(); 12 | let img = DynamicImage::ImageRgba8(open("tests/wolfram-wolf.png").unwrap().into_rgba8()); 13 | // Encode some data into bits. 14 | let code = cfg.render("苟利国家生死以".as_bytes(), &img).unwrap(); 15 | code.save("./tests/wolfram-wolf.qr.png").unwrap(); 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /projects/qr-image-core/tests/wolfram-wolf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oovm/qr-image/07a2586b612d127ae65988d0e919426d92f8f575/projects/qr-image-core/tests/wolfram-wolf.png -------------------------------------------------------------------------------- /projects/qr-image-core/tests/wolfram-wolf.qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oovm/qr-image/07a2586b612d127ae65988d0e919426d92f8f575/projects/qr-image-core/tests/wolfram-wolf.qr.png -------------------------------------------------------------------------------- /projects/qr-image-web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qr-image-web" 3 | publish = false 4 | version = "0.1.0" 5 | authors = ["Aster <192607617@qq.com>"] 6 | description = "..." 7 | repository = "https://github.com/GalAster/qr-art" 8 | readme = "../../Readme.md" 9 | license = "MPL-2.0" 10 | edition = "2018" 11 | 12 | [dependencies] 13 | yew = "0.20" 14 | qr-image-core = {version = "0.2", path = "../qr-image-core"} 15 | colors-transform = "0.2" 16 | image = "0.24" 17 | base64 = "0.21" 18 | anyhow = "1.0" 19 | -------------------------------------------------------------------------------- /projects/qr-image-web/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | 参考 3 | 4 | https://www.zhihu.com/question/39961600 5 | 6 | https://github.com/sylnsfar/qrcode/blob/master/README-cn.md 7 | 8 | https://mathematica.stackexchange.com/questions/120041/how-to-generate-image-like-qr-code-with-mathematica -------------------------------------------------------------------------------- /projects/qr-image-web/Trunk.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # The index HTML file to drive the bundling process. 3 | target = "index.html" 4 | # Build in release mode. 5 | release = true 6 | # The output dir for all final assets. 7 | dist = "dist" 8 | # The public URL from which assets are to be served. 9 | public_url = "/qr-image/" -------------------------------------------------------------------------------- /projects/qr-image-web/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /projects/qr-image-web/assets/main.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 3 | "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", 4 | "Microsoft YaHei", SimSun, sans-serif; 5 | color: #333333; 6 | } 7 | 8 | @media screen and (min-width: 768px) { 9 | .container-fluid { 10 | max-width: 798px; 11 | } 12 | } 13 | 14 | @media screen and (max-width: 470px) { 15 | h1 { 16 | font-size: 26px; 17 | } 18 | } 19 | 20 | textarea { 21 | resize: vertical; 22 | } 23 | -------------------------------------------------------------------------------- /projects/qr-image-web/i18n/zh-Hans/main.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oovm/qr-image/07a2586b612d127ae65988d0e919426d92f8f575/projects/qr-image-web/i18n/zh-Hans/main.yaml -------------------------------------------------------------------------------- /projects/qr-image-web/i18n/zh-Hant/main.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oovm/qr-image/07a2586b612d127ae65988d0e919426d92f8f575/projects/qr-image-web/i18n/zh-Hant/main.yaml -------------------------------------------------------------------------------- /projects/qr-image-web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | QR Image Embed 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /projects/qr-image-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "cargo build --release", 5 | "test": "cargo test --release", 6 | "fmt": "cargo fmt --all", 7 | "push": "wee test && git push", 8 | "reset": "git reset Head~ --soft", 9 | "pub": "cargo-publish-all --yes --allow-dirty", 10 | "dev": "trunk serve" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/qr-image-web/src/form.rs: -------------------------------------------------------------------------------- 1 | use crate::{Event, Model}; 2 | use image::{imageops::FilterType, GenericImageView, ImageFormat, ImageOutputFormat}; 3 | use qr_image_core::{EcLevel, QrImage, Version}; 4 | use yew::prelude::*; 5 | 6 | impl Model { 7 | pub fn format_qr_version(&self) -> String { 8 | let n = match self.qr_version { 9 | Version::Normal(i) => i, 10 | Version::Micro(i) => i, 11 | }; 12 | return format!("{}", n); 13 | } 14 | 15 | pub fn format_ec_level(&self) -> String { 16 | let n = match self.ec_level { 17 | EcLevel::L => "L", 18 | EcLevel::M => "M", 19 | EcLevel::Q => "Q", 20 | EcLevel::H => "H", 21 | }; 22 | return String::from(n); 23 | } 24 | 25 | pub fn qr_render(&self) -> anyhow::Result<(String, u32)> { 26 | let renderer = QrImage { 27 | qr_version: self.qr_version, 28 | ec_level: self.ec_level, 29 | dark_color: self.dark_color, 30 | light_color: self.light_color, 31 | enhanced: self.enhanced, 32 | auto_size: true, 33 | }; 34 | 35 | let input = image::load_from_memory_with_format(&self.image, ImageFormat::Png)?; 36 | let mut base_img = renderer.render(self.input.as_bytes(), &input)?; 37 | if base_img.width() < self.output_size as u32 { 38 | base_img = base_img.resize_exact(self.output_size as u32, self.output_size as u32, FilterType::Nearest) 39 | } 40 | let mut buf = vec![]; 41 | base_img.write_to(&mut buf, ImageOutputFormat::Png)?; 42 | return Ok((base64::encode(&buf), base_img.width().max(self.output_size as u32))); 43 | } 44 | 45 | pub fn qr_code_view(&self) -> Html { 46 | let qr = match self.qr_render() { 47 | Ok((o, size)) => { 48 | html! { 49 | 52 | } 53 | } 54 | Err(e) => { 55 | html! { 56 | 57 | } 58 | } 59 | }; 60 | return html! { 61 |
62 | 63 |
{qr}
64 |
65 | }; 66 | } 67 | 68 | pub fn form_view(&self) -> Html { 69 | html! { 70 |
71 | {self.qr_code_view()} 72 |
73 | 74 |
75 |