diff --git a/qrcode/__init__.py b/qrcode/__init__.py index f6aa53f2..0836a835 100644 --- a/qrcode/__init__.py +++ b/qrcode/__init__.py @@ -1,4 +1,4 @@ -from qrcode.main import QRCode +from qrcode.main import QRCode, MicroQRCode from qrcode.main import make # noqa from qrcode.constants import ( # noqa ERROR_CORRECT_L, ERROR_CORRECT_M, ERROR_CORRECT_Q, ERROR_CORRECT_H) diff --git a/qrcode/base.py b/qrcode/base.py index 076d5c77..24e09915 100644 --- a/qrcode/base.py +++ b/qrcode/base.py @@ -22,254 +22,269 @@ constants.ERROR_CORRECT_H: 3, } -RS_BLOCK_TABLE = [ - +RS_BLOCK_TABLE = { # L # M # Q # H - # 1 - [1, 26, 19], - [1, 26, 16], - [1, 26, 13], - [1, 26, 9], - - # 2 - [1, 44, 34], - [1, 44, 28], - [1, 44, 22], - [1, 44, 16], - - # 3 - [1, 70, 55], - [1, 70, 44], - [2, 35, 17], - [2, 35, 13], - - # 4 - [1, 100, 80], - [2, 50, 32], - [2, 50, 24], - [4, 25, 9], - - # 5 - [1, 134, 108], - [2, 67, 43], - [2, 33, 15, 2, 34, 16], - [2, 33, 11, 2, 34, 12], - - # 6 - [2, 86, 68], - [4, 43, 27], - [4, 43, 19], - [4, 43, 15], - - # 7 - [2, 98, 78], - [4, 49, 31], - [2, 32, 14, 4, 33, 15], - [4, 39, 13, 1, 40, 14], - - # 8 - [2, 121, 97], - [2, 60, 38, 2, 61, 39], - [4, 40, 18, 2, 41, 19], - [4, 40, 14, 2, 41, 15], - - # 9 - [2, 146, 116], - [3, 58, 36, 2, 59, 37], - [4, 36, 16, 4, 37, 17], - [4, 36, 12, 4, 37, 13], - - # 10 - [2, 86, 68, 2, 87, 69], - [4, 69, 43, 1, 70, 44], - [6, 43, 19, 2, 44, 20], - [6, 43, 15, 2, 44, 16], - - # 11 - [4, 101, 81], - [1, 80, 50, 4, 81, 51], - [4, 50, 22, 4, 51, 23], - [3, 36, 12, 8, 37, 13], - - # 12 - [2, 116, 92, 2, 117, 93], - [6, 58, 36, 2, 59, 37], - [4, 46, 20, 6, 47, 21], - [7, 42, 14, 4, 43, 15], - - # 13 - [4, 133, 107], - [8, 59, 37, 1, 60, 38], - [8, 44, 20, 4, 45, 21], - [12, 33, 11, 4, 34, 12], - - # 14 - [3, 145, 115, 1, 146, 116], - [4, 64, 40, 5, 65, 41], - [11, 36, 16, 5, 37, 17], - [11, 36, 12, 5, 37, 13], - - # 15 - [5, 109, 87, 1, 110, 88], - [5, 65, 41, 5, 66, 42], - [5, 54, 24, 7, 55, 25], - [11, 36, 12], - - # 16 - [5, 122, 98, 1, 123, 99], - [7, 73, 45, 3, 74, 46], - [15, 43, 19, 2, 44, 20], - [3, 45, 15, 13, 46, 16], - - # 17 - [1, 135, 107, 5, 136, 108], - [10, 74, 46, 1, 75, 47], - [1, 50, 22, 15, 51, 23], - [2, 42, 14, 17, 43, 15], - - # 18 - [5, 150, 120, 1, 151, 121], - [9, 69, 43, 4, 70, 44], - [17, 50, 22, 1, 51, 23], - [2, 42, 14, 19, 43, 15], - - # 19 - [3, 141, 113, 4, 142, 114], - [3, 70, 44, 11, 71, 45], - [17, 47, 21, 4, 48, 22], - [9, 39, 13, 16, 40, 14], - - # 20 - [3, 135, 107, 5, 136, 108], - [3, 67, 41, 13, 68, 42], - [15, 54, 24, 5, 55, 25], - [15, 43, 15, 10, 44, 16], - - # 21 - [4, 144, 116, 4, 145, 117], - [17, 68, 42], - [17, 50, 22, 6, 51, 23], - [19, 46, 16, 6, 47, 17], - - # 22 - [2, 139, 111, 7, 140, 112], - [17, 74, 46], - [7, 54, 24, 16, 55, 25], - [34, 37, 13], - - # 23 - [4, 151, 121, 5, 152, 122], - [4, 75, 47, 14, 76, 48], - [11, 54, 24, 14, 55, 25], - [16, 45, 15, 14, 46, 16], - - # 24 - [6, 147, 117, 4, 148, 118], - [6, 73, 45, 14, 74, 46], - [11, 54, 24, 16, 55, 25], - [30, 46, 16, 2, 47, 17], - - # 25 - [8, 132, 106, 4, 133, 107], - [8, 75, 47, 13, 76, 48], - [7, 54, 24, 22, 55, 25], - [22, 45, 15, 13, 46, 16], - - # 26 - [10, 142, 114, 2, 143, 115], - [19, 74, 46, 4, 75, 47], - [28, 50, 22, 6, 51, 23], - [33, 46, 16, 4, 47, 17], - - # 27 - [8, 152, 122, 4, 153, 123], - [22, 73, 45, 3, 74, 46], - [8, 53, 23, 26, 54, 24], - [12, 45, 15, 28, 46, 16], - - # 28 - [3, 147, 117, 10, 148, 118], - [3, 73, 45, 23, 74, 46], - [4, 54, 24, 31, 55, 25], - [11, 45, 15, 31, 46, 16], - - # 29 - [7, 146, 116, 7, 147, 117], - [21, 73, 45, 7, 74, 46], - [1, 53, 23, 37, 54, 24], - [19, 45, 15, 26, 46, 16], - - # 30 - [5, 145, 115, 10, 146, 116], - [19, 75, 47, 10, 76, 48], - [15, 54, 24, 25, 55, 25], - [23, 45, 15, 25, 46, 16], - - # 31 - [13, 145, 115, 3, 146, 116], - [2, 74, 46, 29, 75, 47], - [42, 54, 24, 1, 55, 25], - [23, 45, 15, 28, 46, 16], - - # 32 - [17, 145, 115], - [10, 74, 46, 23, 75, 47], - [10, 54, 24, 35, 55, 25], - [19, 45, 15, 35, 46, 16], - - # 33 - [17, 145, 115, 1, 146, 116], - [14, 74, 46, 21, 75, 47], - [29, 54, 24, 19, 55, 25], - [11, 45, 15, 46, 46, 16], - - # 34 - [13, 145, 115, 6, 146, 116], - [14, 74, 46, 23, 75, 47], - [44, 54, 24, 7, 55, 25], - [59, 46, 16, 1, 47, 17], - - # 35 - [12, 151, 121, 7, 152, 122], - [12, 75, 47, 26, 76, 48], - [39, 54, 24, 14, 55, 25], - [22, 45, 15, 41, 46, 16], - - # 36 - [6, 151, 121, 14, 152, 122], - [6, 75, 47, 34, 76, 48], - [46, 54, 24, 10, 55, 25], - [2, 45, 15, 64, 46, 16], - - # 37 - [17, 152, 122, 4, 153, 123], - [29, 74, 46, 14, 75, 47], - [49, 54, 24, 10, 55, 25], - [24, 45, 15, 46, 46, 16], - - # 38 - [4, 152, 122, 18, 153, 123], - [13, 74, 46, 32, 75, 47], - [48, 54, 24, 14, 55, 25], - [42, 45, 15, 32, 46, 16], - - # 39 - [20, 147, 117, 4, 148, 118], - [40, 75, 47, 7, 76, 48], - [43, 54, 24, 22, 55, 25], - [10, 45, 15, 67, 46, 16], - - # 40 - [19, 148, 118, 6, 149, 119], - [18, 75, 47, 31, 76, 48], - [34, 54, 24, 34, 55, 25], - [20, 45, 15, 61, 46, 16] - -] + 'M1': [ + [1, 5, 3], + ], + 'M2': [ + [1, 10, 5], + [1, 10, 4], + ], + 'M3': [ + [1, 17, 10], + [1, 17, 9], + ], + 'M4': [ + [1, 24, 16], + [1, 24, 14], + [1, 24, 10], + ], + 1: [ + [1, 26, 19], + [1, 26, 16], + [1, 26, 13], + [1, 26, 9], + ], + 2: [ + [1, 44, 34], + [1, 44, 28], + [1, 44, 22], + [1, 44, 16], + ], + 3: [ + [1, 70, 55], + [1, 70, 44], + [2, 35, 17], + [2, 35, 13], + ], + 4: [ + [1, 100, 80], + [2, 50, 32], + [2, 50, 24], + [4, 25, 9], + ], + 5: [ + [1, 134, 108], + [2, 67, 43], + [2, 33, 15, 2, 34, 16], + [2, 33, 11, 2, 34, 12], + ], + 6: [ + [2, 86, 68], + [4, 43, 27], + [4, 43, 19], + [4, 43, 15], + ], + 7: [ + [2, 98, 78], + [4, 49, 31], + [2, 32, 14, 4, 33, 15], + [4, 39, 13, 1, 40, 14], + ], + 8: [ + [2, 121, 97], + [2, 60, 38, 2, 61, 39], + [4, 40, 18, 2, 41, 19], + [4, 40, 14, 2, 41, 15], + ], + 9: [ + [2, 146, 116], + [3, 58, 36, 2, 59, 37], + [4, 36, 16, 4, 37, 17], + [4, 36, 12, 4, 37, 13], + ], + 10: [ + [2, 86, 68, 2, 87, 69], + [4, 69, 43, 1, 70, 44], + [6, 43, 19, 2, 44, 20], + [6, 43, 15, 2, 44, 16], + ], + 11: [ + [4, 101, 81], + [1, 80, 50, 4, 81, 51], + [4, 50, 22, 4, 51, 23], + [3, 36, 12, 8, 37, 13], + ], + 12: [ + [2, 116, 92, 2, 117, 93], + [6, 58, 36, 2, 59, 37], + [4, 46, 20, 6, 47, 21], + [7, 42, 14, 4, 43, 15], + ], + 13: [ + [4, 133, 107], + [8, 59, 37, 1, 60, 38], + [8, 44, 20, 4, 45, 21], + [12, 33, 11, 4, 34, 12], + ], + 14: [ + [3, 145, 115, 1, 146, 116], + [4, 64, 40, 5, 65, 41], + [11, 36, 16, 5, 37, 17], + [11, 36, 12, 5, 37, 13], + ], + 15: [ + [5, 109, 87, 1, 110, 88], + [5, 65, 41, 5, 66, 42], + [5, 54, 24, 7, 55, 25], + [11, 36, 12], + ], + 16: [ + [5, 122, 98, 1, 123, 99], + [7, 73, 45, 3, 74, 46], + [15, 43, 19, 2, 44, 20], + [3, 45, 15, 13, 46, 16], + ], + 17: [ + [1, 135, 107, 5, 136, 108], + [10, 74, 46, 1, 75, 47], + [1, 50, 22, 15, 51, 23], + [2, 42, 14, 17, 43, 15], + ], + 18: [ + [5, 150, 120, 1, 151, 121], + [9, 69, 43, 4, 70, 44], + [17, 50, 22, 1, 51, 23], + [2, 42, 14, 19, 43, 15], + ], + 19: [ + [3, 141, 113, 4, 142, 114], + [3, 70, 44, 11, 71, 45], + [17, 47, 21, 4, 48, 22], + [9, 39, 13, 16, 40, 14], + ], + 20: [ + [3, 135, 107, 5, 136, 108], + [3, 67, 41, 13, 68, 42], + [15, 54, 24, 5, 55, 25], + [15, 43, 15, 10, 44, 16], + ], + 21: [ + [4, 144, 116, 4, 145, 117], + [17, 68, 42], + [17, 50, 22, 6, 51, 23], + [19, 46, 16, 6, 47, 17], + ], + 22: [ + [2, 139, 111, 7, 140, 112], + [17, 74, 46], + [7, 54, 24, 16, 55, 25], + [34, 37, 13], + ], + 23: [ + [4, 151, 121, 5, 152, 122], + [4, 75, 47, 14, 76, 48], + [11, 54, 24, 14, 55, 25], + [16, 45, 15, 14, 46, 16], + ], + 24: [ + [6, 147, 117, 4, 148, 118], + [6, 73, 45, 14, 74, 46], + [11, 54, 24, 16, 55, 25], + [30, 46, 16, 2, 47, 17], + ], + 25: [ + [8, 132, 106, 4, 133, 107], + [8, 75, 47, 13, 76, 48], + [7, 54, 24, 22, 55, 25], + [22, 45, 15, 13, 46, 16], + ], + 26: [ + [10, 142, 114, 2, 143, 115], + [19, 74, 46, 4, 75, 47], + [28, 50, 22, 6, 51, 23], + [33, 46, 16, 4, 47, 17], + ], + 27: [ + [8, 152, 122, 4, 153, 123], + [22, 73, 45, 3, 74, 46], + [8, 53, 23, 26, 54, 24], + [12, 45, 15, 28, 46, 16], + ], + 28: [ + [3, 147, 117, 10, 148, 118], + [3, 73, 45, 23, 74, 46], + [4, 54, 24, 31, 55, 25], + [11, 45, 15, 31, 46, 16], + ], + 29: [ + [7, 146, 116, 7, 147, 117], + [21, 73, 45, 7, 74, 46], + [1, 53, 23, 37, 54, 24], + [19, 45, 15, 26, 46, 16], + ], + 30: [ + [5, 145, 115, 10, 146, 116], + [19, 75, 47, 10, 76, 48], + [15, 54, 24, 25, 55, 25], + [23, 45, 15, 25, 46, 16], + ], + 31: [ + [13, 145, 115, 3, 146, 116], + [2, 74, 46, 29, 75, 47], + [42, 54, 24, 1, 55, 25], + [23, 45, 15, 28, 46, 16], + ], + 32: [ + [17, 145, 115], + [10, 74, 46, 23, 75, 47], + [10, 54, 24, 35, 55, 25], + [19, 45, 15, 35, 46, 16], + ], + 33: [ + [17, 145, 115, 1, 146, 116], + [14, 74, 46, 21, 75, 47], + [29, 54, 24, 19, 55, 25], + [11, 45, 15, 46, 46, 16], + ], + 34: [ + [13, 145, 115, 6, 146, 116], + [14, 74, 46, 23, 75, 47], + [44, 54, 24, 7, 55, 25], + [59, 46, 16, 1, 47, 17], + ], + 35: [ + [12, 151, 121, 7, 152, 122], + [12, 75, 47, 26, 76, 48], + [39, 54, 24, 14, 55, 25], + [22, 45, 15, 41, 46, 16], + ], + 36: [ + [6, 151, 121, 14, 152, 122], + [6, 75, 47, 34, 76, 48], + [46, 54, 24, 10, 55, 25], + [2, 45, 15, 64, 46, 16], + ], + 37: [ + [17, 152, 122, 4, 153, 123], + [29, 74, 46, 14, 75, 47], + [49, 54, 24, 10, 55, 25], + [24, 45, 15, 46, 46, 16], + ], + 38: [ + [4, 152, 122, 18, 153, 123], + [13, 74, 46, 32, 75, 47], + [48, 54, 24, 14, 55, 25], + [42, 45, 15, 32, 46, 16], + ], + 39: [ + [20, 147, 117, 4, 148, 118], + [40, 75, 47, 7, 76, 48], + [43, 54, 24, 22, 55, 25], + [10, 45, 15, 67, 46, 16], + ], + 40: [ + [19, 148, 118, 6, 149, 119], + [18, 75, 47, 31, 76, 48], + [34, 54, 24, 34, 55, 25], + [20, 45, 15, 61, 46, 16] + ], +} def glog(n): @@ -344,15 +359,16 @@ def __init__(self, total_count, data_count): def rs_blocks(version, error_correction): - if error_correction not in RS_BLOCK_OFFSET: # pragma: no cover + version_blocks = RS_BLOCK_TABLE[version] + ec_index = RS_BLOCK_OFFSET.get(error_correction) + + if ec_index is None or len(version_blocks) < ec_index: # pragma: no cover raise Exception( "bad rs block @ version: %s / error_correction: %s" % (version, error_correction)) - offset = RS_BLOCK_OFFSET[error_correction] - rs_block = RS_BLOCK_TABLE[(version - 1) * 4 + offset] + rs_block = version_blocks[ec_index] blocks = [] - for i in range(0, len(rs_block), 3): count, total_count, data_count = rs_block[i:i + 3] for j in range(count): diff --git a/qrcode/console_scripts.py b/qrcode/console_scripts.py index dec36473..3cd0f529 100755 --- a/qrcode/console_scripts.py +++ b/qrcode/console_scripts.py @@ -44,7 +44,9 @@ def main(args=sys.argv[1:]): "M (15%, default), Q (25%), and H (30%).") opts, args = parser.parse_args(args) - qr = qrcode.QRCode( + # qr = qrcode.QRCode( + # error_correction=error_correction[opts.error_correction]) + qr = qrcode.MicroQRCode( error_correction=error_correction[opts.error_correction]) if opts.factory: diff --git a/qrcode/main.py b/qrcode/main.py index e983faf6..adefc75d 100644 --- a/qrcode/main.py +++ b/qrcode/main.py @@ -11,29 +11,43 @@ def make(data=None, **kwargs): return qr.make_image() -def _check_version(version): - if version < 1 or version > 40: - raise ValueError( - "Invalid version (was %s, expected 1 to 40)" % version) - - -class QRCode: +class QRCode(object): + valid_error_correction = ( + constants.ERROR_CORRECT_L, + constants.ERROR_CORRECT_M, + constants.ERROR_CORRECT_Q, + constants.ERROR_CORRECT_H, + ) + mask_patterns = list(range(8)) + default_border = 4 + max_version = 40 def __init__(self, version=None, error_correction=constants.ERROR_CORRECT_M, - box_size=10, border=4, + box_size=10, border=None, image_factory=None): self.version = version and int(version) self.error_correction = int(error_correction) + if self.error_correction not in self.valid_error_correction: + raise ValueError('Invalid error correction') self.box_size = int(box_size) # Spec says border should be at least four boxes wide, but allow for # any (e.g. for producing printable QR codes). + if border is None: + border = self.default_border self.border = int(border) self.image_factory = image_factory if image_factory is not None: assert issubclass(image_factory, BaseImage) self.clear() + def check_version(self, version): + if version < 1 or version > self.max_version: + raise ValueError( + "Invalid version (was %s, expected 1 to %s)" % + (version, self.max_version)) + return version + def clear(self): """ Reset the internal data. @@ -71,22 +85,19 @@ def make(self, fit=True): self.best_fit(start=self.version) self.makeImpl(False, self.best_mask_pattern()) - def makeImpl(self, test, mask_pattern): - _check_version(self.version) + def set_modules_count(self): self.modules_count = self.version * 4 + 17 - self.modules = [None] * self.modules_count - for row in range(self.modules_count): - - self.modules[row] = [None] * self.modules_count + def makeImpl(self, test, mask_pattern): + self.version = self.check_version(self.version) + self.set_modules_count() - for col in range(self.modules_count): - self.modules[row][col] = None # (col + row) % 3 + self.modules = [] + for row in range(self.modules_count): + self.modules.append([None] * self.modules_count) - self.setup_position_probe_pattern(0, 0) - self.setup_position_probe_pattern(self.modules_count - 7, 0) - self.setup_position_probe_pattern(0, self.modules_count - 7) - self.setup_position_adjust_pattern() + self.setup_position_probe_patterns() + self.setup_position_alignment_patterns() self.setup_timing_pattern() self.setup_type_info(test, mask_pattern) @@ -94,10 +105,18 @@ def makeImpl(self, test, mask_pattern): self.setup_type_number(test) if self.data_cache is None: - self.data_cache = util.create_data( - self.version, self.error_correction, self.data_list) + self.create_data_cache() self.map_data(self.data_cache, mask_pattern) + def create_data_cache(self): + self.data_cache = util.create_data( + self.version, self.error_correction, self.data_list) + + def setup_position_probe_patterns(self): + self.setup_position_probe_pattern(0, 0) + self.setup_position_probe_pattern(self.modules_count - 7, 0) + self.setup_position_probe_pattern(0, self.modules_count - 7) + def setup_position_probe_pattern(self, row, col): for r in range(-1, 8): @@ -122,7 +141,7 @@ def best_fit(self, start=None): """ if start is None: start = 1 - _check_version(start) + start = self.check_version(start) # Corresponds to the code in util.create_data, except we don't yet know # version, so optimistically assume start and check later @@ -136,7 +155,7 @@ def best_fit(self, start=None): needed_bits = len(buffer) self.version = bisect_left(util.BIT_LIMIT_TABLE[self.error_correction], needed_bits, start) - if self.version == 41: + if self.version > self.max_version: raise exceptions.DataOverflowError() # Now check whether we need more bits for the mode sizes, recursing if @@ -152,7 +171,7 @@ def best_mask_pattern(self): min_lost_point = 0 pattern = 0 - for i in range(8): + for i in self.mask_patterns: self.makeImpl(True, i) lost_point = util.lost_point(self.modules) @@ -275,7 +294,7 @@ def setup_timing_pattern(self): continue self.modules[6][c] = (c % 2 == 0) - def setup_position_adjust_pattern(self): + def setup_position_alignment_patterns(self): pos = util.pattern_position(self.version) for i in range(len(pos)): @@ -352,7 +371,7 @@ def map_data(self, data, mask_pattern): for col in six.moves.xrange(self.modules_count - 1, 0, -2): - if col <= 6: + if col <= 6 and not isinstance(self, MicroQRCode): col -= 1 col_range = (col, col-1) @@ -405,3 +424,62 @@ def get_matrix(self): code += [[False]*width] * self.border return code + + +class MicroQRCode(QRCode): + valid_error_correction = ( + constants.ERROR_CORRECT_L, + constants.ERROR_CORRECT_M, + constants.ERROR_CORRECT_Q, + ) + default_border = 2 + mask_patterns = [1, 4, 6, 7] + max_version = 4 + + def check_version(self, version): + version = super(MicroQRCode, self).check_version(version) + minimum_version = { + constants.ERROR_CORRECT_L: 1, + constants.ERROR_CORRECT_M: 2, + constants.ERROR_CORRECT_Q: 4, + }[self.error_correction] + return max(version, minimum_version) + + def set_modules_count(self): + self.modules_count = self.version * 2 + 9 + + def setup_position_probe_patterns(self): + self.setup_position_probe_pattern(0, 0) + + def setup_position_alignment_patterns(self): + pass + + def setup_timing_pattern(self): + for r in range(8, self.modules_count): + if self.modules[r][0] is not None: + continue + self.modules[r][0] = (r % 2 == 0) + + for c in range(8, self.modules_count): + if self.modules[0][c] is not None: + continue + self.modules[0][c] = (c % 2 == 0) + + def setup_type_info(self, test, mask_pattern): + # TODO: Fixme + data = (self.error_correction << 3) | mask_pattern + bits = util.BCH_type_info(data, mask=util.MICRO_G15_MASK) + + for i, bit in enumerate(util.bits_iter(bits)): + mod = bool(not test and bit) + + if i <= 7: + self.modules[i + 1][8] = mod + else: + self.modules[8][15-i] = mod + + def create_data_cache(self): + version = 'M%s' % self.version + self.data_cache = util.create_data( + version, self.error_correction, self.data_list) + self.data_cache diff --git a/qrcode/util.py b/qrcode/util.py index 89dcf099..bdfcbc39 100644 --- a/qrcode/util.py +++ b/qrcode/util.py @@ -12,7 +12,30 @@ MODE_8BIT_BYTE = 1 << 2 MODE_KANJI = 1 << 3 +MICRO_VERSIONS = ['M1', 'M2', 'M3', 'M4'] + # Encoding mode sizes. +MODE_SIZE_MICRO = { + 'M1': { + MODE_NUMBER: 3, + }, + 'M2': { + MODE_NUMBER: 4, + MODE_ALPHA_NUM: 3, + }, + 'M3': { + MODE_NUMBER: 5, + MODE_ALPHA_NUM: 4, + MODE_8BIT_BYTE: 4, + MODE_KANJI: 3, + }, + 'M4': { + MODE_NUMBER: 6, + MODE_ALPHA_NUM: 5, + MODE_8BIT_BYTE: 5, + MODE_KANJI: 4, + }, +} MODE_SIZE_SMALL = { MODE_NUMBER: 10, MODE_ALPHA_NUM: 9, @@ -88,6 +111,7 @@ (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0)) G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1) +MICRO_G15_MASK = (1 << 14) | (1 << 10) | (1 << 6) | (1 << 2) | (1 << 0) PAD0 = 0xEC PAD1 = 0x11 @@ -101,12 +125,12 @@ ] -def BCH_type_info(data): - d = data << 10 - while BCH_digit(d) - BCH_digit(G15) >= 0: - d ^= (G15 << (BCH_digit(d) - BCH_digit(G15))) +def BCH_type_info(data, mask=G15_MASK): + d = data << 10 + while BCH_digit(d) - BCH_digit(G15) >= 0: + d ^= (G15 << (BCH_digit(d) - BCH_digit(G15))) - return ((data << 10) | d) ^ G15_MASK + return ((data << 10) | d) ^ mask def BCH_type_number(data): @@ -152,6 +176,8 @@ def mask_func(pattern): def mode_sizes_for_version(version): + if version in MICRO_VERSIONS: + return MODE_SIZE_MICRO[version] if version < 10: return MODE_SIZE_SMALL elif version < 27: @@ -161,13 +187,19 @@ def mode_sizes_for_version(version): def length_in_bits(mode, version): - if mode not in ( - MODE_NUMBER, MODE_ALPHA_NUM, MODE_8BIT_BYTE, MODE_KANJI): - raise TypeError("Invalid mode (%s)" % mode) # pragma: no cover + if version in MICRO_VERSIONS: + if mode not in MODE_SIZE_MICRO[version]: + raise TypeError( + "Invalid mode (%s) for MicroQR %s" % + (mode, version)) # pragma: no cover + else: + if mode not in ( + MODE_NUMBER, MODE_ALPHA_NUM, MODE_8BIT_BYTE, MODE_KANJI): + raise TypeError("Invalid mode (%s)" % mode) # pragma: no cover - if version < 1 or version > 40: # pragma: no cover - raise ValueError( - "Invalid version (was %s, expected 1 to 40)" % version) + if version < 1 or version > 40: # pragma: no cover + raise ValueError( + "Invalid version (was %s, expected 1 to 40)" % version) return mode_sizes_for_version(version)[mode] @@ -313,6 +345,8 @@ def optimal_data_chunks(data, minimum=4): :param minimum: The minimum number of bytes in a row to split as a chunk. """ data = to_bytestring(data) + if data: + minimum = min(minimum, len(data)) re_repeat = ( six.b('{') + six.text_type(minimum).encode('ascii') + six.b(',}')) num_pattern = re.compile(six.b('\d') + re_repeat) @@ -554,3 +588,10 @@ def create_data(version, error_correction, data_list): buffer.put(PAD1, 8) return create_bytes(buffer, rs_blocks) + + +def bits_iter(num): + while num: + bit = num & (~num+1) + yield bit + num ^= bit