From 0da56fd2460df121cdff69a75a322614e74485f0 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Fri, 24 Jun 2022 22:46:57 +0900 Subject: [PATCH 01/33] doc: Update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6c054d1..d8c9c94 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,14 @@ This is an rMQR Code image generator implemented in Python. This is implemented based on [ISO/IEC 23941:2022](https://www.iso.org/standard/77404.html). +Try this online (in Japanese): https://rmqr.oudon.xyz . + ## 📌 Important Notice - Please verify an image generated by this software whether it can decode correctly before use. - Because this is in early stage, QR Code readers may have not been supported rMQR Code yet. + ## 🚀 Installation ``` pip install rmqrcode From bf16068b25865d58666ac131436bdb45e6d7f606 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Sat, 25 Jun 2022 13:51:53 +0900 Subject: [PATCH 02/33] doc: Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8c9c94..9eb177e 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The `fit_strategy` parameter is enum value of `rmqrcode.FitStrategy` to specify - **`FitStrategy.BALANCED`**: Try to keep balance of width and height. Here is an example of images genereated by each fit strategies for data `Test test test`: -![Example of fit strategies](https://user-images.githubusercontent.com/14174940/172822478-4f2b5fb8-49bd-464f-b6b2-c7347f71cbf5.png) +![Example of fit strategies](https://user-images.githubusercontent.com/14174940/175758463-07a8e10d-faf8-4e56-8a01-356443e82d19.png) ### Save as image ```py From 34c42812f62b94b785f3a4b46193a444e3bd0be5 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Sat, 25 Jun 2022 14:01:45 +0900 Subject: [PATCH 03/33] doc: Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9eb177e..4a05087 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The `fit_strategy` parameter is enum value of `rmqrcode.FitStrategy` to specify - **`FitStrategy.BALANCED`**: Try to keep balance of width and height. Here is an example of images genereated by each fit strategies for data `Test test test`: -![Example of fit strategies](https://user-images.githubusercontent.com/14174940/175758463-07a8e10d-faf8-4e56-8a01-356443e82d19.png) +![Example of fit strategies](https://user-images.githubusercontent.com/14174940/175758907-714ae349-c81d-4027-86fc-59360d557f18.png) ### Save as image ```py From bf994226dc5c20d6ea09a72017ca11d052cf08f8 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Sat, 25 Jun 2022 14:08:25 +0900 Subject: [PATCH 04/33] doc: Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a05087..d12de5e 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ The `fit_strategy` parameter is enum value of `rmqrcode.FitStrategy` to specify - **`FitStrategy.BALANCED`**: Try to keep balance of width and height. Here is an example of images genereated by each fit strategies for data `Test test test`: -![Example of fit strategies](https://user-images.githubusercontent.com/14174940/175758907-714ae349-c81d-4027-86fc-59360d557f18.png) +![Example of fit strategies](https://user-images.githubusercontent.com/14174940/175759120-7fb5ec71-c258-4646-9b91-6865b3eeac3f.png) ### Save as image ```py From 636dea6e3ef9e4c8001ab869b6d7c7305ad509d5 Mon Sep 17 00:00:00 2001 From: Ar-Ray-code Date: Sun, 24 Jul 2022 16:13:20 +0900 Subject: [PATCH 05/33] add `get_ndarray` --- example.py | 9 +++++++++ src/rmqrcode/qr_image.py | 9 +++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/example.py b/example.py index 7e11b6c..1da5495 100644 --- a/example.py +++ b/example.py @@ -5,6 +5,7 @@ import logging +USE_NUMPY = True def main(): data = "https://oudon.xyz" @@ -26,6 +27,14 @@ def main(): image.show() image.save("my_qr.png") + # Convert to numpy array + if USE_NUMPY: + import cv2 + img = image.get_ndarray() + cv2.imshow("img", img) + cv2.waitKey(0) + cv2.destroyAllWindows() + def _init_logger(): logger = logging.getLogger() diff --git a/src/rmqrcode/qr_image.py b/src/rmqrcode/qr_image.py index a94050f..31b1d07 100644 --- a/src/rmqrcode/qr_image.py +++ b/src/rmqrcode/qr_image.py @@ -3,7 +3,6 @@ from PIL import Image from PIL import ImageDraw - class QRImage: def __init__(self, qr, module_size=10): self._module_size = module_size @@ -14,11 +13,17 @@ def __init__(self, qr, module_size=10): ) self._make_image(qr) - def show(self): self._img.show() pass + def get_ndarray(self): + try: + import numpy as np + except ImportError: + raise ImportError("numpy is not installed") + + return np.array(self._img) def save(self, name): self._img.save(name) From b26089ee406211ed49c00c115e72ccd02baa0ff0 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Wed, 27 Jul 2022 09:30:31 +0900 Subject: [PATCH 06/33] doc: Create CONTRIBUTING.md --- CONTRIBUTING.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..53b92d3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing to rmqrcode-python + +Thank you for interesting in contributing to rmqrcode-python! Any suggestions are welcome. + +## Style Guides +### Git Commit Message + +Consider starting commit message with one of the following prefixes. +- `feat:` : New feature +- `fix:` : Bug fix +- `refactor:` : Refactoring +- `chore:` : Little things +- `doc:` : Documentation From a3609859748708d36459abb14e465040f9bb1511 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Wed, 27 Jul 2022 09:57:52 +0900 Subject: [PATCH 07/33] doc: Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d12de5e..43f8c4d 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ image.save("my_qr.png") ``` -## 📚 Advanced Usage +## 📙 Advanced Usage ### Select rMQR Code size manually To select rMQR Code size manually, use `rMQR()` constructor. ```py @@ -111,6 +111,16 @@ The rMQR Code has the four encoding modes Numeric, Alphanumeric, Byte and Kanji |Byte|✅| |Kanji|| + +## 🤝 Contiributing +Any suggestions are welcome! If you are interesting in contiributing, please read [CONTRIBUTING](https://github.com/OUDON/rmqrcode-python/blob/develop/CONTRIBUTING.md). + + +## 📚 References +- [Rectangular Micro QR Code (rMQR) bar code symbology specification: ISO/IEC 23941:2022](https://www.iso.org/standard/77404.html) +- [Creating a QR Code step by step](https://www.nayuki.io/page/creating-a-qr-code-step-by-step) + + ---- The word "QR Code" is registered trademark of DENSO WAVE Incorporated.
http://www.denso-wave.com/qrcode/faqpatent-e.html From d7c70d9a8d5151430786c190209a39f20a695f9a Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Wed, 27 Jul 2022 21:02:19 +0900 Subject: [PATCH 08/33] feat: Use numpy only when numpy and cv2 are installed --- example.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/example.py b/example.py index 1da5495..16f7eb2 100644 --- a/example.py +++ b/example.py @@ -5,7 +5,14 @@ import logging -USE_NUMPY = True + +try: + import numpy + import cv2 + USE_NUMPY = True +except ImportError: + USE_NUMPY = False + def main(): data = "https://oudon.xyz" @@ -29,7 +36,6 @@ def main(): # Convert to numpy array if USE_NUMPY: - import cv2 img = image.get_ndarray() cv2.imshow("img", img) cv2.waitKey(0) From 37c33623d5897c58015fedaf9ec21570b88289e5 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 10:06:22 +0900 Subject: [PATCH 09/33] doc: Update CONTRIBUTING.md --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53b92d3..a6f9a73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,4 +10,5 @@ Consider starting commit message with one of the following prefixes. - `fix:` : Bug fix - `refactor:` : Refactoring - `chore:` : Little things +- `ci`: CI - `doc:` : Documentation From a4734c20f4eef4e98c96be3560ccfe177a4188ea Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 10:19:12 +0900 Subject: [PATCH 10/33] ci: Setup linter --- Makefile | 10 ++++++++++ pyproject.toml | 5 ++++- setup.cfg | 9 ++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..795901d --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +.PHONY: lint +lint: + flake8 src + isort --check --diff src + black --check src + +.PHONY: format +format: + isort src + black src \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index fa7093a..29402e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,6 @@ [build-system] requires = ["setuptools>=42"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" + +[tool.black] +line-length = 119 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index ada13c1..0ffa01e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,10 +23,17 @@ install_requires = [options.extras_require] dev = pytest + flake8 + isort + black [options.packages.find] where = src [options.entry_points] console_scripts = - rmqr = rmqrcode.console:main \ No newline at end of file + rmqr = rmqrcode.console:main + +[flake8] +max-line-length = 119 +extend-ignore = E203 \ No newline at end of file From 30541e1f6fd1ad9edcc7d6cec1d5a1c4e2daf8dd Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 10:27:35 +0900 Subject: [PATCH 11/33] ci: Exclude generator_polynomials.py from the lint targets --- pyproject.toml | 3 ++- setup.cfg | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 29402e7..9e5326f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,4 +3,5 @@ requires = ["setuptools>=42"] build-backend = "setuptools.build_meta" [tool.black] -line-length = 119 \ No newline at end of file +line-length = 119 +exclude = "generator_polynomials.py" \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 0ffa01e..99662bd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,4 +36,5 @@ console_scripts = [flake8] max-line-length = 119 -extend-ignore = E203 \ No newline at end of file +extend-ignore = E203 +exclude = src/rmqrcode/format/generator_polynomials.py \ No newline at end of file From a5ffd9ddefca679d7c1208f679e3065a0d63e003 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 10:32:08 +0900 Subject: [PATCH 12/33] refactor: Run formatter --- src/rmqrcode/__init__.py | 7 +- src/rmqrcode/console.py | 46 +- src/rmqrcode/encoder/byte_encoder.py | 6 +- src/rmqrcode/enums/color.py | 3 +- src/rmqrcode/enums/fit_strategy.py | 3 +- .../format/alignment_pattern_coordinates.py | 2 +- src/rmqrcode/format/data_capacities.py | 323 +++-- src/rmqrcode/format/error_correction_level.py | 3 +- src/rmqrcode/format/mask.py | 2 +- src/rmqrcode/format/rmqr_versions.py | 1143 ++++++++--------- src/rmqrcode/qr_image.py | 26 +- src/rmqrcode/rmqrcode.py | 146 +-- src/rmqrcode/util/error_correction.py | 16 +- src/rmqrcode/util/galois_fields.py | 4 +- src/rmqrcode/util/utilities.py | 6 +- 15 files changed, 863 insertions(+), 873 deletions(-) diff --git a/src/rmqrcode/__init__.py b/src/rmqrcode/__init__.py index 2b40cee..c537672 100644 --- a/src/rmqrcode/__init__.py +++ b/src/rmqrcode/__init__.py @@ -1,6 +1,3 @@ -from .rmqrcode import rMQR -from .rmqrcode import FitStrategy -from .rmqrcode import DataTooLongError -from .rmqrcode import IllegalVersionError +from .format.error_correction_level import ErrorCorrectionLevel from .qr_image import QRImage -from .format.error_correction_level import ErrorCorrectionLevel \ No newline at end of file +from .rmqrcode import DataTooLongError, FitStrategy, IllegalVersionError, rMQR diff --git a/src/rmqrcode/console.py b/src/rmqrcode/console.py index aa1117e..615ce29 100644 --- a/src/rmqrcode/console.py +++ b/src/rmqrcode/console.py @@ -1,15 +1,10 @@ #!/usr/bin/env python -import rmqrcode -from rmqrcode import rMQR -from rmqrcode import QRImage -from rmqrcode import ErrorCorrectionLevel -from rmqrcode import FitStrategy -from rmqrcode import DataTooLongError -from rmqrcode import IllegalVersionError - import argparse import sys +import rmqrcode +from rmqrcode import DataTooLongError, ErrorCorrectionLevel, FitStrategy, IllegalVersionError, QRImage, rMQR + def _show_error_and_exit(msg): print(msg, file=sys.stderr) @@ -29,7 +24,6 @@ def _make_qr(data, ecc, version, fit_strategy): return qr - def _save_image(qr, output): image = QRImage(qr) try: @@ -42,24 +36,19 @@ def main(): parser = _init_argparser() args = parser.parse_args() - if args.ecc == 'M': + if args.ecc == "M": ecc = ErrorCorrectionLevel.M - elif args.ecc == 'H': + elif args.ecc == "H": ecc = ErrorCorrectionLevel.H fit_strategy = FitStrategy.BALANCED - if args.fit_strategy == 'min_width': + if args.fit_strategy == "min_width": fit_strategy = FitStrategy.MINIMIZE_WIDTH - elif args.fit_strategy == 'min_height': + elif args.fit_strategy == "min_height": fit_strategy = FitStrategy.MINIMIZE_HEIGHT try: - qr = _make_qr( - args.DATA, - ecc=ecc, - version=args.version, - fit_strategy=fit_strategy - ) + qr = _make_qr(args.DATA, ecc=ecc, version=args.version, fit_strategy=fit_strategy) except DataTooLongError: _show_error_and_exit("Error: The data is too long.") @@ -68,13 +57,20 @@ def main(): def _init_argparser(): parser = argparse.ArgumentParser() - parser.add_argument('DATA', type=str, help="Data to encode.") - parser.add_argument('OUTPUT', type=str, help="Output file path") - parser.add_argument('--ecc', help="Error correction level. (default: M)", type=str, choices=["M", "H"], default='M') - parser.add_argument('--version', help="rMQR Code version like 'R11x139'.") - parser.add_argument('--fit-strategy', choices=["min_width", "min_height", "balanced"], help="Strategy how to determine rMQR Code size.", dest="fit_strategy") + parser.add_argument("DATA", type=str, help="Data to encode.") + parser.add_argument("OUTPUT", type=str, help="Output file path") + parser.add_argument( + "--ecc", help="Error correction level. (default: M)", type=str, choices=["M", "H"], default="M" + ) + parser.add_argument("--version", help="rMQR Code version like 'R11x139'.") + parser.add_argument( + "--fit-strategy", + choices=["min_width", "min_height", "balanced"], + help="Strategy how to determine rMQR Code size.", + dest="fit_strategy", + ) return parser if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/rmqrcode/encoder/byte_encoder.py b/src/rmqrcode/encoder/byte_encoder.py index 38b7c22..ed6bbdf 100644 --- a/src/rmqrcode/encoder/byte_encoder.py +++ b/src/rmqrcode/encoder/byte_encoder.py @@ -4,12 +4,11 @@ class ByteEncoder: @staticmethod def _encoded_bits(s): res = "" - encoded = s.encode('utf-8') + encoded = s.encode("utf-8") for byte in encoded: res += bin(byte)[2:].zfill(8) return res - @staticmethod def encode(data, character_count_length): res = ByteEncoder.MODE_INDICATOR @@ -17,7 +16,6 @@ def encode(data, character_count_length): res += ByteEncoder._encoded_bits(data) return res - @staticmethod def length(data): - return len(data.encode('utf-8')) + return len(data.encode("utf-8")) diff --git a/src/rmqrcode/enums/color.py b/src/rmqrcode/enums/color.py index 7aeb18d..324839b 100644 --- a/src/rmqrcode/enums/color.py +++ b/src/rmqrcode/enums/color.py @@ -1,6 +1,7 @@ from enum import Enum + class Color(Enum): UNDEFINED = -1 WHITE = 0 - BLACK = 1 \ No newline at end of file + BLACK = 1 diff --git a/src/rmqrcode/enums/fit_strategy.py b/src/rmqrcode/enums/fit_strategy.py index bd006e9..80588b3 100644 --- a/src/rmqrcode/enums/fit_strategy.py +++ b/src/rmqrcode/enums/fit_strategy.py @@ -1,6 +1,7 @@ from enum import Enum + class FitStrategy(Enum): MINIMIZE_WIDTH = 0 MINIMIZE_HEIGHT = 1 - BALANCED = 2 \ No newline at end of file + BALANCED = 2 diff --git a/src/rmqrcode/format/alignment_pattern_coordinates.py b/src/rmqrcode/format/alignment_pattern_coordinates.py index 249698b..7b95c3f 100644 --- a/src/rmqrcode/format/alignment_pattern_coordinates.py +++ b/src/rmqrcode/format/alignment_pattern_coordinates.py @@ -5,4 +5,4 @@ 77: [25, 51], 99: [23, 49, 75], 139: [27, 55, 83, 111], -} \ No newline at end of file +} diff --git a/src/rmqrcode/format/data_capacities.py b/src/rmqrcode/format/data_capacities.py index 58b3739..304f067 100644 --- a/src/rmqrcode/format/data_capacities.py +++ b/src/rmqrcode/format/data_capacities.py @@ -1,326 +1,325 @@ from .error_correction_level import ErrorCorrectionLevel - # ISO/IEC 23941:2022 Table 6 DataCapacities = { - 'R7x43': { - 'height': 7, - 'width': 43, - 'capacity': { - 'Byte': { + "R7x43": { + "height": 7, + "width": 43, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 5, ErrorCorrectionLevel.H: 2, }, }, }, - 'R7x59': { - 'height': 7, - 'width': 59, - 'capacity': { - 'Byte': { + "R7x59": { + "height": 7, + "width": 59, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 11, ErrorCorrectionLevel.H: 6, }, }, }, - 'R7x77': { - 'height': 7, - 'width': 77, - 'capacity': { - 'Byte': { + "R7x77": { + "height": 7, + "width": 77, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 19, ErrorCorrectionLevel.H: 9, }, }, }, - 'R7x99': { - 'height': 7, - 'width': 99, - 'capacity': { - 'Byte': { + "R7x99": { + "height": 7, + "width": 99, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 27, ErrorCorrectionLevel.H: 13, }, }, }, - 'R7x139': { - 'height': 7, - 'width': 139, - 'capacity': { - 'Byte': { + "R7x139": { + "height": 7, + "width": 139, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 42, ErrorCorrectionLevel.H: 22, }, }, }, - 'R9x43': { - 'height': 9, - 'width': 43, - 'capacity': { - 'Byte': { + "R9x43": { + "height": 9, + "width": 43, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 11, ErrorCorrectionLevel.H: 6, }, }, }, - 'R9x59': { - 'height': 9, - 'width': 59, - 'capacity': { - 'Byte': { + "R9x59": { + "height": 9, + "width": 59, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 20, ErrorCorrectionLevel.H: 10, }, }, }, - 'R9x77': { - 'height': 9, - 'width': 77, - 'capacity': { - 'Byte': { + "R9x77": { + "height": 9, + "width": 77, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 30, ErrorCorrectionLevel.H: 16, }, }, }, - 'R9x99': { - 'height': 9, - 'width': 99, - 'capacity': { - 'Byte': { + "R9x99": { + "height": 9, + "width": 99, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 40, ErrorCorrectionLevel.H: 20, }, }, }, - 'R9x139': { - 'height': 9, - 'width': 139, - 'capacity': { - 'Byte': { + "R9x139": { + "height": 9, + "width": 139, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 61, ErrorCorrectionLevel.H: 31, }, }, }, - 'R11x27': { - 'height': 11, - 'width': 27, - 'capacity': { - 'Byte': { + "R11x27": { + "height": 11, + "width": 27, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 6, ErrorCorrectionLevel.H: 4, }, }, }, - 'R11x43': { - 'height': 11, - 'width': 43, - 'capacity': { - 'Byte': { + "R11x43": { + "height": 11, + "width": 43, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 18, ErrorCorrectionLevel.H: 10, }, }, }, - 'R11x59': { - 'height': 11, - 'width': 59, - 'capacity': { - 'Byte': { + "R11x59": { + "height": 11, + "width": 59, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 30, ErrorCorrectionLevel.H: 14, }, }, }, - 'R11x77': { - 'height': 11, - 'width': 77, - 'capacity': { - 'Byte': { + "R11x77": { + "height": 11, + "width": 77, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 41, ErrorCorrectionLevel.H: 21, }, }, }, - 'R11x99': { - 'height': 11, - 'width': 99, - 'capacity': { - 'Byte': { + "R11x99": { + "height": 11, + "width": 99, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 55, ErrorCorrectionLevel.H: 27, }, }, }, - 'R11x139': { - 'height': 11, - 'width': 139, - 'capacity': { - 'Byte': { + "R11x139": { + "height": 11, + "width": 139, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 82, ErrorCorrectionLevel.H: 40, }, }, }, - 'R13x27': { - 'height': 13, - 'width': 27, - 'capacity': { - 'Byte': { + "R13x27": { + "height": 13, + "width": 27, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 11, ErrorCorrectionLevel.H: 6, }, }, }, - 'R13x43': { - 'height': 13, - 'width': 43, - 'capacity': { - 'Byte': { + "R13x43": { + "height": 13, + "width": 43, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 26, ErrorCorrectionLevel.H: 12, }, }, }, - 'R13x59': { - 'height': 13, - 'width': 59, - 'capacity': { - 'Byte': { + "R13x59": { + "height": 13, + "width": 59, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 36, ErrorCorrectionLevel.H: 18, }, }, }, - 'R13x77': { - 'height': 13, - 'width': 77, - 'capacity': { - 'Byte': { + "R13x77": { + "height": 13, + "width": 77, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 51, ErrorCorrectionLevel.H: 27, }, }, }, - 'R13x99': { - 'height': 13, - 'width': 99, - 'capacity': { - 'Byte': { + "R13x99": { + "height": 13, + "width": 99, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 71, ErrorCorrectionLevel.H: 33, }, }, }, - 'R13x139': { - 'height': 13, - 'width': 139, - 'capacity': { - 'Byte': { + "R13x139": { + "height": 13, + "width": 139, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 104, ErrorCorrectionLevel.H: 52, }, }, }, - 'R15x43': { - 'height': 15, - 'width': 43, - 'capacity': { - 'Byte': { + "R15x43": { + "height": 15, + "width": 43, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 31, ErrorCorrectionLevel.H: 13, }, }, }, - 'R15x59': { - 'height': 15, - 'width': 59, - 'capacity': { - 'Byte': { + "R15x59": { + "height": 15, + "width": 59, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 46, ErrorCorrectionLevel.H: 24, }, }, }, - 'R15x77': { - 'height': 15, - 'width': 77, - 'capacity': { - 'Byte': { + "R15x77": { + "height": 15, + "width": 77, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 65, ErrorCorrectionLevel.H: 29, }, }, }, - 'R15x99': { - 'height': 15, - 'width': 99, - 'capacity': { - 'Byte': { + "R15x99": { + "height": 15, + "width": 99, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 86, ErrorCorrectionLevel.H: 46, }, }, }, - 'R15x139': { - 'height': 15, - 'width': 139, - 'capacity': { - 'Byte': { + "R15x139": { + "height": 15, + "width": 139, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 125, ErrorCorrectionLevel.H: 67, }, }, }, - 'R17x43': { - 'height': 17, - 'width': 43, - 'capacity': { - 'Byte': { + "R17x43": { + "height": 17, + "width": 43, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 37, ErrorCorrectionLevel.H: 19, }, }, }, - 'R17x59': { - 'height': 17, - 'width': 59, - 'capacity': { - 'Byte': { + "R17x59": { + "height": 17, + "width": 59, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 54, ErrorCorrectionLevel.H: 26, }, }, }, - 'R17x77': { - 'height': 17, - 'width': 77, - 'capacity': { - 'Byte': { + "R17x77": { + "height": 17, + "width": 77, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 76, ErrorCorrectionLevel.H: 36, }, }, }, - 'R17x99': { - 'height': 17, - 'width': 99, - 'capacity': { - 'Byte': { + "R17x99": { + "height": 17, + "width": 99, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 98, ErrorCorrectionLevel.H: 54, }, }, }, - 'R17x139': { - 'height': 17, - 'width': 139, - 'capacity': { - 'Byte': { + "R17x139": { + "height": 17, + "width": 139, + "capacity": { + "Byte": { ErrorCorrectionLevel.M: 150, ErrorCorrectionLevel.H: 74, }, }, }, -} \ No newline at end of file +} diff --git a/src/rmqrcode/format/error_correction_level.py b/src/rmqrcode/format/error_correction_level.py index 7483698..84e18e3 100644 --- a/src/rmqrcode/format/error_correction_level.py +++ b/src/rmqrcode/format/error_correction_level.py @@ -1,5 +1,6 @@ from enum import Enum + class ErrorCorrectionLevel(Enum): M = 0 - H = 1 \ No newline at end of file + H = 1 diff --git a/src/rmqrcode/format/mask.py b/src/rmqrcode/format/mask.py index 4822236..4ce92b7 100644 --- a/src/rmqrcode/format/mask.py +++ b/src/rmqrcode/format/mask.py @@ -1,2 +1,2 @@ def mask(x, y): - return (y//2 + x//3) % 2 == 0 + return (y // 2 + x // 3) % 2 == 0 diff --git a/src/rmqrcode/format/rmqr_versions.py b/src/rmqrcode/format/rmqr_versions.py index 2bc9232..6861772 100644 --- a/src/rmqrcode/format/rmqr_versions.py +++ b/src/rmqrcode/format/rmqr_versions.py @@ -1,878 +1,877 @@ from .error_correction_level import ErrorCorrectionLevel - rMQRVersions = { - 'R7x43': { - 'version_indicator': 0b00000, - 'height': 7, - 'width': 43, - 'remainder_bits': 0, - 'character_count_length': 3, - 'codewords_total': 13, - 'blocks': { + "R7x43": { + "version_indicator": 0b00000, + "height": 7, + "width": 43, + "remainder_bits": 0, + "character_count_length": 3, + "codewords_total": 13, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 13, - 'k': 6, + "num": 1, + "c": 13, + "k": 6, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 13, - 'k': 3, + "num": 1, + "c": 13, + "k": 3, }, - ] - } + ], + }, }, - 'R7x59': { - 'version_indicator': 0b00001, - 'height': 7, - 'width': 59, - 'remainder_bits': 3, - 'character_count_length': 4, - 'codewords_total': 21, - 'blocks': { + "R7x59": { + "version_indicator": 0b00001, + "height": 7, + "width": 59, + "remainder_bits": 3, + "character_count_length": 4, + "codewords_total": 21, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 21, - 'k': 12, + "num": 1, + "c": 21, + "k": 12, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 21, - 'k': 7, + "num": 1, + "c": 21, + "k": 7, }, - ] - } + ], + }, }, - 'R7x77': { - 'version_indicator': 0b00010, - 'height': 7, - 'width': 77, - 'remainder_bits': 5, - 'character_count_length': 5, - 'codewords_total': 32, - 'blocks': { + "R7x77": { + "version_indicator": 0b00010, + "height": 7, + "width": 77, + "remainder_bits": 5, + "character_count_length": 5, + "codewords_total": 32, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 32, - 'k': 20, + "num": 1, + "c": 32, + "k": 20, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 32, - 'k': 10, + "num": 1, + "c": 32, + "k": 10, }, - ] - } + ], + }, }, - 'R7x99': { - 'version_indicator': 0b00011, - 'height': 7, - 'width': 99, - 'remainder_bits': 6, - 'character_count_length': 5, - 'codewords_total': 44, - 'blocks': { + "R7x99": { + "version_indicator": 0b00011, + "height": 7, + "width": 99, + "remainder_bits": 6, + "character_count_length": 5, + "codewords_total": 44, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 44, - 'k': 28, + "num": 1, + "c": 44, + "k": 28, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 44, - 'k': 14, + "num": 1, + "c": 44, + "k": 14, }, - ] - } + ], + }, }, - 'R7x139': { - 'version_indicator': 0b00100, - 'height': 7, - 'width': 139, - 'remainder_bits': 1, - 'character_count_length': 6, - 'codewords_total': 68, - 'blocks': { + "R7x139": { + "version_indicator": 0b00100, + "height": 7, + "width": 139, + "remainder_bits": 1, + "character_count_length": 6, + "codewords_total": 68, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 68, - 'k': 44, + "num": 1, + "c": 68, + "k": 44, }, ], ErrorCorrectionLevel.H: [ { - 'num': 2, - 'c': 34, - 'k': 12, + "num": 2, + "c": 34, + "k": 12, }, - ] - } + ], + }, }, - 'R9x43': { - 'version_indicator': 0b00101, - 'height': 9, - 'width': 43, - 'remainder_bits': 2, - 'character_count_length': 4, - 'codewords_total': 21, - 'blocks': { + "R9x43": { + "version_indicator": 0b00101, + "height": 9, + "width": 43, + "remainder_bits": 2, + "character_count_length": 4, + "codewords_total": 21, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 21, - 'k': 12, + "num": 1, + "c": 21, + "k": 12, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 21, - 'k': 7, + "num": 1, + "c": 21, + "k": 7, }, - ] - } + ], + }, }, - 'R9x59': { - 'version_indicator': 0b00110, - 'height': 9, - 'width': 59, - 'remainder_bits': 3, - 'character_count_length': 5, - 'codewords_total': 33, - 'blocks': { + "R9x59": { + "version_indicator": 0b00110, + "height": 9, + "width": 59, + "remainder_bits": 3, + "character_count_length": 5, + "codewords_total": 33, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 33, - 'k': 21, + "num": 1, + "c": 33, + "k": 21, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 33, - 'k': 11, + "num": 1, + "c": 33, + "k": 11, }, - ] - } + ], + }, }, - 'R9x77': { - 'version_indicator': 0b00111, - 'height': 9, - 'width': 77, - 'remainder_bits': 1, - 'character_count_length': 5, - 'codewords_total': 49, - 'blocks': { + "R9x77": { + "version_indicator": 0b00111, + "height": 9, + "width": 77, + "remainder_bits": 1, + "character_count_length": 5, + "codewords_total": 49, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 49, - 'k': 31, + "num": 1, + "c": 49, + "k": 31, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 24, - 'k': 8, + "num": 1, + "c": 24, + "k": 8, }, { - 'num': 1, - 'c': 25, - 'k': 9, + "num": 1, + "c": 25, + "k": 9, }, - ] - } + ], + }, }, - 'R9x99': { - 'version_indicator': 0b01000, - 'height': 9, - 'width': 99, - 'remainder_bits': 4, - 'character_count_length': 6, - 'codewords_total': 66, - 'blocks': { + "R9x99": { + "version_indicator": 0b01000, + "height": 9, + "width": 99, + "remainder_bits": 4, + "character_count_length": 6, + "codewords_total": 66, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 66, - 'k': 42, + "num": 1, + "c": 66, + "k": 42, }, ], ErrorCorrectionLevel.H: [ { - 'num': 2, - 'c': 33, - 'k': 11, + "num": 2, + "c": 33, + "k": 11, }, - ] - } + ], + }, }, - 'R9x139': { - 'version_indicator': 0b01001, - 'height': 9, - 'width': 139, - 'remainder_bits': 5, - 'character_count_length': 6, - 'codewords_total': 99, - 'blocks': { + "R9x139": { + "version_indicator": 0b01001, + "height": 9, + "width": 139, + "remainder_bits": 5, + "character_count_length": 6, + "codewords_total": 99, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 49, - 'k': 31, + "num": 1, + "c": 49, + "k": 31, }, { - 'num': 1, - 'c': 50, - 'k': 32, + "num": 1, + "c": 50, + "k": 32, }, ], ErrorCorrectionLevel.H: [ { - 'num': 3, - 'c': 33, - 'k': 11, + "num": 3, + "c": 33, + "k": 11, } - ] - } + ], + }, }, - 'R11x27': { - 'version_indicator': 0b01010, - 'height': 11, - 'width': 27, - 'remainder_bits': 2, - 'character_count_length': 3, - 'codewords_total': 15, - 'blocks': { + "R11x27": { + "version_indicator": 0b01010, + "height": 11, + "width": 27, + "remainder_bits": 2, + "character_count_length": 3, + "codewords_total": 15, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 15, - 'k': 7, + "num": 1, + "c": 15, + "k": 7, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 15, - 'k': 5, + "num": 1, + "c": 15, + "k": 5, } - ] - } + ], + }, }, - 'R11x43': { - 'version_indicator': 0b01011, - 'height': 11, - 'width': 43, - 'remainder_bits': 1, - 'character_count_length': 5, - 'codewords_total': 31, - 'blocks': { + "R11x43": { + "version_indicator": 0b01011, + "height": 11, + "width": 43, + "remainder_bits": 1, + "character_count_length": 5, + "codewords_total": 31, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 31, - 'k': 19, + "num": 1, + "c": 31, + "k": 19, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 31, - 'k': 11, + "num": 1, + "c": 31, + "k": 11, } - ] - } + ], + }, }, - 'R11x59': { - 'version_indicator': 0b01100, - 'height': 11, - 'width': 59, - 'remainder_bits': 0, - 'character_count_length': 5, - 'codewords_total': 47, - 'blocks': { + "R11x59": { + "version_indicator": 0b01100, + "height": 11, + "width": 59, + "remainder_bits": 0, + "character_count_length": 5, + "codewords_total": 47, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 47, - 'k': 31, + "num": 1, + "c": 47, + "k": 31, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 23, - 'k': 7, + "num": 1, + "c": 23, + "k": 7, }, { - 'num': 1, - 'c': 24, - 'k': 8, + "num": 1, + "c": 24, + "k": 8, }, - ] - } + ], + }, }, - 'R11x77': { - 'version_indicator': 0b01101, - 'height': 11, - 'width': 77, - 'remainder_bits': 2, - 'character_count_length': 6, - 'codewords_total': 67, - 'blocks': { + "R11x77": { + "version_indicator": 0b01101, + "height": 11, + "width": 77, + "remainder_bits": 2, + "character_count_length": 6, + "codewords_total": 67, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 67, - 'k': 43, + "num": 1, + "c": 67, + "k": 43, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 33, - 'k': 11, + "num": 1, + "c": 33, + "k": 11, }, { - 'num': 1, - 'c': 34, - 'k': 12, + "num": 1, + "c": 34, + "k": 12, }, - ] - } + ], + }, }, - 'R11x99': { - 'version_indicator': 0b01110, - 'height': 11, - 'width': 99, - 'remainder_bits': 7, - 'character_count_length': 6, - 'codewords_total': 89, - 'blocks': { + "R11x99": { + "version_indicator": 0b01110, + "height": 11, + "width": 99, + "remainder_bits": 7, + "character_count_length": 6, + "codewords_total": 89, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 44, - 'k': 28, + "num": 1, + "c": 44, + "k": 28, }, { - 'num': 1, - 'c': 45, - 'k': 29, + "num": 1, + "c": 45, + "k": 29, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 44, - 'k': 14, + "num": 1, + "c": 44, + "k": 14, }, { - 'num': 1, - 'c': 45, - 'k': 15, + "num": 1, + "c": 45, + "k": 15, }, - ] - } + ], + }, }, - 'R11x139': { - 'version_indicator': 0b01111, - 'height': 11, - 'width': 139, - 'remainder_bits': 6, - 'character_count_length': 7, - 'codewords_total': 132, - 'blocks': { + "R11x139": { + "version_indicator": 0b01111, + "height": 11, + "width": 139, + "remainder_bits": 6, + "character_count_length": 7, + "codewords_total": 132, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 2, - 'c': 66, - 'k': 42, + "num": 2, + "c": 66, + "k": 42, }, ], ErrorCorrectionLevel.H: [ { - 'num': 3, - 'c': 44, - 'k': 14, + "num": 3, + "c": 44, + "k": 14, } - ] - } + ], + }, }, - 'R13x27': { - 'version_indicator': 0b10000, - 'height': 13, - 'width': 27, - 'character_count_length': 4, - 'remainder_bits': 4, - 'codewords_total': 21, - 'blocks': { + "R13x27": { + "version_indicator": 0b10000, + "height": 13, + "width": 27, + "character_count_length": 4, + "remainder_bits": 4, + "codewords_total": 21, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 21, - 'k': 14, + "num": 1, + "c": 21, + "k": 14, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 21, - 'k': 7, + "num": 1, + "c": 21, + "k": 7, } - ] - } + ], + }, }, - 'R13x43': { - 'version_indicator': 0b10001, - 'height': 13, - 'width': 43, - 'remainder_bits': 1, - 'character_count_length': 5, - 'codewords_total': 41, - 'blocks': { + "R13x43": { + "version_indicator": 0b10001, + "height": 13, + "width": 43, + "remainder_bits": 1, + "character_count_length": 5, + "codewords_total": 41, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 41, - 'k': 27, + "num": 1, + "c": 41, + "k": 27, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 41, - 'k': 13, + "num": 1, + "c": 41, + "k": 13, } - ] - } + ], + }, }, - 'R13x59': { - 'version_indicator': 0b10010, - 'height': 13, - 'width': 59, - 'remainder_bits': 6, - 'character_count_length': 6, - 'codewords_total': 60, - 'blocks': { + "R13x59": { + "version_indicator": 0b10010, + "height": 13, + "width": 59, + "remainder_bits": 6, + "character_count_length": 6, + "codewords_total": 60, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 60, - 'k': 38, + "num": 1, + "c": 60, + "k": 38, }, ], ErrorCorrectionLevel.H: [ { - 'num': 2, - 'c': 30, - 'k': 10, + "num": 2, + "c": 30, + "k": 10, } - ] - } + ], + }, }, - 'R13x77': { - 'version_indicator': 0b10011, - 'height': 13, - 'width': 77, - 'remainder_bits': 4, - 'character_count_length': 6, - 'codewords_total': 85, - 'blocks': { + "R13x77": { + "version_indicator": 0b10011, + "height": 13, + "width": 77, + "remainder_bits": 4, + "character_count_length": 6, + "codewords_total": 85, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 42, - 'k': 26, + "num": 1, + "c": 42, + "k": 26, }, { - 'num': 1, - 'c': 43, - 'k': 27, + "num": 1, + "c": 43, + "k": 27, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 42, - 'k': 14, + "num": 1, + "c": 42, + "k": 14, }, { - 'num': 1, - 'c': 43, - 'k': 15, + "num": 1, + "c": 43, + "k": 15, }, - ] - } + ], + }, }, - 'R13x99': { - 'version_indicator': 0b10100, - 'height': 13, - 'width': 99, - 'remainder_bits': 3, - 'character_count_length': 7, - 'codewords_total': 113, - 'blocks': { + "R13x99": { + "version_indicator": 0b10100, + "height": 13, + "width": 99, + "remainder_bits": 3, + "character_count_length": 7, + "codewords_total": 113, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 56, - 'k': 36, + "num": 1, + "c": 56, + "k": 36, }, { - 'num': 1, - 'c': 57, - 'k': 37, + "num": 1, + "c": 57, + "k": 37, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 37, - 'k': 11, + "num": 1, + "c": 37, + "k": 11, }, { - 'num': 2, - 'c': 38, - 'k': 12, + "num": 2, + "c": 38, + "k": 12, }, - ] - } + ], + }, }, - 'R13x139': { - 'version_indicator': 0b10101, - 'height': 13, - 'width': 139, - 'remainder_bits': 0, - 'character_count_length': 7, - 'codewords_total': 166, - 'blocks': { + "R13x139": { + "version_indicator": 0b10101, + "height": 13, + "width": 139, + "remainder_bits": 0, + "character_count_length": 7, + "codewords_total": 166, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 2, - 'c': 55, - 'k': 35, + "num": 2, + "c": 55, + "k": 35, }, { - 'num': 1, - 'c': 56, - 'k': 36, + "num": 1, + "c": 56, + "k": 36, }, ], ErrorCorrectionLevel.H: [ { - 'num': 2, - 'c': 41, - 'k': 13, + "num": 2, + "c": 41, + "k": 13, }, { - 'num': 2, - 'c': 42, - 'k': 14, + "num": 2, + "c": 42, + "k": 14, }, - ] - } + ], + }, }, - 'R15x43': { - 'version_indicator': 0b10110, - 'height': 15, - 'width': 43, - 'remainder_bits': 1, - 'character_count_length': 6, - 'codewords_total': 51, - 'blocks': { + "R15x43": { + "version_indicator": 0b10110, + "height": 15, + "width": 43, + "remainder_bits": 1, + "character_count_length": 6, + "codewords_total": 51, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 51, - 'k': 33, + "num": 1, + "c": 51, + "k": 33, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 25, - 'k': 7, + "num": 1, + "c": 25, + "k": 7, }, { - 'num': 1, - 'c': 26, - 'k': 8, + "num": 1, + "c": 26, + "k": 8, }, - ] - } + ], + }, }, - 'R15x59': { - 'version_indicator': 0b10111, - 'height': 15, - 'width': 59, - 'remainder_bits': 4, - 'character_count_length': 6, - 'codewords_total': 74, - 'blocks': { + "R15x59": { + "version_indicator": 0b10111, + "height": 15, + "width": 59, + "remainder_bits": 4, + "character_count_length": 6, + "codewords_total": 74, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 74, - 'k': 48, + "num": 1, + "c": 74, + "k": 48, }, ], ErrorCorrectionLevel.H: [ { - 'num': 2, - 'c': 37, - 'k': 13, + "num": 2, + "c": 37, + "k": 13, } - ] - } + ], + }, }, - 'R15x77': { - 'version_indicator': 0b11000, - 'height': 15, - 'width': 77, - 'remainder_bits': 6, - 'character_count_length': 7, - 'codewords_total': 103, - 'blocks': { + "R15x77": { + "version_indicator": 0b11000, + "height": 15, + "width": 77, + "remainder_bits": 6, + "character_count_length": 7, + "codewords_total": 103, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 51, - 'k': 33, + "num": 1, + "c": 51, + "k": 33, }, { - 'num': 1, - 'c': 52, - 'k': 34, + "num": 1, + "c": 52, + "k": 34, }, ], ErrorCorrectionLevel.H: [ { - 'num': 2, - 'c': 34, - 'k': 10, + "num": 2, + "c": 34, + "k": 10, }, { - 'num': 1, - 'c': 35, - 'k': 11, + "num": 1, + "c": 35, + "k": 11, }, - ] - } + ], + }, }, - 'R15x99': { - 'version_indicator': 0b11001, - 'height': 15, - 'width': 99, - 'remainder_bits': 7, - 'character_count_length': 7, - 'codewords_total': 136, - 'blocks': { + "R15x99": { + "version_indicator": 0b11001, + "height": 15, + "width": 99, + "remainder_bits": 7, + "character_count_length": 7, + "codewords_total": 136, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 2, - 'c': 68, - 'k': 44, + "num": 2, + "c": 68, + "k": 44, }, ], ErrorCorrectionLevel.H: [ { - 'num': 4, - 'c': 34, - 'k': 12, + "num": 4, + "c": 34, + "k": 12, }, ], - } + }, }, - 'R15x139': { - 'version_indicator': 0b11010, - 'height': 15, - 'width': 139, - 'remainder_bits': 2, - 'character_count_length': 7, - 'codewords_total': 199, - 'blocks': { + "R15x139": { + "version_indicator": 0b11010, + "height": 15, + "width": 139, + "remainder_bits": 2, + "character_count_length": 7, + "codewords_total": 199, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 2, - 'c': 66, - 'k': 42, + "num": 2, + "c": 66, + "k": 42, }, { - 'num': 1, - 'c': 67, - 'k': 43, + "num": 1, + "c": 67, + "k": 43, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 39, - 'k': 13, + "num": 1, + "c": 39, + "k": 13, }, { - 'num': 4, - 'c': 40, - 'k': 14, + "num": 4, + "c": 40, + "k": 14, }, ], - } + }, }, - 'R17x43': { - 'version_indicator': 0b11011, - 'height': 17, - 'width': 43, - 'remainder_bits': 1, - 'character_count_length': 6, - 'codewords_total': 61, - 'blocks': { + "R17x43": { + "version_indicator": 0b11011, + "height": 17, + "width": 43, + "remainder_bits": 1, + "character_count_length": 6, + "codewords_total": 61, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 1, - 'c': 60, - 'k': 39, + "num": 1, + "c": 60, + "k": 39, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 30, - 'k': 10, + "num": 1, + "c": 30, + "k": 10, }, { - 'num': 1, - 'c': 31, - 'k': 11, + "num": 1, + "c": 31, + "k": 11, }, - ] - } + ], + }, }, - 'R17x59': { - 'version_indicator': 0b11100, - 'height': 17, - 'width': 59, - 'remainder_bits': 2, - 'character_count_length': 6, - 'codewords_total': 88, - 'blocks': { + "R17x59": { + "version_indicator": 0b11100, + "height": 17, + "width": 59, + "remainder_bits": 2, + "character_count_length": 6, + "codewords_total": 88, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 2, - 'c': 44, - 'k': 28, + "num": 2, + "c": 44, + "k": 28, }, ], ErrorCorrectionLevel.H: [ { - 'num': 2, - 'c': 44, - 'k': 14, + "num": 2, + "c": 44, + "k": 14, } - ] - } + ], + }, }, - 'R17x77': { - 'version_indicator': 0b11101, - 'height': 17, - 'width': 77, - 'remainder_bits': 0, - 'character_count_length': 7, - 'codewords_total': 122, - 'blocks': { + "R17x77": { + "version_indicator": 0b11101, + "height": 17, + "width": 77, + "remainder_bits": 0, + "character_count_length": 7, + "codewords_total": 122, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 2, - 'c': 61, - 'k': 39, + "num": 2, + "c": 61, + "k": 39, }, ], ErrorCorrectionLevel.H: [ { - 'num': 1, - 'c': 40, - 'k': 12, + "num": 1, + "c": 40, + "k": 12, }, { - 'num': 2, - 'c': 41, - 'k': 13, + "num": 2, + "c": 41, + "k": 13, }, ], - } + }, }, - 'R17x99': { - 'version_indicator': 0b11110, - 'height': 17, - 'width': 99, - 'remainder_bits': 3, - 'character_count_length': 7, - 'codewords_total': 160, - 'blocks': { + "R17x99": { + "version_indicator": 0b11110, + "height": 17, + "width": 99, + "remainder_bits": 3, + "character_count_length": 7, + "codewords_total": 160, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 2, - 'c': 53, - 'k': 33, + "num": 2, + "c": 53, + "k": 33, }, { - 'num': 1, - 'c': 54, - 'k': 34, - } + "num": 1, + "c": 54, + "k": 34, + }, ], ErrorCorrectionLevel.H: [ { - 'num': 4, - 'c': 40, - 'k': 14, + "num": 4, + "c": 40, + "k": 14, }, ], - } + }, }, - 'R17x139': { - 'version_indicator': 0b11111, - 'height': 17, - 'width': 139, - 'remainder_bits': 4, - 'character_count_length': 8, - 'codewords_total': 232, - 'blocks': { + "R17x139": { + "version_indicator": 0b11111, + "height": 17, + "width": 139, + "remainder_bits": 4, + "character_count_length": 8, + "codewords_total": 232, + "blocks": { ErrorCorrectionLevel.M: [ { - 'num': 4, - 'c': 58, - 'k': 38, + "num": 4, + "c": 58, + "k": 38, }, ], ErrorCorrectionLevel.H: [ { - 'num': 2, - 'c': 38, - 'k': 12, + "num": 2, + "c": 38, + "k": 12, }, { - 'num': 4, - 'c': 39, - 'k': 13, + "num": 4, + "c": 39, + "k": 13, }, ], - } + }, }, } diff --git a/src/rmqrcode/qr_image.py b/src/rmqrcode/qr_image.py index 31b1d07..f17d7b9 100644 --- a/src/rmqrcode/qr_image.py +++ b/src/rmqrcode/qr_image.py @@ -1,15 +1,13 @@ +from PIL import Image, ImageDraw + from .enums.color import Color -from PIL import Image -from PIL import ImageDraw class QRImage: def __init__(self, qr, module_size=10): self._module_size = module_size self._img = Image.new( - 'RGB', - ((qr.width() + 4) * module_size, (qr.height() + 4) * module_size), - (255, 255, 255) + "RGB", ((qr.width() + 4) * module_size, (qr.height() + 4) * module_size), (255, 255, 255) ) self._make_image(qr) @@ -28,7 +26,6 @@ def get_ndarray(self): def save(self, name): self._img.save(name) - def _make_image(self, qr): draw = ImageDraw.Draw(self._img) for y in range(qr.height()): @@ -37,8 +34,17 @@ def _make_image(self, qr): if qr.value_at(x, y) == Color.BLACK: r, g, b = 0, 0, 0 elif qr.value_at(x, y) == Color.WHITE: - r, g, b, = 255, 255, 255 + r, g, b, = ( + 255, + 255, + 255, + ) draw.rectangle( - xy=((x + 2) * self._module_size, (y + 2) * self._module_size, (x + 1 + 2) * self._module_size, (y + 1 + 2) * self._module_size), - fill=(r, g, b) - ) \ No newline at end of file + xy=( + (x + 2) * self._module_size, + (y + 2) * self._module_size, + (x + 1 + 2) * self._module_size, + (y + 1 + 2) * self._module_size, + ), + fill=(r, g, b), + ) diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index 466934b..4187484 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -1,17 +1,17 @@ -from .format.error_correction_level import ErrorCorrectionLevel -from .format.rmqr_versions import rMQRVersions -from .format.data_capacities import DataCapacities +import logging + +from .encoder.byte_encoder import ByteEncoder +from .enums.color import Color +from .enums.fit_strategy import FitStrategy from .format.alignment_pattern_coordinates import AlignmentPatternCoordinates +from .format.data_capacities import DataCapacities +from .format.error_correction_level import ErrorCorrectionLevel from .format.generator_polynomials import GeneratorPolynomials from .format.mask import mask - -from .encoder.byte_encoder import ByteEncoder +from .format.rmqr_versions import rMQRVersions from .util.error_correction import compute_bch, compute_reed_solomon from .util.utilities import split_into_8bits -from .enums.color import Color -from .enums.fit_strategy import FitStrategy -import logging class rMQR: @staticmethod @@ -22,9 +22,8 @@ def _init_logger(): logger.propagate = True return logger - @staticmethod - def fit(data,ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): + def fit(data, ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): logger = rMQR._init_logger() data_length = ByteEncoder.length(data) @@ -34,35 +33,36 @@ def fit(data,ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): logger.debug("Select rMQR Code version") for version_name, qr_version in DataCapacities.items(): - if data_length <= qr_version['capacity']['Byte'][ecc]: - width, height = qr_version['width'], qr_version['height'] + if data_length <= qr_version["capacity"]["Byte"][ecc]: + width, height = qr_version["width"], qr_version["height"] if not width in determined_width and not height in determined_height: determined_width.add(width) determined_height.add(height) - ok_versions.append({ - 'version': version_name, - 'width': width, - 'height': height, - }) + ok_versions.append( + { + "version": version_name, + "width": width, + "height": height, + } + ) logger.debug(f"ok: {version_name}") if len(ok_versions) == 0: raise DataTooLongError("The data is too long.") if fit_strategy == FitStrategy.MINIMIZE_WIDTH: - sort_key = lambda x: x['width'] + sort_key = lambda x: x["width"] elif fit_strategy == FitStrategy.MINIMIZE_HEIGHT: - sort_key = lambda x: x['height'] + sort_key = lambda x: x["height"] elif fit_strategy == FitStrategy.BALANCED: - sort_key = lambda x: x['height'] * 9 + x['width'] + sort_key = lambda x: x["height"] * 9 + x["width"] selected = sorted(ok_versions, key=sort_key)[0] logger.debug(f"selected: {selected}") - qr = rMQR(selected['version'], ecc) + qr = rMQR(selected["version"], ecc) qr.make(data) return qr - def __init__(self, version, ecc, logger=None): self._logger = logger or rMQR._init_logger() @@ -71,46 +71,38 @@ def __init__(self, version, ecc, logger=None): qr_version = rMQRVersions[version] self._version = version - self._height = qr_version['height'] - self._width = qr_version['width'] + self._height = qr_version["height"] + self._width = qr_version["width"] self._error_correction_level = ecc self._qr = [[Color.UNDEFINED for x in range(self._width)] for y in range(self._height)] - def make(self, data): self._put_finder_pattern() self._put_corner_finder_pattern() self._put_alignment_pattern() self._put_timing_pattern() self._put_version_information() - mask_area = self._put_data(data); + mask_area = self._put_data(data) self._apply_mask(mask_area) - def version_name(self): return f"R{self._height}x{self._width}" - def size(self): return (self.width(), self.height()) - def height(self): return self._height - def width(self): return self._width - def value_at(self, x, y): return self._qr[y][x] - def to_list(self): return [list(map(lambda x: 1 if x == Color.BLACK else 0, column)) for column in self._qr] - def __str__(self): res = "" @@ -131,7 +123,6 @@ def __str__(self): res += "\n" return res - def _put_finder_pattern(self): # Finder pattern # Outer square @@ -145,7 +136,7 @@ def _put_finder_pattern(self): # Inner square for i in range(3): for j in range(3): - self._qr[2+i][2+j] = Color.BLACK + self._qr[2 + i][2 + j] = Color.BLACK # Separator for n in range(8): @@ -160,29 +151,27 @@ def _put_finder_pattern(self): for i in range(5): for j in range(5): color = Color.BLACK if i == 0 or i == 4 or j == 0 or j == 4 else Color.WHITE - self._qr[self._height-i-1][self._width-j-1] = color + self._qr[self._height - i - 1][self._width - j - 1] = color # Inner square - self._qr[self._height-1-2][self._width-1-2] = Color.BLACK - + self._qr[self._height - 1 - 2][self._width - 1 - 2] = Color.BLACK def _put_corner_finder_pattern(self): # Corner finder pattern # Bottom left - self._qr[self._height-1][0] = Color.BLACK - self._qr[self._height-1][1] = Color.BLACK - self._qr[self._height-1][2] = Color.BLACK + self._qr[self._height - 1][0] = Color.BLACK + self._qr[self._height - 1][1] = Color.BLACK + self._qr[self._height - 1][2] = Color.BLACK if self._height >= 11: - self._qr[self._height-2][0] = Color.BLACK - self._qr[self._height-2][1] = Color.WHITE + self._qr[self._height - 2][0] = Color.BLACK + self._qr[self._height - 2][1] = Color.WHITE # Top right - self._qr[0][self._width-1] = Color.BLACK - self._qr[0][self._width-2] = Color.BLACK - self._qr[1][self._width-1] = Color.BLACK - self._qr[1][self._width-2] = Color.WHITE - + self._qr[0][self._width - 1] = Color.BLACK + self._qr[0][self._width - 2] = Color.BLACK + self._qr[1][self._width - 1] = Color.BLACK + self._qr[1][self._width - 2] = Color.WHITE def _put_alignment_pattern(self): # Alignment pattern @@ -194,8 +183,7 @@ def _put_alignment_pattern(self): # Top side self._qr[i][center_x + j - 1] = color # Bottom side - self._qr[self._height-1-i][center_x + j - 1] = color - + self._qr[self._height - 1 - i][center_x + j - 1] = color def _put_timing_pattern(self): # Timing pattern @@ -215,13 +203,11 @@ def _put_timing_pattern(self): if self._qr[i][j] == Color.UNDEFINED: self._qr[i][j] = color - def _put_version_information(self): version_information = self._compute_version_info() self._put_version_information_finder_pattern_side(version_information) self._put_version_information_finder_sub_pattern_side(version_information) - def _put_version_information_finder_pattern_side(self, version_information): mask = 0b011111101010110010 version_information ^= mask @@ -230,8 +216,7 @@ def _put_version_information_finder_pattern_side(self, version_information): for n in range(18): di = n % 5 dj = n // 5 - self._qr[si+di][sj+dj] = Color.BLACK if version_information>>n & 1 else Color.WHITE - + self._qr[si + di][sj + dj] = Color.BLACK if version_information >> n & 1 else Color.WHITE def _put_version_information_finder_sub_pattern_side(self, version_information): mask = 0b100000101001111011 @@ -241,27 +226,31 @@ def _put_version_information_finder_sub_pattern_side(self, version_information): for n in range(15): di = n % 5 dj = n // 5 - self._qr[si+di][sj+dj] = Color.BLACK if version_information>>n & 1 else Color.WHITE - self._qr[self._height-1-5][self._width-1-4] = Color.BLACK if version_information>>15 & 1 else Color.WHITE - self._qr[self._height-1-5][self._width-1-3] = Color.BLACK if version_information>>16 & 1 else Color.WHITE - self._qr[self._height-1-5][self._width-1-2] = Color.BLACK if version_information>>17 & 1 else Color.WHITE - + self._qr[si + di][sj + dj] = Color.BLACK if version_information >> n & 1 else Color.WHITE + self._qr[self._height - 1 - 5][self._width - 1 - 4] = ( + Color.BLACK if version_information >> 15 & 1 else Color.WHITE + ) + self._qr[self._height - 1 - 5][self._width - 1 - 3] = ( + Color.BLACK if version_information >> 16 & 1 else Color.WHITE + ) + self._qr[self._height - 1 - 5][self._width - 1 - 2] = ( + Color.BLACK if version_information >> 17 & 1 else Color.WHITE + ) def _compute_version_info(self): qr_version = rMQRVersions[self.version_name()] - version_information_data = qr_version['version_indicator'] + version_information_data = qr_version["version_indicator"] if self._error_correction_level == ErrorCorrectionLevel.H: - version_information_data |= 1<<6 + version_information_data |= 1 << 6 reminder_polynomial = compute_bch(version_information_data) - version_information_data = version_information_data<<12 | reminder_polynomial + version_information_data = version_information_data << 12 | reminder_polynomial return version_information_data - def _put_data(self, data): qr_version = rMQRVersions[self.version_name()] - character_count_length = qr_version['character_count_length'] - codewords_total = qr_version['codewords_total'] + character_count_length = qr_version["character_count_length"] + codewords_total = qr_version["codewords_total"] encoded_data = self._convert_to_bites_data(data, character_count_length, codewords_total) codewords = split_into_8bits(encoded_data) @@ -278,8 +267,7 @@ def _put_data(self, data): codewords.append("00010001") data_codewords_per_block, rs_codewords_per_block = self._split_into_blocks( - codewords, - qr_version['blocks'][self._error_correction_level] + codewords, qr_version["blocks"][self._error_correction_level] ) # Construct the final message codeword sequence @@ -301,15 +289,15 @@ def _put_data(self, data): self._logger.debug(f"Put RS data codewords {i} : {rs_codewords[i]}") # Codeword placement - dy = -1 # Up + dy = -1 # Up current_codeword_idx = 0 current_bit_idx = 0 cx, cy = self._width - 2, self._height - 6 - remainder_bits = qr_version['remainder_bits'] + remainder_bits = qr_version["remainder_bits"] mask_area = [[False for i in range(self._width)] for j in range(self._height)] while True: - for x in [cx, cx-1]: + for x in [cx, cx - 1]: if self._qr[cy][x] == Color.UNDEFINED: # Process only empty cell if current_codeword_idx == len(final_codewords): @@ -319,7 +307,11 @@ def _put_data(self, data): remainder_bits -= 1 else: # Codewords - self._qr[cy][x] = Color.BLACK if final_codewords[current_codeword_idx][current_bit_idx] == '1' else Color.WHITE + self._qr[cy][x] = ( + Color.BLACK + if final_codewords[current_codeword_idx][current_bit_idx] == "1" + else Color.WHITE + ) mask_area[cy][x] = True current_bit_idx += 1 if current_bit_idx == 8: @@ -344,15 +336,14 @@ def _put_data(self, data): return mask_area - def _split_into_blocks(self, codewords, blocks_definition): data_idx, error_idx = 0, 0 data_codewords_per_block = [] rs_codewords_per_block = [] for block_definition in blocks_definition: - for i in range(block_definition['num']): - data_codewords_num = block_definition['k'] - rs_codewords_num = block_definition['c'] - block_definition['k'] + for i in range(block_definition["num"]): + data_codewords_num = block_definition["k"] + rs_codewords_num = block_definition["c"] - block_definition["k"] g = GeneratorPolynomials[rs_codewords_num] codewords_in_block = codewords[data_idx : data_idx + data_codewords_num] @@ -366,7 +357,6 @@ def _split_into_blocks(self, codewords, blocks_definition): return data_codewords_per_block, rs_codewords_per_block - def _convert_to_bites_data(self, data, character_count_length, codewords_total): encoded_data = ByteEncoder.encode(data, character_count_length) @@ -376,7 +366,6 @@ def _convert_to_bites_data(self, data, character_count_length, codewords_total): return encoded_data - def _apply_mask(self, mask_area): for y in range(self._height): for x in range(self._width): @@ -388,7 +377,6 @@ def _apply_mask(self, mask_area): elif self._qr[y][x] == Color.WHITE: self._qr[y][x] = Color.BLACK - @staticmethod def validate_version(version_name): return version_name in rMQRVersions @@ -399,4 +387,4 @@ class DataTooLongError(ValueError): class IllegalVersionError(ValueError): - pass \ No newline at end of file + pass diff --git a/src/rmqrcode/util/error_correction.py b/src/rmqrcode/util/error_correction.py index a3b47de..14943a5 100644 --- a/src/rmqrcode/util/error_correction.py +++ b/src/rmqrcode/util/error_correction.py @@ -1,12 +1,13 @@ -from .utilities import msb, to_binary from .galois_fields import GaloisFields +from .utilities import msb, to_binary + def compute_bch(data): data <<= 12 - g = 1<<12 | 1<<11 | 1<<10 | 1<<9 | 1<<8 | 1<<5 | 1<<2 | 1<<0 + g = 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0 tmp_data = data - while (msb(tmp_data) >= 13): + while msb(tmp_data) >= 13: multiple = msb(tmp_data) - 13 tmp_g = g << multiple tmp_data ^= tmp_g @@ -14,6 +15,8 @@ def compute_bch(data): gf = GaloisFields() + + def compute_reed_solomon(data, g, num_error_codewords): f = list(map(lambda x: int(x, 2), data)) @@ -21,13 +24,14 @@ def compute_reed_solomon(data, g, num_error_codewords): f.append(0) for i in range(len(data)): - if f[i] == 0: continue + if f[i] == 0: + continue mult = gf.i2e[f[i]] for j in range(len(g)): - f[i+j] ^= gf.e2i[(g[j]+mult)%255] + f[i + j] ^= gf.e2i[(g[j] + mult) % 255] rs_codewords = [] for i in range(num_error_codewords): rs_codewords.append(to_binary(f[-num_error_codewords + i], 8)) - return rs_codewords \ No newline at end of file + return rs_codewords diff --git a/src/rmqrcode/util/galois_fields.py b/src/rmqrcode/util/galois_fields.py index 6b3c076..4a0cdf7 100644 --- a/src/rmqrcode/util/galois_fields.py +++ b/src/rmqrcode/util/galois_fields.py @@ -5,7 +5,7 @@ class GaloisFields: def __init__(self): # Irreducible polynomial in GF(2^8) - p = (1<<8)|(1<<4)|(1<<3)|(1<<2)|1 + p = (1 << 8) | (1 << 4) | (1 << 3) | (1 << 2) | 1 self.e2i[0] = 1 self.e2i[255] = 1 @@ -15,7 +15,7 @@ def __init__(self): tmp = 1 for e in range(1, 255): tmp <<= 1 - if tmp & (1<<8): + if tmp & (1 << 8): tmp ^= p self.e2i[e] = tmp self.i2e[tmp] = e diff --git a/src/rmqrcode/util/utilities.py b/src/rmqrcode/util/utilities.py index 8de88ac..1f4f4c4 100644 --- a/src/rmqrcode/util/utilities.py +++ b/src/rmqrcode/util/utilities.py @@ -8,9 +8,9 @@ def to_binary(data, len): def split_into_8bits(data): codewords = [] - while (len(data) >= 8): + while len(data) >= 8: codewords.append(data[:8]) data = data[8:] if data != "": - codewords.append(data.ljust(8, '0')) - return codewords \ No newline at end of file + codewords.append(data.ljust(8, "0")) + return codewords From 8442b963751aea305235ed5a96d98a5388973ef5 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 10:39:30 +0900 Subject: [PATCH 13/33] ci: Run make lint in the python-app workflow --- .github/workflows/python-app.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index c00e7dd..0d47c15 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -27,12 +27,9 @@ jobs: pip install flake8 pytest pip install . if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Lint with flake8 + - name: Lint run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + make lint - name: Test with pytest run: | - python -m pytest + python -m pytest \ No newline at end of file From 69dff273d58d82a9c5aa81c8de8ca89e1bcc01a3 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 10:57:03 +0900 Subject: [PATCH 14/33] ci: flake8 --- src/rmqrcode/__init__.py | 2 ++ src/rmqrcode/console.py | 3 +-- src/rmqrcode/rmqrcode.py | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/rmqrcode/__init__.py b/src/rmqrcode/__init__.py index c537672..aca9a2f 100644 --- a/src/rmqrcode/__init__.py +++ b/src/rmqrcode/__init__.py @@ -1,3 +1,5 @@ from .format.error_correction_level import ErrorCorrectionLevel from .qr_image import QRImage from .rmqrcode import DataTooLongError, FitStrategy, IllegalVersionError, rMQR + +__all__ = ("rMQR", "DataTooLongError", "FitStrategy", "IllegalVersionError", "QRImage", "ErrorCorrectionLevel") diff --git a/src/rmqrcode/console.py b/src/rmqrcode/console.py index 615ce29..e02a4f8 100644 --- a/src/rmqrcode/console.py +++ b/src/rmqrcode/console.py @@ -2,7 +2,6 @@ import argparse import sys -import rmqrcode from rmqrcode import DataTooLongError, ErrorCorrectionLevel, FitStrategy, IllegalVersionError, QRImage, rMQR @@ -12,7 +11,7 @@ def _show_error_and_exit(msg): def _make_qr(data, ecc, version, fit_strategy): - if version == None: + if version is None: qr = rMQR.fit(data, ecc=ecc, fit_strategy=fit_strategy) else: try: diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index 4187484..0aa599f 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -35,7 +35,7 @@ def fit(data, ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): for version_name, qr_version in DataCapacities.items(): if data_length <= qr_version["capacity"]["Byte"][ecc]: width, height = qr_version["width"], qr_version["height"] - if not width in determined_width and not height in determined_height: + if width not in determined_width and height not in determined_height: determined_width.add(width) determined_height.add(height) ok_versions.append( @@ -51,11 +51,20 @@ def fit(data, ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): raise DataTooLongError("The data is too long.") if fit_strategy == FitStrategy.MINIMIZE_WIDTH: - sort_key = lambda x: x["width"] + + def sort_key(x): + return x["width"] + elif fit_strategy == FitStrategy.MINIMIZE_HEIGHT: - sort_key = lambda x: x["height"] + + def sort_key(x): + return x["height"] + elif fit_strategy == FitStrategy.BALANCED: - sort_key = lambda x: x["height"] * 9 + x["width"] + + def sort_key(x): + return x["height"] * 9 + x["width"] + selected = sorted(ok_versions, key=sort_key)[0] logger.debug(f"selected: {selected}") From 81adf743456f0fac07544c36cfca0194847c6a65 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 10:59:26 +0900 Subject: [PATCH 15/33] ci: Use profile=black in isrot --- setup.cfg | 5 ++++- src/rmqrcode/console.py | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 99662bd..8360b5f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,4 +37,7 @@ console_scripts = [flake8] max-line-length = 119 extend-ignore = E203 -exclude = src/rmqrcode/format/generator_polynomials.py \ No newline at end of file +exclude = src/rmqrcode/format/generator_polynomials.py + +[isort] +profile=black diff --git a/src/rmqrcode/console.py b/src/rmqrcode/console.py index e02a4f8..5220c64 100644 --- a/src/rmqrcode/console.py +++ b/src/rmqrcode/console.py @@ -2,7 +2,14 @@ import argparse import sys -from rmqrcode import DataTooLongError, ErrorCorrectionLevel, FitStrategy, IllegalVersionError, QRImage, rMQR +from rmqrcode import ( + DataTooLongError, + ErrorCorrectionLevel, + FitStrategy, + IllegalVersionError, + QRImage, + rMQR, +) def _show_error_and_exit(msg): From a376f524a53770dd0fc4871eba71af2ac1b6ad64 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 11:02:31 +0900 Subject: [PATCH 16/33] ci: install with the dev extra --- .github/workflows/python-app.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 0d47c15..5e21ba2 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -24,8 +24,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install flake8 pytest - pip install . + pip install .[dev] if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint run: | From 1f8b5d44ea4c56bf449774a301d80b968016a743 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 11:41:09 +0900 Subject: [PATCH 17/33] doc: Update CONTRIBUTING.md --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53b92d3..a0327ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,3 +11,10 @@ Consider starting commit message with one of the following prefixes. - `refactor:` : Refactoring - `chore:` : Little things - `doc:` : Documentation + + +## Pull Requests +Before make a pull request, please do the following. +1. `make format` +2. `make lint` +3. `python -m pytest` and make sure all tests pass. \ No newline at end of file From 3f9ef196682b70b78b40f60eefc5267765ff3d08 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 11:45:40 +0900 Subject: [PATCH 18/33] chore: Change CONTRIBUTING.md --- CONTRIBUTING.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a0327ec..6023c90 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,8 @@ # Contributing to rmqrcode-python - Thank you for interesting in contributing to rmqrcode-python! Any suggestions are welcome. ## Style Guides ### Git Commit Message - Consider starting commit message with one of the following prefixes. - `feat:` : New feature - `fix:` : Bug fix @@ -12,9 +10,8 @@ Consider starting commit message with one of the following prefixes. - `chore:` : Little things - `doc:` : Documentation - ## Pull Requests Before make a pull request, please do the following. 1. `make format` 2. `make lint` -3. `python -m pytest` and make sure all tests pass. \ No newline at end of file +3. `python -m pytest` and make sure all tests are passed. \ No newline at end of file From 7b8b325cbb0aa1103bed4a956efed756c36479a1 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 11:58:22 +0900 Subject: [PATCH 19/33] chore: Add trailing new line --- CONTRIBUTING.md | 2 +- Makefile | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6023c90..1e79947 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,4 +14,4 @@ Consider starting commit message with one of the following prefixes. Before make a pull request, please do the following. 1. `make format` 2. `make lint` -3. `python -m pytest` and make sure all tests are passed. \ No newline at end of file +3. `python -m pytest` and make sure all tests are passed. diff --git a/Makefile b/Makefile index 795901d..64421c3 100644 --- a/Makefile +++ b/Makefile @@ -7,4 +7,4 @@ lint: .PHONY: format format: isort src - black src \ No newline at end of file + black src diff --git a/pyproject.toml b/pyproject.toml index 9e5326f..8969d2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,4 +4,4 @@ build-backend = "setuptools.build_meta" [tool.black] line-length = 119 -exclude = "generator_polynomials.py" \ No newline at end of file +exclude = "generator_polynomials.py" From afdc6c3d319b90c9dc9edadc73ada61d48e6128d Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 12:01:27 +0900 Subject: [PATCH 20/33] chore: Add trailing new line --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 5e21ba2..472aafc 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -31,4 +31,4 @@ jobs: make lint - name: Test with pytest run: | - python -m pytest \ No newline at end of file + python -m pytest From aa696a9ab4fd90d47b34dd0c0613b5df72aff653 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 28 Jul 2022 19:15:44 +0900 Subject: [PATCH 21/33] ci: Create dependabot.yml --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..91abb11 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "pip" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" From 68392b3a780738ccbb1473cbadfb87f70b300362 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 4 Aug 2022 08:30:47 +0900 Subject: [PATCH 22/33] doc: Update the references in the README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 43f8c4d..8184e8f 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,8 @@ Any suggestions are welcome! If you are interesting in contiributing, please rea ## 📚 References -- [Rectangular Micro QR Code (rMQR) bar code symbology specification: ISO/IEC 23941:2022](https://www.iso.org/standard/77404.html) +- [Rectangular Micro QR Code (rMQR) bar code symbology specification: ISO/IEC 23941](https://www.iso.org/standard/77404.html) +- [rMQR Code | QRcode.com | DENSO WAVE](https://www.qrcode.com/en/codes/rmqr.html) - [Creating a QR Code step by step](https://www.nayuki.io/page/creating-a-qr-code-step-by-step) From 56592fb5837d07d4d3fb7419dda75156d14d70aa Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 4 Aug 2022 10:03:27 +0900 Subject: [PATCH 23/33] feat: Add the option `with_quiet_zone` into rMQR#to_list and rMQR#__str__ --- src/rmqrcode/rmqrcode.py | 39 +++++++++++++++++++++++++++++++++------ tests/rmqrcode_test.py | 6 ++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index 0aa599f..31fe70b 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -14,6 +14,8 @@ class rMQR: + QUIET_ZONE_MODULES = 2 + @staticmethod def _init_logger(): logger = logging.getLogger(__name__) @@ -72,7 +74,7 @@ def sort_key(x): qr.make(data) return qr - def __init__(self, version, ecc, logger=None): + def __init__(self, version, ecc, with_quiet_zone=True, logger=None): self._logger = logger or rMQR._init_logger() if not rMQR.validate_version(version): @@ -109,10 +111,23 @@ def width(self): def value_at(self, x, y): return self._qr[y][x] - def to_list(self): - return [list(map(lambda x: 1 if x == Color.BLACK else 0, column)) for column in self._qr] + def to_list(self, with_quiet_zone=True): + res = [] + if with_quiet_zone: + for y in range(self.QUIET_ZONE_MODULES): + res.append([0] * (self.width() + self.QUIET_ZONE_MODULES * 2)) + for row in self._to_binary_list(): + res.append([0] * self.QUIET_ZONE_MODULES + row + [0] * self.QUIET_ZONE_MODULES) + for y in range(self.QUIET_ZONE_MODULES): + res.append([0] * (self.width() + self.QUIET_ZONE_MODULES * 2)) + else: + res = self._to_binary_list() + return res + + def _to_binary_list(self): + return [list(map(lambda x: 1 if x == Color.BLACK else 0, column)) for column in self._qr] - def __str__(self): + def __str__(self, with_quiet_zone=True): res = "" show = {} @@ -123,13 +138,25 @@ def __str__(self): show[False] = "_" res += f"rMQR Version R{self._height}x{self._width}:\n" - for i in range(self._height): - for j in range(self._width): + if with_quiet_zone: + res += (show[False] * (self.width() + self.QUIET_ZONE_MODULES * 2) + '\n') * self.QUIET_ZONE_MODULES + + for i in range(self.height()): + if with_quiet_zone: + res += show[False] * self.QUIET_ZONE_MODULES + + for j in range(self.width()): if self._qr[i][j] in show: res += show[self._qr[i][j]] else: res += self._qr[i][j] + + if with_quiet_zone: + res += show[False] * self.QUIET_ZONE_MODULES res += "\n" + + if with_quiet_zone: + res += (show[False] * (self.width() + self.QUIET_ZONE_MODULES * 2) + '\n') * self.QUIET_ZONE_MODULES return res def _put_finder_pattern(self): diff --git a/tests/rmqrcode_test.py b/tests/rmqrcode_test.py index b857821..c075f77 100644 --- a/tests/rmqrcode_test.py +++ b/tests/rmqrcode_test.py @@ -14,6 +14,12 @@ def test_make(self): qr = rMQR('R13x99', ErrorCorrectionLevel.M) qr.make("abc") + assert len(qr.to_list(with_quiet_zone=True)) is 17 + assert len(qr.to_list(with_quiet_zone=True)[0]) is 103 + + assert len(qr.to_list(with_quiet_zone=False)) is 13 + assert len(qr.to_list(with_quiet_zone=False)[0]) is 99 + def test_raise_too_long_error(self): with pytest.raises(DataTooLongError) as e: s = "a".ljust(200, "a") From 36a7c662fd5f0e3bf5ad331cdf64b226c8dd72bd Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 4 Aug 2022 10:17:06 +0900 Subject: [PATCH 24/33] chore: make format --- src/rmqrcode/rmqrcode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index 31fe70b..41851d5 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -125,7 +125,7 @@ def to_list(self, with_quiet_zone=True): return res def _to_binary_list(self): - return [list(map(lambda x: 1 if x == Color.BLACK else 0, column)) for column in self._qr] + return [list(map(lambda x: 1 if x == Color.BLACK else 0, column)) for column in self._qr] def __str__(self, with_quiet_zone=True): res = "" @@ -139,7 +139,7 @@ def __str__(self, with_quiet_zone=True): res += f"rMQR Version R{self._height}x{self._width}:\n" if with_quiet_zone: - res += (show[False] * (self.width() + self.QUIET_ZONE_MODULES * 2) + '\n') * self.QUIET_ZONE_MODULES + res += (show[False] * (self.width() + self.QUIET_ZONE_MODULES * 2) + "\n") * self.QUIET_ZONE_MODULES for i in range(self.height()): if with_quiet_zone: @@ -156,7 +156,7 @@ def __str__(self, with_quiet_zone=True): res += "\n" if with_quiet_zone: - res += (show[False] * (self.width() + self.QUIET_ZONE_MODULES * 2) + '\n') * self.QUIET_ZONE_MODULES + res += (show[False] * (self.width() + self.QUIET_ZONE_MODULES * 2) + "\n") * self.QUIET_ZONE_MODULES return res def _put_finder_pattern(self): From fbb9015aa597f060ea3c4ee1722dcc082bed421d Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 4 Aug 2022 10:42:41 +0900 Subject: [PATCH 25/33] refactor: Use with-quiet-zone in QRIMage#_make_image --- src/rmqrcode/qr_image.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/rmqrcode/qr_image.py b/src/rmqrcode/qr_image.py index f17d7b9..f62db66 100644 --- a/src/rmqrcode/qr_image.py +++ b/src/rmqrcode/qr_image.py @@ -6,10 +6,11 @@ class QRImage: def __init__(self, qr, module_size=10): self._module_size = module_size + qr_list = qr.to_list() self._img = Image.new( - "RGB", ((qr.width() + 4) * module_size, (qr.height() + 4) * module_size), (255, 255, 255) + "RGB", (len(qr_list[0]) * module_size, len(qr_list) * module_size), (255, 255, 255) ) - self._make_image(qr) + self._make_image(qr_list) def show(self): self._img.show() @@ -26,25 +27,17 @@ def get_ndarray(self): def save(self, name): self._img.save(name) - def _make_image(self, qr): + def _make_image(self, qr_list): draw = ImageDraw.Draw(self._img) - for y in range(qr.height()): - for x in range(qr.width()): - r, g, b = 125, 125, 125 - if qr.value_at(x, y) == Color.BLACK: - r, g, b = 0, 0, 0 - elif qr.value_at(x, y) == Color.WHITE: - r, g, b, = ( - 255, - 255, - 255, - ) + for y in range(len(qr_list)): + for x in range(len(qr_list[0])): + r, g, b = (0, 0, 0) if qr_list[y][x] else (255, 255, 255) draw.rectangle( xy=( - (x + 2) * self._module_size, - (y + 2) * self._module_size, - (x + 1 + 2) * self._module_size, - (y + 1 + 2) * self._module_size, + x * self._module_size, + y * self._module_size, + (x + 1) * self._module_size, + (y + 1) * self._module_size, ), fill=(r, g, b), ) From fb962fba1e04832e059719de6819a8cf0fc40366 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 4 Aug 2022 10:43:12 +0900 Subject: [PATCH 26/33] chore: make format --- src/rmqrcode/qr_image.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rmqrcode/qr_image.py b/src/rmqrcode/qr_image.py index f62db66..e8f4fd8 100644 --- a/src/rmqrcode/qr_image.py +++ b/src/rmqrcode/qr_image.py @@ -7,9 +7,7 @@ class QRImage: def __init__(self, qr, module_size=10): self._module_size = module_size qr_list = qr.to_list() - self._img = Image.new( - "RGB", (len(qr_list[0]) * module_size, len(qr_list) * module_size), (255, 255, 255) - ) + self._img = Image.new("RGB", (len(qr_list[0]) * module_size, len(qr_list) * module_size), (255, 255, 255)) self._make_image(qr_list) def show(self): From 3bd7b10178162e6b25cff4ff185fa46d0859afa1 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 4 Aug 2022 10:44:10 +0900 Subject: [PATCH 27/33] chore: Remove unused import --- src/rmqrcode/qr_image.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rmqrcode/qr_image.py b/src/rmqrcode/qr_image.py index e8f4fd8..a2ee637 100644 --- a/src/rmqrcode/qr_image.py +++ b/src/rmqrcode/qr_image.py @@ -1,7 +1,5 @@ from PIL import Image, ImageDraw -from .enums.color import Color - class QRImage: def __init__(self, qr, module_size=10): From 45706288ac9964887ed92ed963804e087fcf9129 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Fri, 5 Aug 2022 13:00:19 +0900 Subject: [PATCH 28/33] doc: Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8184e8f..212cb63 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ ![reop-url](https://user-images.githubusercontent.com/14174940/172978619-accbf9d0-9dd8-4b19-b47e-ad139a68dcc9.png) -This is an rMQR Code image generator implemented in Python. This is implemented based on [ISO/IEC 23941:2022](https://www.iso.org/standard/77404.html). +The rMQR Code is a rectangular two-dimensional barcode. This package can generate an rMQR Code image. This is implemented based on [ISO/IEC 23941: Rectangular Micro QR Code (rMQR) bar code symbology specification](https://www.iso.org/standard/77404.html). -Try this online (in Japanese): https://rmqr.oudon.xyz . +## 🎮 Online Demo Site +You can try this online: https://rmqr.oudon.xyz . - -## 📌 Important Notice +## 📌 Notice - Please verify an image generated by this software whether it can decode correctly before use. - Because this is in early stage, QR Code readers may have not been supported rMQR Code yet. From efad49567b51ddd53757624691e922e39fd760f8 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Sat, 6 Aug 2022 10:24:12 +0900 Subject: [PATCH 29/33] wip: Add docstrings --- CONTRIBUTING.md | 4 ++ src/rmqrcode/rmqrcode.py | 83 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4a2116..40429cb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,10 @@ Thank you for interesting in contributing to rmqrcode-python! Any suggestions are welcome. ## Style Guides + +### Docstrings +This project uses [Google-Style format](https://google.github.io/styleguide/pyguide.html#381-docstrings) docstrings. + ### Git Commit Message Consider starting commit message with one of the following prefixes. - `feat:` : New feature diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index 41851d5..cb234f0 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -18,6 +18,12 @@ class rMQR: @staticmethod def _init_logger(): + """ Initializes a logger and returns it. + + Returns: + logging.RootLogger: Logger + + """ logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) logger.setLevel(logging.DEBUG) @@ -26,6 +32,20 @@ def _init_logger(): @staticmethod def fit(data, ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): + """ Attempts to make an rMQR have optimized version for given data. + + Args: + data (str): Data to encode. + ecc (rmqrcode.ErrorCorrectionLevel): Error correction level. + fit_strategy (rmqrcode.FitStrategy): Strategy how determine rMQR Code version. + + Returns: + rmqrcode.rMQR: Optimized rMQR Code. + + Raises: + rmqrcode.DataTooLongError: If the data is too long to encode. + + """ logger = rMQR._init_logger() data_length = ByteEncoder.length(data) @@ -97,21 +117,84 @@ def make(self, data): self._apply_mask(mask_area) def version_name(self): + """ Returns the version name. + + Returns: + str: The version name. + + Examples: + >>> qr.version_name() + "R13x77" + + """ return f"R{self._height}x{self._width}" def size(self): + """ Returns the size. + + Returns: + tuple: The rMQR Code size. + + Examples: + >>> qr.size() + (77, 13) + + Note: + This not includes the quiet zone. + + """ return (self.width(), self.height()) def height(self): + """ Returns the height. + + Returns: + int: The height. + + Note: + This not includes the quiet zone. + + """ return self._height def width(self): + """ Returns the width. + + Returns: + int: The width. + + Note: + This not includes the quiet zone. + + """ return self._width def value_at(self, x, y): + """ DEPRECATED: Returns the color at the point of (x, y). + + Returns: + rmqrcode.Color: The color of rMQRCode at the point of (x, y). + + Note: + This method is deprecated. Use to_list() alternatively. + This not includes the quiet zone. + + """ return self._qr[y][x] def to_list(self, with_quiet_zone=True): + """ Convert to two-dimensional list and returns it. + + The value is 1 for the dark module and 0 for the light module. + + Arguments: + with_quiet_zone (bool): Flag to select whether include the quiet zone. + + Returns: + list: Converted list. + + """ + res = [] if with_quiet_zone: for y in range(self.QUIET_ZONE_MODULES): From 6a3d79e648d8869cefbdc4378d3590310a625c0b Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Sun, 7 Aug 2022 08:23:09 +0900 Subject: [PATCH 30/33] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 212cb63..6602109 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ The rMQR Code is a rectangular two-dimensional barcode. This package can generate an rMQR Code image. This is implemented based on [ISO/IEC 23941: Rectangular Micro QR Code (rMQR) bar code symbology specification](https://www.iso.org/standard/77404.html). +[![pytest](https://github.com/OUDON/rmqrcode-python/actions/workflows/python-app.yml/badge.svg?branch=main)](https://github.com/OUDON/rmqrcode-python/actions/workflows/python-app.yml) +![PyPI](https://img.shields.io/pypi/v/rmqrcode?color=blue) +![PyPI - Python Version](https://img.shields.io/pypi/pyversions/rmqrcode) +![PyPI - Downloads](https://img.shields.io/pypi/dm/rmqrcode?color=orange) + ## 🎮 Online Demo Site You can try this online: https://rmqr.oudon.xyz . From c9be13c6f6feddd9f220482e5f209668353d8ae7 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Tue, 9 Aug 2022 10:04:11 +0900 Subject: [PATCH 31/33] doc: Add docstrings to src/rmqrcode/rmqrcode.py --- src/rmqrcode/rmqrcode.py | 120 ++++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 14 deletions(-) diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index cb234f0..a4bbdb0 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -1,3 +1,20 @@ +"""A module to make an rMQR Code. + +Example: + Use the rMQR.fit method to make an rMQR automatically with some options. + + qr = rMQR.fit( + "https://oudon.xyz", + ecc=ErrorCorrectionLevel.M, + fit_strategy=FitStrategy.MINIMIZE_WIDTH + ) + + The following example shows how to select the size of an rMQR Code. + + qr = rMQR("R11x139", ErrorCorrectionLevel.H) + qr.make("https://oudon.xyz") + +""" import logging from .encoder.byte_encoder import ByteEncoder @@ -14,11 +31,17 @@ class rMQR: + """A class to make an rMQR Code. + + Attributes: + QUIET_ZONE_MODULES (int): The width of the quiet zone. + + """ QUIET_ZONE_MODULES = 2 @staticmethod def _init_logger(): - """ Initializes a logger and returns it. + """Initializes a logger and returns it. Returns: logging.RootLogger: Logger @@ -32,10 +55,10 @@ def _init_logger(): @staticmethod def fit(data, ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): - """ Attempts to make an rMQR have optimized version for given data. + """Attempts to make an rMQR have optimized version for given data. Args: - data (str): Data to encode. + data (str): Data string to encode. ecc (rmqrcode.ErrorCorrectionLevel): Error correction level. fit_strategy (rmqrcode.FitStrategy): Strategy how determine rMQR Code version. @@ -108,6 +131,14 @@ def __init__(self, version, ecc, with_quiet_zone=True, logger=None): self._qr = [[Color.UNDEFINED for x in range(self._width)] for y in range(self._height)] def make(self, data): + """Makes an rMQR Code for given data + + Args: + data (str): Data string. + + Returns: + void + """ self._put_finder_pattern() self._put_corner_finder_pattern() self._put_alignment_pattern() @@ -117,7 +148,7 @@ def make(self, data): self._apply_mask(mask_area) def version_name(self): - """ Returns the version name. + """Returns the version name. Returns: str: The version name. @@ -130,7 +161,7 @@ def version_name(self): return f"R{self._height}x{self._width}" def size(self): - """ Returns the size. + """Returns the size. Returns: tuple: The rMQR Code size. @@ -146,7 +177,7 @@ def size(self): return (self.width(), self.height()) def height(self): - """ Returns the height. + """Returns the height. Returns: int: The height. @@ -158,7 +189,7 @@ def height(self): return self._height def width(self): - """ Returns the width. + """Returns the width. Returns: int: The width. @@ -170,7 +201,7 @@ def width(self): return self._width def value_at(self, x, y): - """ DEPRECATED: Returns the color at the point of (x, y). + """DEPRECATED: Returns the color at the point of (x, y). Returns: rmqrcode.Color: The color of rMQRCode at the point of (x, y). @@ -183,15 +214,15 @@ def value_at(self, x, y): return self._qr[y][x] def to_list(self, with_quiet_zone=True): - """ Convert to two-dimensional list and returns it. + """Converts to two-dimensional list and returns it. - The value is 1 for the dark module and 0 for the light module. + The value is 1 for the dark module and 0 for the light module. - Arguments: - with_quiet_zone (bool): Flag to select whether include the quiet zone. + Args: + with_quiet_zone (bool): Flag to select whether include the quiet zone. - Returns: - list: Converted list. + Returns: + list: Converted list. """ @@ -208,6 +239,19 @@ def to_list(self, with_quiet_zone=True): return res def _to_binary_list(self): + """Converts to two-dimensional list and returns it. + + The value is 1 for the dark module and 0 for the light module. + + Args: + with_quiet_zone (bool): Flag to select whether include the quiet zone. + + Returns: + list: Converted list. + + Note: + This not includes the quiet zone. + """ return [list(map(lambda x: 1 if x == Color.BLACK else 0, column)) for column in self._qr] def __str__(self, with_quiet_zone=True): @@ -366,6 +410,21 @@ def _compute_version_info(self): return version_information_data def _put_data(self, data): + """Symbol character placement. + + This method puts data into the encoding region of the rMQR Code. Also this + method computes a two-dimensional list shows where encoding region at the + same time. And returns the list. + + See: "7.7.3 Symbol character placement" in the ISO/IEC 23941. + + Args: + data (str): Data string. + + Returns: + list: A two-dimensional list shows where encoding region. + + """ qr_version = rMQRVersions[self.version_name()] character_count_length = qr_version["character_count_length"] @@ -486,6 +545,18 @@ def _convert_to_bites_data(self, data, character_count_length, codewords_total): return encoded_data def _apply_mask(self, mask_area): + """Data masking. + + This method applies the data mask. + + Args: + mask_area (list): A two-dimensional list shows where encoding region. + This is computed by self._put_data(). + + Returns: + void + + """ for y in range(self._height): for x in range(self._width): if not mask_area[y][x]: @@ -498,12 +569,33 @@ def _apply_mask(self, mask_area): @staticmethod def validate_version(version_name): + """Check if the given version_name is valid + + Args: + version_name (str): Version name. + + Returns: + bool: Validation result. + + Example: + >>> rMQR.validate_version("R13x77") + True + + >>> rMQR.validate_version("R14x55") + False + + >>> rMQR.validate_version("13, 77") + False + + """ return version_name in rMQRVersions class DataTooLongError(ValueError): + "A class represents an error raised when the given data is too long." pass class IllegalVersionError(ValueError): + "A class represents an error raised when the given version name is illegal." pass From a6c54670e87d23913beb484dd4126cd93571c317 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Tue, 9 Aug 2022 18:13:46 +0900 Subject: [PATCH 32/33] chore: make format --- src/rmqrcode/rmqrcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index a4bbdb0..5117d8e 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -15,6 +15,7 @@ qr.make("https://oudon.xyz") """ + import logging from .encoder.byte_encoder import ByteEncoder @@ -37,6 +38,7 @@ class rMQR: QUIET_ZONE_MODULES (int): The width of the quiet zone. """ + QUIET_ZONE_MODULES = 2 @staticmethod From 11c2d7e660120fef3b6039b86b98452b7e494007 Mon Sep 17 00:00:00 2001 From: Takahiro Tomita Date: Thu, 11 Aug 2022 07:23:19 +0900 Subject: [PATCH 33/33] chore: Bumps to v1.2.0 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 8360b5f..cf930d6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rmqrcode -version = 0.1.3 +version = 0.2.0 author = Takahiro Tomita author_email = ttp8101@gmail.com description = An rMQR Code Generetor