diff --git a/README.md b/README.md index 9ecbab0..a5eb448 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ The rMQR Code is a rectangular two-dimensional barcode. This is easy to print in You can try this online: https://rmqr.oudon.xyz . ## 📌 Notice +- To read rMQR code, we can use [QRQR app](https://www.denso-wave.com/en/system/qr/product/reader.html). However many other QR code readers may have not been supported yet. - 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 @@ -27,7 +27,7 @@ pip install rmqrcode ### CLI Generate an rMQR Code image from your command line, use `rmqr` command: ```sh -rmqr 'Text data' 'my_qr.png' +rmqr "Text data" "my_qr.png" ``` See the help to list the options: diff --git a/example.py b/example.py index 983c57d..0fefe1a 100644 --- a/example.py +++ b/example.py @@ -16,7 +16,7 @@ def main(): - data = "https://oudon.xyz" + data = "https://github.com/OUDON/rmqrcode-python" error_correction_level = ErrorCorrectionLevel.M fit_strategy = FitStrategy.BALANCED diff --git a/setup.cfg b/setup.cfg index 59f03bb..b3dad6b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rmqrcode -version = 0.3.1 +version = 0.3.2 author = Takahiro Tomita author_email = ttp8101@gmail.com description = An rMQR Code Generetor diff --git a/src/rmqrcode/encoder/encoder_base.py b/src/rmqrcode/encoder/encoder_base.py index a5220fd..ccb07ce 100644 --- a/src/rmqrcode/encoder/encoder_base.py +++ b/src/rmqrcode/encoder/encoder_base.py @@ -88,7 +88,7 @@ def characters_num(cls, data): @classmethod @abstractmethod def is_valid_characters(cls, data): - """Checks wether the data does not include invalid character. + """Checks whether the data does not include invalid character. Args: data (str): Data to validate. diff --git a/src/rmqrcode/format/data_capacities.py b/src/rmqrcode/format/data_capacities.py deleted file mode 100644 index adeb1d7..0000000 --- a/src/rmqrcode/format/data_capacities.py +++ /dev/null @@ -1,453 +0,0 @@ -from .error_correction_level import ErrorCorrectionLevel - -# ISO/IEC 23941:2022 Table 6 -DataCapacities = { - "R7x43": { - "height": 7, - "width": 43, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 48, - ErrorCorrectionLevel.H: 24, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 5, - ErrorCorrectionLevel.H: 2, - }, - }, - }, - "R7x59": { - "height": 7, - "width": 59, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 96, - ErrorCorrectionLevel.H: 56, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 11, - ErrorCorrectionLevel.H: 6, - }, - }, - }, - "R7x77": { - "height": 7, - "width": 77, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 160, - ErrorCorrectionLevel.H: 80, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 19, - ErrorCorrectionLevel.H: 9, - }, - }, - }, - "R7x99": { - "height": 7, - "width": 99, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 224, - ErrorCorrectionLevel.H: 112, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 27, - ErrorCorrectionLevel.H: 13, - }, - }, - }, - "R7x139": { - "height": 7, - "width": 139, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 352, - ErrorCorrectionLevel.H: 192, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 42, - ErrorCorrectionLevel.H: 22, - }, - }, - }, - "R9x43": { - "height": 9, - "width": 43, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 96, - ErrorCorrectionLevel.H: 56, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 11, - ErrorCorrectionLevel.H: 6, - }, - }, - }, - "R9x59": { - "height": 9, - "width": 59, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 168, - ErrorCorrectionLevel.H: 88, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 20, - ErrorCorrectionLevel.H: 10, - }, - }, - }, - "R9x77": { - "height": 9, - "width": 77, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 248, - ErrorCorrectionLevel.H: 136, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 30, - ErrorCorrectionLevel.H: 16, - }, - }, - }, - "R9x99": { - "height": 9, - "width": 99, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 336, - ErrorCorrectionLevel.H: 176, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 40, - ErrorCorrectionLevel.H: 20, - }, - }, - }, - "R9x139": { - "height": 9, - "width": 139, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 504, - ErrorCorrectionLevel.H: 264, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 61, - ErrorCorrectionLevel.H: 31, - }, - }, - }, - "R11x27": { - "height": 11, - "width": 27, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 56, - ErrorCorrectionLevel.H: 40, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 6, - ErrorCorrectionLevel.H: 4, - }, - }, - }, - "R11x43": { - "height": 11, - "width": 43, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 152, - ErrorCorrectionLevel.H: 88, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 18, - ErrorCorrectionLevel.H: 10, - }, - }, - }, - "R11x59": { - "height": 11, - "width": 59, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 248, - ErrorCorrectionLevel.H: 120, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 30, - ErrorCorrectionLevel.H: 14, - }, - }, - }, - "R11x77": { - "height": 11, - "width": 77, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 344, - ErrorCorrectionLevel.H: 184, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 41, - ErrorCorrectionLevel.H: 21, - }, - }, - }, - "R11x99": { - "height": 11, - "width": 99, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 456, - ErrorCorrectionLevel.H: 232, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 55, - ErrorCorrectionLevel.H: 27, - }, - }, - }, - "R11x139": { - "height": 11, - "width": 139, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 672, - ErrorCorrectionLevel.H: 336, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 82, - ErrorCorrectionLevel.H: 40, - }, - }, - }, - "R13x27": { - "height": 13, - "width": 27, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 96, - ErrorCorrectionLevel.H: 56, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 11, - ErrorCorrectionLevel.H: 6, - }, - }, - }, - "R13x43": { - "height": 13, - "width": 43, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 216, - ErrorCorrectionLevel.H: 104, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 26, - ErrorCorrectionLevel.H: 12, - }, - }, - }, - "R13x59": { - "height": 13, - "width": 59, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 304, - ErrorCorrectionLevel.H: 160, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 36, - ErrorCorrectionLevel.H: 18, - }, - }, - }, - "R13x77": { - "height": 13, - "width": 77, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 424, - ErrorCorrectionLevel.H: 232, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 51, - ErrorCorrectionLevel.H: 27, - }, - }, - }, - "R13x99": { - "height": 13, - "width": 99, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 584, - ErrorCorrectionLevel.H: 280, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 71, - ErrorCorrectionLevel.H: 33, - }, - }, - }, - "R13x139": { - "height": 13, - "width": 139, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 848, - ErrorCorrectionLevel.H: 432, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 104, - ErrorCorrectionLevel.H: 52, - }, - }, - }, - "R15x43": { - "height": 15, - "width": 43, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 264, - ErrorCorrectionLevel.H: 120, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 31, - ErrorCorrectionLevel.H: 13, - }, - }, - }, - "R15x59": { - "height": 15, - "width": 59, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 384, - ErrorCorrectionLevel.H: 208, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 46, - ErrorCorrectionLevel.H: 24, - }, - }, - }, - "R15x77": { - "height": 15, - "width": 77, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 536, - ErrorCorrectionLevel.H: 248, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 65, - ErrorCorrectionLevel.H: 29, - }, - }, - }, - "R15x99": { - "height": 15, - "width": 99, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 704, - ErrorCorrectionLevel.H: 384, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 86, - ErrorCorrectionLevel.H: 46, - }, - }, - }, - "R15x139": { - "height": 15, - "width": 139, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 1016, - ErrorCorrectionLevel.H: 552, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 125, - ErrorCorrectionLevel.H: 67, - }, - }, - }, - "R17x43": { - "height": 17, - "width": 43, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 312, - ErrorCorrectionLevel.H: 168, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 37, - ErrorCorrectionLevel.H: 19, - }, - }, - }, - "R17x59": { - "height": 17, - "width": 59, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 448, - ErrorCorrectionLevel.H: 224, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 54, - ErrorCorrectionLevel.H: 26, - }, - }, - }, - "R17x77": { - "height": 17, - "width": 77, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 624, - ErrorCorrectionLevel.H: 304, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 76, - ErrorCorrectionLevel.H: 36, - }, - }, - }, - "R17x99": { - "height": 17, - "width": 99, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 800, - ErrorCorrectionLevel.H: 448, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 98, - ErrorCorrectionLevel.H: 54, - }, - }, - }, - "R17x139": { - "height": 17, - "width": 139, - "number_of_data_bits": { - ErrorCorrectionLevel.M: 1216, - ErrorCorrectionLevel.H: 608, - }, - "capacity": { - "Byte": { - ErrorCorrectionLevel.M: 150, - ErrorCorrectionLevel.H: 74, - }, - }, - }, -} diff --git a/src/rmqrcode/format/generator_polynomials.py b/src/rmqrcode/format/generator_polynomials.py index 3a1a46b..dff2cde 100644 --- a/src/rmqrcode/format/generator_polynomials.py +++ b/src/rmqrcode/format/generator_polynomials.py @@ -3,14 +3,14 @@ 8 : [0, 175, 238, 208, 249, 215, 252, 196, 28], 9 : [0, 95, 246, 137, 231, 235, 149, 11, 123, 36], 10: [0, 251, 67, 46, 61, 118, 70, 64, 94, 32, 45], - 12: [0, 102, 43, 98, 121, 187, 113, 198, 143, 207, 59, 22, 91], + 12: [0, 102, 43, 98, 121, 187, 113, 198, 143, 131, 87, 157, 66], 14: [0, 199, 249, 155, 48, 190, 124, 218, 137, 216, 87, 207, 59, 22, 91], 16: [0, 120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, 169, 182, 194, 225, 120], 18: [0, 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 96, 153], 20: [0, 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 212, 212, 188, 190], 22: [0, 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80, 219, 134, 160, 105, 165, 231], - 24: [0, 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 111, 1, 117, 232, 87, 96, 227, 21], + 24: [0, 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 111, 0, 117, 232, 87, 96, 227, 21], 26: [0, 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, 21, 245, 142, 13, 102, 48, 227, 153, 145, 218, 70], 28: [0, 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, 21, 43, 245, 87, 42, 195, 212, 119, 242, 37, 9, 123], 30: [0, 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 42, 173, 226, 193, 224, 130, 156, 37, 251, 216, 238, 40, 192, 180], -} \ No newline at end of file +} diff --git a/src/rmqrcode/format/rmqr_versions.py b/src/rmqrcode/format/rmqr_versions.py index c35c472..561b6c8 100644 --- a/src/rmqrcode/format/rmqr_versions.py +++ b/src/rmqrcode/format/rmqr_versions.py @@ -30,6 +30,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 48, + ErrorCorrectionLevel.H: 24, + }, }, "R7x59": { "version_indicator": 0b00001, @@ -59,6 +63,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 96, + ErrorCorrectionLevel.H: 56, + }, }, "R7x77": { "version_indicator": 0b00010, @@ -88,6 +96,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 160, + ErrorCorrectionLevel.H: 80, + }, }, "R7x99": { "version_indicator": 0b00011, @@ -117,6 +129,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 224, + ErrorCorrectionLevel.H: 112, + }, }, "R7x139": { "version_indicator": 0b00100, @@ -146,6 +162,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 352, + ErrorCorrectionLevel.H: 192, + }, }, "R9x43": { "version_indicator": 0b00101, @@ -175,6 +195,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 96, + ErrorCorrectionLevel.H: 56, + }, }, "R9x59": { "version_indicator": 0b00110, @@ -204,6 +228,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 168, + ErrorCorrectionLevel.H: 88, + }, }, "R9x77": { "version_indicator": 0b00111, @@ -238,6 +266,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 248, + ErrorCorrectionLevel.H: 136, + }, }, "R9x99": { "version_indicator": 0b01000, @@ -267,6 +299,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 336, + ErrorCorrectionLevel.H: 176, + }, }, "R9x139": { "version_indicator": 0b01001, @@ -301,6 +337,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 504, + ErrorCorrectionLevel.H: 264, + }, }, "R11x27": { "version_indicator": 0b01010, @@ -330,6 +370,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 56, + ErrorCorrectionLevel.H: 40, + }, }, "R11x43": { "version_indicator": 0b01011, @@ -359,6 +403,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 152, + ErrorCorrectionLevel.H: 88, + }, }, "R11x59": { "version_indicator": 0b01100, @@ -393,6 +441,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 248, + ErrorCorrectionLevel.H: 120, + }, }, "R11x77": { "version_indicator": 0b01101, @@ -427,6 +479,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 344, + ErrorCorrectionLevel.H: 184, + }, }, "R11x99": { "version_indicator": 0b01110, @@ -466,6 +522,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 456, + ErrorCorrectionLevel.H: 232, + }, }, "R11x139": { "version_indicator": 0b01111, @@ -495,6 +555,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 672, + ErrorCorrectionLevel.H: 336, + }, }, "R13x27": { "version_indicator": 0b10000, @@ -524,6 +588,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 96, + ErrorCorrectionLevel.H: 56, + }, }, "R13x43": { "version_indicator": 0b10001, @@ -553,6 +621,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 216, + ErrorCorrectionLevel.H: 104, + }, }, "R13x59": { "version_indicator": 0b10010, @@ -582,6 +654,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 304, + ErrorCorrectionLevel.H: 160, + }, }, "R13x77": { "version_indicator": 0b10011, @@ -621,6 +697,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 424, + ErrorCorrectionLevel.H: 232, + }, }, "R13x99": { "version_indicator": 0b10100, @@ -660,6 +740,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 584, + ErrorCorrectionLevel.H: 280, + }, }, "R13x139": { "version_indicator": 0b10101, @@ -699,6 +783,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 848, + ErrorCorrectionLevel.H: 432, + }, }, "R15x43": { "version_indicator": 0b10110, @@ -733,6 +821,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 264, + ErrorCorrectionLevel.H: 120, + }, }, "R15x59": { "version_indicator": 0b10111, @@ -762,6 +854,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 384, + ErrorCorrectionLevel.H: 208, + }, }, "R15x77": { "version_indicator": 0b11000, @@ -801,6 +897,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 536, + ErrorCorrectionLevel.H: 248, + }, }, "R15x99": { "version_indicator": 0b11001, @@ -830,6 +930,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 704, + ErrorCorrectionLevel.H: 384, + }, }, "R15x139": { "version_indicator": 0b11010, @@ -869,6 +973,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 1016, + ErrorCorrectionLevel.H: 552, + }, }, "R17x43": { "version_indicator": 0b11011, @@ -903,6 +1011,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 312, + ErrorCorrectionLevel.H: 168, + }, }, "R17x59": { "version_indicator": 0b11100, @@ -932,6 +1044,10 @@ } ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 448, + ErrorCorrectionLevel.H: 224, + }, }, "R17x77": { "version_indicator": 0b11101, @@ -966,6 +1082,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 624, + ErrorCorrectionLevel.H: 304, + }, }, "R17x99": { "version_indicator": 0b11110, @@ -1000,6 +1120,10 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 800, + ErrorCorrectionLevel.H: 448, + }, }, "R17x139": { "version_indicator": 0b11111, @@ -1034,5 +1158,9 @@ }, ], }, + "number_of_data_bits": { + ErrorCorrectionLevel.M: 1216, + ErrorCorrectionLevel.H: 608, + }, }, } diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index 8330a8a..c546062 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -14,6 +14,9 @@ qr = rMQR("R11x139", ErrorCorrectionLevel.H) qr.make("https://oudon.xyz") +Attributes: + QUIET_ZONE_MODULES (int): The width of the quiet zone. + """ import logging @@ -24,7 +27,6 @@ from .enums.fit_strategy import FitStrategy from .errors import DataTooLongError, IllegalVersionError, NoSegmentError 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 @@ -32,16 +34,11 @@ from .util.error_correction import compute_bch, compute_reed_solomon from .util.utilities import split_into_8bits +QUIET_ZONE_MODULES = 2 -class rMQR: - """A class to make an rMQR Code. - - Attributes: - QUIET_ZONE_MODULES (int): The width of the quiet zone. - - """ - QUIET_ZONE_MODULES = 2 +class rMQR: + """A class to make an rMQR Code.""" @staticmethod def _init_logger(): @@ -59,7 +56,7 @@ 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. + """Compute optimized rMQR code with the rMQROptimizer class. Args: data (str): Data string to encode. @@ -73,61 +70,18 @@ def fit(data, ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): rmqrcode.DataTooLongError: If the data is too long to encode. """ - logger = rMQR._init_logger() - - ok_versions = [] - determined_width = set() - determined_height = set() - - logger.debug("Select rMQR Code version") - for version_name, qr_version in DataCapacities.items(): - optimizer = qr_segments.SegmentOptimizer() - try: - optimized_segments = optimizer.compute(data, version_name, ecc) - except DataTooLongError: - continue - - width, height = qr_version["width"], qr_version["height"] - if width not in determined_width and height not in determined_height: - determined_width.add(width) - determined_height.add(height) - ok_versions.append( - { - "version": version_name, - "width": width, - "height": height, - "segments": optimized_segments, - } - ) - logger.debug(f"ok: {version_name}") - - if len(ok_versions) == 0: - raise DataTooLongError("The data is too long.") - - if fit_strategy == FitStrategy.MINIMIZE_WIDTH: - - def sort_key(x): - return x["width"] - - elif fit_strategy == FitStrategy.MINIMIZE_HEIGHT: - - def sort_key(x): - return x["height"] - - elif fit_strategy == FitStrategy.BALANCED: + return rMQROptimizer.compute(data, ecc, fit_strategy) - def sort_key(x): - return x["height"] * 9 + x["width"] + def _optimized_segments(self, data): + """Returns optimized segments computed by SegmentOptimizer. - selected = sorted(ok_versions, key=sort_key)[0] - logger.debug(f"selected: {selected}") + Args: + data (str): The data to encode. - qr = rMQR(selected["version"], ecc) - qr.add_segments(selected["segments"]) - qr.make() - return qr + Returns: + list: The list of segments. - def _optimized_segments(self, data): + """ optimizer = qr_segments.SegmentOptimizer() return optimizer.compute(data, self.version_name(), self._error_correction_level) @@ -137,12 +91,12 @@ def __init__(self, version, ecc, with_quiet_zone=True, logger=None): if not rMQR.validate_version(version): raise IllegalVersionError("The rMQR version is illegal.") - qr_version = rMQRVersions[version] - self._version = version - self._height = qr_version["height"] - self._width = qr_version["width"] + self._version_name = version + self._qr_version = rMQRVersions[version] + self._height = self._qr_version["height"] + self._width = self._qr_version["width"] self._error_correction_level = ecc - self._qr = [[Color.UNDEFINED for x in range(self._width)] for y in range(self._height)] + self._qr = rMQRCore(self._width, self._height) self._segments = [] def add_segment(self, data, encoder_class=encoder.ByteEncoder): @@ -162,6 +116,15 @@ def add_segment(self, data, encoder_class=encoder.ByteEncoder): self._segments.append({"data": data, "encoder_class": encoder_class}) def add_segments(self, segments): + """Add the segments. + + Args: + segments (list): The list of segments. + + Returns: + void + + """ for segment in segments: self.add_segment(segment["data"], segment["encoder_class"]) @@ -185,13 +148,19 @@ def make(self): except DataTooLongError: raise DataTooLongError() - 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(encoded_data) - self._apply_mask(mask_area) + self._qr.put_finder_patterns() + self._qr.put_corner_finder_pattern() + self._qr.put_alignment_pattern() + self._qr.put_timing_pattern() + + format_information = self._compute_format_info() + self._qr.put_format_information(format_information) + + codewords_num = self._qr_version["codewords_total"] + codewords = self._make_codewords(encoded_data, codewords_num) + blocks = self._split_into_blocks(codewords, self._qr_version["blocks"][self._error_correction_level]) + final_codewords = self._make_final_codewords(blocks) + self._qr.put_data(final_codewords, self._qr_version["remainder_bits"]) def _encode_data(self): """Encodes the data. @@ -203,12 +172,13 @@ def _encode_data(self): str: The encoded data. """ - qr_version = rMQRVersions[self.version_name()] - data_bits_max = DataCapacities[self.version_name()]["number_of_data_bits"][self._error_correction_level] + data_bits_max = self._qr_version["number_of_data_bits"][self._error_correction_level] res = "" for segment in self._segments: - character_count_indicator_length = qr_version["character_count_indicator_length"][segment["encoder_class"]] + character_count_indicator_length = self._qr_version["character_count_indicator_length"][ + segment["encoder_class"] + ] res += segment["encoder_class"].encode(segment["data"], character_count_indicator_length) res = self._append_terminator_if_possible(res, data_bits_max) @@ -316,66 +286,270 @@ 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)) + for y in range(QUIET_ZONE_MODULES): + res.append([0] * (self.width() + QUIET_ZONE_MODULES * 2)) + for row in self._qr.to_binary_list(): + res.append([0] * QUIET_ZONE_MODULES + row + [0] * QUIET_ZONE_MODULES) + for y in range(QUIET_ZONE_MODULES): + res.append([0] * (self.width() + QUIET_ZONE_MODULES * 2)) else: - res = self._to_binary_list() + res = self._qr.to_binary_list() return res - def _to_binary_list(self): - """Converts to two-dimensional list and returns it. + def __str__(self, with_quiet_zone=True): + res = f"rMQR Version R{self._height}x{self._width}:\n" + res += self._qr.__str__(with_quiet_zone) + return res - The value is 1 for the dark module and 0 for the light module. + def _compute_format_info(self): + """Computes format information with BCH code.""" + format_information_data = self._qr_version["version_indicator"] + if self._error_correction_level == ErrorCorrectionLevel.H: + format_information_data |= 1 << 5 + reminder_polynomial = compute_bch(format_information_data) + format_information_data = format_information_data << 12 | reminder_polynomial + return format_information_data + + def _make_codewords(self, encoded_data, codewords_num): + """Makes codeword sequence from encoded data. + + If the length of generated codeword sequence is less than the `codewords_num`, + appends the reminder codewords 11101100 and 00010001 alternately to meet the + requirements of number of codewords. Args: - with_quiet_zone (bool): Flag to select whether include the quiet zone. + encoded_data (str): The encoded data. + codewords_num (int): The number of codewords. Returns: - list: Converted list. + list: The list of codeword strings. - 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] + codewords = split_into_8bits(encoded_data) + while True: + if len(codewords) >= codewords_num: + break + codewords.append("11101100") + if len(codewords) >= codewords_num: + break + codewords.append("00010001") + return codewords - def __str__(self, with_quiet_zone=True): - res = "" + def _split_into_blocks(self, codewords, blocks_definition): + """Splits codewords into several blocks. - show = {} - show[Color.WHITE] = "_" - show[Color.BLACK] = "X" - show[Color.UNDEFINED] = "?" - show[True] = "X" - show[False] = "_" + Args: + codewords (list): The list of codeword strings. + blocks_definition: The list of dict. - 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 + Returns: + list: The list of Block object. - for i in range(self.height()): - if with_quiet_zone: - res += show[False] * self.QUIET_ZONE_MODULES + """ + data_idx = 0 + blocks = [] + for block_definition in blocks_definition: + for i in range(block_definition["num"]): + data_codewords_num = block_definition["k"] + ecc_codewords_num = block_definition["c"] - block_definition["k"] + codewords_in_block = codewords[data_idx : data_idx + data_codewords_num] + block = Block(data_codewords_num, ecc_codewords_num) + block.set_data_and_compute_ecc(codewords_in_block) + blocks.append(block) + data_idx += data_codewords_num + return blocks + + def _make_final_codewords(self, blocks): + """Makes the final message codeword sequence. + + This method computes the final codeword sequence from the given blocks. For example, + we consider the following blocks. The blocks consists of three blocks. Block1 contains + two data blocks and three ecc blocks. Block2 contains three data blocks and three ecc blocks. + Block3 contains three data blocks and three ecc blocks. + + Block1: Data#1 Data#2 ------ Ecc#1 Ecc#2 Ecc#3 + Block2: Data#3 Data#4 Data#5 Ecc#4 Ecc#5 Ecc#6 + Block3: Data#6 Data#7 Data#8 Ecc#7 Ecc#8 Ecc#9 + + The final codeword sequence for this example is placed in the following order. + + [Data#1, Data#3, Data#6, Data#2, Data#4, Data#7, Data#5, Data#8, + Ecc#1, Ecc#4, Ecc#7, Ecc#2, Ecc#5, Ecc#8, Ecc#3, Ecc#6, Ecc#9] - for j in range(self.width()): - if self._qr[i][j] in show: - res += show[self._qr[i][j]] + Args: + blocks (list): The list of Block objects. + + Returns: + list: The list of codeword strings. + + """ + final_codewords = [] + # Add data codewords + # The last block always has the most codewords. + for i in range(blocks[-1].data_length()): + for block in blocks: + try: + data_codeword = block.get_data_at(i) + except IndexError: + break else: - res += self._qr[i][j] + final_codewords.append(data_codeword) + self._logger.debug(f"Put QR data codeword {i} : {data_codeword}") + + # Add ecc codewords + # The last block always has the most codewords. + for i in range(blocks[-1].ecc_length()): + for block in blocks: + try: + ecc_codeword = block.get_ecc_at(i) + except IndexError: + break + else: + final_codewords.append(ecc_codeword) + self._logger.debug(f"Put RS data codewords {i} : {ecc_codeword}") + return final_codewords - if with_quiet_zone: - res += show[False] * self.QUIET_ZONE_MODULES - res += "\n" + @staticmethod + def validate_version(version_name): + """Check if the given version_name is valid - if with_quiet_zone: - res += (show[False] * (self.width() + self.QUIET_ZONE_MODULES * 2) + "\n") * self.QUIET_ZONE_MODULES - return res + 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 rMQROptimizer: + """A class to compute optimized rMQR code for input data.""" + + @staticmethod + def compute(data, ecc, fit_strategy): + """Attempts to make an rMQR have optimized version for given data. + + Args: + data (str): Data string 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. + + """ + ok_versions = [] + determined_width = set() + determined_height = set() + + for version_name, qr_version in rMQRVersions.items(): + optimizer = qr_segments.SegmentOptimizer() + try: + optimized_segments = optimizer.compute(data, version_name, ecc) + except DataTooLongError: + continue + + width, height = qr_version["width"], qr_version["height"] + if width not in determined_width and height not in determined_height: + determined_width.add(width) + determined_height.add(height) + ok_versions.append( + { + "version": version_name, + "width": width, + "height": height, + "segments": optimized_segments, + } + ) + + if len(ok_versions) == 0: + raise DataTooLongError("The data is too long.") + + if fit_strategy == FitStrategy.MINIMIZE_WIDTH: + + def sort_key(x): + return x["width"] + + elif fit_strategy == FitStrategy.MINIMIZE_HEIGHT: + + def sort_key(x): + return x["height"] + + elif fit_strategy == FitStrategy.BALANCED: + + def sort_key(x): + return x["height"] * 9 + x["width"] + + selected = sorted(ok_versions, key=sort_key)[0] + qr = rMQR(selected["version"], ecc) + qr.add_segments(selected["segments"]) + qr.make() + return qr + + +class rMQRCore: + "A class correspond to a grid of modules of rMQR code." + + def __init__(self, width, height): + self._width = width + self._height = height + self._qr = [[Color.UNDEFINED for x in range(self._width)] for y in range(self._height)] + + def get_data(self, x, y): + """Returns the module value at x-th column and y-th row. + + Args: + x (int): The index of x. + y (int): The index of y. + + Returns: + rmqrcode.Color: The module value. + + Raises: + IndexError: If the index is invalid. + + """ + if 0 > x or self._width < x: + raise IndexError("x index out of range") + if 0 > y or self._height < y: + raise IndexError("y index out of range") + return self._qr[y][x] + + 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 put_finder_patterns(self): + self._put_finder_pattern() + self._put_finder_sub_pattern() def _put_finder_pattern(self): - # Finder pattern # Outer square for i in range(7): for j in range(7): @@ -397,7 +571,7 @@ def _put_finder_pattern(self): if self._height >= 9: self._qr[7][n] = Color.WHITE - # Finder sub pattern + def _put_finder_sub_pattern(self): # Outer square for i in range(5): for j in range(5): @@ -407,7 +581,7 @@ def _put_finder_pattern(self): # Inner square self._qr[self._height - 1 - 2][self._width - 1 - 2] = Color.BLACK - def _put_corner_finder_pattern(self): + def put_corner_finder_pattern(self): # Corner finder pattern # Bottom left self._qr[self._height - 1][0] = Color.BLACK @@ -424,8 +598,7 @@ def _put_corner_finder_pattern(self): self._qr[1][self._width - 1] = Color.BLACK self._qr[1][self._width - 2] = Color.WHITE - def _put_alignment_pattern(self): - # Alignment pattern + def put_alignment_pattern(self): center_xs = AlignmentPatternCoordinates[self._width] for center_x in center_xs: for i in range(3): @@ -436,16 +609,18 @@ def _put_alignment_pattern(self): # Bottom side self._qr[self._height - 1 - i][center_x + j - 1] = color - def _put_timing_pattern(self): - # Timing pattern - # Horizontal + def put_timing_pattern(self): + self._put_timing_pattern_horizontal() + self._put_timing_pattern_vertical() + + def _put_timing_pattern_horizontal(self): for j in range(self._width): color = Color.BLACK if (j + 1) % 2 else Color.WHITE for i in [0, self._height - 1]: if self._qr[i][j] == Color.UNDEFINED: self._qr[i][j] = color - # Vertical + def _put_timing_pattern_vertical(self): center_xs = [0, self._width - 1] center_xs.extend(AlignmentPatternCoordinates[self._width]) for i in range(self._height): @@ -454,50 +629,73 @@ 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_format_information(self, format_information): + """Format information placement. + + Args: + format_information (int): The format information. + + Returns: + void + + """ + self._put_format_information_finder_pattern_side(format_information) + self._put_format_information_finder_sub_pattern_side(format_information) + + def _put_format_information_finder_pattern_side(self, format_information): + """Format information placement (finder pattern side). + + This method computes masked format information data and puts it. The mask + pattern is 011111101010110010. + + Args: + format_information (int): The format information. + + Returns: + void - def _put_version_information_finder_pattern_side(self, version_information): + """ mask = 0b011111101010110010 - version_information ^= mask + format_information ^= mask si, sj = 1, 8 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 format_information >> n & 1 else Color.WHITE + + def _put_format_information_finder_sub_pattern_side(self, format_information): + """Format information placement (finder sub pattern side). + + This method computes masked format information data and puts it. The mask + pattern is 100000101001111011. - def _put_version_information_finder_sub_pattern_side(self, version_information): + Args: + format_information (int): The format information. + + Returns: + void + + """ mask = 0b100000101001111011 - version_information ^= mask + format_information ^= mask si, sj = self._height - 1 - 5, self._width - 1 - 7 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[si + di][sj + dj] = Color.BLACK if format_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 + Color.BLACK if format_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 + Color.BLACK if format_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 + Color.BLACK if format_information >> 17 & 1 else Color.WHITE ) - def _compute_version_info(self): - qr_version = rMQRVersions[self.version_name()] - version_information_data = qr_version["version_indicator"] - if self._error_correction_level == ErrorCorrectionLevel.H: - version_information_data |= 1 << 6 - reminder_polynomial = compute_bch(version_information_data) - version_information_data = version_information_data << 12 | reminder_polynomial - return version_information_data - - def _put_data(self, encoded_data): + def put_data(self, final_codewords, remainder_bits_num): """Symbol character placement. This method puts data into the encoding region of the rMQR Code. The data @@ -508,52 +706,36 @@ def _put_data(self, encoded_data): Args: encoded_data (str): The data after encoding. Expected all segments are joined. + reminder_bits_num (int): The number of modules without data. Returns: list: A two-dimensional list shows where encoding region. """ - codewords = split_into_8bits(encoded_data) + mask_area = self._put_final_codewords(final_codewords, remainder_bits_num) + self._apply_mask(mask_area) - # Add the remainder codewords - qr_version = rMQRVersions[self.version_name()] - codewords_total = qr_version["codewords_total"] - while True: - if len(codewords) >= codewords_total: - break - codewords.append("11101100") - if len(codewords) >= codewords_total: - break - codewords.append("00010001") + def _put_final_codewords(self, final_codewords, reminder_bits_num): + """Puts the final codeword sequence. - data_codewords_per_block, rs_codewords_per_block = self._split_into_blocks( - codewords, qr_version["blocks"][self._error_correction_level] - ) + This method puts the final codeword sequence into the encoding region of the rMQR Code. + The `final_codewords` is computed by self._make_final_codewords method. Also, this method + computes a two-dimensional list shows where encoding region at the same time. + And returns the list. - # Construct the final message codeword sequence - # Data codewords - final_codewords = [] - for i in range(len(data_codewords_per_block[-1])): - for data_codewords in data_codewords_per_block: - if i >= len(data_codewords): - continue - final_codewords.append(data_codewords[i]) - self._logger.debug(f"Put QR data codeword {i} : {data_codewords[i]}") + Args: + final_codewords (list): The list of the final codeword strings. + reminder_bits_num (int): The number of modules without data. - # RS Codewords - for i in range(len(rs_codewords_per_block[-1])): - for rs_codewords in rs_codewords_per_block: - if i >= len(rs_codewords): - continue - final_codewords.append(rs_codewords[i]) - self._logger.debug(f"Put RS data codewords {i} : {rs_codewords[i]}") + Returns: + list: A two-dimensional list shows where encoding region. - # Codeword placement + """ 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"] + remaining_remainder_bits = reminder_bits_num mask_area = [[False for i in range(self._width)] for j in range(self._height)] while True: @@ -564,7 +746,7 @@ def _put_data(self, encoded_data): # Remainder bits self._qr[cy][x] = Color.WHITE mask_area[cy][x] = True - remainder_bits -= 1 + remaining_remainder_bits -= 1 else: # Codewords self._qr[cy][x] = ( @@ -578,10 +760,10 @@ def _put_data(self, encoded_data): current_bit_idx = 0 current_codeword_idx += 1 - if current_codeword_idx == len(final_codewords) and remainder_bits == 0: + if current_codeword_idx == len(final_codewords) and remaining_remainder_bits == 0: break - if current_codeword_idx == len(final_codewords) and remainder_bits == 0: + if current_codeword_idx == len(final_codewords) and remaining_remainder_bits == 0: break # Update current coordinates @@ -596,27 +778,6 @@ def _put_data(self, encoded_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"] - g = GeneratorPolynomials[rs_codewords_num] - - codewords_in_block = codewords[data_idx : data_idx + data_codewords_num] - rs_codewords_in_block = compute_reed_solomon(codewords_in_block, g, rs_codewords_num) - - data_codewords_per_block.append(codewords_in_block) - rs_codewords_per_block.append(rs_codewords_in_block) - - data_idx += data_codewords_num - error_idx += rs_codewords_num - - return data_codewords_per_block, rs_codewords_per_block - def _apply_mask(self, mask_area): """Data masking. @@ -640,25 +801,98 @@ 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): - """Check if the given version_name is valid + def __str__(self, with_quiet_zone=True): + show = { + Color.WHITE: "_", + Color.BLACK: "X", + Color.UNDEFINED: "?", + True: "X", + False: "_", + } + + res = "" + if with_quiet_zone: + res += (show[False] * (self._width + QUIET_ZONE_MODULES * 2) + "\n") * QUIET_ZONE_MODULES + + for y in range(self._height): + if with_quiet_zone: + res += show[False] * QUIET_ZONE_MODULES + + for x in range(self._width): + if self._qr[y][x] in show: + res += show[self._qr[y][x]] + else: + res += self._qr.get_data[y][x] + + if with_quiet_zone: + res += show[False] * QUIET_ZONE_MODULES + res += "\n" + + if with_quiet_zone: + res += (show[False] * (self._width + QUIET_ZONE_MODULES * 2) + "\n") * QUIET_ZONE_MODULES + return res + + +class Block: + """A class represents data block. + + This class represents data block. A block consists data part and error correction + code (ecc) part. + + """ + + def __init__(self, data_codewords_num, ecc_codewords_num): + self._data_codewords_num = data_codewords_num + self._data_codewords = [] + self._ecc_codewords_num = ecc_codewords_num + self._ecc_codewords = [] + + def set_data_and_compute_ecc(self, data_codewords): + """Set data and compute ecc. Args: - version_name (str): Version name. + data_codewords (list): The list of codeword strings. Returns: - bool: Validation result. + void - Example: - >>> rMQR.validate_version("R13x77") - True + """ + self._data_codewords = data_codewords + self._compute_ecc_codewords() - >>> rMQR.validate_version("R14x55") - False + def get_data_at(self, index): + """Get data codeword at the index. - >>> rMQR.validate_version("13, 77") - False + Args: + index (int): The index. + + Return: + str: The data codeword. """ - return version_name in rMQRVersions + return self._data_codewords[index] + + def get_ecc_at(self, index): + """Get ecc codeword at the index. + + Args: + index (int): The index. + + Return: + str: The ecc codeword. + + """ + return self._ecc_codewords[index] + + def data_length(self): + """Get the number of data codewords""" + return len(self._data_codewords) + + def ecc_length(self): + """Get the number of ecc codewords""" + return len(self._ecc_codewords) + + def _compute_ecc_codewords(self): + """Computes the ecc codewords with the data codewords.""" + g = GeneratorPolynomials[self._ecc_codewords_num] + self._ecc_codewords = compute_reed_solomon(self._data_codewords, g, self._ecc_codewords_num) diff --git a/src/rmqrcode/segments.py b/src/rmqrcode/segments.py index 02aa436..1dc0fd6 100644 --- a/src/rmqrcode/segments.py +++ b/src/rmqrcode/segments.py @@ -1,6 +1,5 @@ from . import encoder from .errors import DataTooLongError -from .format.data_capacities import DataCapacities from .format.rmqr_versions import rMQRVersions encoders = [ @@ -69,7 +68,7 @@ def compute(self, data, version, ecc): self.qr_version = rMQRVersions[version] self._compute_costs(data) best = self._find_best(data) - if best["cost"] > DataCapacities[version]["number_of_data_bits"][ecc]: + if best["cost"] > self.qr_version["number_of_data_bits"][ecc]: raise DataTooLongError path = self._reconstruct_path(best["index"])