├── .gitignore
├── CLA-signed
├── CLA.wolfmcnally.943652EE38441760C3DC35364B6C2FCF894780AE.md
└── README.md
├── CLA.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── Demo
└── QRCodeGeneratorDemo
│ ├── .gitignore
│ ├── CLA-signed
│ └── CLA.wolfmcnally.943652EE38441760C3DC35364B6C2FCF894780AE.md
│ ├── CLA.md
│ ├── CODEOWNERS
│ ├── CONTRIBUTING.md
│ ├── Docs
│ ├── screenshot-1.png
│ └── screenshot-2.png
│ ├── LICENSE
│ ├── QRCodeGeneratorDemo
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── QRCodeGeneratorDemo.entitlements
│ └── QRCodeGeneratorDemoApp.swift
│ └── README.md
├── LICENSE
├── Package.swift
├── README.md
├── Sources
└── QRCodeGenerator
│ ├── BitBuffer.swift
│ ├── CorrectionLevel.swift
│ ├── OSImage.swift
│ ├── Private
│ ├── Canvas.swift
│ ├── IntPoint.swift
│ └── IntSize.swift
│ ├── QRCode.swift
│ ├── QRError.swift
│ ├── Segment.swift
│ └── ShiftJISData.swift
└── Tests
└── QRCodeGeneratorTests
└── QRCodeGeneratorTests.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | Packages/
41 | Package.pins
42 | Package.resolved
43 | *.xcodeproj
44 |
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/CLA-signed/CLA.wolfmcnally.943652EE38441760C3DC35364B6C2FCF894780AE.md:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP SIGNED MESSAGE-----
2 | Hash: SHA256
3 |
4 | # Contributor License Agreement
5 |
6 | Version 1.0
7 |
8 | Name: Wolf McNally
9 |
10 | E-Mail: wolf@wolfmcnally.com
11 |
12 | Legal Jurisdiction: Wyoming, United States of America
13 |
14 | Project: https://github.com/BlockchainCommons/QRCodeGenerator
15 |
16 | Date: July 23, 2021
17 |
18 | ## Purpose
19 |
20 | This agreement gives Blockchain Commons, LLC the permission it needs in order to accept my contributions into its open software project and to manage the intellectual property in that project over time.
21 |
22 | ## License
23 |
24 | I hereby license Blockchain Commons, LLC to:
25 |
26 | 1. do anything with my contributions that would otherwise infringe my copyright in them
27 |
28 | 2. do anything with my contributions that would otherwise infringe patents that I can or become able to license
29 |
30 | 3. sublicense these rights to others on any terms they like
31 |
32 | ## Reliability
33 |
34 | I understand that Blockchain Commons will rely on this license. I may not revoke this license.
35 |
36 | ## Awareness
37 |
38 | I promise that I am familiar with legal rules, like ["work made for hire" rules](http://worksmadeforhire.com), that can give employers and clients ownership of intellectual property in work that I do. I am also aware that legal agreements I might sign, like confidential information and invention assignment agreements, will usually give ownership of intellectual property in my work to employers, clients, and companies that I found. If someone else owns intellectual property in my work, I need their permission to license it.
39 |
40 | ## Copyright Guarantee
41 |
42 | I promise not to offer contributions to the project that contain copyrighted work that I do not have legally binding permission to contribute under these terms. When I offer a contribution with permission, I promise to document in the contribution who owns copyright in what work, and how they gave permission to contribute it. If I later become aware that one of my contributions may have copyrighted work of others that I did not have permission to contribute, I will notify Blockchain Commons, in confidence, immediately.
43 |
44 | ## Patent Guarantee
45 |
46 | I promise not to offer contributions to the project that I know infringe patents of others that I do not have permission to contribute under these terms.
47 |
48 | ## Open Source Guarantee
49 |
50 | I promise not to offer contributions that contain or depend on the work of others, unless that work is available under a license that [Blue Oak Council rates bronze or better](https://blueoakconcil.org/list), such as the MIT License, two- or three-clause BSD License, the Apache License Version 2.0, or the Blue Oak Model License 1.0.0. When I offer a contribution containing or depending on others' work, I promise to document in the contribution who licenses that work, along with copies of their license terms.
51 |
52 | ## Disclaimers
53 |
54 | ***As far as the law allows, my contributions come as is, without any warranty or condition. Other than under [Copyright Guarantee](#copyright-guarantee), [Patent Guarantee](#patent-guarantee), or [Open Source Guarantee](#open-source-guarantee), I will not be liable to anyone for any damages related to my contributions or this contributor license agreement, under any kind of legal claim.***
55 | -----BEGIN PGP SIGNATURE-----
56 |
57 | iQIzBAEBCAAdFiEElDZS7jhEF2DD3DU2S2wvz4lHgK4FAmD6c8oACgkQS2wvz4lH
58 | gK44Dw//ce+I6E6TwPuF+dCYst7OjLTJfik8sRu3rYoLd9B1qMc4OXLf0teT3WoO
59 | wsKgZKlhsdxT5+kFUqN5BxJKFI+GSa2jlPMLk+1ejx93rBry/JakWBPPvBUgPQou
60 | HrlTwy/jR50fnGDCf6EWFSbfzCWUEe9j5r9dgCY//OQGjrCFJ/npGWgAuVfYfUwY
61 | YnCALm5AWIicdJSbPS58r9IhFKgkIZJEqXVr488CmjoDSrFWWv/KLwypmrFo7Ytz
62 | rzEX5E7LyHKkVqV88zgcFopHkyxnbUYISzGWCVOyeJQuI8fKdgtr542/GstebXZ/
63 | Yczq9cVgOKdpp5lrDQunC77utpYsYIiooSQKV7TGw+byoMms5kyBB6khJlUL/TZi
64 | 5CGQqaEYcADzWYx2tL6ktPeV6Um8u8HJvrzOmXyR5f/aV1eHz4+uatjvpCW9hH++
65 | LGqMTnE+28IoKfMxcocGQt/0nqwTTOvoAXlSgQuCEsJSeKk8Edc/l6wKB4OJBQmU
66 | 6Eewdr4ldZhFlvb2yaPC9607Vl9f/Z3/9aQQ6Z5AIMWO3eDJhYubvs2BFfo29i2k
67 | XU91vENdY6oul3KFoU4xBxp8uy1ykfNYOWCHVVngz3HdL6YREF/9P5C2FIj8Y06M
68 | y+rNiY4xVTMDIQrWlSHMPUUO5p6Ze+Z4cpLdZm9R4BzAhVcRUAU=
69 | =90se
70 | -----END PGP SIGNATURE-----
71 |
--------------------------------------------------------------------------------
/CLA-signed/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlockchainCommons/QRCodeGenerator/57f800f5dcb90942bb49ce0f8458726d1424dc64/CLA-signed/README.md
--------------------------------------------------------------------------------
/CLA.md:
--------------------------------------------------------------------------------
1 | # Contributor License Agreement
2 |
3 | Version 1.0
4 |
5 | Name: `$name`
6 |
7 | E-Mail: `$email`
8 |
9 | Legal Jurisdiction: Wyoming, United States of America
10 |
11 | Project: https://github.com/BlockchainCommons/QRCodeGenerator
12 |
13 | Date: `$date`
14 |
15 | ## Purpose
16 |
17 | This agreement gives Blockchain Commons, LLC the permission it needs in order to accept my contributions into its open software project and to manage the intellectual property in that project over time.
18 |
19 | ## License
20 |
21 | I hereby license Blockchain Commons, LLC to:
22 |
23 | 1. do anything with my contributions that would otherwise infringe my copyright in them
24 |
25 | 2. do anything with my contributions that would otherwise infringe patents that I can or become able to license
26 |
27 | 3. sublicense these rights to others on any terms they like
28 |
29 | ## Reliability
30 |
31 | I understand that Blockchain Commons will rely on this license. I may not revoke this license.
32 |
33 | ## Awareness
34 |
35 | I promise that I am familiar with legal rules, like ["work made for hire" rules](http://worksmadeforhire.com), that can give employers and clients ownership of intellectual property in work that I do. I am also aware that legal agreements I might sign, like confidential information and invention assignment agreements, will usually give ownership of intellectual property in my work to employers, clients, and companies that I found. If someone else owns intellectual property in my work, I need their permission to license it.
36 |
37 | ## Copyright Guarantee
38 |
39 | I promise not to offer contributions to the project that contain copyrighted work that I do not have legally binding permission to contribute under these terms. When I offer a contribution with permission, I promise to document in the contribution who owns copyright in what work, and how they gave permission to contribute it. If I later become aware that one of my contributions may have copyrighted work of others that I did not have permission to contribute, I will notify Blockchain Commons, in confidence, immediately.
40 |
41 | ## Patent Guarantee
42 |
43 | I promise not to offer contributions to the project that I know infringe patents of others that I do not have permission to contribute under these terms.
44 |
45 | ## Open Source Guarantee
46 |
47 | I promise not to offer contributions that contain or depend on the work of others, unless that work is available under a license that [Blue Oak Council rates bronze or better](https://blueoakconcil.org/list), such as the MIT License, two- or three-clause BSD License, the Apache License Version 2.0, or the Blue Oak Model License 1.0.0. When I offer a contribution containing or depending on others' work, I promise to document in the contribution who licenses that work, along with copies of their license terms.
48 |
49 | ## Disclaimers
50 |
51 | ***As far as the law allows, my contributions come as is, without any warranty or condition. Other than under [Copyright Guarantee](#copyright-guarantee), [Patent Guarantee](#patent-guarantee), or [Open Source Guarantee](#open-source-guarantee), I will not be liable to anyone for any damages related to my contributions or this contributor license agreement, under any kind of legal claim.***
52 |
53 | ---
54 |
55 | To sign this Contributor License Agreement, fill in `$name`, `$email`, and `$date` above. Then sign using GPG using the following command `gpg --armor --clearsign --output ./CLA-signed/CLA.YOURGITHUBNAME.YOURGPGFINGERPRINT.md CLA.md`, then either submit your signed Contributor License Agreement to this repo as a GPG signed Pull Request or email it to [ChristopherA@BlockchainCommons.com](mailto:ChristopherA@BlockchainCommons.com).
56 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in this repo.
2 |
3 | * @ChristopherA
4 | * @WolfMcNally
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
4 |
5 | - Reporting a bug
6 | - Discussing the current state of the code
7 | - Submitting a fix
8 | - Proposing new features
9 | - Becoming a maintainer
10 |
11 | ## We Develop with Github
12 | We use GitHub to host code, to track issues and feature requests, and to accept Pull Requests.
13 |
14 | ## Report Bugs using Github's [issues](https://github.com/briandk/transcriptase-atom/issues)
15 |
16 | If you find bugs, mistakes, or inconsistencies in this project's code or documents, please let us know by [opening a new issue](./issues), but consider searching through existing issues first to check and see if the problem has already been reported. If it has, it never hurts to add a quick "+1" or "I have this problem too". This helps prioritize the most common problems and requests.
17 |
18 | ### Write Bug Reports with Detail, Background, and Sample Code
19 |
20 | [This is an example](http://stackoverflow.com/q/12488905/180626) of a good bug report by @briandk. Here's [another example from craig.hockenberry](http://www.openradar.me/11905408).
21 |
22 | **Great Bug Reports** tend to have:
23 |
24 | - A quick summary and/or background
25 | - Steps to reproduce
26 | - Be specific!
27 | - Give sample code if you can. [The stackoverflow bug report](http://stackoverflow.com/q/12488905/180626) includes sample code that *anyone* with a base R setup can run to reproduce what I was seeing
28 | - What you expected would happen
29 | - What actually happens
30 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
31 |
32 | People *love* thorough bug reports. I'm not even kidding.
33 |
34 | ## Submit Code Changes through Pull Requests
35 |
36 | Simple Pull Requests to fix typos, to document, or to fix small bugs are always welcome.
37 |
38 | We ask that more significant improvements to the project be first proposed before anybody starts to code as an [issue](./issues) or as a [draft Pull Request](./pulls), which is a [nice new feature](https://github.blog/2019-02-14-introducing-draft-pull-requests/) that gives other contributors a chance to point you in the right direction, give feedback on the design, and maybe discuss if related work is already under way.
39 |
40 | ### Use a Consistent Coding Style
41 |
42 | * We indent using two spaces (soft tabs)
43 | * We ALWAYS put spaces after list items and method parameters ([1, 2, 3], not [1,2,3]), around operators (x += 1, not x+=1), and around hash arrows.
44 | * This is open-source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible.
45 |
46 | ### Use [Github Flow](https://guides.github.com/introduction/flow/index.html) for Pull Requests
47 |
48 | We use [Github Flow](https://guides.github.com/introduction/flow/index.html). When you submit Pull Requests, please:
49 |
50 | 1. Fork the repo and create your branch from `master`.
51 | 2. If you've added code that should be tested, add tests.
52 | 3. If you've changed APIs, update the documentation.
53 | 4. Ensure the test suite passes.
54 | 5. Make sure your code lints.
55 | 6. Issue that Pull Request!
56 |
57 | ### Submit Under the BSD-2-Clause Plus Patent License
58 |
59 | In short, when you submit code changes, your submissions are understood to be available under the same [BSD-2-Clause Plus Patent License](./LICENSE.md) that covers the project. We also ask all code contributors to GPG sign the [Contributor License Agreement (CLA.md)](./CLA.md) to protect future users of this project. Feel free to contact the maintainers if that's a concern.
60 |
61 | ## References
62 |
63 | Portions of this CONTRIBUTING.md document were adopted from best practices of a number of open source projects, including:
64 | * [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
65 | * [IPFS Contributing](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md)
66 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | Packages/
41 | Package.pins
42 | Package.resolved
43 | *.xcodeproj
44 |
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/CLA-signed/CLA.wolfmcnally.943652EE38441760C3DC35364B6C2FCF894780AE.md:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP SIGNED MESSAGE-----
2 | Hash: SHA256
3 |
4 | # Contributor License Agreement
5 |
6 | Version 1.0
7 |
8 | Name: Wolf McNally
9 |
10 | E-Mail: wolf@wolfmcnally.com
11 |
12 | Legal Jurisdiction: Wyoming, United States of America
13 |
14 | Project: https://github.com/BlockchainCommons/QRCodeGeneratorDemo
15 |
16 | Date: July 23, 2021
17 |
18 | ## Purpose
19 |
20 | This agreement gives Blockchain Commons, LLC the permission it needs in order to accept my contributions into its open software project and to manage the intellectual property in that project over time.
21 |
22 | ## License
23 |
24 | I hereby license Blockchain Commons, LLC to:
25 |
26 | 1. do anything with my contributions that would otherwise infringe my copyright in them
27 |
28 | 2. do anything with my contributions that would otherwise infringe patents that I can or become able to license
29 |
30 | 3. sublicense these rights to others on any terms they like
31 |
32 | ## Reliability
33 |
34 | I understand that Blockchain Commons will rely on this license. I may not revoke this license.
35 |
36 | ## Awareness
37 |
38 | I promise that I am familiar with legal rules, like ["work made for hire" rules](http://worksmadeforhire.com), that can give employers and clients ownership of intellectual property in work that I do. I am also aware that legal agreements I might sign, like confidential information and invention assignment agreements, will usually give ownership of intellectual property in my work to employers, clients, and companies that I found. If someone else owns intellectual property in my work, I need their permission to license it.
39 |
40 | ## Copyright Guarantee
41 |
42 | I promise not to offer contributions to the project that contain copyrighted work that I do not have legally binding permission to contribute under these terms. When I offer a contribution with permission, I promise to document in the contribution who owns copyright in what work, and how they gave permission to contribute it. If I later become aware that one of my contributions may have copyrighted work of others that I did not have permission to contribute, I will notify Blockchain Commons, in confidence, immediately.
43 |
44 | ## Patent Guarantee
45 |
46 | I promise not to offer contributions to the project that I know infringe patents of others that I do not have permission to contribute under these terms.
47 |
48 | ## Open Source Guarantee
49 |
50 | I promise not to offer contributions that contain or depend on the work of others, unless that work is available under a license that [Blue Oak Council rates bronze or better](https://blueoakconcil.org/list), such as the MIT License, two- or three-clause BSD License, the Apache License Version 2.0, or the Blue Oak Model License 1.0.0. When I offer a contribution containing or depending on others' work, I promise to document in the contribution who licenses that work, along with copies of their license terms.
51 |
52 | ## Disclaimers
53 |
54 | ***As far as the law allows, my contributions come as is, without any warranty or condition. Other than under [Copyright Guarantee](#copyright-guarantee), [Patent Guarantee](#patent-guarantee), or [Open Source Guarantee](#open-source-guarantee), I will not be liable to anyone for any damages related to my contributions or this contributor license agreement, under any kind of legal claim.***
55 |
56 | -----BEGIN PGP SIGNATURE-----
57 |
58 | iQIzBAEBCAAdFiEElDZS7jhEF2DD3DU2S2wvz4lHgK4FAmD7rGYACgkQS2wvz4lH
59 | gK6NBxAAvb6Q3n0bl06IE+Qs9Sq4OyoOWhlr1MP65TUGAW769sYzZu1SoM0Hjk6R
60 | wgYQ6Hf6OL8aiMtmD/AMZkP0qdBDtBe0C0k/5Q79FSs07ev+7YLCmFXIN9brZAVM
61 | BnVNdonbwms37uTONQImLrM2bfFq5n7gs+ppeDLs7VsN0TcWym93KCX+/WWoJEgn
62 | uQ8wYt/N02anDTPHrGkA+FhLHqAwPFbl4kdBUKgzetwd1b7r617tE02EkWlObcwc
63 | 2JR32q1xuAqzQb/M3FnnAPta6jWWXh355wsdc2Dm1uSoaLP2Ce0OnDZr3d6iDYM5
64 | JdjwrjxTxDW6uNW7ZF7AzIuaPwDaLoXt0DarUuXBcDmElUKSux/8+27i0L8Roz5p
65 | exBIF1wtgOn1+p0iVgCFJo2ssta3cg66xIFE7O8LiVEXBFVgx+eVwnwEW9fGscKR
66 | 7JrgDY3iGO03faICbWFOyEbiNcKqZ7xLgDxvCp/mBcrx3+RED0cFdoL0gLlr7fwJ
67 | jh7WAds1qVjzwsAlffo17IddlH9840twSBxMkG78Ihh8B1uyLy3LgVfJSCrRtff4
68 | SdrvhrxU7L3/VAxPbx2eipByRXNP0gZfHKvwgiHbgAvqDduw1Sp9uNMfTaoYuPev
69 | djeKXpvgR7GaZ5i5Wai83ovYbuncb2Kv/oTRlb50mjRJFlGuCfg=
70 | =lx08
71 | -----END PGP SIGNATURE-----
72 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/CLA.md:
--------------------------------------------------------------------------------
1 | # Contributor License Agreement
2 |
3 | Version 1.0
4 |
5 | Name: `$name`
6 |
7 | E-Mail: `$email`
8 |
9 | Legal Jurisdiction: Wyoming, United States of America
10 |
11 | Project: https://github.com/BlockchainCommons/QRCodeGeneratorDemo
12 |
13 | Date: `$date`
14 |
15 | ## Purpose
16 |
17 | This agreement gives Blockchain Commons, LLC the permission it needs in order to accept my contributions into its open software project and to manage the intellectual property in that project over time.
18 |
19 | ## License
20 |
21 | I hereby license Blockchain Commons, LLC to:
22 |
23 | 1. do anything with my contributions that would otherwise infringe my copyright in them
24 |
25 | 2. do anything with my contributions that would otherwise infringe patents that I can or become able to license
26 |
27 | 3. sublicense these rights to others on any terms they like
28 |
29 | ## Reliability
30 |
31 | I understand that Blockchain Commons will rely on this license. I may not revoke this license.
32 |
33 | ## Awareness
34 |
35 | I promise that I am familiar with legal rules, like ["work made for hire" rules](http://worksmadeforhire.com), that can give employers and clients ownership of intellectual property in work that I do. I am also aware that legal agreements I might sign, like confidential information and invention assignment agreements, will usually give ownership of intellectual property in my work to employers, clients, and companies that I found. If someone else owns intellectual property in my work, I need their permission to license it.
36 |
37 | ## Copyright Guarantee
38 |
39 | I promise not to offer contributions to the project that contain copyrighted work that I do not have legally binding permission to contribute under these terms. When I offer a contribution with permission, I promise to document in the contribution who owns copyright in what work, and how they gave permission to contribute it. If I later become aware that one of my contributions may have copyrighted work of others that I did not have permission to contribute, I will notify Blockchain Commons, in confidence, immediately.
40 |
41 | ## Patent Guarantee
42 |
43 | I promise not to offer contributions to the project that I know infringe patents of others that I do not have permission to contribute under these terms.
44 |
45 | ## Open Source Guarantee
46 |
47 | I promise not to offer contributions that contain or depend on the work of others, unless that work is available under a license that [Blue Oak Council rates bronze or better](https://blueoakconcil.org/list), such as the MIT License, two- or three-clause BSD License, the Apache License Version 2.0, or the Blue Oak Model License 1.0.0. When I offer a contribution containing or depending on others' work, I promise to document in the contribution who licenses that work, along with copies of their license terms.
48 |
49 | ## Disclaimers
50 |
51 | ***As far as the law allows, my contributions come as is, without any warranty or condition. Other than under [Copyright Guarantee](#copyright-guarantee), [Patent Guarantee](#patent-guarantee), or [Open Source Guarantee](#open-source-guarantee), I will not be liable to anyone for any damages related to my contributions or this contributor license agreement, under any kind of legal claim.***
52 |
53 | ---
54 |
55 | To sign this Contributor License Agreement, fill in `$name`, `$email`, and `$date` above. Then sign using GPG using the following command `gpg --armor --clearsign --output ./CLA-signed/CLA.YOURGITHUBNAME.YOURGPGFINGERPRINT.md CLA.md`, then either submit your signed Contributor License Agreement to this repo as a GPG signed Pull Request or email it to [ChristopherA@BlockchainCommons.com](mailto:ChristopherA@BlockchainCommons.com).
56 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in this repo.
2 |
3 | * @ChristopherA
4 | * @WolfMcNally
5 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
4 |
5 | - Reporting a bug
6 | - Discussing the current state of the code
7 | - Submitting a fix
8 | - Proposing new features
9 | - Becoming a maintainer
10 |
11 | ## We Develop with Github
12 | We use GitHub to host code, to track issues and feature requests, and to accept Pull Requests.
13 |
14 | ## Report Bugs using Github's [issues](https://github.com/briandk/transcriptase-atom/issues)
15 |
16 | If you find bugs, mistakes, or inconsistencies in this project's code or documents, please let us know by [opening a new issue](./issues), but consider searching through existing issues first to check and see if the problem has already been reported. If it has, it never hurts to add a quick "+1" or "I have this problem too". This helps prioritize the most common problems and requests.
17 |
18 | ### Write Bug Reports with Detail, Background, and Sample Code
19 |
20 | [This is an example](http://stackoverflow.com/q/12488905/180626) of a good bug report by @briandk. Here's [another example from craig.hockenberry](http://www.openradar.me/11905408).
21 |
22 | **Great Bug Reports** tend to have:
23 |
24 | - A quick summary and/or background
25 | - Steps to reproduce
26 | - Be specific!
27 | - Give sample code if you can. [The stackoverflow bug report](http://stackoverflow.com/q/12488905/180626) includes sample code that *anyone* with a base R setup can run to reproduce what I was seeing
28 | - What you expected would happen
29 | - What actually happens
30 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
31 |
32 | People *love* thorough bug reports. I'm not even kidding.
33 |
34 | ## Submit Code Changes through Pull Requests
35 |
36 | Simple Pull Requests to fix typos, to document, or to fix small bugs are always welcome.
37 |
38 | We ask that more significant improvements to the project be first proposed before anybody starts to code as an [issue](./issues) or as a [draft Pull Request](./pulls), which is a [nice new feature](https://github.blog/2019-02-14-introducing-draft-pull-requests/) that gives other contributors a chance to point you in the right direction, give feedback on the design, and maybe discuss if related work is already under way.
39 |
40 | ### Use a Consistent Coding Style
41 |
42 | * We indent using two spaces (soft tabs)
43 | * We ALWAYS put spaces after list items and method parameters ([1, 2, 3], not [1,2,3]), around operators (x += 1, not x+=1), and around hash arrows.
44 | * This is open-source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible.
45 |
46 | ### Use [Github Flow](https://guides.github.com/introduction/flow/index.html) for Pull Requests
47 |
48 | We use [Github Flow](https://guides.github.com/introduction/flow/index.html). When you submit Pull Requests, please:
49 |
50 | 1. Fork the repo and create your branch from `master`.
51 | 2. If you've added code that should be tested, add tests.
52 | 3. If you've changed APIs, update the documentation.
53 | 4. Ensure the test suite passes.
54 | 5. Make sure your code lints.
55 | 6. Issue that Pull Request!
56 |
57 | ### Submit Under the BSD-2-Clause Plus Patent License
58 |
59 | In short, when you submit code changes, your submissions are understood to be available under the same [BSD-2-Clause Plus Patent License](./LICENSE.md) that covers the project. We also ask all code contributors to GPG sign the [Contributor License Agreement (CLA.md)](./CLA.md) to protect future users of this project. Feel free to contact the maintainers if that's a concern.
60 |
61 | ## References
62 |
63 | Portions of this CONTRIBUTING.md document were adopted from best practices of a number of open source projects, including:
64 | * [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
65 | * [IPFS Contributing](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md)
66 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/Docs/screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlockchainCommons/QRCodeGenerator/57f800f5dcb90942bb49ce0f8458726d1424dc64/Demo/QRCodeGeneratorDemo/Docs/screenshot-1.png
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/Docs/screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BlockchainCommons/QRCodeGenerator/57f800f5dcb90942bb49ce0f8458726d1424dc64/Demo/QRCodeGeneratorDemo/Docs/screenshot-2.png
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/LICENSE:
--------------------------------------------------------------------------------
1 | Unless otherwise noted (either in /README.md or in the file's header comments) the contents of this repository are released under the following license:
2 |
3 | BSD-2-Clause Plus Patent License
4 |
5 | SPDX-License-Identifier: [BSD-2-Clause-Patent](https://spdx.org/licenses/BSD-2-Clause-Patent.html)
6 |
7 | Copyright © 2021 Blockchain Commons, LLC
8 |
9 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10 |
11 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by:
14 |
15 | (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or
16 | (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution.
17 | Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise.
18 |
19 | DISCLAIMER
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/QRCodeGeneratorDemo/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/QRCodeGeneratorDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/QRCodeGeneratorDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/QRCodeGeneratorDemo/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // QRCodeGeneratorDemo
4 | //
5 | // Created by Wolf McNally on 7/23/21.
6 | //
7 |
8 | import SwiftUI
9 | import QRCodeGenerator
10 |
11 | struct QRCodeView: View {
12 | let qrCode: QRCode
13 | let title: String
14 | let caption: String
15 | let border: Int
16 | let foregroundColor: UIColor
17 | let backgroundColor: UIColor
18 |
19 | init(title: String, text: String, correctionLevel: CorrectionLevel = .medium, mask: Int? = nil, border: Int = 1, foregroundColor: UIColor = .black, backgroundColor: UIColor = .white) {
20 | self.title = title
21 | self.qrCode = try! QRCode.encode(text: text, correctionLevel: correctionLevel, mask: mask)
22 | self.caption = text
23 | self.border = border
24 | self.foregroundColor = foregroundColor
25 | self.backgroundColor = backgroundColor
26 | }
27 |
28 | init(title: String, utf8Bytes: [UInt8], correctionLevel: CorrectionLevel = .medium, mask: Int? = nil, border: Int = 1, foregroundColor: UIColor = .black, backgroundColor: UIColor = .white) {
29 | self.title = title
30 | let text = String(data: Data(utf8Bytes), encoding: .utf8)!
31 | self.qrCode = try! QRCode.encode(text: text, correctionLevel: correctionLevel, mask: mask)
32 | self.caption = text
33 | self.border = border
34 | self.foregroundColor = foregroundColor
35 | self.backgroundColor = backgroundColor
36 | }
37 |
38 | init(title: String, segments: [Segment], caption: String, correctionLevel: CorrectionLevel = .medium, mask: Int? = nil, border: Int = 1, foregroundColor: UIColor = .black, backgroundColor: UIColor = .white) {
39 | self.title = title
40 | self.qrCode = try! QRCode.encode(segments: segments, correctionLevel: correctionLevel, mask: mask)
41 | self.caption = caption
42 | self.border = border
43 | self.foregroundColor = foregroundColor
44 | self.backgroundColor = backgroundColor
45 | }
46 |
47 | var body: some View {
48 | VStack {
49 | Text(title)
50 | .bold()
51 | .fixedSize(horizontal: false, vertical: true)
52 | qrCode
53 | .image(border: border, foregroundColor: foregroundColor, backgroundColor: backgroundColor)
54 | .resizable()
55 | .aspectRatio(contentMode: .fit)
56 | .frame(maxWidth: 300, maxHeight: 300)
57 | Text(caption)
58 | .font(.caption)
59 | .fixedSize(horizontal: false, vertical: true)
60 | }
61 | .padding()
62 | }
63 | }
64 |
65 | struct ContentView: View {
66 | // Unicode text as UTF-8
67 | // こんにちwa、世界! αβγδ
68 | let utf8Bytes: [UInt8] = [
69 | 0xE3, 0x81, 0x93, 0xE3, 0x82, 0x93, 0xE3, 0x81, 0xAB, 0xE3,
70 | 0x81, 0xA1, 0x77, 0x61, 0xE3, 0x80, 0x81, 0xE4, 0xB8, 0x96,
71 | 0xE7, 0x95, 0x8C, 0xEF, 0xBC, 0x81, 0x20, 0xCE, 0xB1, 0xCE,
72 | 0xB2, 0xCE, 0xB3, 0xCE, 0xB4
73 | ]
74 | // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland)
75 | let alice = "Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversations?' So she was considering in her own mind (as well as she could, for the hot day made her feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink eyes ran close by her."
76 |
77 | let silver0 = "THE SQUARE ROOT OF 2 IS 1."
78 | let silver1 = "41421356237309504880168872420969807856967187537694807317667973799"
79 |
80 | let golden0 = "Golden ratio φ = 1."
81 | let golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"
82 | let golden2 = "......"
83 |
84 | // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters
85 | // 「魔法少女まどか☆マギカ」って、 ИАИ desu κα?
86 | let madoka: [UInt8] = [
87 | 0xE3, 0x80, 0x8C, 0xE9, 0xAD, 0x94, 0xE6, 0xB3, 0x95, 0xE5, 0xB0, 0x91, 0xE5, 0xA5, 0xB3, 0xE3, 0x81, 0xBE, 0xE3, 0x81, 0xA9, 0xE3, 0x81, 0x8B, 0xE2, 0x98, 0x86, 0xE3, 0x83, 0x9E, 0xE3, 0x82, 0xAE, 0xE3, 0x82, 0xAB, 0xE3, 0x80, 0x8D, 0xE3, 0x81, 0xA3, 0xE3, 0x81, 0xA6, 0xE3, 0x80, 0x81, 0xE3, 0x80, 0x80, 0xD0, 0x98, 0xD0, 0x90, 0xD0, 0x98, 0xE3, 0x80, 0x80, 0xEF, 0xBD, 0x84, 0xEF, 0xBD, 0x85, 0xEF, 0xBD, 0x93, 0xEF, 0xBD, 0x95, 0xE3, 0x80, 0x80, 0xCE, 0xBA, 0xCE, 0xB1, 0xEF, 0xBC, 0x9F
88 | ]
89 |
90 | // Kanji mode encoding (13 bits per character)
91 | // 「魔法少女まどか☆マギカ」って、 ИАИ desu κα?
92 | static let kanjiChars: [UInt16] = [
93 | 0x0035, 0x1002, 0x0FC0, 0x0AED, 0x0AD7,
94 | 0x015C, 0x0147, 0x0129, 0x0059, 0x01BD,
95 | 0x018D, 0x018A, 0x0036, 0x0141, 0x0144,
96 | 0x0001, 0x0000, 0x0249, 0x0240, 0x0249,
97 | 0x0000, 0x0104, 0x0105, 0x0113, 0x0115,
98 | 0x0000, 0x0208, 0x01FF, 0x0008
99 | ]
100 |
101 | let kanjiBits: [Bool] = {
102 | var bb: [Bool] = []
103 | for c in kanjiChars {
104 | bb.appendBits(c, 13)
105 | }
106 | return bb
107 | }()
108 |
109 | // Chinese text as UTF-8
110 | // 維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫
111 | static let chineseChars: [UInt8] = [
112 | 0xE7, 0xB6, 0xAD, 0xE5, 0x9F, 0xBA, 0xE7, 0x99, 0xBE, 0xE7, 0xA7, 0x91, 0xEF, 0xBC, 0x88, 0x57, 0x69, 0x6B, 0x69, 0x70, 0x65, 0x64, 0x69, 0x61, 0xEF, 0xBC, 0x8C, 0xE8, 0x81, 0x86, 0xE8, 0x81, 0xBD, 0x69, 0x2F, 0xCB, 0x8C, 0x77, 0xC9, 0xAA, 0x6B, 0xE1, 0xB5, 0xBB, 0xCB, 0x88, 0x70, 0x69, 0xCB, 0x90, 0x64, 0x69, 0x2E, 0xC9, 0x99, 0x2F, 0xEF, 0xBC, 0x89, 0xE6, 0x98, 0xAF, 0xE4, 0xB8, 0x80, 0xE5, 0x80, 0x8B, 0xE8, 0x87, 0xAA, 0xE7, 0x94, 0xB1, 0xE5, 0x85, 0xA7, 0xE5, 0xAE, 0xB9, 0xE3, 0x80, 0x81, 0xE5, 0x85, 0xAC, 0xE9, 0x96, 0x8B, 0xE7, 0xB7, 0xA8, 0xE8, 0xBC, 0xAF, 0xE4, 0xB8, 0x94, 0xE5, 0xA4, 0x9A, 0xE8, 0xAA, 0x9E, 0xE8, 0xA8, 0x80, 0xE7, 0x9A, 0x84, 0xE7, 0xB6, 0xB2, 0xE8, 0xB7, 0xAF, 0xE7, 0x99, 0xBE, 0xE7, 0xA7, 0x91, 0xE5, 0x85, 0xA8, 0xE6, 0x9B, 0xB8, 0xE5, 0x8D, 0x94, 0xE4, 0xBD, 0x9C, 0xE8, 0xA8, 0x88, 0xE7, 0x95, 0xAB
113 | ]
114 |
115 | let chineseString = String(data: Data(chineseChars), encoding: .utf8)!
116 |
117 | var body: some View {
118 | ScrollView {
119 | VStack {
120 | Group {
121 | QRCodeView(title: "Basic QR code", text: "Hello, world!", correctionLevel: .low)
122 | QRCodeView(title: "Basic QR code, colored with 3 module border", text: "Hello, world!", correctionLevel: .low, border: 3, foregroundColor: .blue, backgroundColor: .yellow)
123 |
124 | QRCodeView(title: "Numeric mode encoding (3.33 bits per digit)", text: "314159265358979323846264338327950288419716939937510", correctionLevel: .medium)
125 |
126 | QRCodeView(title: "Alphanumeric mode encoding (5.5 bits per character)", text: "DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", correctionLevel: .high)
127 |
128 | QRCodeView(title: "Unicode text as UTF-8", utf8Bytes: utf8Bytes, correctionLevel: .quartile)
129 |
130 | QRCodeView(title: "Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland)", text: alice, correctionLevel: .high)
131 | }
132 |
133 | Group {
134 | // Illustration "silver"
135 | QRCodeView(title: "Arbitrary text encoded in binary mode.", text: silver0 + silver1, correctionLevel: .low)
136 | try! QRCodeView(title: "Same text, encoded as an alphanumeric segment and a numeric segment.", segments: [
137 | Segment.makeAlphanumeric(text: silver0),
138 | Segment.makeNumeric(digits: silver1)
139 | ], caption: silver0 + silver1, correctionLevel: .low)
140 |
141 | // Illustration "golden"
142 | QRCodeView(title: "Arbitrary text encoded in binary mode.", text: golden0 + golden1 + golden2, correctionLevel: .low)
143 | try! QRCodeView(title: "Same text, encoded as three segments: binary, then numeric, then alphanumeric.", segments: [
144 | Segment.makeBytes(data: golden0),
145 | Segment.makeNumeric(digits: golden1),
146 | Segment.makeAlphanumeric(text: golden2)
147 | ], caption: golden0 + golden1 + golden2, correctionLevel: .low)
148 |
149 | // Illustration "Madoka"
150 | QRCodeView(title: "Kanji, kana, Cyrillic, full-width Latin, Greek characters, UTF-8.", text: String(data: Data(madoka), encoding: .utf8)!, correctionLevel: .low)
151 |
152 | // Kanji
153 | try! QRCodeView(title: "Same text, Kanji mode encoding (13 bits per character).", segments: [
154 | Segment(mode: .kanji, characterCount: Self.kanjiChars.count, data: kanjiBits)
155 | ], caption: "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?", correctionLevel: .low)
156 | }
157 |
158 | Group {
159 | try! QRCodeView(title: "Automatic mask.", segments: Segment.makeSegments(text: "https://www.nayuki.io/"), caption: "https://www.nayuki.io/", correctionLevel: .high, mask: nil)
160 | try! QRCodeView(title: "Force mask 3.", segments: Segment.makeSegments(text: "https://www.nayuki.io/"), caption: "https://www.nayuki.io/", correctionLevel: .high, mask: 3)
161 | }
162 |
163 | Group {
164 | try! QRCodeView(title: "Chinese test, mask 0", segments: Segment.makeSegments(text: chineseString), caption: chineseString, correctionLevel: .medium, mask: 0)
165 | try! QRCodeView(title: "Chinese test, mask 1", segments: Segment.makeSegments(text: chineseString), caption: chineseString, correctionLevel: .medium, mask: 1)
166 | try! QRCodeView(title: "Chinese test, mask 5", segments: Segment.makeSegments(text: chineseString), caption: chineseString, correctionLevel: .medium, mask: 5)
167 | try! QRCodeView(title: "Chinese test, mask 7", segments: Segment.makeSegments(text: chineseString), caption: chineseString, correctionLevel: .medium, mask: 7)
168 | try! QRCodeView(title: "Chinese test, automatic mask, optimal encoding", segments: Segment.makeSegmentsOptimally(text: chineseString), caption: chineseString, correctionLevel: .medium)
169 | }
170 |
171 | Group {
172 | let part1 = "shc:/"
173 | let part2 = String((0..<1580).map({ _ in "0123456789".randomElement()!}))
174 |
175 | QRCodeView(title: "Simulated Smart Health Card, encoded in a single binary segment.", text: part1 + part2)
176 |
177 | try! QRCodeView(title: "Simulated Smart Health Card, manually encoded as a binary segment for header, and numeric segment for body.", segments: [
178 | Segment.makeBytes(data: part1),
179 | Segment.makeNumeric(digits: part2)
180 | ], caption: part1 + part2)
181 |
182 | try! QRCodeView(title: "Simulated Smart Health Card, automatically encoded using optimal encoding.", segments: Segment.makeSegmentsOptimally(text: part1 + part2), caption: part1 + part2)
183 | }
184 | }
185 | }
186 | }
187 | }
188 |
189 | struct ContentView_Previews: PreviewProvider {
190 | static var previews: some View {
191 | ContentView()
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/QRCodeGeneratorDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/QRCodeGeneratorDemo/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/QRCodeGeneratorDemo/QRCodeGeneratorDemo.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.network.client
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/QRCodeGeneratorDemo/QRCodeGeneratorDemoApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // QRCodeGeneratorDemoApp.swift
3 | // QRCodeGeneratorDemo
4 | //
5 | // Created by Wolf McNally on 7/23/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct QRCodeGeneratorDemoApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Demo/QRCodeGeneratorDemo/README.md:
--------------------------------------------------------------------------------
1 | # Blockchain Commons QRCodeGeneratorDemo
2 |
3 | ### by Wolf McNally
4 |
5 | QRCodeGeneratorDemo is a SwiftUI-based iOS app that demonstrates the [Blockchain Commons QRCodeGenerator Swift package](https://github.com/BlockchainCommons/QRCodeGenerator). It displays a simple scrolling list of generated QR codes, many of which show the multi-segment encoding features it enables.
6 |
7 | 
8 |
9 | 
10 |
11 | ## Installation Instructions
12 |
13 | Open `QRCodeGeneratorDemo.xcworkspace` in Xcode 12 or later and build the `QRCodeGeneratorDemo` target. It will run on iOS devices or on macOS using Catalyst.
14 |
15 | ## Origin, Authors, Copyright & Licenses
16 |
17 | Unless otherwise noted (either in this [/README.md](./README.md) or in the file's header comments) the contents of this repository are Copyright © 2021 by Blockchain Commons, LLC, and are [licensed](./LICENSE) under the [spdx:BSD-2-Clause Plus Patent License](https://spdx.org/licenses/BSD-2-Clause-Patent.html).
18 |
19 | ### Derived from ...
20 |
21 | The QRCodeGenerator project is either derived from or was inspired by:
22 |
23 | * The [Project Nayuki QR Code generator library](https://www.nayuki.io/page/qr-code-generator-library)
24 |
25 | ## Financial Support
26 |
27 | QRCodeGenerator is a project of [Blockchain Commons](https://www.blockchaincommons.com/). We are proudly a "not-for-profit" social benefit corporation committed to open source & open development. Our work is funded entirely by donations and collaborative partnerships with people like you. Every contribution will be spent on building open tools, technologies, and techniques that sustain and advance blockchain and internet security infrastructure and promote an open web.
28 |
29 | To financially support further development of QRCodeGenerator and other projects, please consider becoming a Patron of Blockchain Commons through ongoing monthly patronage as a [GitHub Sponsor](https://github.com/sponsors/BlockchainCommons). You can also support Blockchain Commons with bitcoins at our [BTCPay Server](https://btcpay.blockchaincommons.com/).
30 |
31 | ## Contributing
32 |
33 | We encourage public contributions through issues and pull requests! Please review [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our development process. All contributions to this repository require a GPG signed [Contributor License Agreement](./CLA.md).
34 |
35 | ### Discussions
36 |
37 | The best place to talk about Blockchain Commons and its projects is in our GitHub Discussions areas.
38 |
39 | [**Gordian System Discussions**](https://github.com/BlockchainCommons/Gordian/discussions). For users and developers of the Gordian system, including the Gordian Server, Bitcoin Standup technology, QuickConnect, and the Gordian Wallet. If you want to talk about our linked full-node and wallet technology, suggest new additions to our Bitcoin Standup standards, or discuss the implementation our standalone wallet, the Discussions area of the [main Gordian repo](https://github.com/BlockchainCommons/Gordian) is the place.
40 |
41 | [**Wallet Standard Discussions**](https://github.com/BlockchainCommons/AirgappedSigning/discussions). For standards and open-source developers who want to talk about wallet standards, please use the Discussions area of the [Airgapped Signing repo](https://github.com/BlockchainCommons/AirgappedSigning). This is where you can talk about projects like our [LetheKit](https://github.com/BlockchainCommons/bc-lethekit) and command line tools such as [seedtool](https://github.com/BlockchainCommons/bc-seedtool-cli), both of which are intended to testbed wallet technologies, plus the libraries that we've built to support your own deployment of wallet technology such as [bc-bip39](https://github.com/BlockchainCommons/bc-bip39), [bc-slip39](https://github.com/BlockchainCommons/bc-slip39), [bc-shamir](https://github.com/BlockchainCommons/bc-shamir), [Sharded Secret Key Reconstruction](https://github.com/BlockchainCommons/bc-sskr), [bc-ur](https://github.com/BlockchainCommons/bc-ur), and the [bc-crypto-base](https://github.com/BlockchainCommons/bc-crypto-base). If it's a wallet-focused technology or a more general discussion of wallet standards,discuss it here.
42 |
43 | [**Blockchain Commons Discussions**](https://github.com/BlockchainCommons/Community/discussions). For developers, interns, and patrons of Blockchain Commons, please use the discussions area of the [Community repo](https://github.com/BlockchainCommons/Community) to talk about general Blockchain Commons issues, the intern program, or topics other than the [Gordian System](https://github.com/BlockchainCommons/Gordian/discussions) or the [wallet standards](https://github.com/BlockchainCommons/AirgappedSigning/discussions), each of which have their own discussion areas.
44 |
45 | ### Other Questions & Problems
46 |
47 | As an open-source, open-development community, Blockchain Commons does not have the resources to provide direct support of our projects. Please consider the discussions area as a locale where you might get answers to questions. Alternatively, please use this repository's [issues](./issues) feature. Unfortunately, we can not make any promises on response time.
48 |
49 | If your company requires support to use our projects, please feel free to contact us directly about options. We may be able to offer you a contract for support from one of our contributors, or we might be able to point you to another entity who can offer the contractual support that you need.
50 |
51 | ### Credits
52 |
53 | The following people directly contributed to this repository. You can add your name here by getting involved. The first step is learning how to contribute from our [CONTRIBUTING.md](./CONTRIBUTING.md) documentation.
54 |
55 | | Name | Role | Github | Email | GPG Fingerprint |
56 | | ----------------- | ------------------- | ------------------------------------------------- | ------------------------------------- | -------------------------------------------------- |
57 | | Wolf McNally | Project Lead | [@WolfMcNally](https://github.com/WolfMcNally) | \ | 9436 52EE 3844 1760 C3DC 3536 4B6C 2FCF 8947 80AE |
58 | | Christopher Allen | Principal Architect | [@ChristopherA](https://github.com/ChristopherA) | \ | FDFE 14A5 4ECB 30FC 5D22 74EF F8D3 6C91 3574 05ED |
59 |
60 | ## Responsible Disclosure
61 |
62 | We want to keep all of our software safe for everyone. If you have discovered a security vulnerability, we appreciate your help in disclosing it to us in a responsible manner. We are unfortunately not able to offer bug bounties at this time.
63 |
64 | We do ask that you offer us good faith and use best efforts not to leak information or harm any user, their data, or our developer community. Please give us a reasonable amount of time to fix the issue before you publish it. Do not defraud our users or us in the process of discovery. We promise not to bring legal action against researchers who point out a problem provided they do their best to follow the these guidelines.
65 |
66 | ### Reporting a Vulnerability
67 |
68 | Please report suspected security vulnerabilities in private via email to ChristopherA@BlockchainCommons.com (do not use this email for support). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
69 |
70 | The following keys may be used to communicate sensitive information to developers:
71 |
72 | | Name | Fingerprint |
73 | | ----------------- | -------------------------------------------------- |
74 | | Christopher Allen | FDFE 14A5 4ECB 30FC 5D22 74EF F8D3 6C91 3574 05ED |
75 |
76 | You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys ""` Ensure that you put quotes around fingerprints that contain spaces.
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Unless otherwise noted (either in /README.md or in the file's header comments) the contents of this repository are released under the following license:
2 |
3 | BSD-2-Clause Plus Patent License
4 |
5 | SPDX-License-Identifier: [BSD-2-Clause-Patent](https://spdx.org/licenses/BSD-2-Clause-Patent.html)
6 |
7 | Copyright © 2021 Blockchain Commons, LLC
8 |
9 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10 |
11 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
13 | Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by:
14 |
15 | (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or
16 | (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution.
17 | Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise.
18 |
19 | DISCLAIMER
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:6.0
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "QRCodeGenerator",
7 | platforms: [
8 | .macOS(.v11),
9 | .iOS(.v14)
10 | ],
11 | products: [
12 | .library(
13 | name: "QRCodeGenerator",
14 | targets: ["QRCodeGenerator"]),
15 | ],
16 | targets: [
17 | .target(
18 | name: "QRCodeGenerator",
19 | dependencies: []),
20 | .testTarget(
21 | name: "QRCodeGeneratorTests",
22 | dependencies: ["QRCodeGenerator"]),
23 | ]
24 | )
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Blockchain Commons QRCodeGenerator
2 |
3 | ### by Wolf McNally
4 |
5 | QRCodeGenerator is a pure Swift translation of the [Project Nayuki QR code generator library](https://www.nayuki.io/page/qr-code-generator-library). Unlike Apple's built-in APIs for QR code generation, this package allows more sophisticated and tuneable encoding techniques that produce more compact QR codes in certain circumstances, including the ability to automatically optimize segmenting for greatest compactness. See the unit tests for examples.
6 |
7 | See the [QRCodeGeneratorDemo](https://github.com/BlockchainCommons/QRCodeGeneratorDemo) repo for an iOS app that demonstrates this library.
8 |
9 | ## Why Another QR Code Generator?
10 |
11 | The QR code standard provides for QR codes to be composed of a heterogenous sequence of "segments," each of which is optimized for encoding different sorts of data, and each requiring a different number of bits per character to encode. Therefore, choosing which segment type (or series of segment types) to use for efficient encoding is important.
12 |
13 | | Segment Type | Bits Per Character | Character Set |
14 | | :--- | :--- | :--- |
15 | | `bytes` | 8 | `0x00` - `0xff` |
16 | | `numeric` | 3.33 | `0123456789` |
17 | | `alphanumeric ` | 5.5 | `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:` |
18 | | `kanji` | 13 | [Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS) |
19 |
20 | Many QR code generators, including the one built into iOS and macOS, take an undifferentiated block of byte data as input and analyze it to determine which single segment type can most efficiently encode it. For instance, if the block contains only the ASCII Arabic numerals, the `numeric` segment type is selected. This results in the encoding only taking 3.33 bits per character. Similarly, if every character in the input block is in the limited character set provided by the `alphanumeric` encoding node, the encoding takes only 5.5 bits per character.
21 |
22 | The [Blockchain Commons UR standard](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-005-ur.md) is designed to be compatible with the `alphanumeric` segment encoding node: URs are case-insensitive, and when a UR is transformed to upper case, it always is encodable in the `alphanumeric` mode. This results in an efficient encoding and therefore a less-dense QR code.
23 |
24 | However, not all formats are this straightforward. For example the data for the [SMART Health Card (SHC)](https://smarthealth.cards/) format is a URI that starts `shc:/` followed by often over a thousand numeric digits. The first five characters require the `bytes` encoding mode, while the following digits would be most efficiently encoded using the `numeric` mode. But if only one segment type can be selected, then it must be the `bytes` mode. For the SHC format, this results in a much denser QR code that requires more screen resolution to display and higher quality cameras to read. The solution is to encode the data as two segments: the first encoding the header using the `bytes` mode, and the second with the body using the `numeric` mode.
25 |
26 | It important to understand that no matter how many segments of any kind are used to create a QR code, all QR code readers are capable of reading them. So which segments to use is strictly a matter for the encoder to decide: the decoder always sees a scanned QR code as a string of UTF-8 bytes.
27 |
28 | So for certain data types, the QR code generator built into iOS and macOS will have less than efficient results. This QRCodeGenerator package allows you to automatically pick a segment type as the built-in generator does, or specify a sequence of segments, giving you control of encoding efficiency over all the segments.
29 |
30 | If you know the most efficient encoding for a data format in advance, you can manually specify a series of segments. But QRCodeGenerator also includes "optimal encoding" functions that search for and discover not a *single* segment type, but a *sequence* of most-efficient segment types. The use of this optimal encoding is optional because it does take a bit more processing power to search the space of possible segment encoding types to arrive at the optimal one.
31 |
32 | The ability to manually specify a sequence of segment types, or to have the package pick an optimal one automatically are the major advantages that QRCodeGenerator has over the built-in generator.
33 |
34 | As an example: the following Chinese string includes a variety of types of characters:
35 |
36 | ```
37 | 維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫
38 | ```
39 |
40 | If `Segment.makeBytes(text:)` is called with the above string, it returns the following single `Segment` that requires 1120 bits of space to encode:
41 |
42 | ```
43 | Segment(mode: byte, characterCount: 140, data: <1120 bits>)
44 | ```
45 |
46 | However, if `Segment.makeSegmentsOptimally(text:)` is called, it returns the following sequence of `Segment`s:
47 |
48 | ```
49 | Segment(mode: kanji, characterCount: 5, data: <65 bits>)
50 | Segment(mode: byte, characterCount: 9, data: <72 bits>)
51 | Segment(mode: kanji, characterCount: 3, data: <39 bits>)
52 | Segment(mode: byte, characterCount: 23, data: <184 bits>)
53 | Segment(mode: kanji, characterCount: 6, data: <78 bits>)
54 | Segment(mode: byte, characterCount: 3, data: <24 bits>)
55 | Segment(mode: kanji, characterCount: 21, data: <273 bits>)
56 | ```
57 |
58 | This totals to 735 bits, a 48% savings.
59 |
60 | ## Status - Release
61 |
62 | QRCodeGenerator is a translation of [Nayuki's library](https://www.nayuki.io/page/qr-code-generator-library). Its unit tests verify that it produces the exact same QR codes as those produced in the original source demo app. The maintainers believe this release to be stable and useful.
63 |
64 | ## Prerequisites
65 |
66 | This package has no dependencies. When compiled for iOS or macOS, the `QRCode` type has a `makeImage` function that produces a `UIImage` or `NSImage` that allows the specification of border size, module size, and colors.
67 |
68 | ## Installation Instructions
69 |
70 | Add this package to your project as a normal Swift package.
71 |
72 | ## Usage Instructions
73 |
74 | See the unit tests for extensive examples of use. A simple example:
75 |
76 | ```swift
77 | let qr = try QRCode.encode(text: "Hello, world!", correctionLevel: .low)
78 | print(qr.emojiString)
79 |
80 | ⬛️⬛️⬛️⬛️⬛️⬛️⬛️⬜️⬜️⬜️⬜️⬜️⬛️⬜️⬛️⬛️⬛️⬛️⬛️⬛️⬛️
81 | ⬛️⬜️⬜️⬜️⬜️⬜️⬛️⬜️⬜️⬛️⬜️⬛️⬜️⬜️⬛️⬜️⬜️⬜️⬜️⬜️⬛️
82 | ⬛️⬜️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬜️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬛️⬛️⬜️⬛️
83 | ⬛️⬜️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬜️⬜️⬜️⬜️⬜️⬛️⬜️⬛️⬛️⬛️⬜️⬛️
84 | ⬛️⬜️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬛️⬜️⬜️⬛️⬜️⬛️⬜️⬛️⬛️⬛️⬜️⬛️
85 | ⬛️⬜️⬜️⬜️⬜️⬜️⬛️⬜️⬛️⬛️⬛️⬛️⬜️⬜️⬛️⬜️⬜️⬜️⬜️⬜️⬛️
86 | ⬛️⬛️⬛️⬛️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬜️⬛️⬜️⬛️⬛️⬛️⬛️⬛️⬛️⬛️
87 | ⬜️⬜️⬜️⬜️⬜️⬜️⬜️⬜️⬛️⬜️⬛️⬜️⬜️⬜️⬜️⬜️⬜️⬜️⬜️⬜️⬜️
88 | ⬛️⬜️⬛️⬛️⬛️⬛️⬛️⬜️⬜️⬛️⬛️⬛️⬜️⬜️⬛️⬛️⬛️⬛️⬛️⬜️⬜️
89 | ⬜️⬜️⬜️⬛️⬛️⬜️⬜️⬛️⬛️⬜️⬜️⬜️⬛️⬛️⬜️⬜️⬛️⬛️⬛️⬜️⬛️
90 | ⬜️⬜️⬜️⬛️⬜️⬜️⬛️⬜️⬛️⬛️⬛️⬜️⬛️⬛️⬛️⬜️⬜️⬛️⬛️⬛️⬜️
91 | ⬜️⬛️⬛️⬜️⬜️⬛️⬜️⬛️⬜️⬜️⬛️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬛️⬜️⬜️
92 | ⬛️⬛️⬜️⬛️⬛️⬛️⬛️⬜️⬛️⬜️⬜️⬜️⬛️⬜️⬛️⬛️⬜️⬜️⬜️⬜️⬛️
93 | ⬜️⬜️⬜️⬜️⬜️⬜️⬜️⬜️⬛️⬜️⬜️⬜️⬜️⬛️⬛️⬛️⬛️⬛️⬜️⬜️⬜️
94 | ⬛️⬛️⬛️⬛️⬛️⬛️⬛️⬜️⬜️⬛️⬛️⬜️⬛️⬛️⬛️⬛️⬜️⬜️⬛️⬛️⬜️
95 | ⬛️⬜️⬜️⬜️⬜️⬜️⬛️⬜️⬛️⬜️⬛️⬜️⬛️⬛️⬜️⬛️⬜️⬛️⬛️⬛️⬜️
96 | ⬛️⬜️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬛️⬜️⬛️⬛️⬛️⬛️⬜️⬛️⬜️⬜️⬛️⬛️
97 | ⬛️⬜️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬜️⬛️⬜️⬜️⬜️⬜️⬛️⬛️⬛️⬜️⬜️⬜️
98 | ⬛️⬜️⬛️⬛️⬛️⬜️⬛️⬜️⬛️⬛️⬛️⬛️⬛️⬜️⬛️⬛️⬜️⬜️⬛️⬜️⬜️
99 | ⬛️⬜️⬜️⬜️⬜️⬜️⬛️⬜️⬜️⬜️⬛️⬜️⬛️⬛️⬜️⬜️⬛️⬛️⬛️⬜️⬜️
100 | ⬛️⬛️⬛️⬛️⬛️⬛️⬛️⬜️⬛️⬛️⬜️⬛️⬜️⬜️⬛️⬜️⬛️⬜️⬜️⬛️⬜️
101 | ```
102 |
103 | ## Origin, Authors, Copyright & Licenses
104 |
105 | Unless otherwise noted (either in this [/README.md](./README.md) or in the file's header comments) the contents of this repository are Copyright © 2021 by Blockchain Commons, LLC, and are [licensed](./LICENSE) under the [spdx:BSD-2-Clause Plus Patent License](https://spdx.org/licenses/BSD-2-Clause-Patent.html).
106 |
107 | ### Derived from ...
108 |
109 | This QRCodeGenerator project is either derived from or was inspired by:
110 |
111 | * The [Project Nayuki QR Code generator library](https://www.nayuki.io/page/qr-code-generator-library)
112 |
113 | While this project does not include any actual source code from the Nayuki's library, it is closely based on that library's C++ implementation, which is released under the MIT license.
114 |
115 | ## Financial Support
116 |
117 | QRCodeGenerator is a project of [Blockchain Commons](https://www.blockchaincommons.com/). We are proudly a "not-for-profit" social benefit corporation committed to open source & open development. Our work is funded entirely by donations and collaborative partnerships with people like you. Every contribution will be spent on building open tools, technologies, and techniques that sustain and advance blockchain and internet security infrastructure and promote an open web.
118 |
119 | To financially support further development of QRCodeGenerator and other projects, please consider becoming a Patron of Blockchain Commons through ongoing monthly patronage as a [GitHub Sponsor](https://github.com/sponsors/BlockchainCommons). You can also support Blockchain Commons with bitcoins at our [BTCPay Server](https://btcpay.blockchaincommons.com/).
120 |
121 | ## Contributing
122 |
123 | We encourage public contributions through issues and pull requests! Please review [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our development process. All contributions to this repository require a GPG signed [Contributor License Agreement](./CLA.md).
124 |
125 | ### Discussions
126 |
127 | The best place to talk about Blockchain Commons and its projects is in our GitHub Discussions areas.
128 |
129 | [**Gordian Developer Community**](https://github.com/BlockchainCommons/Gordian-Developer-Community/discussions). For standards and open-source developers who want to talk about interoperable wallet specifications, please use the Discussions area of the [Gordian Developer Community repo](https://github.com/BlockchainCommons/Gordian-Developer-Community/discussions). This is where you talk about Gordian specifications such as [Gordian Envelope](https://github.com/BlockchainCommons/Gordian/tree/master/Envelope#articles), [bc-shamir](https://github.com/BlockchainCommons/bc-shamir), [Sharded Secret Key Reconstruction](https://github.com/BlockchainCommons/bc-sskr), and [bc-ur](https://github.com/BlockchainCommons/bc-ur) as well as the larger [Gordian Architecture](https://github.com/BlockchainCommons/Gordian/blob/master/Docs/Overview-Architecture.md), its [Principles](https://github.com/BlockchainCommons/Gordian#gordian-principles) of independence, privacy, resilience, and openness, and its macro-architectural ideas such as functional partition (including airgapping, the original name of this community).
130 |
131 | [**Blockchain Commons Discussions**](https://github.com/BlockchainCommons/Community/discussions). For developers, interns, and patrons of Blockchain Commons, please use the discussions area of the [Community repo](https://github.com/BlockchainCommons/Community) to talk about general Blockchain Commons issues, the intern program, or topics other than those covered by the [Gordian Developer Community](https://github.com/BlockchainCommons/Gordian-Developer-Community/discussions) or the
132 | [Gordian User Community](https://github.com/BlockchainCommons/Gordian/discussions).
133 | ### Other Questions & Problems
134 |
135 | As an open-source, open-development community, Blockchain Commons does not have the resources to provide direct support of our projects. Please consider the discussions area as a locale where you might get answers to questions. Alternatively, please use this repository's [issues](./issues) feature. Unfortunately, we can not make any promises on response time.
136 |
137 | If your company requires support to use our projects, please feel free to contact us directly about options. We may be able to offer you a contract for support from one of our contributors, or we might be able to point you to another entity who can offer the contractual support that you need.
138 |
139 | ### Credits
140 |
141 | The following people directly contributed to this repository. You can add your name here by getting involved. The first step is learning how to contribute from our [CONTRIBUTING.md](./CONTRIBUTING.md) documentation.
142 |
143 | | Name | Role | Github | Email | GPG Fingerprint |
144 | | ----------------- | ------------------- | ------------------------------------------------- | ------------------------------------- | -------------------------------------------------- |
145 | | Wolf McNally | Project Lead | [@WolfMcNally](https://github.com/WolfMcNally) | \ | 9436 52EE 3844 1760 C3DC 3536 4B6C 2FCF 8947 80AE |
146 | | Christopher Allen | Principal Architect | [@ChristopherA](https://github.com/ChristopherA) | \ | FDFE 14A5 4ECB 30FC 5D22 74EF F8D3 6C91 3574 05ED |
147 |
148 | ## Responsible Disclosure
149 |
150 | We want to keep all of our software safe for everyone. If you have discovered a security vulnerability, we appreciate your help in disclosing it to us in a responsible manner. We are unfortunately not able to offer bug bounties at this time.
151 |
152 | We do ask that you offer us good faith and use best efforts not to leak information or harm any user, their data, or our developer community. Please give us a reasonable amount of time to fix the issue before you publish it. Do not defraud our users or us in the process of discovery. We promise not to bring legal action against researchers who point out a problem provided they do their best to follow the these guidelines.
153 |
154 | ### Reporting a Vulnerability
155 |
156 | Please report suspected security vulnerabilities in private via email to ChristopherA@BlockchainCommons.com (do not use this email for support). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
157 |
158 | The following keys may be used to communicate sensitive information to developers:
159 |
160 | | Name | Fingerprint |
161 | | ----------------- | -------------------------------------------------- |
162 | | Christopher Allen | FDFE 14A5 4ECB 30FC 5D22 74EF F8D3 6C91 3574 05ED |
163 |
164 | You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys ""` Ensure that you put quotes around fingerprints that contain spaces.
165 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/BitBuffer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BitBuffer.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 7/20/21.
9 | //
10 | // Based on: QR Code generator library (C++)
11 | // Copyright (c) Project Nayuki. (MIT License)
12 | // https://www.nayuki.io/page/qr-code-generator-library
13 |
14 | import Foundation
15 |
16 | public extension Array where Element == Bool {
17 | mutating func appendBits(_ val: T, _ len: Int) where T: BinaryInteger {
18 | precondition((0...31).contains(len) && val >> len == 0)
19 |
20 | guard len > 0 else {
21 | return
22 | }
23 |
24 | for i in (0...(len - 1)).reversed() {
25 | append(((val >> i) & 1) != 0) // Append bit by bit
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/CorrectionLevel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OSImage.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 7/23/21.
9 | //
10 |
11 | import Foundation
12 |
13 | // MARK: - Public helper enumeration
14 |
15 | public enum CorrectionLevel: Int {
16 | case low // The QR Code can tolerate about 7% erroneous codewords
17 | case medium // The QR Code can tolerate about 15% erroneous codewords
18 | case quartile // The QR Code can tolerate about 25% erroneous codewords
19 | case high // The QR Code can tolerate about 30% erroneous codewords
20 |
21 | var formatBits: Int {
22 | Self._formatBits[rawValue]
23 | }
24 |
25 | private static let _formatBits = [1, 0, 3, 2]
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/OSImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OSImage.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 12/31/20.
9 | //
10 |
11 | #if canImport(UIKit)
12 | import UIKit
13 | public typealias OSImage = UIImage
14 | public typealias OSColor = UIColor
15 | public let blackOSColor = UIColor.black
16 | public let whiteOSColor = UIColor.white
17 | #elseif canImport(AppKit)
18 | import AppKit
19 | public typealias OSImage = NSImage
20 | public typealias OSColor = NSColor
21 | public let blackOSColor = NSColor(red: 0, green: 0, blue: 0, alpha: 1)
22 | public let whiteOSColor = NSColor(red: 1, green: 1, blue: 1, alpha: 1)
23 | #endif
24 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/Private/Canvas.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Canvas.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 7/6/20.
9 | //
10 |
11 | #if canImport(AppKit) || canImport(UIKit)
12 | import Foundation
13 | import Accelerate
14 | import CoreGraphics
15 | #endif
16 |
17 | #if canImport(UIKit)
18 | import UIKit
19 | #elseif canImport(AppKit)
20 | import AppKit
21 | #endif
22 |
23 | #if canImport(AppKit) || canImport(UIKit)
24 | class Canvas {
25 | let size: IntSize
26 |
27 | private let chunkyBytesCount: Int
28 | private let planarFloatsCount: Int
29 | private let planarFloatsPerRow: Int
30 |
31 | private let argb8Data: UnsafeMutablePointer
32 | private let argb8PremultipliedData: UnsafeMutablePointer
33 | private let alphaFData: UnsafeMutablePointer
34 | private let redFData: UnsafeMutablePointer
35 | private let greenFData: UnsafeMutablePointer
36 | private let blueFData: UnsafeMutablePointer
37 |
38 | private var argb8: vImage_Buffer
39 | private var argb8Premultiplied: vImage_Buffer
40 | private var alphaF: vImage_Buffer
41 | private var redF: vImage_Buffer
42 | private var greenF: vImage_Buffer
43 | private var blueF: vImage_Buffer
44 |
45 | private var maxPixelValues: [Float] = [1, 1, 1, 1]
46 | private var minPixelValues: [Float] = [0, 0, 0, 0]
47 | private let context: CGContext
48 | private var _image: CGImage?
49 |
50 | init(size: IntSize) {
51 | self.size = size
52 |
53 | assert(size.width >= 1, "width must be >= 1")
54 | assert(size.height >= 1, "height must be >= 1")
55 |
56 | let colorSpace = CGColorSpaceCreateDeviceRGB()
57 | let componentsPerPixel = Int(colorSpace.numberOfComponents) + 1 // alpha
58 |
59 | let chunkyBytesPerComponent = 1
60 | let chunkyBitsPerComponent = chunkyBytesPerComponent * 8
61 | let chunkyBytesPerPixel = componentsPerPixel * chunkyBytesPerComponent
62 | let chunkyBytesPerRow = Int(UInt(size.width * chunkyBytesPerPixel + 15) & ~UInt(0xf))
63 | chunkyBytesCount = size.height * chunkyBytesPerRow
64 |
65 | let planarBytesPerComponent = MemoryLayout.size
66 | let planarBytesPerRow = Int(UInt(size.width * planarBytesPerComponent * componentsPerPixel + 15) & ~UInt(0xf))
67 | planarFloatsPerRow = planarBytesPerRow >> 2
68 | planarFloatsCount = size.height * planarFloatsPerRow
69 |
70 | argb8Data = UnsafeMutablePointer.allocate(capacity: chunkyBytesCount)
71 | argb8PremultipliedData = UnsafeMutablePointer.allocate(capacity: chunkyBytesCount)
72 | alphaFData = UnsafeMutablePointer.allocate(capacity: planarFloatsCount)
73 | redFData = UnsafeMutablePointer.allocate(capacity: planarFloatsCount)
74 | greenFData = UnsafeMutablePointer.allocate(capacity: planarFloatsCount)
75 | blueFData = UnsafeMutablePointer.allocate(capacity: planarFloatsCount)
76 |
77 | let uWidth = vImagePixelCount(size.width)
78 | let uHeight = vImagePixelCount(size.height)
79 |
80 | // argb8 = vImage_Buffer(data: argb8Data, height: uHeight, width: uWidth, rowBytes: chunkyBytesPerRow)
81 | argb8 = vImage_Buffer(data: argb8Data, height: vImagePixelCount(size.height), width: uWidth, rowBytes: chunkyBytesPerRow)
82 | argb8Premultiplied = vImage_Buffer(data: argb8PremultipliedData, height: uHeight, width: uWidth, rowBytes: chunkyBytesPerRow)
83 | alphaF = vImage_Buffer(data: alphaFData, height: uHeight, width: uWidth, rowBytes: planarBytesPerRow)
84 | redF = vImage_Buffer(data: redFData, height: uHeight, width: uWidth, rowBytes: planarBytesPerRow)
85 | greenF = vImage_Buffer(data: greenFData, height: uHeight, width: uWidth, rowBytes: planarBytesPerRow)
86 | blueF = vImage_Buffer(data: blueFData, height: uHeight, width: uWidth, rowBytes: planarBytesPerRow)
87 |
88 | let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
89 | context = CGContext(data: argb8PremultipliedData, width: size.width, height: size.height, bitsPerComponent: chunkyBitsPerComponent, bytesPerRow: chunkyBytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!
90 | }
91 |
92 | deinit {
93 | argb8Data.deallocate()
94 | argb8PremultipliedData.deallocate()
95 | alphaFData.deallocate()
96 | redFData.deallocate()
97 | greenFData.deallocate()
98 | blueFData.deallocate()
99 | }
100 |
101 | #if canImport(UIKit)
102 | var image: UIImage {
103 | UIImage(cgImage: cgImage)
104 | }
105 | #elseif canImport(AppKit)
106 | var image: NSImage {
107 | let size = CGSize(width: CGFloat(self.size.width), height: CGFloat(self.size.height))
108 | return NSImage(cgImage: cgImage, size: size)
109 | }
110 | #endif
111 |
112 | var cgImage: CGImage {
113 | get {
114 | if self._image == nil {
115 | var error = vImageConvert_PlanarFToARGB8888(&alphaF, &redF, &greenF, &blueF, &argb8, &maxPixelValues, &minPixelValues, UInt32(kvImageNoFlags))
116 | assert(error == kvImageNoError, "Error when converting canvas to chunky")
117 | error = vImagePremultiplyData_ARGB8888(&argb8, &argb8Premultiplied, UInt32(kvImageNoFlags))
118 | assert(error == kvImageNoError, "Error when premultiplying canvas")
119 | _image = self.context.makeImage()
120 | assert(self._image != nil, "Error when converting")
121 | }
122 | return self._image!
123 | }
124 | }
125 |
126 | func invalidateImage() {
127 | self._image = nil
128 | }
129 |
130 | private func offsetForPoint(_ point: IntPoint) -> Int {
131 | return planarFloatsPerRow * point.y + point.x
132 | }
133 |
134 | func setPoint(_ point: IntPoint, to color: OSColor) {
135 | invalidateImage()
136 |
137 | var r: CGFloat = 0
138 | var g: CGFloat = 0
139 | var b: CGFloat = 0
140 | var a: CGFloat = 0
141 | #if canImport(UIKit)
142 | let success = color.getRed(&r, green: &g, blue: &b, alpha: &a)
143 | precondition(success)
144 | #elseif canImport(AppKit)
145 | if let rgbColor = color.usingColorSpace(.deviceRGB) {
146 | rgbColor.getRed(&r, green: &g, blue: &b, alpha: &a)
147 | } else {
148 | fatalError("Failed to convert to RGB color space")
149 | }
150 | #endif
151 | let offset = offsetForPoint(point)
152 | alphaFData[offset] = Float(a)
153 | redFData[offset] = Float(r)
154 | greenFData[offset] = Float(g)
155 | blueFData[offset] = Float(b)
156 | }
157 | }
158 | #endif
159 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/Private/IntPoint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IntPoint.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 7/6/20.
9 | //
10 |
11 | import Foundation
12 |
13 | struct IntPoint {
14 | var x: Int
15 | var y: Int
16 |
17 | static let zero = IntPoint(x: 0, y: 0)
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/Private/IntSize.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IntSize.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 7/6/20.
9 | //
10 |
11 | import Foundation
12 |
13 | struct IntSize {
14 | var width: Int
15 | var height: Int
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRCode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // QRCode.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 7/20/21.
9 | //
10 | // Based on: QR Code generator library (C++)
11 | // Copyright (c) Project Nayuki. (MIT License)
12 | // https://www.nayuki.io/page/qr-code-generator-library
13 | //
14 |
15 | import Foundation
16 |
17 | #if canImport(AppKit)
18 | import AppKit
19 | #elseif canImport(UIKit)
20 | import UIKit
21 | #endif
22 |
23 | #if canImport(SwiftUI)
24 | import SwiftUI
25 | #endif
26 |
27 | /**
28 | * A QR Code symbol, which is a type of two-dimension barcode.
29 | * Invented by Denso Wave and described in the ISO/IEC 18004 standard.
30 | * Instances of this class represent an immutable square grid of black and white cells.
31 | * The class provides static factory functions to create a QR Code from text or binary data.
32 | * The class covers the QR Code Model 2 specification, supporting all versions (sizes)
33 | * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
34 | *
35 | * Ways to create a QR Code object:
36 | * - High level: Take the payload data and call QRCode.encodeText() or QRCode.encodeBinary().
37 | * - Mid level: Custom-make the list of segments and call QRCode.encodeSegments().
38 | * - Low level: Custom-make the array of data codeword bytes (including
39 | * segment headers and final padding, excluding error correction codewords),
40 | * supply the appropriate version number, and call the QRCode() constructor.
41 | * (Note that all ways require supplying the desired error correction level.)
42 | */
43 | public struct QRCode {
44 | // MARK: - Instance fields
45 |
46 | // Immutable scalar parameters:
47 |
48 | /// The version number of this QR Code, which is between 1 and 40 (inclusive). This determines the size of this barcode.
49 | public let version: Int
50 |
51 | /// The width and height of this QR Code, measured in modules, between 21 and 177 (inclusive). This is equal to version * 4 + 17.
52 | public let size: Int
53 |
54 | /// The error correction level used in this QR Code.
55 | public let correctionLevel: CorrectionLevel
56 |
57 | /// The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). Even if a QR Code is created with automatic masking requested (mask = nil), the resulting object still has a mask value between 0 and 7.
58 | public private(set) var mask: Int?
59 |
60 | /// The modules of this QR Code (false = white, true = black).
61 | public subscript(_ x: Int, _ y: Int) -> Bool {
62 | return 0 <= x && x < size && 0 <= y && y < size && module(x, y)
63 | }
64 |
65 | // MARK: - Private grids of modules/pixels, with dimensions of size*size
66 |
67 | /// The modules of this QR Code (false = white, true = black).
68 | /// Immutable after constructor finishes. Accessed through subscript(x, y).
69 | private var modules: [[Bool]]
70 |
71 | /// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
72 | private var isFunction: [[Bool]]
73 |
74 | // MARK: - Output functions
75 |
76 | public var emojiString: String {
77 | var lines: [String] = []
78 | for y in 0 ..< size {
79 | var s = ""
80 | for x in 0 ..< size {
81 | s.append(self[x, y] ? "⬛️" : "⬜️")
82 | }
83 | lines.append(s)
84 | }
85 | return lines.joined(separator: "\n")
86 | }
87 |
88 | #if canImport(AppKit) || canImport(UIKit)
89 | public func makeImage(border: Int = 3, moduleSize: Int = 1, foregroundColor: OSColor = blackOSColor, backgroundColor: OSColor = whiteOSColor) -> OSImage {
90 | let fullSize = size + 2 * border
91 | let scaledSize = fullSize * moduleSize
92 | let canvas = Canvas(size: IntSize(width: scaledSize, height: scaledSize))
93 | for y in 0 ..< fullSize {
94 | for x in 0 ..< fullSize {
95 | let color = self[x - border, y - border] ? foregroundColor : backgroundColor
96 | for py in 0 ..< moduleSize {
97 | for px in 0 ..< moduleSize {
98 | canvas.setPoint(IntPoint(x: x * moduleSize + px, y: y * moduleSize + py), to: color)
99 | }
100 | }
101 | }
102 | }
103 | return canvas.image
104 | }
105 |
106 | #if canImport(SwiftUI)
107 | #if canImport(UIKit)
108 | @available(iOS 13.0, *)
109 | public func image(border: Int = 3, moduleSize: Int = 1, foregroundColor: OSColor = blackOSColor, backgroundColor: OSColor = whiteOSColor) -> Image {
110 | Image(uiImage: makeImage(border: border, moduleSize: moduleSize, foregroundColor: foregroundColor, backgroundColor: backgroundColor)).interpolation(.none)
111 | }
112 | #elseif canImport(AppKit)
113 | @available(macOS 10.15, *)
114 | public func image(border: Int = 3, moduleSize: Int = 1, foregroundColor: OSColor = blackOSColor, backgroundColor: OSColor = whiteOSColor) -> Image {
115 | Image(nsImage: makeImage(border: border, moduleSize: moduleSize, foregroundColor: foregroundColor, backgroundColor: backgroundColor)).interpolation(.none)
116 | }
117 | #endif
118 | #endif
119 |
120 | #endif
121 |
122 | // MARK: - Static factory functions (high level)
123 |
124 | /**
125 | * Returns a QR Code representing the given Unicode text string at the given error correction level.
126 | * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer
127 | * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible
128 | * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than
129 | * the ecl argument if it can be done without increasing the version.
130 | */
131 | public static func encode(text: String, correctionLevel: CorrectionLevel = .medium, minVersion: Int = 1, maxVersion: Int = 40, mask: Int? = nil, boostEcl: Bool = true, optimize: Bool = false) throws -> QRCode {
132 | let segments = try Segment.makeText(text: text, correctionLevel: correctionLevel, minVersion: minVersion, maxVersion: maxVersion, optimize: optimize)
133 | return try encode(segments: segments, correctionLevel: correctionLevel, minVersion: minVersion, maxVersion: maxVersion, mask: mask, boostEcl: boostEcl)
134 | }
135 |
136 | public static func getInfo(text: String, correctionLevel: CorrectionLevel = .medium, minVersion: Int = 1, maxVersion: Int = 40, optimize: Bool = false) throws -> Info {
137 | let segments = try Segment.makeText(text: text, correctionLevel: correctionLevel, minVersion: minVersion, maxVersion: maxVersion, optimize: optimize)
138 | return try Self.getInfo(segments: segments)
139 | }
140 |
141 | /**
142 | * Returns a QR Code representing the given binary data at the given error correction level.
143 | * This function always encodes using the binary segment mode, not any text mode. The maximum number of
144 | * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
145 | * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.
146 | */
147 | public static func encode(data: [UInt8], correctionLevel: CorrectionLevel = .medium, minVersion: Int = 1, maxVersion: Int = 40, mask: Int? = nil, boostEcl: Bool = true) throws -> QRCode {
148 | let segment = try Segment.makeBytes(data: data)
149 | return try encode(segments: [segment], correctionLevel: correctionLevel, minVersion: minVersion, maxVersion: maxVersion, mask: mask, boostEcl: boostEcl)
150 | }
151 |
152 | public static func getInfo(data: [UInt8], correctionLevel: CorrectionLevel = .medium, minVersion: Int = 1, maxVersion: Int = 40) throws -> Info {
153 | let segment = try Segment.makeBytes(data: data)
154 | return try Self.getInfo(segments: [segment])
155 | }
156 |
157 | // MARK: - Static factory functions (mid level)
158 |
159 | public static func dataCapacityBits(_ version: Int, _ correctionLevel: CorrectionLevel = .medium) -> Int {
160 | getNumDataCodewords(version, correctionLevel) * 8
161 | }
162 |
163 | public struct Info {
164 | public let version: Int
165 | public let size: Int
166 | public let correctionLevel: CorrectionLevel
167 | public let dataUsedBits: Int
168 | public let dataCapacityBits: Int
169 | }
170 |
171 | public static func getInfo(segments: [Segment], correctionLevel: CorrectionLevel = .medium, minVersion: Int = 1, maxVersion: Int = 40) throws -> Info {
172 | try checkVersion(minVersion: minVersion, maxVersion: maxVersion)
173 |
174 | // Find the minimal version number to use
175 | var version = minVersion
176 | var capacityBits = -1
177 | var usedBits = -1
178 | while true {
179 | capacityBits = Self.dataCapacityBits(version, correctionLevel) // Number of data bits available
180 | usedBits = Segment.getTotalBits(segments, version)
181 | if usedBits != -1 && usedBits <= capacityBits {
182 | break // This version number is found to be suitable
183 | }
184 | if version >= maxVersion { // All versions in the range could not fit the given data
185 | throw QRError.dataTooLong(usedBits, capacityBits)
186 | }
187 |
188 | version += 1
189 | }
190 | precondition(usedBits != -1)
191 |
192 | return Info(version: version, size: version * 4 + 17, correctionLevel: correctionLevel, dataUsedBits: usedBits, dataCapacityBits: capacityBits)
193 | }
194 |
195 | /**
196 | * Returns a QR Code representing the given segments with the given encoding parameters.
197 | * The smallest possible QR Code version within the given range is automatically
198 | * chosen for the output. Iff boostEcl is true, then the ECC level of the result
199 | * may be higher than the ecl argument if it can be done without increasing the
200 | * version. The mask number is either between 0 to 7 (inclusive) to force that
201 | * mask, or `nil` to automatically choose an appropriate mask (which may be slow).
202 | * This function allows the user to create a custom sequence of segments that switches
203 | * between modes (such as alphanumeric and byte) to encode text in less space.
204 | * This is a mid-level API; the high-level API is encodeText() and encodeBinary().
205 | */
206 | public static func encode(segments: [Segment], correctionLevel: CorrectionLevel = .medium, minVersion: Int = 1, maxVersion: Int = 40, mask: Int? = nil, boostEcl: Bool = true) throws -> QRCode {
207 | let info = try Self.getInfo(segments: segments, correctionLevel: correctionLevel, minVersion: minVersion, maxVersion: maxVersion)
208 |
209 | let dataUsedBits = info.dataUsedBits
210 | let version = info.version
211 |
212 | // Increase the error correction level while the data still fits in the current version number
213 | var ecl = correctionLevel
214 | for newEcl in [CorrectionLevel.medium, CorrectionLevel.quartile, CorrectionLevel.high] { // From low to high
215 | if boostEcl && dataUsedBits <= Self.dataCapacityBits(version, newEcl) {
216 | ecl = newEcl
217 | }
218 | }
219 |
220 | // Concatenate all segments to create the data bit string
221 | var bb: [Bool] = []
222 | for seg in segments {
223 | bb.appendBits(seg.mode.modeBits, 4)
224 | bb.appendBits(seg.characterCount, seg.mode.numCharCountBits(version))
225 | bb.append(contentsOf: seg.data)
226 | }
227 | precondition(bb.count == dataUsedBits)
228 |
229 | // Add terminator and pad up to a byte if applicable
230 | let dataCapacityBits = Self.dataCapacityBits(version, ecl)
231 | precondition(bb.count <= dataCapacityBits)
232 | bb.appendBits(0, min(4, dataCapacityBits - bb.count))
233 | bb.appendBits(0, (8 - bb.count % 8) % 8)
234 | precondition(bb.count % 8 == 0)
235 |
236 | // Pad with alternating bytes until data capacity is reached
237 | var padByte = 0xEC
238 | while bb.count < dataCapacityBits {
239 | bb.appendBits(padByte, 8)
240 | padByte ^= 0xEC ^ 0x11
241 | }
242 |
243 | // Pack bits into bytes in big endian
244 | var dataCodewords = [UInt8](repeating: 0, count: bb.count / 8)
245 | for i in 0 ..< bb.count {
246 | dataCodewords[i >> 3] |= (bb[i] ? 1 : 0) << (7 - (i & 7))
247 | }
248 |
249 | // Create the QR Code object
250 | return try QRCode(version: version, correctionLevel: ecl, dataCodewords: dataCodewords, mask: mask)
251 | }
252 |
253 | static func checkVersion(minVersion: Int, maxVersion: Int) throws {
254 | guard [minVersion, maxVersion].allSatisfy({ (Self.minVersion...Self.maxVersion).contains($0) }) else {
255 | throw QRError.invalidVersion
256 | }
257 | }
258 |
259 | // MARK: - Constructor (low level)
260 |
261 | /**
262 | * Creates a new QR Code with the given version number,
263 | * error correction level, data codeword bytes, and mask number.
264 | * This is a low-level API that most users should not use directly.
265 | * A mid-level API is the encodeSegments() function.
266 | */
267 | public init(version: Int, correctionLevel: CorrectionLevel = .medium, dataCodewords: [UInt8], mask: Int?) throws {
268 | self.version = version
269 | self.correctionLevel = correctionLevel
270 | self.mask = nil
271 |
272 | guard (Self.minVersion...Self.maxVersion).contains(version) else {
273 | throw QRError.invalidVersion
274 | }
275 | guard mask == nil || (0...7).contains(mask!) else {
276 | throw QRError.invalidMask
277 | }
278 | self.size = version * 4 + 17
279 | self.modules = [[Bool]](repeating: [Bool](repeating: false, count: size), count: size) // Initially all white
280 | self.isFunction = [[Bool]](repeating: [Bool](repeating: false, count: size), count: size)
281 |
282 | // Compute ECC, draw modules
283 | drawFunctionPatterns()
284 | let allCodewords = addEccAndInterleave(dataCodewords)
285 | drawCodewords(allCodewords)
286 |
287 | // Do masking
288 | var mask: Int! = mask
289 | if mask == nil { // Automatically choose best mask
290 | var minPenalty = Int.max
291 | for i in 0 ..< 8 {
292 | applyMask(i)
293 | drawFormatBits(i)
294 | let penalty = getPenaltyScore()
295 | if penalty < minPenalty {
296 | mask = i
297 | minPenalty = penalty
298 | }
299 | applyMask(i) // Undoes the mask due to XOR
300 | }
301 | }
302 | precondition((0...7).contains(mask))
303 | self.mask = mask
304 | applyMask(mask) // Apply the final choice of mask
305 | drawFormatBits(mask) // Overwrite old format bits
306 |
307 | isFunction.removeAll()
308 | }
309 |
310 | // MARK: - Private helper methods for constructor: Drawing function modules
311 |
312 | /// Reads this object's version field, and draws and marks all function modules.
313 | private mutating func drawFunctionPatterns() {
314 | // Draw horizontal and vertical timing patterns
315 | for i in 0 ..< size {
316 | setFunctionModule(6, i, i % 2 == 0)
317 | setFunctionModule(i, 6, i % 2 == 0)
318 | }
319 |
320 | // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
321 | drawFinderPattern(3, 3)
322 | drawFinderPattern(size - 4, 3)
323 | drawFinderPattern(3, size - 4)
324 |
325 | // Draw numerous alignment patterns
326 | let alignPatPos = getAlignmentPatternPositions()
327 | let numAlign = alignPatPos.count
328 | for i in 0 ..< numAlign {
329 | for j in 0 ..< numAlign {
330 | // Don't draw on the three finder corners
331 | if !((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) {
332 | drawAlignmentPattern(alignPatPos[i], alignPatPos[j])
333 | }
334 | }
335 | }
336 |
337 | // Draw configuration data
338 | drawFormatBits(0) // Dummy mask value; overwritten later in the constructor
339 | drawVersion()
340 | }
341 |
342 | /// Draws two copies of the format bits (with its own error correction code)
343 | /// based on the given mask and this object's error correction level field.
344 | private mutating func drawFormatBits(_ msk: Int) {
345 | // Calculate error correction code and pack bits
346 | let data = correctionLevel.formatBits << 3 | msk // errCorrLvl is uint2, msk is uint3
347 | var rem = data
348 | for _ in 0 ..< 10 {
349 | rem = (rem << 1) ^ ((rem >> 9) * 0x537)
350 | }
351 | let bits = (data << 10 | rem) ^ 0x5412 // uint15
352 | precondition(bits >> 15 == 0)
353 |
354 | // Draw first copy
355 | for i in 0...5 {
356 | setFunctionModule(8, i, Self.getBit(bits, i))
357 | }
358 | setFunctionModule(8, 7, Self.getBit(bits, 6))
359 | setFunctionModule(8, 8, Self.getBit(bits, 7))
360 | setFunctionModule(7, 8, Self.getBit(bits, 8))
361 | for i in 9..<15 {
362 | setFunctionModule(14 - i, 8, Self.getBit(bits, i))
363 | }
364 |
365 | // Draw second copy
366 | for i in 0 ..< 8 {
367 | setFunctionModule(size - 1 - i, 8, Self.getBit(bits, i))
368 | }
369 | for i in 8 ..< 15 {
370 | setFunctionModule(8, size - 15 + i, Self.getBit(bits, i))
371 | }
372 | setFunctionModule(8, size - 8, true) // Always black
373 | }
374 |
375 |
376 | /// Draws two copies of the version bits (with its own error correction code),
377 | /// based on this object's version field, iff 7 <= version <= 40.
378 | private mutating func drawVersion() {
379 | guard version >= 7 else {
380 | return
381 | }
382 |
383 | // Calculate error correction code and pack bits
384 | var rem = version // version is uint6, in the range [7, 40]
385 | for _ in 0 ..< 12 {
386 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25)
387 | }
388 | let bits = version << 12 | rem // uint18
389 | precondition(bits >> 18 == 0)
390 |
391 | // Draw two copies
392 | for i in 0 ..< 18 {
393 | let bit = Self.getBit(bits, i)
394 | let a = size - 11 + i % 3
395 | let b = i / 3
396 | setFunctionModule(a, b, bit)
397 | setFunctionModule(b, a, bit)
398 | }
399 | }
400 |
401 | /// Draws a 9*9 finder pattern including the border separator,
402 | /// with the center module at (x, y). Modules can be out of bounds.
403 | private mutating func drawFinderPattern(_ x: Int, _ y: Int) {
404 | for dy in -4...4 {
405 | for dx in -4...4 {
406 | let dist = max(abs(dx), abs(dy)) // Chebyshev/infinity norm
407 | let xx = x + dx
408 | let yy = y + dy
409 | if 0 <= xx && xx < size && 0 <= yy && yy < size {
410 | setFunctionModule(xx, yy, dist != 2 && dist != 4)
411 | }
412 | }
413 | }
414 | }
415 |
416 | /// Draws a 5*5 alignment pattern, with the center module
417 | /// at (x, y). All modules must be in bounds.
418 | private mutating func drawAlignmentPattern(_ x: Int, _ y: Int) {
419 | for dy in -2...2 {
420 | for dx in -2...2 {
421 | setFunctionModule(x + dx, y + dy, max(abs(dx), abs(dy)) != 1)
422 | }
423 | }
424 | }
425 |
426 | /// Sets the color of a module and marks it as a function module.
427 | /// Only used by the constructor. Coordinates must be in bounds.
428 | private mutating func setFunctionModule(_ x: Int, _ y: Int, _ isBlack: Bool) {
429 | modules[y][x] = isBlack
430 | isFunction[y][x] = true
431 | }
432 |
433 | /// Returns the color of the module at the given coordinates, which must be in range.
434 | private func module(_ x: Int, _ y: Int) -> Bool {
435 | modules[y][x]
436 | }
437 |
438 | // MARK: - Private helper methods for constructor: Codewords and masking
439 |
440 | /// Returns a new byte string representing the given data with the appropriate error correction
441 | /// codewords appended to it, based on this object's version and error correction level.
442 | private func addEccAndInterleave(_ data: [UInt8]) -> [UInt8] {
443 | let codeWords = Self.getNumDataCodewords(version, correctionLevel)
444 | precondition(data.count == codeWords)
445 |
446 | // Calculate parameter numbers
447 | let numBlocks = Self.numErrorCorrectionBlocks[correctionLevel.rawValue][version]
448 | let blockEccLen = Self.eccCodewordsPerBlock[correctionLevel.rawValue][version]
449 | let rawCodewords = Self.getNumRawDataModules(version) / 8
450 | let numShortBlocks = numBlocks - rawCodewords % numBlocks
451 | let shortBlockLen = rawCodewords / numBlocks
452 |
453 | // Split data into blocks and append ECC to each block
454 | var blocks = [[UInt8]]()
455 | let rsDiv = Self.reedSolomonComputeDivisor(blockEccLen)
456 | var k = 0
457 | for i in 0 ..< numBlocks {
458 | var dat = Array(data[k..<(k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))])
459 | k += dat.count
460 | let ecc = Self.reedSolomonComputeRemainder(dat, rsDiv)
461 | if i < numShortBlocks {
462 | dat.append(0)
463 | }
464 | dat.append(contentsOf: ecc)
465 | blocks.append(dat)
466 | }
467 |
468 | // Interleave (not concatenate) the bytes from every block into a single sequence
469 | var result: [UInt8] = []
470 | for i in 0 ..< blocks[0].count {
471 | for j in 0 ..< blocks.count {
472 | // Skip the padding byte in short blocks
473 | if i != (shortBlockLen - blockEccLen) || j >= numShortBlocks {
474 | result.append(blocks[j][i])
475 | }
476 | }
477 | }
478 | precondition(result.count == rawCodewords)
479 | return result
480 | }
481 |
482 | /// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
483 | /// data area of this QR Code. Function modules need to be marked off before this is called.
484 | private mutating func drawCodewords(_ data: [UInt8]) {
485 | precondition(data.count == Self.getNumRawDataModules(version) / 8)
486 |
487 | var i = 0 // Bit index into the data
488 | // Do the funny zigzag scan
489 | var right = size - 1
490 | while right >= 1 { // Index of right column in each column pair}
491 | if right == 6 {
492 | right = 5
493 | }
494 | for vert in 0 ..< size { // Vertical counter
495 | for j in 0 ..< 2 {
496 | let x = right - j // Actual x coordinate
497 | let upward = ((right + 1) & 2) == 0
498 | let y = upward ? size - 1 - vert : vert // Actual y coordinate
499 | if !isFunction[y][x] && i < data.count * 8 {
500 | modules[y][x] = Self.getBit(Int(data[i >> 3]), 7 - (i & 7))
501 | i += 1
502 | }
503 | // If this QR Code has any remainder bits (0 to 7), they were assigned as
504 | // 0/false/white by the constructor and are left unchanged by this method
505 | }
506 | }
507 | right -= 2
508 | }
509 | precondition(i == data.count * 8)
510 | }
511 |
512 | /// XORs the codeword modules in this QR Code with the given mask pattern.
513 | /// The function modules must be marked and the codeword bits must be drawn
514 | /// before masking. Due to the arithmetic of XOR, calling applyMask() with
515 | /// the same mask value a second time will undo the mask. A final well-formed
516 | /// QR Code needs exactly one (not zero, two, etc.) mask applied.
517 | private mutating func applyMask(_ msk: Int) {
518 | precondition((0...7).contains(msk))
519 | for y in 0 ..< size {
520 | for x in 0 ..< size {
521 | var invert: Bool
522 | switch msk {
523 | case 0:
524 | invert = (x + y) % 2 == 0
525 | case 1:
526 | invert = y % 2 == 0
527 | case 2:
528 | invert = x % 3 == 0
529 | case 3:
530 | invert = (x + y) % 3 == 0
531 | case 4:
532 | invert = (x / 3 + y / 2) % 2 == 0
533 | case 5:
534 | invert = x * y % 2 + x * y % 3 == 0
535 | case 6:
536 | invert = (x * y % 2 + x * y % 3) % 2 == 0
537 | case 7:
538 | invert = ((x + y) % 2 + x * y % 3) % 2 == 0
539 | default:
540 | fatalError()
541 | }
542 | modules[y][x] = modules[y][x] != (invert && !isFunction[y][x])
543 | }
544 | }
545 | }
546 |
547 | /// Calculates and returns the penalty score based on state of this QR Code's current modules.
548 | /// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
549 | private func getPenaltyScore() -> Int {
550 | var result = 0
551 |
552 | // Adjacent modules in row having same color, and finder-like patterns
553 | for y in 0 ..< size {
554 | var runColor = false
555 | var runX = 0
556 | var runHistory = [Int](repeating: 0, count: 7)
557 | for x in 0 ..< size {
558 | if module(x, y) == runColor {
559 | runX += 1
560 | if runX == 5 {
561 | result += Self.penaltyN1
562 | } else if runX > 5 {
563 | result += 1
564 | }
565 | } else {
566 | finderPenaltyAddHistory(runX, &runHistory)
567 | if !runColor {
568 | result += finderPenaltyCountPatterns(&runHistory) * Self.penaltyN3
569 | }
570 | runColor = module(x, y)
571 | runX = 1
572 | }
573 | }
574 | result += finderPenaltyTerminateAndCount(runColor, runX, &runHistory) * Self.penaltyN3
575 | }
576 | // Adjacent modules in column having same color, and finder-like patterns
577 | for x in 0 ..< size {
578 | var runColor = false
579 | var runY = 0
580 | var runHistory = [Int](repeating: 0, count: 7)
581 | for y in 0 ..< size {
582 | if module(x, y) == runColor {
583 | runY += 1
584 | if runY == 5 {
585 | result += Self.penaltyN1
586 | } else if runY > 5 {
587 | result += 1
588 | }
589 | } else {
590 | finderPenaltyAddHistory(runY, &runHistory)
591 | if !runColor {
592 | result += finderPenaltyCountPatterns(&runHistory) * Self.penaltyN3
593 | }
594 | runColor = module(x, y)
595 | runY = 1
596 | }
597 | }
598 | result += finderPenaltyTerminateAndCount(runColor, runY, &runHistory) * Self.penaltyN3
599 | }
600 |
601 | // 2*2 blocks of modules having same color
602 | for y in 0 ..< size - 1 {
603 | for x in 0 ..< size - 1 {
604 | let color = module(x, y)
605 | if color == module(x + 1, y) &&
606 | color == module(x, y + 1) &&
607 | color == module(x + 1, y + 1) {
608 | result += Self.penaltyN2
609 | }
610 | }
611 | }
612 |
613 | // Balance of black and white modules
614 | var black = 0
615 | for row in modules {
616 | for color in row {
617 | if color {
618 | black += 1
619 | }
620 | }
621 | }
622 | let total = size * size // Note that size is odd, so black/total != 1/2
623 | // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)%
624 | let k = ((abs(black * 20 - total * 10) + total - 1) / total) - 1
625 | result += k * Self.penaltyN4
626 | return result
627 | }
628 |
629 | // MARK: - Private helper functions
630 |
631 | /// Returns an ascending list of positions of alignment patterns for this version number.
632 | /// Each position is in the range [0,177), and are used on both the x and y axes.
633 | /// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
634 | private func getAlignmentPatternPositions() -> [Int] {
635 | guard version > 1 else {
636 | return []
637 | }
638 |
639 | let numAlign = version / 7 + 2
640 | let step = (version == 32) ? 26 :
641 | (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2
642 | var result: [Int] = []
643 | var pos = size - 7
644 | for _ in 0 ..< (numAlign - 1) {
645 | result.insert(pos, at: 0)
646 | pos -= step
647 | }
648 | result.insert(6, at: 0)
649 | return result
650 | }
651 |
652 | /// Returns the number of data bits that can be stored in a QR Code of the given version number, after
653 | /// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
654 | /// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
655 | private static func getNumRawDataModules(_ ver: Int) -> Int {
656 | precondition((minVersion...maxVersion).contains(ver))
657 | var result = (16 * ver + 128) * ver + 64
658 | if ver >= 2 {
659 | let numAlign = ver / 7 + 2
660 | result -= (25 * numAlign - 10) * numAlign - 55
661 | if ver >= 7 {
662 | result -= 36
663 | }
664 | }
665 | precondition((208...29648).contains(result))
666 | return result
667 | }
668 |
669 | /// Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
670 | /// QR Code of the given version number and error correction level, with remainder bits discarded.
671 | /// This stateless pure function could be implemented as a (40*4)-cell lookup table.
672 | static func getNumDataCodewords(_ ver: Int, _ ecl: CorrectionLevel) -> Int {
673 | getNumRawDataModules(ver) / 8
674 | - eccCodewordsPerBlock[ecl.rawValue][ver]
675 | * numErrorCorrectionBlocks[ecl.rawValue][ver]
676 | }
677 |
678 | /// Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be
679 | /// implemented as a lookup table over all possible parameter values, instead of as an algorithm.
680 | private static func reedSolomonComputeDivisor(_ degree: Int) -> [UInt8] {
681 | precondition((1...255).contains(degree))
682 | // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
683 | // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
684 | var result = [UInt8](repeating: 0, count: degree)
685 | result[result.count - 1] = 1 // Start off with the monomial x^0
686 |
687 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
688 | // and drop the highest monomial term which is always 1x^degree.
689 | // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
690 | var root: UInt8 = 1
691 | for _ in 0 ..< degree {
692 | // Multiply the current product by (x - r^i)
693 | for j in 0.. [UInt8] {
706 | var result = [UInt8](repeating: 0, count: divisor.count)
707 | for b in data { // Polynomial division
708 | let factor = b ^ result[0]
709 | result.removeFirst()
710 | result.append(0)
711 | for i in 0.. UInt8 {
721 | // Russian peasant multiplication
722 | var z = 0
723 | for i in (0...7).reversed() {
724 | z = (z << 1) ^ ((z >> 7) * 0x11D)
725 | z ^= ((Int(y) >> i) & 1) * Int(x)
726 | }
727 | precondition(z >> 8 == 0)
728 | return UInt8(z)
729 | }
730 |
731 | /// Can only be called immediately after a white run is added, and
732 | /// returns either 0, 1, or 2. A helper function for getPenaltyScore().
733 | private func finderPenaltyCountPatterns(_ runHistory: inout [Int]) -> Int {
734 | let n = runHistory[1]
735 | precondition(n <= size * 3)
736 | let core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n
737 | return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)
738 | + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0)
739 | }
740 |
741 | /// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
742 | private func finderPenaltyTerminateAndCount(_ currentRunColor: Bool, _ currentRunLength: Int, _ runHistory: inout [Int]) -> Int {
743 | var currentRunLength = currentRunLength
744 | if currentRunColor { // Terminate black run
745 | finderPenaltyAddHistory(currentRunLength, &runHistory)
746 | currentRunLength = 0
747 | }
748 | currentRunLength += size // Add white border to final run
749 | finderPenaltyAddHistory(currentRunLength, &runHistory)
750 | return finderPenaltyCountPatterns(&runHistory)
751 | }
752 |
753 | /// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
754 | private func finderPenaltyAddHistory(_ currentRunLength: Int, _ runHistory: inout [Int]) {
755 | var currentRunLength = currentRunLength
756 | if runHistory[0] == 0 {
757 | currentRunLength += size // Add white border to initial run
758 | }
759 |
760 | // runHistory.insert(currentRunLength, at: 0)
761 | // runHistory.removeLast()
762 |
763 | for i in (0.. Bool {
771 | ((x >> i) & 1) != 0
772 | }
773 |
774 | // MARK: - Constants and tables
775 |
776 | // The minimum version number supported in the QR Code Model 2 standard.
777 | public static let minVersion = 1
778 |
779 | // The maximum version number supported in the QR Code Model 2 standard.
780 | public static let maxVersion = 40
781 |
782 | private static let penaltyN1 = 3
783 | private static let penaltyN2 = 3
784 | private static let penaltyN3 = 40
785 | private static let penaltyN4 = 10
786 |
787 | private static let eccCodewordsPerBlock = [
788 | // Version: (note that index 0 is for padding, and is set to an illegal value)
789 | //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
790 | [-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Low
791 | [-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28], // Medium
792 | [-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Quartile
793 | [-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // High
794 | ]
795 |
796 | private static let numErrorCorrectionBlocks = [
797 | // Version: (note that index 0 is for padding, and is set to an illegal value)
798 | //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
799 | [-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], // Low
800 | [-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], // Medium
801 | [-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], // Quartile
802 | [-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81], // High
803 | ]
804 | }
805 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // QRError.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 7/20/21.
9 | //
10 | // Based on: QR Code generator library (C++)
11 | // Copyright (c) Project Nayuki. (MIT License)
12 | // https://www.nayuki.io/page/qr-code-generator-library
13 | //
14 |
15 | import Foundation
16 |
17 | public enum QRError: LocalizedError {
18 | case invalidVersion
19 | case invalidMask
20 | case invalidNumericString
21 | case invalidAlphanumericString
22 | case invalidKanjiString
23 | case invalidECIDesignator
24 | case invalidCharacterCount
25 | case dataTooLong(Int, Int)
26 |
27 | public var errorDescription: String? {
28 | switch self {
29 | case .invalidVersion:
30 | return "Invalid version."
31 | case .invalidMask:
32 | return "Invalid mask."
33 | case .invalidNumericString:
34 | return "String contains non-numeric characters."
35 | case .invalidAlphanumericString:
36 | return "String contains unencodable characters in alphanumeric mode."
37 | case .invalidKanjiString:
38 | return "String contains non-kanji-mode characters."
39 | case .invalidECIDesignator:
40 | return "Invalid ECI designator."
41 | case .invalidCharacterCount:
42 | return "Invalid number of characters."
43 | case .dataTooLong(let dataUsedBits, let dataCapacityBits):
44 | var sb = "Data too long."
45 | if dataUsedBits != -1 {
46 | sb += " Data length = \(dataUsedBits) bits, Max capacity = \(dataCapacityBits) bits."
47 | }
48 | return sb
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/Segment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Segment.swift
3 | // QRCodeGenerator
4 | //
5 | // Copyright © 2021 by Blockchain Commons, LLC
6 | // Licensed under the "BSD-2-Clause Plus Patent License"
7 | //
8 | // Created by Wolf McNally on 7/20/21.
9 | //
10 | // Based on: QR Code generator library (C++)
11 | // Copyright (c) Project Nayuki. (MIT License)
12 | // https://www.nayuki.io/page/qr-code-generator-library
13 | //
14 |
15 | import Foundation
16 |
17 | /**
18 | A segment of character/binary/control data in a QR Code symbol.
19 | Instances of this class are immutable.
20 | The mid-level way to create a segment is to take the payload data
21 | and call a static factory function such as QrSegment::makeNumeric().
22 | The low-level way to create a segment is to custom-make the bit buffer
23 | and call the QrSegment() constructor with appropriate values.
24 | This segment class imposes no length restrictions, but QR Codes have restrictions.
25 | Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
26 | Any segment longer than this is meaningless for the purpose of generating QR Codes.
27 | */
28 | public struct Segment {
29 | // MARK: - Instance fields
30 |
31 | /// The mode indicator of this segment.
32 | public let mode: Mode
33 |
34 | /** The length of this segment's unencoded data. Measured in characters for
35 | * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
36 | * Always zero or positive. Not the same as the data's bit length.
37 | */
38 | public let characterCount: Int
39 |
40 | /// The data bits of this segment.
41 | public let data: [Bool]
42 |
43 | // MARK: - Public helper enumeration
44 |
45 | /// Describes how a segment's data bits are interpreted. Immutable.
46 | public enum Mode: Int {
47 | case numeric
48 | case alphanumeric
49 | case byte
50 | case kanji
51 | case eci
52 |
53 | /// The mode indicator bits, which is a uint4 value (range 0 to 15).
54 | public var modeBits: Int {
55 | Self._modeBits[rawValue]
56 | }
57 |
58 | /// Returns the bit width of the character count field for a segment in this mode in a QR Code at the given version number. The result is in the range [0, 16].
59 | public func numCharCountBits(_ ver: Int) -> Int {
60 | Self._numCharCountBits[rawValue][(ver + 7) / 17]
61 | }
62 |
63 | private static let _modeBits = [
64 | 1, // numeric
65 | 2, // alphanumeric
66 | 4, // byte
67 | 8, // kanji
68 | 7 // eci
69 | ]
70 |
71 | private static let _numCharCountBits = [
72 | [10, 12, 14], // numeric
73 | [ 9, 11, 13], // alphanumeric
74 | [ 8, 16, 16], // byte
75 | [ 8, 10, 12], // kanji
76 | [ 0, 0, 0] // eci
77 | ]
78 | }
79 |
80 | // MARK: - Static factory functions (mid level)
81 |
82 | /**
83 | * Returns a segment representing the given binary data encoded in
84 | * byte mode. All input byte vectors are acceptable. Any text string
85 | * can be converted to UTF-8 bytes and encoded as a byte mode segment.
86 | */
87 | public static func makeBytes(data: [UInt8]) throws -> Segment {
88 | var bb: [Bool] = []
89 | for b in data {
90 | bb.appendBits(b, 8)
91 | }
92 | return try Segment(mode: .byte, characterCount: data.count, data: bb)
93 | }
94 |
95 | public static func makeBytes(data: Data) throws -> Segment {
96 | return try makeBytes(data: Array(data))
97 | }
98 |
99 | public static func makeBytes(data: String) throws -> Segment {
100 | return try makeBytes(data: data.data(using: .utf8)!)
101 | }
102 |
103 | public static func makeText(text: String, correctionLevel: CorrectionLevel = .medium, minVersion: Int = 1, maxVersion: Int = 40, optimize: Bool = false) throws -> [Segment] {
104 | let segments: [Segment]
105 | if optimize {
106 | segments = try Segment.makeSegmentsOptimally(text: text, correctionLevel: correctionLevel, minVersion: minVersion, maxVersion: maxVersion)
107 | } else {
108 | segments = try Segment.makeSegments(text: text)
109 | }
110 | return segments
111 | }
112 |
113 | /**
114 | * Returns a segment representing the given string of decimal digits encoded in numeric mode.
115 | */
116 | public static func makeNumeric(digits: String) throws -> Segment {
117 | var bb: [Bool] = []
118 | var accumData: UInt = 0
119 | var accumCount = 0
120 | for c in digits.utf8 {
121 | guard isNumeric(c) else {
122 | throw QRError.invalidNumericString
123 | }
124 | accumData = accumData * 10 + UInt(c - ascii0)
125 | accumCount += 1
126 | if accumCount == 3 {
127 | bb.appendBits(accumData, 10)
128 | accumData = 0
129 | accumCount = 0
130 | }
131 | }
132 | if accumCount > 0 { // 1 or 2 digits remaining
133 | bb.appendBits(accumData, accumCount * 3 + 1)
134 | }
135 | return try Segment(mode: .numeric, characterCount: digits.count, data: bb)
136 | }
137 |
138 | private static let ascii0 = Character("0").asciiValue!
139 | private static let ascii9 = Character("9").asciiValue!
140 |
141 | static func isNumeric(_ c: I) -> Bool where I: BinaryInteger {
142 | guard c <= UInt8.max else {
143 | return false
144 | }
145 | return (ascii0...ascii9).contains(UInt8(c))
146 | }
147 |
148 | static let alphanumericCharset: [UInt8] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".utf8)
149 |
150 | static func alphanumericIndex(of c: I) -> Int? where I: BinaryInteger {
151 | guard c <= UInt8.max else {
152 | return nil
153 | }
154 | return Self.alphanumericCharset.firstIndex(of: UInt8(c))
155 | }
156 |
157 | static func isAlphanumeric(_ c: I) -> Bool where I: BinaryInteger {
158 | alphanumericIndex(of: c) != nil
159 | }
160 |
161 | static func isAlphanumeric(_ c: UnicodeScalar) -> Bool {
162 | isAlphanumeric(c.value)
163 | }
164 |
165 | /**
166 | * Returns a segment representing the given text string encoded in alphanumeric mode.
167 | * The characters allowed are: 0 to 9, A to Z (uppercase only), space,
168 | * dollar, percent, asterisk, plus, hyphen, period, slash, colon.
169 | */
170 | public static func makeAlphanumeric(text: String) throws -> Segment {
171 | var bb: [Bool] = []
172 | var accumData: UInt = 0
173 | var accumCount = 0
174 | for c in text.utf8 {
175 | guard let index = alphanumericIndex(of: c) else {
176 | throw QRError.invalidAlphanumericString
177 | }
178 | accumData = accumData * 45 + UInt(index)
179 | accumCount += 1
180 | if accumCount == 2 {
181 | bb.appendBits(accumData, 11)
182 | accumData = 0
183 | accumCount = 0
184 | }
185 | }
186 | if accumCount > 0 { // 1 character remaining
187 | bb.appendBits(accumData, 6)
188 | }
189 | return try Segment(mode: .alphanumeric, characterCount: text.count, data: bb)
190 | }
191 |
192 | /**
193 | * Returns a segment representing the specified text string encoded in kanji mode. Throws if the string contains any non-kanji-mode characters.
194 | *
195 | * Broadly speaking, the set of encodable characters are {kanji used in Japan,
196 | * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
197 | * Examples of non-encodable characters include {ordinary ASCII, half-width katakana,
198 | * more extensive Chinese hanzi}.
199 | */
200 | public static func makeKanji(text: String) throws -> Segment {
201 | var bb: [Bool] = []
202 | try text.forEach {
203 | guard let val = toKanji($0) else {
204 | throw QRError.invalidKanjiString
205 | }
206 | bb.appendBits(val, 13)
207 | }
208 | return try Segment(mode: .kanji, characterCount: text.count, data: bb)
209 | }
210 |
211 | public static func isEncodableAsKanji(_ s: String) -> Bool {
212 | s.allSatisfy(isKanji)
213 | }
214 |
215 | public static func toKanji(_ c: Character) -> Int? {
216 | let scalars = c.unicodeScalars
217 | guard scalars.count == 1 else {
218 | return nil
219 | }
220 | let value = Int(scalars.first!.value)
221 | return toKanji(value)
222 | }
223 |
224 | static func toKanji(_ value: Int) -> Int? {
225 | guard value < (1 << 16) else {
226 | return nil
227 | }
228 | guard value < unicodeToQRKanji.count && unicodeToQRKanji[value] != -1 else {
229 | return nil
230 | }
231 | let result = Int(unicodeToQRKanji[value])
232 | guard result != -1 else {
233 | return nil
234 | }
235 | return result
236 | }
237 |
238 | public static func isKanji(_ c: Character) -> Bool {
239 | toKanji(c) != nil
240 | }
241 |
242 | public static func isKanji(_ c: I) -> Bool where I: BinaryInteger {
243 | toKanji(Int(c)) != nil
244 | }
245 |
246 | public static func isKanji(_ c: UnicodeScalar) -> Bool {
247 | isKanji(c.value)
248 | }
249 |
250 | static let unicodeToQRKanji: [Int16] = {
251 | var result = [Int16](repeating: -1, count: 1 << 16)
252 | let bytes = Data(base64Encoded: packedQRKanjiToUnicode)!
253 | for i in stride(from: 0, to: bytes.count, by: 2) {
254 | let c = ((UInt16(bytes[i]) & 0xff) << 8) | (UInt16(bytes[i + 1]) & 0xff)
255 | guard c != 0xffff else {
256 | continue
257 | }
258 | precondition(result[Int(c)] == -1)
259 | result[Int(c)] = Int16(i / 2)
260 | }
261 | return result
262 | }()
263 |
264 | /**
265 | * Returns a list of zero or more segments to represent the given text string. The result
266 | * may use various segment modes and switch modes to optimize the length of the bit stream.
267 | */
268 | public static func makeSegments(text: String) throws -> [Segment] {
269 | var result: [Segment] = []
270 | if text.isEmpty {
271 | // Leave result empty
272 | } else if isNumeric(text: text) {
273 | try result.append(makeNumeric(digits: text))
274 | } else if isAlphanumeric(text: text) {
275 | try result.append(makeAlphanumeric(text: text))
276 | } else {
277 | try result.append(makeBytes(data: Array(text.utf8)))
278 | }
279 | return result
280 | }
281 |
282 | /**
283 | * Returns a segment representing an Extended Channel Interpretation
284 | * (ECI) designator with the given assignment value.
285 | */
286 | public static func makeECI(designator: Int) throws -> Segment {
287 | var bb: [Bool] = []
288 | if designator < 0 {
289 | throw QRError.invalidECIDesignator
290 | } else if designator < (1 << 7) {
291 | bb.appendBits(UInt32(designator), 8)
292 | } else if designator < (1 << 14) {
293 | bb.appendBits(UInt32(designator), 14)
294 | } else if designator < 1_000_000 {
295 | bb.appendBits(UInt32(6), 3)
296 | bb.appendBits(UInt32(designator), 21)
297 | } else {
298 | throw QRError.invalidECIDesignator
299 | }
300 | return try Segment(mode: .eci, characterCount: 0, data: bb)
301 | }
302 |
303 | // MARK: - Public static helper functions
304 |
305 | /**
306 | * Tests whether the given string can be encoded as a segment in alphanumeric mode.
307 | * A string is encodable iff each character is in the following set: 0 to 9, A to Z
308 | * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
309 | */
310 | public static func isAlphanumeric(text: String) -> Bool {
311 | text.utf8.allSatisfy { Self.alphanumericCharset.contains($0) }
312 | }
313 |
314 | /**
315 | * Tests whether the given string can be encoded as a segment in numeric mode.
316 | * A string is encodable iff each character is in the range 0 to 9.
317 | */
318 | public static func isNumeric(text: String) -> Bool {
319 | text.utf8.allSatisfy { isNumeric($0) }
320 | }
321 |
322 | public static func isNumeric(_ c: UnicodeScalar) -> Bool {
323 | isNumeric(c.value)
324 | }
325 |
326 | // MARK: - Constructors (low level)
327 |
328 | /**
329 | * Creates a new QR Code segment with the given attributes and data.
330 | * The character count (numCh) must agree with the mode and the bit buffer length,
331 | * but the constraint isn't checked.
332 | */
333 | public init(mode: Segment.Mode, characterCount: Int, data: [Bool]) throws {
334 | guard characterCount >= 0 else {
335 | throw QRError.invalidCharacterCount
336 | }
337 | self.mode = mode
338 | self.characterCount = characterCount
339 | self.data = data
340 | }
341 |
342 | // Calculates the number of bits needed to encode the given segments at
343 | // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a
344 | // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX.
345 | static func getTotalBits(_ segs: [Segment], _ version: Int) -> Int {
346 | var result = 0
347 | for seg in segs {
348 | let ccbits = seg.mode.numCharCountBits(version)
349 | if seg.characterCount >= (1 << ccbits) {
350 | return -1 // The segment's length doesn't fit the field's bit width
351 | } else if 4 + ccbits > Int.max - result {
352 | return -1 // The sum will overflow an int type
353 | }
354 | result += 4 + ccbits
355 | if seg.data.count > Int.max - result {
356 | return -1 // The sum will overflow an int type
357 | }
358 | result += seg.data.count
359 | }
360 | return result
361 | }
362 |
363 | // MARK: - Optimal Segmentation
364 |
365 | /**
366 | * Returns a list of zero or more segments to represent the specified Unicode text string.
367 | * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints
368 | * in the specified {error correction level, minimum version number, maximum version number}.
369 | *
370 | * This function can utilize all four text encoding modes: numeric, alphanumeric, byte (UTF-8),
371 | * and kanji. This can be considered as a sophisticated but slower replacement for
372 | * `makeSegments()`. This requires more input parameters because it searches a
373 | * range of versions, like `QRCode.encode(segments:correctionLevel:minVersion:maxVersion:mask:booscEcl:)`.
374 | */
375 | public static func makeSegmentsOptimally(text: String, correctionLevel: CorrectionLevel = .medium, minVersion: Int = 1, maxVersion: Int = 40) throws -> [Segment] {
376 | try QRCode.checkVersion(minVersion: minVersion, maxVersion: maxVersion)
377 |
378 | // Iterate through version numbers, and make tentative segments
379 | var segs: [Segment]!
380 | let codePoints = Array(text.unicodeScalars)
381 | var version = minVersion
382 | repeat {
383 | defer {
384 | version += 1
385 | }
386 | if version == minVersion || version == 10 || version == 27 {
387 | segs = makeSegmentsOptimally(codePoints: codePoints, version: version)
388 | }
389 | precondition(segs != nil)
390 |
391 | // Check if the segments fit
392 | let dataCapacityBits = QRCode.dataCapacityBits(version, correctionLevel)
393 | let dataUsedBits = Segment.getTotalBits(segs, version)
394 | if dataUsedBits != -1 && dataUsedBits <= dataCapacityBits {
395 | return segs // This version number is found to be suitable
396 | }
397 | if version >= maxVersion { // All versions in the range could not fit the given text
398 | throw QRError.dataTooLong(dataUsedBits, dataCapacityBits)
399 | }
400 | } while true
401 | }
402 |
403 | /// Returns a new list of segments that is optimal for the given text at the given version number.
404 | static func makeSegmentsOptimally(codePoints: [UnicodeScalar], version: Int) -> [Segment] {
405 | guard !codePoints.isEmpty else {
406 | return []
407 | }
408 | let charModes = computeCharacterModes(codePoints: codePoints, version: version)
409 | return splitIntoSegments(codePoints: codePoints, charModes: charModes)
410 | }
411 |
412 | /// Returns a new array representing the optimal mode per code point based on the given text and version.
413 | static func computeCharacterModes(codePoints: [UnicodeScalar], version: Int) -> [Mode] {
414 | precondition(!codePoints.isEmpty)
415 | let modeTypes: [Segment.Mode] = [.byte, .alphanumeric, .numeric, .kanji] // Do not change
416 | let modesCount = modeTypes.count
417 |
418 | // Segment header sizes, measured in 1/6 bits
419 | let headCosts = modeTypes.map {
420 | (4 + $0.numCharCountBits(version)) * 6
421 | }
422 |
423 | // charModes[i][j] represents the mode to encode the code point at
424 | // index i such that the final segment ends in modeTypes[j] and the
425 | // total number of bits is minimized over all possible choices
426 | var charModes: [[Segment.Mode?]] =
427 | Array(repeating:
428 | Array(repeating: nil, count: modesCount),
429 | count: codePoints.count)
430 |
431 | // At the beginning of each iteration of the loop below,
432 | // prevCosts[j] is the exact minimum number of 1/6 bits needed to
433 | // encode the entire string prefix of length i, and end in modeTypes[j]
434 | var prevCosts = headCosts
435 |
436 | // Calculate costs using dynamic programming
437 | for (i, c) in codePoints.enumerated() {
438 | var curCosts = [Int](repeating: 0, count: modesCount)
439 | do { // Always extend a byte mode segment
440 | curCosts[0] = prevCosts[0] + countUTF8Bytes(c) * 8 * 6
441 | charModes[i][0] = modeTypes[0]
442 | }
443 | // Extend a segment if possible
444 | if isAlphanumeric(c) { // Is alphanumeric
445 | curCosts[1] = prevCosts[1] + 33 // 5.5 bits per alphanumeric char
446 | charModes[i][1] = modeTypes[1]
447 | }
448 | if isNumeric(c) { // Is numeric
449 | curCosts[2] = prevCosts[2] + 20 // 3.33 bits per digit
450 | charModes[i][2] = modeTypes[2]
451 | }
452 | if isKanji(c) {
453 | curCosts[3] = prevCosts[3] + 78 // 13 bits per Shift JIS char
454 | charModes[i][3] = modeTypes[3]
455 | }
456 |
457 | // Start new segment at the end to switch modes
458 | for j in 0 ..< modesCount { // To mode
459 | for k in 0 ..< modesCount { // From mode
460 | let newCost = (curCosts[k] + 5) / 6 * 6 + headCosts[j]
461 | if charModes[i][k] != nil && (charModes[i][j] == nil || newCost < curCosts[j]) {
462 | curCosts[j] = newCost
463 | charModes[i][j] = modeTypes[k]
464 | }
465 | }
466 | }
467 |
468 | prevCosts = curCosts
469 | }
470 |
471 | // Find optimal ending mode
472 | var curMode: Mode?
473 | var minCost = 0
474 | for i in 0 ..< modesCount {
475 | if curMode == nil || prevCosts[i] < minCost {
476 | minCost = prevCosts[i]
477 | curMode = modeTypes[i]
478 | }
479 | }
480 |
481 | // Get optimal mode for each code point by tracing backwards
482 | var r = [Mode?](repeating: nil, count: charModes.count)
483 | for i in (0 ..< charModes.count).reversed() {
484 | for j in 0 ..< modesCount {
485 | if modeTypes[j] == curMode {
486 | curMode = charModes[i][j]
487 | r[i] = curMode
488 | break
489 | }
490 | }
491 | }
492 |
493 | let result = r.compactMap { $0 }
494 | precondition(result.count == r.count)
495 |
496 | return result
497 | }
498 |
499 | // Returns a new list of segments based on the given text and modes, such that
500 | // consecutive code points in the same mode are put into the same segment.
501 | static func splitIntoSegments(codePoints: [UnicodeScalar], charModes: [Mode]) -> [Segment] {
502 | precondition(!codePoints.isEmpty)
503 | var result: [Segment] = []
504 |
505 | // Accumulate run of modes
506 | var curMode = charModes.first!
507 | var start = 0
508 | var i = 1
509 | repeat {
510 | defer {
511 | i += 1
512 | }
513 | if i < codePoints.count && charModes[i] == curMode {
514 | continue
515 | }
516 | let s = String(String.UnicodeScalarView(codePoints[start ..< i]))
517 | switch curMode {
518 | case .byte:
519 | try! result.append(makeBytes(data: Array(s.utf8)))
520 | case .numeric:
521 | try! result.append(makeNumeric(digits: s))
522 | case .alphanumeric:
523 | try! result.append(makeAlphanumeric(text: s))
524 | case .kanji:
525 | try! result.append(makeKanji(text: s))
526 | default:
527 | preconditionFailure()
528 | }
529 |
530 | if i >= codePoints.count {
531 | return result
532 | }
533 | curMode = charModes[i]
534 | start = i
535 | } while true
536 | }
537 |
538 | /// Returns the number of UTF-8 bytes needed to encode the given Unicode code point.
539 | static func countUTF8Bytes(_ cp: UnicodeScalar) -> Int {
540 | let v = cp.value
541 | if v < 0x80 { return 1 }
542 | else if v < 0x800 { return 2 }
543 | else if v < 0x10000 { return 3 }
544 | else if v < 0x110000 { return 4 }
545 | preconditionFailure()
546 | }
547 | }
548 |
549 | extension Segment: CustomStringConvertible {
550 | public var description: String {
551 | "Segment(mode: \(mode), characterCount: \(characterCount), data: <\(data.count) bits>)"
552 | }
553 | }
554 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/ShiftJISData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Wolf McNally on 8/28/21.
6 | //
7 |
8 | import Foundation
9 |
10 | let packedQRKanjiToUnicode = "MAAwATAC/wz/DjD7/xr/G/8f/wEwmzCcALT/QACo/z7/4/8/MP0w/jCdMJ4wA07dMAUwBjAHMPwgFSAQ/w8AXDAcIBb/XCAmICUgGCAZIBwgHf8I/wkwFDAV/zv/Pf9b/10wCDAJMAowCzAMMA0wDjAPMBAwEf8LIhIAsQDX//8A9/8dImD/HP8eImYiZyIeIjQmQiZAALAgMiAzIQP/5f8EAKIAo/8F/wP/Bv8K/yAApyYGJgUlyyXPJc4lxyXGJaEloCWzJbIlvSW8IDswEiGSIZAhkSGTMBP/////////////////////////////IggiCyKGIocigiKDIioiKf////////////////////8iJyIoAKwh0iHUIgAiA/////////////////////////////8iICKlIxIiAiIHImEiUiJqImsiGiI9Ih0iNSIrIiz//////////////////yErIDAmbyZtJmogICAhALb//////////yXv/////////////////////////////////////////////////xD/Ef8S/xP/FP8V/xb/F/8Y/xn///////////////////8h/yL/I/8k/yX/Jv8n/yj/Kf8q/yv/LP8t/y7/L/8w/zH/Mv8z/zT/Nf82/zf/OP85/zr///////////////////9B/0L/Q/9E/0X/Rv9H/0j/Sf9K/0v/TP9N/07/T/9Q/1H/Uv9T/1T/Vf9W/1f/WP9Z/1r//////////zBBMEIwQzBEMEUwRjBHMEgwSTBKMEswTDBNME4wTzBQMFEwUjBTMFQwVTBWMFcwWDBZMFowWzBcMF0wXjBfMGAwYTBiMGMwZDBlMGYwZzBoMGkwajBrMGwwbTBuMG8wcDBxMHIwczB0MHUwdjB3MHgweTB6MHswfDB9MH4wfzCAMIEwgjCDMIQwhTCGMIcwiDCJMIowizCMMI0wjjCPMJAwkTCSMJP/////////////////////////////////////MKEwojCjMKQwpTCmMKcwqDCpMKowqzCsMK0wrjCvMLAwsTCyMLMwtDC1MLYwtzC4MLkwujC7MLwwvTC+ML8wwDDBMMIwwzDEMMUwxjDHMMgwyTDKMMswzDDNMM4wzzDQMNEw0jDTMNQw1TDWMNcw2DDZMNow2zDcMN0w3jDf//8w4DDhMOIw4zDkMOUw5jDnMOgw6TDqMOsw7DDtMO4w7zDwMPEw8jDzMPQw9TD2/////////////////////wORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDowOkA6UDpgOnA6gDqf////////////////////8DsQOyA7MDtAO1A7YDtwO4A7kDugO7A7wDvQO+A78DwAPBA8MDxAPFA8YDxwPIA8n/////////////////////////////////////////////////////////////////////////////////////////////////////////////BBAEEQQSBBMEFAQVBAEEFgQXBBgEGQQaBBsEHAQdBB4EHwQgBCEEIgQjBCQEJQQmBCcEKAQpBCoEKwQsBC0ELgQv////////////////////////////////////////BDAEMQQyBDMENAQ1BFEENgQ3BDgEOQQ6BDsEPAQ9//8EPgQ/BEAEQQRCBEMERARFBEYERwRIBEkESgRLBEwETQROBE///////////////////////////////////yUAJQIlDCUQJRglFCUcJSwlJCU0JTwlASUDJQ8lEyUbJRclIyUzJSslOyVLJSAlLyUoJTclPyUdJTAlJSU4JUL//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////06cVRZaA5Y/VMBhG2MoWfaQIoR1gxx6UGCqY+FuJWXthGaCppv1aJNXJ2WhYnFbm1nQhnuY9H1ifb6bjmIWfJ+It1uJXrVjCWaXaEiVx5eNZ09O5U8KT01PnVBJVvJZN1nUWgFcCWDfYQ9hcGYTaQVwunVPdXB5+32tfe+Aw4QOiGOLApBVkHpTO06VTqVX34CykMF4704AWPFuopA4ejKDKIKLnC9RQVNwVL1U4VbgWftfFZjybeuA5IUt////////lmKWcJagl/tUC1PzW4dwz3+9j8KW6FNvnVx6uk4ReJOB/G4mVhhVBGsdhRqcO1nlU6ltZnTclY9WQk6RkEuW8oNPmQxT4VW2WzBfcWYgZvNoBGw4bPNtKXRbdsh6Tpg0gvGIW4pgku1tsnWrdsqZxWCmiwGNipWyaY5TrVGG//9XElgwWURbtF72YChjqWP0bL9vFHCOcRRxWXHVcz9+AYJ2gtGFl5BgkludG1hpZbxsWnUlUflZLlllX4Bf3GK8ZfpqKmsna7Rzi3/BiVadLJ0OnsRcoWyWg3tRBFxLYbaBxmh2cmFOWU/6U3hgaW4pek+X804LUxZO7k9VTz1PoU9zUqBT71YJWQ9awVu2W+F50WaHZ5xntmtMbLNwa3PCeY15vno8e4eCsYLbgwSDd4Pvg9OHZoqyVimMqI/mkE6XHoaKT8Rc6GIRcll1O4Hlgr2G/ozAlsWZE5nVTstPGonjVt5YSljKXvtf62AqYJRgYmHQYhJi0GU5////////m0FmZmiwbXdwcHVMdoZ9dYKlh/mVi5aOjJ1R8VK+WRZUs1uzXRZhaGmCba94jYTLiFeKcpOnmrhtbJmohtlXo2f/hs6SDlKDVodUBF7TYuFkuWg8aDhru3NyeLp6a4maidKNa48DkO2Vo5aUl2lbZlyzaX2YTZhOY5t7IGor//9qf2i2nA1vX1JyVZ1gcGLsbTtuB27RhFuJEI9EThScOVP2aRtqOpeEaCpRXHrDhLKR3JOMVludKGgigwWEMXylUgiCxXTmTn5Pg1GgW9JSClLYUudd+1WaWCpZ5luMW5hb215yXnlgo2EfYWNhvmPbZWJn0WhTaPprPmtTbFdvIm+Xb0V0sHUYduN3C3r/e6F8IX3pfzZ/8ICdgmaDnomzisyMq5CElFGVk5WRlaKWZZfTmSiCGE44VCtcuF3Mc6l2THc8XKl/640LlsGYEZhUmFhPAU8OU3FVnFZoV/pZR1sJW8RckF4MXn5fzGPuZzpl12XiZx9oy2jE////////al9eMGvFbBdsfXV/eUhbY3oAfQBfvYmPihiMtI13jsyPHZjimg6bPE6AUH1RAFmTW5xiL2KAZOxrOnKgdZF5R3+ph/uKvItwY6yDypegVAlUA1WraFRqWIpweCdndZ7NU3RbooEahlCQBk4YTkVOx08RU8pUOFuuXxNgJWVR//9nPWxCbHJs43B4dAN6dnquewh9Gnz+fWZl53JbU7tcRV3oYtJi4GMZbiCGWooxjd2S+G8BeaabWk6oTqtOrE+bT6BQ0VFHevZRcVH2U1RTIVN/U+tVrFiDXOFfN19KYC9gUGBtYx9lWWpLbMFywnLtd++A+IEFggiFTpD3k+GX/5lXmlpO8FHdXC1mgWltXEBm8ml1c4loUHyBUMVS5FdHXf6TJmWkayNrPXQ0eYF5vXtLfcqCuYPMiH+JX4s5j9GR0VQfkoBOXVA2U+VTOnLXc5Z36YLmjq+ZxpnImdJRd2Eahl5VsHp6UHZb05BHloVOMmrbkedcUVxI////////Y5h6n2yTl3SPYXqqcYqWiHyCaBd+cGhRk2xS8lQbhauKE3+kjs2Q4VNmiIh5QU/CUL5SEVFEVVNXLXPqV4tZUV9iX4RgdWF2YWdhqWOyZDplbGZvaEJuE3Vmej18+31MfZl+S39rgw6DSobNigiKY4tmjv2YGp2PgriPzpvo//9Sh2IfZINvwJaZaEFQkWsgbHpvVHp0fVCIQIojZwhO9lA5UCZQZVF8UjhSY1WnVw9YBVrMXvphsmH4YvNjcmkcailyfXKscy54FHhvfXl3DICpiYuLGYzijtKQY5N1lnqYVZoTnnhRQ1OfU7Nee18mbhtukHOEc/59Q4I3igCK+pZQTk5QC1PkVHxW+lnRW2Rd8V6rXydiOGVFZ69uVnLQfMqItIChgOGD8IZOioeN6JI3lseYZ58TTpROkk8NU0hUSVQ+Wi9fjF+hYJ9op2qOdFp4gYqeiqSLd5GQTl6byU6kT3xPr1AZUBZRSVFsUp9SuVL+U5pT41QR////////VA5ViVdRV6JZfVtUW11bj13lXedd9154XoNeml63XxhgUmFMYpdi2GOnZTtmAmZDZvRnbWghaJdpy2xfbSptaW4vbp11MnaHeGx6P3zgfQV9GH1efbGAFYADgK+AsYFUgY+CKoNSiEyIYYsbjKKM/JDKkXWScXg/kvyVpJZN//+YBZmZmtidO1JbUqtT91QIWNVi92/gjGqPX565UUtSO1RKVv16QJF3nWCe0nNEbwmBcHURX/1g2pqoctuPvGtkmANOylbwV2RYvlpaYGhhx2YPZgZoOWixbfd11X06gm6bQk6bT1BTyVUGXW9d5l3uZ/tsmXRzeAKKUJOWiN9XUF6nYytQtVCsUY1nAFTJWF5Zu1uwX2liTWOhaD1rc24IcH2Rx3KAeBV4JnltZY59MIPciMGPCZabUmRXKGdQf2qMoVG0V0KWKlg6aYqAtFSyXQ5X/HiVnfpPXFJKVItkPmYoZxRn9XqEe1Z9IpMvaFybrXs5UxlRilI3////////W99i9mSuZOZnLWu6hamW0XaQm9ZjTJMGm6t2v2ZSTglQmFPCXHFg6GSSZWNoX3Hmc8p1I3uXfoKGlYuDjNuReJkQZaxmq2uLTtVO1E86T39SOlP4U/JV41bbWOtZy1nJWf9bUFxNXgJeK1/XYB1jB2UvW1xlr2W9ZehnnWti//9re2wPc0V5SXnBfPh9GX0rgKKBAoHziZaKXoppimaKjIrujMeM3JbMmPxrb06LTzxPjVFQW1db+mFIYwFmQmshbstsu3I+dL111HjBeTqADIAzgeqElI+ebFCef18Pi1idK3r6jvhbjZbrTgNT8Vf3WTFayVukYIluf28Gdb6M6lufhQB74FByZ/SCnVxhhUp+HoIOUZlcBGNojWZlnHFueT59F4AFix2OypBuhseQqlAfUvpcOmdTcHxyNZFMkciTK4LlW8JfMWD5TjtT1luIYktnMWuKculz4HougWuNo5FSmZZRElPXVGpb/2OIajl9rJcAVtpTzlRo////////W5dcMV3eT+5hAWL+bTJ5wHnLfUJ+TX/Sge2CH4SQiEaJcouQjnSPL5AxkUuRbJbGkZxOwE9PUUVTQV+TYg5n1GxBbgtzY34mkc2Sg1PUWRlbv23ReV1+LnybWH5xn1H6iFOP8E/KXPtmJXeseuOCHJn/UcZfqmXsaW9riW3z//9ulm9kdv59FF3hkHWRh5gGUeZSHWJAZpFm2W4aXrZ90n9yZviFr4X3ivhSqVPZWXNej1+QYFWS5JZkULdRH1LdUyBTR1PsVOhVRlUxVhdZaFm+WjxbtVwGXA9cEVwaXoReil7gX3Bif2KEYttjjGN3ZgdmDGYtZnZnfmiiah9qNWy8bYhuCW5YcTxxJnFndcd3AXhdeQF5ZXnweuB7EXynfTmAloPWhIuFSYhdiPOKH4o8ilSKc4xhjN6RpJJmk36UGJacl5hOCk4ITh5OV1GXUnBXzlg0WMxbIl44YMVk/mdhZ1ZtRHK2dXN6Y4S4i3KRuJMgVjFX9Jj+////////Yu1pDWuWce1+VIB3gnKJ5pjfh1WPsVw7TzhP4U+1VQdaIFvdW+lfw2FOYy9lsGZLaO5pm214bfF1M3W5dx95XnnmfTOB44KvhaqJqoo6jquPm5Aykd2XB066TsFSA1h1WOxcC3UaXD2BTooKj8WWY5dteyWKz5gIkWJW81Oo//+QF1Q5V4JeJWOobDRwindhfIt/4IhwkEKRVJMQkxiWj3RemsRdB11pZXBnoo2olttjbmdJaRmDxZgXlsCI/m+EZHpb+E4WcCx1XWYvUcRSNlLiWdNfgWAnYhBlP2V0Zh9mdGjyaBZrY24FcnJ1H3bbfL6AVljwiP2Jf4qgipOKy5AdkZKXUpdZZYl6DoEGlrteLWDcYhplpWYUZ5B383pNfE1+PoEKjKyNZI3hjl94qVIHYtljpWRCYpiKLXqDe8CKrJbqfXaCDIdJTtlRSFNDU2Bbo1wCXBZd3WImYkdksGgTaDRsyW1FbRdn029ccU5xfWXLen97rX3a////////fkp/qIF6ghuCOYWmim6Mzo31kHiQd5KtkpGVg5uuUk1VhG84cTZRaHmFflWBs3zOVkxYUVyoY6pm/mb9aVpy2XWPdY55DnlWed98l30gfUSGB4o0ljuQYZ8gUOdSdVPMU+JQCVWqWO5ZT3I9W4tcZFMdYONg82NcY4NjP2O7//9kzWXpZvld42nNaf1vFXHlTol16Xb4epN8333PfZyAYYNJg1iEbIS8hfuIxY1wkAGQbZOXlxyaElDPWJdhjoHThTWNCJAgT8NQdFJHU3Ngb2NJZ19uLI2zkB9P11xejMplz32aU1KIllF2Y8NbWFtrXApkDWdRkFxO1lkaWSpscIpRVT5YFVmlYPBiU2fBgjVpVZZAmcSaKE9TWAZb/oAQXLFeL1+FYCBhS2I0Zv9s8G7egM6Bf4LUiIuMuJAAkC6Wip7bm9tO41PwWSd7LJGNmEyd+W7dcCdTU1VEW4ViWGKeYtNsom/vdCKKF5Q4b8GK/oM4UeeG+FPq////////U+lPRpBUj7BZaoExXf166o+/aNqMN3L4nEhqPYqwTjlTWFYGV2ZixWOiZeZrTm3hbltwrXfteu97qn27gD2AxobLipWTW1bjWMdfPmWtZpZqgGu1dTeKx1Akd+VXMF8bYGVmemxgdfR6Gn9ugfSHGJBFmbN7yXVcevl7UYTE//+QEHnpepKDNlrhd0BOLU7yW5lf4GK9Zjxn8WzohmuId4o7kU6S85nQahdwJnMqgueEV4yvTgFRRlHLVYtb9V4WXjNegV8UXzVfa1+0YfJjEWaiZx1vbnJSdTp3OoB0gTmBeId2ir+K3I2FjfOSmpV3mAKc5VLFY1d29GcVbIhzzYzDk66Wc20lWJxpDmnMj/2TmnXbkBpYWmgCY7Rp+09Dbyxn2I+7hSZ9tJNUaT9vcFdqWPdbLH0scipUCpHjnbROrU9OUFxQdVJDjJ5USFgkW5peHV6VXq1e918fYIxitWM6Y9Bor2xAeId5jnoLfeCCR4oCiuaORJAT////////kLiRLZHYnw5s5WRYZOJldW70doR7G5Bpk9FuulTyX7lkpI9Nj+2SRFF4WGtZKVxVXpdt+36PdRyMvI7imFtwuU8da79vsXUwlvtRTlQQWDVYV1msXGBfkmWXZ1xuIXZ7g9+M7ZAUkP2TTXgleDpSql6mVx9ZdGASUBJRWlGs//9RzVIAVRBYVFhYWVdblVz2XYtgvGKVZC1ncWhDaLxo33bXbdhub22bcG9xyF9Tddh5d3tJe1R7UnzWfXFSMIRjhWmF5IoOiwSMRo4PkAOQD5QZlnaYLZowldhQzVLVVAxYAlwOYadknm0ed7N65YD0hASQU5KFXOCdB1M/X5dfs22ccnl3Y3m/e+Rr0nLsiq1oA2phUfh6gWk0XEqc9oLrW8WRSXAeVnhcb2DHZWZsjIxakEGYE1RRZseSDVlIkKNRhU5NUeqFmYsOcFhjepNLaWKZtH4EdXdTV2lgjt+W42xdToxcPF8Qj+lTAozRgImGeV7/ZeVOc1Fl////////WYJcP5fuTvtZil/Nio1v4XmweWJb54RxcytxsV50X/Vje2SaccN8mE5DXvxOS1fcVqJgqW/DfQ2A/YEzgb+PsomXhqRd9GKKZK2Jh2d3bOJtPnQ2eDRaRn91gq2ZrE/zXsNi3WOSZVdnb3bDckyAzIC6jymRTVANV/lakmiF//9pc3Fkcv2Mt1jyjOCWapAZh3955HfnhClPL1JlU1pizWfPbMp2fXuUfJWCNoWEj+tm3W8gcgZ+G4OrmcGeplH9e7F4cnu4gId7SGroXmGAjHVRdWBRa5Jibox2epGXmupPEH9wYpx7T5WlnOlWelhZhuSWvE80UiRTSlPNU9teBmQsZZFnf2w+bE5ySHKvc+11VH5BgiyF6Yype8SRxnFpmBKY72M9Zml1anbkeNCFQ4buUypTUVQmWYNeh198YLJiSWJ5YqtlkGvUbMx1snaueJF52H3Lf3eApYirirmMu5B/l16Y22oLfDhQmVw+X65nh2vYdDV3CX+O////////nztnynoXUzl1i5rtX2aBnYPxgJhfPF/FdWJ7RpA8aGdZ61qbfRB2fossT/VfamoZbDdvAnTieWiIaIpVjHle32PPdcV50oLXkyiS8oSchu2cLVTBX2xljG1ccBWMp4zTmDtlT3T2Tg1O2FfgWStaZlvMUaheA16cYBZidmV3//9lp2ZubW5yNnsmgVCBmoKZi1yMoIzmjXSWHJZET65kq2tmgh6EYYVqkOhcAWlTmKiEeoVXTw9Sb1+pXkVnDXmPgXmJB4mGbfVfF2JVbLhOz3Jpm5JSBlQ7VnRYs2GkYm5xGllufIl83n0blvBlh4BeThlPdVF1WEBeY15zXwpnxE4mhT2ViZZbfHOYAVD7WMF2VninUiV3pYURe4ZQT1kJckd7x33oj7qP1JBNT79SyVopXwGXrU/dgheS6lcDY1VraXUriNyPFHpCUt9Yk2FVYgpmrmvNfD+D6VAjT/hTBVRGWDFZSVudXPBc710pXpZisWNnZT5luWcL////////bNVs4XD5eDJ+K4DegrOEDITshwKJEooqjEqQppLSmP2c851sTk9OoVCNUlZXSlmoXj1f2F/ZYj9mtGcbZ9Bo0lGSfSGAqoGoiwCMjIy/kn6WMlQgmCxTF1DVU1xYqGSyZzRyZ3dmekaR5lLDbKFrhlgAXkxZVGcsf/tR4XbG//9kaXjom1Seu1fLWblmJ2eaa85U6WnZXlWBnGeVm6pn/pxSaF1Opk/jU8hiuWcrbKuPxE+tfm2ev04HYWJugG8rhRNUc2cqm0Vd83uVXKxbxoccbkqE0XoUgQhZmXyNbBF3IFLZWSJxIXJfd9uXJ51haQtaf1oYUaVUDVR9Zg5234/3kpic9Fnqcl1uxVFNaMl9v33sl2KeumR4aiGDAlmEW19r23MbdvJ9soAXhJlRMmcontl27mdiUv+ZBVwkYjt8foywVU9gtn0LlYBTAU5fUbZZHHI6gDaRzl8ld+JThF95fQSFrIozjo2XVmfzha6UU2EJYQhsuXZS////////iu2POFUvT1FRKlLHU8tbpV59YKBhgmPWZwln2m5nbYxzNnM3dTF5UIjVipiQSpCRkPWWxIeNWRVOiE9ZTg6KiY8/mBBQrV58WZZbuV64Y9pj+mTBZtxpSmnYbQtutnGUdSh6r3+KgACESYTJiYGLIY4KkGWWfZkKYX5ikWsy//9sg210f8x//G3Af4WHuoj4Z2WDsZg8lvdtG31hhD2Rak5xU3VdUGsEb+uFzYYtiadSKVQPXGVnTmiodAZ0g3XiiM+I4ZHMluKWeF+Lc4d6y4ROY6B1ZVKJbUFunHQJdVl4a3ySloZ63J+NT7ZhbmXFhlxOhk6uUNpOIVHMW+5lmWiBbbxzH3ZCd616HHzngm+K0pB8kc+WdZgYUpt90VArU5hnl23LcdB0M4HojyqWo5xXnp90YFhBbZl9L5heTuRPNk+LUbdSsV26YBxzsnk8gtOSNJa3lvaXCp6Xn2Jmpmt0UhdSo3DIiMJeyWBLYZBvI3FJfD599IBv////////hO6QI5MsVEKbb2rTcImMwo3vlzJStFpBXspfBGcXaXxplG1qbw9yYnL8e+2AAYB+h0uQzlFtnpN5hICLkzKK1lAtVIyKcWtqjMSBB2DRZ6Cd8k6ZTpicEIprhcGFaGkAbn54l4FV/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////18MThBOFU4qTjFONk48Tj9OQk5WTlhOgk6FjGtOioISXw1Ojk6eTp9OoE6iTrBOs062Ts5OzU7ETsZOwk7XTt5O7U7fTvdPCU9aTzBPW09dT1dPR092T4hPj0+YT3tPaU9wT5FPb0+GT5ZRGE/UT99Pzk/YT9tP0U/aT9BP5E/lUBpQKFAUUCpQJVAFTxxP9lAhUClQLE/+T+9QEVAGUENQR2cDUFVQUFBIUFpQVlBsUHhQgFCaUIVQtFCy////////UMlQylCzUMJQ1lDeUOVQ7VDjUO5Q+VD1UQlRAVECURZRFVEUURpRIVE6UTdRPFE7UT9RQFFSUUxRVFFievhRaVFqUW5RgFGCVthRjFGJUY9RkVGTUZVRllGkUaZRolGpUapRq1GzUbFRslGwUbVRvVHFUclR21HghlVR6VHt//9R8FH1Uf5SBFILUhRSDlInUipSLlIzUjlST1JEUktSTFJeUlRSalJ0UmlSc1J/Un1SjVKUUpJScVKIUpGPqI+nUqxSrVK8UrVSwVLNUtdS3lLjUuaY7VLgUvNS9VL4UvlTBlMIdThTDVMQUw9TFVMaUyNTL1MxUzNTOFNAU0ZTRU4XU0lTTVHWU15TaVNuWRhTe1N3U4JTllOgU6ZTpVOuU7BTtlPDfBKW2VPfZvxx7lPuU+hT7VP6VAFUPVRAVCxULVQ8VC5UNlQpVB1UTlSPVHVUjlRfVHFUd1RwVJJUe1SAVHZUhFSQVIZUx1SiVLhUpVSsVMRUyFSo////////VKtUwlSkVL5UvFTYVOVU5lUPVRRU/VTuVO1U+lTiVTlVQFVjVUxVLlVcVUVVVlVXVThVM1VdVZlVgFSvVYpVn1V7VX5VmFWeVa5VfFWDValVh1WoVdpVxVXfVcRV3FXkVdRWFFX3VhZV/lX9VhtV+VZOVlBx31Y0VjZWMlY4//9Wa1ZkVi9WbFZqVoZWgFaKVqBWlFaPVqVWrla2VrRWwla8VsFWw1bAVshWzlbRVtNW11buVvlXAFb/VwRXCVcIVwtXDVcTVxhXFlXHVxxXJlc3VzhXTlc7V0BXT1dpV8BXiFdhV39XiVeTV6BXs1ekV6pXsFfDV8ZX1FfSV9NYClfWV+NYC1gZWB1YclghWGJYS1hwa8BYUlg9WHlYhVi5WJ9Yq1i6WN5Yu1i4WK5YxVjTWNFY11jZWNhY5VjcWORY31jvWPpY+Vj7WPxY/VkCWQpZEFkbaKZZJVksWS1ZMlk4WT560llVWVBZTllaWVhZYllgWWdZbFlp////////WXhZgVmdT15Pq1mjWbJZxlnoWdxZjVnZWdpaJVofWhFaHFoJWhpaQFpsWklaNVo2WmJaalqaWrxavlrLWsJavVrjWtda5lrpWtZa+lr7WwxbC1sWWzJa0FsqWzZbPltDW0VbQFtRW1VbWltbW2VbaVtwW3NbdVt4ZYhbeluA//9bg1umW7hbw1vHW8lb1FvQW+Rb5lviW95b5VvrW/Bb9lvzXAVcB1wIXA1cE1wgXCJcKFw4XDlcQVxGXE5cU1xQXE9bcVxsXG5OYlx2XHlcjFyRXJRZm1yrXLtctly8XLdcxVy+XMdc2VzpXP1c+lztXYxc6l0LXRVdF11cXR9dG10RXRRdIl0aXRldGF1MXVJdTl1LXWxdc112XYddhF2CXaJdnV2sXa5dvV2QXbddvF3JXc1d013SXdZd213rXfJd9V4LXhpeGV4RXhteNl43XkReQ15AXk5eV15UXl9eYl5kXkdedV52XnqevF5/XqBewV7CXshe0F7P////////XtZe417dXtpe217iXuFe6F7pXuxe8V7zXvBe9F74Xv5fA18JX11fXF8LXxFfFl8pXy1fOF9BX0hfTF9OXy9fUV9WX1dfWV9hX21fc193X4Nfgl9/X4pfiF+RX4dfnl+ZX5hfoF+oX61fvF/WX/tf5F/4X/Ff3WCzX/9gIWBg//9gGWAQYClgDmAxYBtgFWArYCZgD2A6YFpgQWBqYHdgX2BKYEZgTWBjYENgZGBCYGxga2BZYIFgjWDnYINgmmCEYJtglmCXYJJgp2CLYOFguGDgYNNgtF/wYL1gxmC1YNhhTWEVYQZg9mD3YQBg9GD6YQNhIWD7YPFhDWEOYUdhPmEoYSdhSmE/YTxhLGE0YT1hQmFEYXNhd2FYYVlhWmFrYXRhb2FlYXFhX2FdYVNhdWGZYZZhh2GsYZRhmmGKYZFhq2GuYcxhymHJYfdhyGHDYcZhumHLf3lhzWHmYeNh9mH6YfRh/2H9Yfxh/mIAYghiCWINYgxiFGIb////////Yh5iIWIqYi5iMGIyYjNiQWJOYl5iY2JbYmBiaGJ8YoJiiWJ+YpJik2KWYtRig2KUYtdi0WK7Ys9i/2LGZNRiyGLcYsxiymLCYsdim2LJYwxi7mLxYydjAmMIYu9i9WNQYz5jTWQcY09jlmOOY4Bjq2N2Y6Njj2OJY59jtWNr//9jaWO+Y+ljwGPGY+NjyWPSY/ZjxGQWZDRkBmQTZCZkNmUdZBdkKGQPZGdkb2R2ZE5lKmSVZJNkpWSpZIhkvGTaZNJkxWTHZLtk2GTCZPFk54IJZOBk4WKsZONk72UsZPZk9GTyZPplAGT9ZRhlHGUFZSRlI2UrZTRlNWU3ZTZlOHVLZUhlVmVVZU1lWGVeZV1lcmV4ZYJlg4uKZZtln2WrZbdlw2XGZcFlxGXMZdJl22XZZeBl4WXxZ3JmCmYDZftnc2Y1ZjZmNGYcZk9mRGZJZkFmXmZdZmRmZ2ZoZl9mYmZwZoNmiGaOZolmhGaYZp1mwWa5Zslmvma8////////ZsRmuGbWZtpm4GY/ZuZm6WbwZvVm92cPZxZnHmcmZyeXOGcuZz9nNmdBZzhnN2dGZ15nYGdZZ2NnZGeJZ3BnqWd8Z2pnjGeLZ6ZnoWeFZ7dn72e0Z+xns2fpZ7hn5GfeZ91n4mfuZ7lnzmfGZ+dqnGgeaEZoKWhAaE1oMmhO//9os2graFloY2h3aH9on2iPaK1olGidaJtog2quaLlodGi1aKBoumkPaI1ofmkBaMppCGjYaSJpJmjhaQxozWjUaOdo1Wk2aRJpBGjXaONpJWj5aOBo72koaSppGmkjaSFoxml5aXdpXGl4aWtpVGl+aW5pOWl0aT1pWWkwaWFpXmldaYFpammyaa5p0Gm/acFp02m+ac5b6GnKad1pu2nDaadqLmmRaaBpnGmVabRp3mnoagJqG2n/awpp+WnyaedqBWmxah5p7WoUaetqCmoSasFqI2oTakRqDGpyajZqeGpHamJqWWpmakhqOGoiapBqjWqgaoRqomqj////////apeGF2q7asNqwmq4arNqrGreatFq32qqatpq6mr7awWGFmr6axJrFpsxax9rOGs3dtxrOZjua0drQ2tJa1BrWWtUa1trX2tha3hreWt/a4BrhGuDa41rmGuVa55rpGuqa6trr2uya7Frs2u3a7xrxmvLa9Nr32vsa+tr82vv//+evmwIbBNsFGwbbCRsI2xebFVsYmxqbIJsjWyabIFsm2x+bGhsc2ySbJBsxGzxbNNsvWzXbMVs3WyubLFsvmy6bNts72zZbOptH4hNbTZtK209bThtGW01bTNtEm0MbWNtk21kbVpteW1ZbY5tlW/kbYVt+W4VbgpttW3HbeZtuG3Gbext3m3Mbeht0m3Fbfpt2W3kbdVt6m3ubi1ubm4ubhlucm5fbj5uI25rbitudm5Nbh9uQ246bk5uJG7/bh1uOG6CbqpumG7Jbrdu0269bq9uxG6ybtRu1W6PbqVuwm6fb0FvEXBMbuxu+G7+bz9u8m8xbu9vMm7M////////bz5vE273b4Zvem94b4FvgG9vb1tv829tb4JvfG9Yb45vkW/Cb2Zvs2+jb6FvpG+5b8Zvqm/fb9Vv7G/Ub9hv8W/ub9twCXALb/pwEXABcA9v/nAbcBpvdHAdcBhwH3AwcD5wMnBRcGNwmXCScK9w8XCscLhws3CucN9wy3Dd//9w2XEJcP1xHHEZcWVxVXGIcWZxYnFMcVZxbHGPcftxhHGVcahxrHHXcblxvnHScclx1HHOceBx7HHncfVx/HH5cf9yDXIQchtyKHItcixyMHIycjtyPHI/ckByRnJLclhydHJ+coJygXKHcpJylnKicqdyuXKycsNyxnLEcs5y0nLicuBy4XL5cvdQD3MXcwpzHHMWcx1zNHMvcylzJXM+c05zT57Yc1dzanNoc3BzeHN1c3tzenPIc7NzznO7c8Bz5XPuc950onQFdG90JXP4dDJ0OnRVdD90X3RZdEF0XHRpdHB0Y3RqdHZ0fnSLdJ50p3TKdM901HPx////////dOB043TndOl07nTydPB08XT4dPd1BHUDdQV1DHUOdQ11FXUTdR51JnUsdTx1RHVNdUp1SXVbdUZ1WnVpdWR1Z3VrdW11eHV2dYZ1h3V0dYp1iXWCdZR1mnWddaV1o3XCdbN1w3W1db11uHW8dbF1zXXKddJ12XXjdd51/nX///91/HYBdfB1+nXydfN2C3YNdgl2H3YndiB2IXYidiR2NHYwdjt2R3ZIdkZ2XHZYdmF2YnZodml2anZndmx2cHZydnZ2eHZ8doB2g3aIdot2jnaWdpN2mXaadrB2tHa4drl2unbCds121nbSdt524Xbldud26oYvdvt3CHcHdwR3KXckdx53JXcmdxt3N3c4d0d3Wndod2t3W3dld393fnd5d453i3eRd6B3nnewd7Z3uXe/d7x3vXe7d8d3zXfXd9p33Hfjd+53/HgMeBJ5JnggeSp4RXiOeHR4hnh8eJp4jHijeLV4qniveNF4xnjLeNR4vni8eMV4ynjs////////eOd42nj9ePR5B3kSeRF5GXkseSt5QHlgeVd5X3laeVV5U3l6eX95inmdeaefS3mqea55s3m5ebp5yXnVeed57HnheeN6CHoNehh6GXogeh95gHoxejt6Pno3ekN6V3pJemF6Ynppn516cHp5en16iHqXepV6mHqWeql6yHqw//96tnrFesR6v5CDesd6ynrNes961XrTetl62nrdeuF64nrmeu168HsCew97CnsGezN7GHsZex57NXsoezZ7UHt6ewR7TXsLe0x7RXt1e2V7dHtne3B7cXtse257nXuYe597jXuce5p7i3uSe497XXuZe8t7wXvMe897tHvGe9176XwRfBR75nvlfGB8AHwHfBN783v3fBd8DXv2fCN8J3wqfB98N3wrfD18THxDfFR8T3xAfFB8WHxffGR8VnxlfGx8dXyDfJB8pHytfKJ8q3yhfKh8s3yyfLF8rny5fL18wHzFfMJ82HzSfNx84ps7fO988nz0fPZ8+n0G////////fQJ9HH0VfQp9RX1LfS59Mn0/fTV9Rn1zfVZ9Tn1yfWh9bn1PfWN9k32JfVt9j319fZt9un2ufaN9tX3Hfb19q349faJ9r33cfbh9n32wfdh93X3kfd59+33yfeF+BX4KfiN+IX4SfjF+H34Jfgt+In5GfmZ+O341fjl+Q343//9+Mn46fmd+XX5Wfl5+WX5afnl+an5pfnx+e36DfdV+fY+ufn9+iH6Jfox+kn6QfpN+lH6Wfo5+m36cfzh/On9Ff0x/TX9Of1B/UX9Vf1R/WH9ff2B/aH9pf2d/eH+Cf4Z/g3+If4d/jH+Uf55/nX+af6N/r3+yf7l/rn+2f7iLcX/Ff8Z/yn/Vf9R/4X/mf+l/83/5mNyABoAEgAuAEoAYgBmAHIAhgCiAP4A7gEqARoBSgFiAWoBfgGKAaIBzgHKAcIB2gHmAfYB/gISAhoCFgJuAk4CagK1RkICsgNuA5YDZgN2AxIDagNaBCYDvgPGBG4EpgSOBL4FL////////louBRoE+gVOBUYD8gXGBboFlgWaBdIGDgYiBioGAgYKBoIGVgaSBo4FfgZOBqYGwgbWBvoG4gb2BwIHCgbqByYHNgdGB2YHYgciB2oHfgeCB54H6gfuB/oIBggKCBYIHggqCDYIQghaCKYIrgjiCM4JAglmCWIJdglqCX4Jk//+CYoJogmqCa4IugnGCd4J4gn6CjYKSgquCn4K7gqyC4YLjgt+C0oL0gvOC+oOTgwOC+4L5gt6DBoLcgwmC2YM1gzSDFoMygzGDQIM5g1CDRYMvgyuDF4MYg4WDmoOqg5+DooOWgyODjoOHg4qDfIO1g3ODdYOgg4mDqIP0hBOD64POg/2EA4PYhAuDwYP3hAeD4IPyhA2EIoQgg72EOIUGg/uEbYQqhDyFWoSEhHeEa4SthG6EgoRphEaELIRvhHmENYTKhGKEuYS/hJ+E2YTNhLuE2oTQhMGExoTWhKGFIYT/hPSFF4UYhSyFH4UVhRSE/IVAhWOFWIVI////////hUGGAoVLhVWFgIWkhYiFkYWKhaiFbYWUhZuF6oWHhZyFd4V+hZCFyYW6hc+FuYXQhdWF3YXlhdyF+YYKhhOGC4X+hfqGBoYihhqGMIY/hk1OVYZUhl+GZ4ZxhpOGo4aphqqGi4aMhraGr4bEhsaGsIbJiCOGq4bUht6G6Ybs//+G34bbhu+HEocGhwiHAIcDhvuHEYcJhw2G+YcKhzSHP4c3hzuHJYcphxqHYIdfh3iHTIdOh3SHV4doh26HWYdTh2OHaogFh6KHn4eCh6+Hy4e9h8CH0JbWh6uHxIezh8eHxoe7h++H8ofgiA+IDYf+h/aH94gOh9KIEYgWiBWIIoghiDGINog5iCeIO4hEiEKIUohZiF6IYohriIGIfoieiHWIfYi1iHKIgoiXiJKIroiZiKKIjYikiLCIv4ixiMOIxIjUiNiI2YjdiPmJAoj8iPSI6IjyiQSJDIkKiROJQ4keiSWJKokriUGJRIk7iTaJOIlMiR2JYIle////////iWaJZIltiWqJb4l0iXeJfomDiYiJiomTiZiJoYmpiaaJrImvibKJuom9ib+JwInaidyJ3YnnifSJ+IoDihaKEIoMihuKHYolijaKQYpbilKKRopIinyKbYpsimKKhYqCioSKqIqhipGKpYqmipqKo4rEis2KworaiuuK84rn//+K5IrxixSK4IriiveK3orbiwyLB4saiuGLFosQixeLIIszl6uLJosriz6LKItBi0yLT4tOi0mLVotbi1qLa4tfi2yLb4t0i32LgIuMi46LkouTi5aLmYuajDqMQYw/jEiMTIxOjFCMVYxijGyMeIx6jIKMiYyFjIqMjYyOjJSMfIyYYh2MrYyqjL2MsoyzjK6MtozIjMGM5IzjjNqM/Yz6jPuNBI0FjQqNB40PjQ2NEJ9OjROMzY0UjRaNZ41tjXGNc42BjZmNwo2+jbqNz43ajdaNzI3bjcuN6o3rjd+N4438jgiOCY3/jh2OHo4Qjh+OQo41jjCONI5K////////jkeOSY5MjlCOSI5ZjmSOYI4qjmOOVY52jnKOfI6BjoeOhY6EjouOio6TjpGOlI6ZjqqOoY6sjrCOxo6xjr6OxY7IjsuO247jjvyO+47rjv6PCo8FjxWPEo8ZjxOPHI8fjxuPDI8mjzOPO485j0WPQo8+j0yPSY9Gj06PV49c//+PYo9jj2SPnI+fj6OPrY+vj7eP2o/lj+KP6o/vkIeP9JAFj/mP+pARkBWQIZANkB6QFpALkCeQNpA1kDmP+JBPkFCQUZBSkA6QSZA+kFaQWJBekGiQb5B2lqiQcpCCkH2QgZCAkIqQiZCPkKiQr5CxkLWQ4pDkYkiQ25ECkRKRGZEykTCRSpFWkViRY5FlkWmRc5FykYuRiZGCkaKRq5GvkaqRtZG0kbqRwJHBkcmRy5HQkdaR35HhkduR/JH1kfaSHpH/khSSLJIVkhGSXpJXkkWSSZJkkkiSlZI/kkuSUJKckpaSk5KbklqSz5K5kreS6ZMPkvqTRJMu////////kxmTIpMakyOTOpM1kzuTXJNgk3yTbpNWk7CTrJOtk5STuZPWk9eT6JPlk9iTw5Pdk9CTyJPklBqUFJQTlAOUB5QQlDaUK5Q1lCGUOpRBlFKURJRblGCUYpRelGqSKZRwlHWUd5R9lFqUfJR+lIGUf5WClYeVipWUlZaVmJWZ//+VoJWolaeVrZW8lbuVuZW+lcpv9pXDlc2VzJXVldSV1pXcleGV5ZXiliGWKJYuli+WQpZMlk+WS5Z3llyWXpZdll+WZpZylmyWjZaYlpWWl5aqlqeWsZaylrCWtJa2lriWuZbOlsuWyZbNiU2W3JcNltWW+ZcElwaXCJcTlw6XEZcPlxaXGZcklyqXMJc5lz2XPpdEl0aXSJdCl0mXXJdgl2SXZpdoUtKXa5dxl3mXhZd8l4GXepeGl4uXj5eQl5yXqJeml6OXs5e0l8OXxpfIl8uX3Jftn0+X8nrfl/aX9ZgPmAyYOJgkmCGYN5g9mEaYT5hLmGuYb5hw////////mHGYdJhzmKqYr5ixmLaYxJjDmMaY6ZjrmQOZCZkSmRSZGJkhmR2ZHpkkmSCZLJkumT2ZPplCmUmZRZlQmUuZUZlSmUyZVZmXmZiZpZmtma6ZvJnfmduZ3ZnYmdGZ7ZnumfGZ8pn7mfiaAZoPmgWZ4poZmiuaN5pFmkKaQJpD//+aPppVmk2aW5pXml+aYpplmmSaaZprmmqarZqwmryawJrPmtGa05rUmt6a35rimuOa5prvmuua7pr0mvGa95r7mwabGJsamx+bIpsjmyWbJ5somymbKpsumy+bMptEm0ObT5tNm06bUZtYm3Sbk5uDm5GblpuXm5+boJuom7SbwJvKm7mbxpvPm9Gb0pvjm+Kb5JvUm+GcOpvym/Gb8JwVnBScCZwTnAycBpwInBKcCpwEnC6cG5wlnCScIZwwnEecMpxGnD6cWpxgnGecdpx4nOec7JzwnQmdCJzrnQOdBp0qnSadr50jnR+dRJ0VnRKdQZ0/nT6dRp1I////////nV2dXp1knVGdUJ1ZnXKdiZ2Hnaudb516nZqdpJ2pnbKdxJ3BnbuduJ26ncadz53Cndmd0534nead7Z3vnf2eGp4bnh6edZ55nn2egZ6InouejJ6SnpWekZ6dnqWeqZ64nqqerZdhnsyezp7PntCe1J7cnt6e3Z7gnuWe6J7v//+e9J72nvee+Z77nvye/Z8Hnwh2t58VnyGfLJ8+n0qfUp9Un2OfX59gn2GfZp9nn2yfap93n3Kfdp+Vn5yfoFgvaceQWXRkUdxxmf///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////w=="
11 |
--------------------------------------------------------------------------------