pull/118/head
Project Nayuki 3 years ago
commit 0532c7a237

@ -175,7 +175,7 @@ Rust language:
License License
------- -------
Copyright © 2020 Project Nayuki. (MIT License) Copyright © 2021 Project Nayuki. (MIT License)
[https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library)
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

@ -65,11 +65,11 @@ clean:
# Executable files # Executable files
%: %.o $(LIBFILE) %: %.o $(LIBFILE)
$(CC) $(CFLAGS) -o $@ $< -L . -l $(LIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -L . -l $(LIB)
# Special executable # Special executable
qrcodegen-test: qrcodegen-test.c $(LIBOBJ:%.o=%.c) qrcodegen-test: qrcodegen-test.c $(LIBOBJ:%.o=%.c)
$(CC) $(CFLAGS) -DQRCODEGEN_TEST -o $@ $^ $(CC) $(CFLAGS) $(LDFLAGS) -DQRCODEGEN_TEST -o $@ $^
# The library # The library
$(LIBFILE): $(LIBOBJ) $(LIBFILE): $(LIBOBJ)

@ -65,7 +65,7 @@ clean:
# Executable files # Executable files
%: %.o $(LIBFILE) %: %.o $(LIBFILE)
$(CXX) $(CXXFLAGS) -o $@ $< -L . -l $(LIB) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $< -L . -l $(LIB)
# The library # The library
$(LIBFILE): $(LIBOBJ) $(LIBFILE): $(LIBOBJ)

@ -279,7 +279,7 @@ QrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl,
throw std::logic_error("Assertion error"); throw std::logic_error("Assertion error");
// Increase the error correction level while the data still fits in the current version number // Increase the error correction level while the data still fits in the current version number
for (Ecc newEcl : vector<Ecc>{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high
if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8)
ecl = newEcl; ecl = newEcl;
} }
@ -310,7 +310,7 @@ QrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl,
// Pack bits into bytes in big endian // Pack bits into bytes in big endian
vector<uint8_t> dataCodewords(bb.size() / 8); vector<uint8_t> dataCodewords(bb.size() / 8);
for (size_t i = 0; i < bb.size(); i++) for (size_t i = 0; i < bb.size(); i++)
dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7));
// Create the QR Code object // Create the QR Code object
return QrCode(version, ecl, dataCodewords, mask); return QrCode(version, ecl, dataCodewords, mask);
@ -351,7 +351,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int msk)
} }
if (msk < 0 || msk > 7) if (msk < 0 || msk > 7)
throw std::logic_error("Assertion error"); throw std::logic_error("Assertion error");
this->mask = msk; mask = msk;
applyMask(msk); // Apply the final choice of mask applyMask(msk); // Apply the final choice of mask
drawFormatBits(msk); // Overwrite old format bits drawFormatBits(msk); // Overwrite old format bits

@ -263,7 +263,7 @@ public final class QrCode {
drawFunctionPatterns(); drawFunctionPatterns();
byte[] allCodewords = addEccAndInterleave(dataCodewords); byte[] allCodewords = addEccAndInterleave(dataCodewords);
drawCodewords(allCodewords); drawCodewords(allCodewords);
this.mask = handleConstructorMasking(msk); mask = handleConstructorMasking(msk);
isFunction = null; isFunction = null;
} }

@ -68,7 +68,9 @@ def main() -> None:
def do_trial() -> None: def do_trial() -> None:
mode = random.randrange(4) mode: int = random.randrange(4)
length: int
data: List[int]
if mode == 0: # Numeric if mode == 0: # Numeric
length = round((2 * 7089) ** random.random()) length = round((2 * 7089) ** random.random())
data = random.choices(b"0123456789", k=length) data = random.choices(b"0123456789", k=length)
@ -88,12 +90,12 @@ def do_trial() -> None:
for b in data: for b in data:
write_all(b) write_all(b)
errcorlvl = random.randrange(4) errcorlvl: int = random.randrange(4)
minversion = random.randint(1, 40) minversion: int = random.randint(1, 40)
maxversion = random.randint(1, 40) maxversion: int = random.randint(1, 40)
if minversion > maxversion: if minversion > maxversion:
minversion, maxversion = maxversion, minversion minversion, maxversion = maxversion, minversion
mask = -1 mask: int = -1
if random.random() < 0.5: if random.random() < 0.5:
mask = random.randrange(8) mask = random.randrange(8)
boostecl = int(random.random() < 0.2) boostecl = int(random.random() < 0.2)
@ -106,11 +108,11 @@ def do_trial() -> None:
write_all(boostecl) write_all(boostecl)
flush_all() flush_all()
version = read_verify() version: int = read_verify()
print(" version={}".format(version), end="") print(" version={}".format(version), end="")
if version == -1: if version == -1:
return return
size = version * 4 + 17 size: int = version * 4 + 17
for _ in range(size**2): for _ in range(size**2):
read_verify() read_verify()
@ -124,7 +126,7 @@ def flush_all() -> None:
not_none(proc.stdin).flush() not_none(proc.stdin).flush()
def read_verify() -> int: def read_verify() -> int:
val = not_none(subprocs[0].stdout).readline().rstrip("\r\n") val: str = not_none(subprocs[0].stdout).readline().rstrip("\r\n")
for proc in subprocs[1 : ]: for proc in subprocs[1 : ]:
if not_none(proc.stdout).readline().rstrip("\r\n") != val: if not_none(proc.stdout).readline().rstrip("\r\n") != val:
raise ValueError("Mismatch") raise ValueError("Mismatch")

@ -27,6 +27,7 @@
# #
import sys import sys
from typing import List, Sequence
import qrcodegen import qrcodegen
@ -38,26 +39,26 @@ def main() -> None:
while True: while True:
# Read data or exit # Read data or exit
length = read_int() length: int = read_int()
if length == -1: if length == -1:
break break
data = bytearray(read_int() for _ in range(length)) data = bytearray(read_int() for _ in range(length))
# Read encoding parameters # Read encoding parameters
errcorlvl = read_int() errcorlvl : int = read_int()
minversion = read_int() minversion: int = read_int()
maxversion = read_int() maxversion: int = read_int()
mask = read_int() mask : int = read_int()
boostecl = read_int() boostecl : int = read_int()
# Make segments for encoding # Make segments for encoding
if all((b < 128) for b in data): # Is ASCII if all((b < 128) for b in data): # Is ASCII
segs = qrcodegen.QrSegment.make_segments(data.decode("ASCII")) segs: List[qrcodegen.QrSegment] = qrcodegen.QrSegment.make_segments(data.decode("ASCII"))
else: else:
segs = [qrcodegen.QrSegment.make_bytes(data)] segs = [qrcodegen.QrSegment.make_bytes(data)]
try: # Try to make QR Code symbol try: # Try to make QR Code symbol
qr = qrcodegen.QrCode.encode_segments(segs, ECC_LEVELS[errcorlvl], minversion, maxversion, mask, boostecl != 0) qr: qrcodegen.QrCode = qrcodegen.QrCode.encode_segments(segs, ECC_LEVELS[errcorlvl], minversion, maxversion, mask, boostecl != 0)
# Print grid of modules # Print grid of modules
print(qr.get_version()) print(qr.get_version())
for y in range(qr.get_size()): for y in range(qr.get_size()):
@ -69,7 +70,7 @@ def main() -> None:
sys.stdout.flush() sys.stdout.flush()
ECC_LEVELS = ( ECC_LEVELS: Sequence[qrcodegen.QrCode.Ecc] = (
qrcodegen.QrCode.Ecc.LOW, qrcodegen.QrCode.Ecc.LOW,
qrcodegen.QrCode.Ecc.MEDIUM, qrcodegen.QrCode.Ecc.MEDIUM,
qrcodegen.QrCode.Ecc.QUARTILE, qrcodegen.QrCode.Ecc.QUARTILE,

@ -23,41 +23,8 @@
from __future__ import annotations from __future__ import annotations
import collections, itertools, re import collections, itertools, re
from typing import List, Optional, Tuple from collections.abc import Sequence
from typing import Callable, Dict, List, Optional, Tuple, Union
"""
This module "qrcodegen", public members:
- Class QrCode:
- Function encode_text(str text, QrCode.Ecc ecl) -> QrCode
- Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode
- Function encode_segments(list<QrSegment> segs, QrCode.Ecc ecl,
int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode
- Constants int MIN_VERSION, MAX_VERSION
- Constructor QrCode(int version, QrCode.Ecc ecl, bytes datacodewords, int mask)
- Method get_version() -> int
- Method get_size() -> int
- Method get_error_correction_level() -> QrCode.Ecc
- Method get_mask() -> int
- Method get_module(int x, int y) -> bool
- Method to_svg_str(int border) -> str
- Enum Ecc:
- Constants LOW, MEDIUM, QUARTILE, HIGH
- Field int ordinal
- Class QrSegment:
- Function make_bytes(bytes data) -> QrSegment
- Function make_numeric(str digits) -> QrSegment
- Function make_alphanumeric(str text) -> QrSegment
- Function make_segments(str text) -> list<QrSegment>
- Function make_eci(int assignval) -> QrSegment
- Constructor QrSegment(QrSegment.Mode mode, int numch, list<int> bitdata)
- Method get_mode() -> QrSegment.Mode
- Method get_num_chars() -> int
- Method get_data() -> list<int>
- Constants regex NUMERIC_REGEX, ALPHANUMERIC_REGEX
- Enum Mode:
- Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI
"""
# ---- QR Code symbol class ---- # ---- QR Code symbol class ----
@ -87,25 +54,23 @@ class QrCode:
Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible
QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the
ecl argument if it can be done without increasing the version.""" ecl argument if it can be done without increasing the version."""
segs = QrSegment.make_segments(text) segs: List[QrSegment] = QrSegment.make_segments(text)
return QrCode.encode_segments(segs, ecl) return QrCode.encode_segments(segs, ecl)
@staticmethod @staticmethod
def encode_binary(data: bytes, ecl: QrCode.Ecc) -> QrCode: def encode_binary(data: Union[bytes,Sequence[int]], ecl: QrCode.Ecc) -> QrCode:
"""Returns a QR Code representing the given binary data at the given error correction level. """Returns a QR Code representing the given binary data at the given error correction level.
This function always encodes using the binary segment mode, not any text mode. The maximum number of This function always encodes using the binary segment mode, not any text mode. The maximum number of
bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.""" The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version."""
if not isinstance(data, (bytes, bytearray)):
raise TypeError("Byte string/list expected")
return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl)
# ---- Static factory functions (mid level) ---- # ---- Static factory functions (mid level) ----
@staticmethod @staticmethod
def encode_segments(segs: List[QrSegment], ecl: QrCode.Ecc, minversion: int = 1, maxversion: int = 40, mask: int = -1, boostecl: bool = True) -> QrCode: def encode_segments(segs: Sequence[QrSegment], ecl: QrCode.Ecc, minversion: int = 1, maxversion: int = 40, mask: int = -1, boostecl: bool = True) -> QrCode:
"""Returns a QR Code representing the given segments with the given encoding parameters. """Returns a QR Code representing the given segments with the given encoding parameters.
The smallest possible QR Code version within the given range is automatically The smallest possible QR Code version within the given range is automatically
chosen for the output. Iff boostecl is true, then the ECC level of the result chosen for the output. Iff boostecl is true, then the ECC level of the result
@ -121,12 +86,12 @@ class QrCode:
# Find the minimal version number to use # Find the minimal version number to use
for version in range(minversion, maxversion + 1): for version in range(minversion, maxversion + 1):
datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available datacapacitybits: int = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available
datausedbits = QrSegment.get_total_bits(segs, version) datausedbits: Optional[int] = QrSegment.get_total_bits(segs, version)
if datausedbits is not None and datausedbits <= datacapacitybits: if (datausedbits is not None) and (datausedbits <= datacapacitybits):
break # This version number is found to be suitable break # This version number is found to be suitable
if version >= maxversion: # All versions in the range could not fit the given data if version >= maxversion: # All versions in the range could not fit the given data
msg = "Segment too long" msg: str = "Segment too long"
if datausedbits is not None: if datausedbits is not None:
msg = "Data length = {} bits, Max capacity = {} bits".format(datausedbits, datacapacitybits) msg = "Data length = {} bits, Max capacity = {} bits".format(datausedbits, datacapacitybits)
raise DataTooLongError(msg) raise DataTooLongError(msg)
@ -135,7 +100,7 @@ class QrCode:
# Increase the error correction level while the data still fits in the current version number # Increase the error correction level while the data still fits in the current version number
for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): # From low to high for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): # From low to high
if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8: if boostecl and (datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8):
ecl = newecl ecl = newecl
# Concatenate all segments to create the data bit string # Concatenate all segments to create the data bit string
@ -160,7 +125,7 @@ class QrCode:
bb.append_bits(padbyte, 8) bb.append_bits(padbyte, 8)
# Pack bits into bytes in big endian # Pack bits into bytes in big endian
datacodewords = [0] * (len(bb) // 8) datacodewords = bytearray([0] * (len(bb) // 8))
for (i, bit) in enumerate(bb): for (i, bit) in enumerate(bb):
datacodewords[i >> 3] |= bit << (7 - (i & 7)) datacodewords[i >> 3] |= bit << (7 - (i & 7))
@ -168,9 +133,35 @@ class QrCode:
return QrCode(version, ecl, datacodewords, mask) return QrCode(version, ecl, datacodewords, mask)
# ---- Private fields ----
# The version number of this QR Code, which is between 1 and 40 (inclusive).
# This determines the size of this barcode.
_version: int
# The width and height of this QR Code, measured in modules, between
# 21 and 177 (inclusive). This is equal to version * 4 + 17.
_size: int
# The error correction level used in this QR Code.
_errcorlvl: QrCode.Ecc
# The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
# Even if a QR Code is created with automatic masking requested (mask = -1),
# the resulting object still has a mask value between 0 and 7.
_mask: int
# The modules of this QR Code (False = white, True = black).
# Immutable after constructor finishes. Accessed through get_module().
_modules: List[List[bool]]
# Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
_isfunction: List[List[bool]]
# ---- Constructor (low level) ---- # ---- Constructor (low level) ----
def __init__(self, version: int, errcorlvl: QrCode.Ecc, datacodewords: List[int], mask: int) -> None: def __init__(self, version: int, errcorlvl: QrCode.Ecc, datacodewords: Union[bytes,Sequence[int]], mask: int) -> None:
"""Creates a new QR Code with the given version number, """Creates a new QR Code with the given version number,
error correction level, data codeword bytes, and mask number. error correction level, data codeword bytes, and mask number.
This is a low-level API that most users should not use directly. This is a low-level API that most users should not use directly.
@ -184,32 +175,22 @@ class QrCode:
if not isinstance(errcorlvl, QrCode.Ecc): if not isinstance(errcorlvl, QrCode.Ecc):
raise TypeError("QrCode.Ecc expected") raise TypeError("QrCode.Ecc expected")
# The version number of this QR Code, which is between 1 and 40 (inclusive).
# This determines the size of this barcode.
self._version = version self._version = version
# The width and height of this QR Code, measured in modules, between
# 21 and 177 (inclusive). This is equal to version * 4 + 17.
self._size = version * 4 + 17 self._size = version * 4 + 17
# The error correction level used in this QR Code.
self._errcorlvl = errcorlvl self._errcorlvl = errcorlvl
# Initialize both grids to be size*size arrays of Boolean false # Initialize both grids to be size*size arrays of Boolean false
# The modules of this QR Code (False = white, True = black).
# Immutable after constructor finishes. Accessed through get_module().
self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white
# Indicates function modules that are not subjected to masking. Discarded when constructor finishes
self._isfunction = [[False] * self._size for _ in range(self._size)] self._isfunction = [[False] * self._size for _ in range(self._size)]
# Compute ECC, draw modules # Compute ECC, draw modules
self._draw_function_patterns() self._draw_function_patterns()
allcodewords = self._add_ecc_and_interleave(datacodewords) allcodewords: bytes = self._add_ecc_and_interleave(bytearray(datacodewords))
self._draw_codewords(allcodewords) self._draw_codewords(allcodewords)
# Do masking # Do masking
if mask == -1: # Automatically choose best mask if mask == -1: # Automatically choose best mask
minpenalty = 1 << 32 minpenalty: int = 1 << 32
for i in range(8): for i in range(8):
self._apply_mask(i) self._apply_mask(i)
self._draw_format_bits(i) self._draw_format_bits(i)
@ -221,10 +202,6 @@ class QrCode:
assert 0 <= mask <= 7 assert 0 <= mask <= 7
self._apply_mask(mask) # Apply the final choice of mask self._apply_mask(mask) # Apply the final choice of mask
self._draw_format_bits(mask) # Overwrite old format bits self._draw_format_bits(mask) # Overwrite old format bits
# The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
# Even if a QR Code is created with automatic masking requested (mask = -1),
# the resulting object still has a mask value between 0 and 7.
self._mask = mask self._mask = mask
del self._isfunction del self._isfunction
@ -248,7 +225,7 @@ class QrCode:
"""Returns this QR Code's mask, in the range [0, 7].""" """Returns this QR Code's mask, in the range [0, 7]."""
return self._mask return self._mask
def get_module(self, x, y) -> bool: def get_module(self, x: int, y: int) -> bool:
"""Returns the color of the module (pixel) at the given coordinates, which is False """Returns the color of the module (pixel) at the given coordinates, which is False
for white or True for black. The top left corner has the coordinates (x=0, y=0). for white or True for black. The top left corner has the coordinates (x=0, y=0).
If the given coordinates are out of bounds, then False (white) is returned.""" If the given coordinates are out of bounds, then False (white) is returned."""
@ -262,7 +239,7 @@ class QrCode:
of border modules. The string always uses Unix newlines (\n), regardless of the platform.""" of border modules. The string always uses Unix newlines (\n), regardless of the platform."""
if border < 0: if border < 0:
raise ValueError("Border must be non-negative") raise ValueError("Border must be non-negative")
parts = [] parts: List[str] = []
for y in range(self._size): for y in range(self._size):
for x in range(self._size): for x in range(self._size):
if self.get_module(x, y): if self.get_module(x, y):
@ -291,9 +268,9 @@ class QrCode:
self._draw_finder_pattern(3, self._size - 4) self._draw_finder_pattern(3, self._size - 4)
# Draw numerous alignment patterns # Draw numerous alignment patterns
alignpatpos = self._get_alignment_pattern_positions() alignpatpos: List[int] = self._get_alignment_pattern_positions()
numalign = len(alignpatpos) numalign: int = len(alignpatpos)
skips = ((0, 0), (0, numalign - 1), (numalign - 1, 0)) skips: Sequence[Tuple[int,int]] = ((0, 0), (0, numalign - 1), (numalign - 1, 0))
for i in range(numalign): for i in range(numalign):
for j in range(numalign): for j in range(numalign):
if (i, j) not in skips: # Don't draw on the three finder corners if (i, j) not in skips: # Don't draw on the three finder corners
@ -304,15 +281,15 @@ class QrCode:
self._draw_version() self._draw_version()
def _draw_format_bits(self, mask) -> None: def _draw_format_bits(self, mask: int) -> None:
"""Draws two copies of the format bits (with its own error correction code) """Draws two copies of the format bits (with its own error correction code)
based on the given mask and this object's error correction level field.""" based on the given mask and this object's error correction level field."""
# Calculate error correction code and pack bits # Calculate error correction code and pack bits
data = self._errcorlvl.formatbits << 3 | mask # errCorrLvl is uint2, mask is uint3 data: int = self._errcorlvl.formatbits << 3 | mask # errCorrLvl is uint2, mask is uint3
rem = data rem: int = data
for _ in range(10): for _ in range(10):
rem = (rem << 1) ^ ((rem >> 9) * 0x537) rem = (rem << 1) ^ ((rem >> 9) * 0x537)
bits = (data << 10 | rem) ^ 0x5412 # uint15 bits: int = (data << 10 | rem) ^ 0x5412 # uint15
assert bits >> 15 == 0 assert bits >> 15 == 0
# Draw first copy # Draw first copy
@ -339,22 +316,22 @@ class QrCode:
return return
# Calculate error correction code and pack bits # Calculate error correction code and pack bits
rem = self._version # version is uint6, in the range [7, 40] rem: int = self._version # version is uint6, in the range [7, 40]
for _ in range(12): for _ in range(12):
rem = (rem << 1) ^ ((rem >> 11) * 0x1F25) rem = (rem << 1) ^ ((rem >> 11) * 0x1F25)
bits = self._version << 12 | rem # uint18 bits: int = self._version << 12 | rem # uint18
assert bits >> 18 == 0 assert bits >> 18 == 0
# Draw two copies # Draw two copies
for i in range(18): for i in range(18):
bit = _get_bit(bits, i) bit: bool = _get_bit(bits, i)
a = self._size - 11 + i % 3 a: int = self._size - 11 + i % 3
b = i // 3 b: int = i // 3
self._set_function_module(a, b, bit) self._set_function_module(a, b, bit)
self._set_function_module(b, a, bit) self._set_function_module(b, a, bit)
def _draw_finder_pattern(self, x, y) -> None: def _draw_finder_pattern(self, x: int, y: int) -> None:
"""Draws a 9*9 finder pattern including the border separator, """Draws a 9*9 finder pattern including the border separator,
with the center module at (x, y). Modules can be out of bounds.""" with the center module at (x, y). Modules can be out of bounds."""
for dy in range(-4, 5): for dy in range(-4, 5):
@ -365,7 +342,7 @@ class QrCode:
self._set_function_module(xx, yy, max(abs(dx), abs(dy)) not in (2, 4)) self._set_function_module(xx, yy, max(abs(dx), abs(dy)) not in (2, 4))
def _draw_alignment_pattern(self, x, y) -> None: def _draw_alignment_pattern(self, x: int, y: int) -> None:
"""Draws a 5*5 alignment pattern, with the center module """Draws a 5*5 alignment pattern, with the center module
at (x, y). All modules must be in bounds.""" at (x, y). All modules must be in bounds."""
for dy in range(-2, 3): for dy in range(-2, 3):
@ -383,59 +360,59 @@ class QrCode:
# ---- Private helper methods for constructor: Codewords and masking ---- # ---- Private helper methods for constructor: Codewords and masking ----
def _add_ecc_and_interleave(self, data: List[int]) -> List[int]: def _add_ecc_and_interleave(self, data: bytearray) -> bytes:
"""Returns a new byte string representing the given data with the appropriate error correction """Returns a new byte string representing the given data with the appropriate error correction
codewords appended to it, based on this object's version and error correction level.""" codewords appended to it, based on this object's version and error correction level."""
version = self._version version: int = self._version
assert len(data) == QrCode._get_num_data_codewords(version, self._errcorlvl) assert len(data) == QrCode._get_num_data_codewords(version, self._errcorlvl)
# Calculate parameter numbers # Calculate parameter numbers
numblocks = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version] numblocks: int = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version]
blockecclen = QrCode._ECC_CODEWORDS_PER_BLOCK [self._errcorlvl.ordinal][version] blockecclen: int = QrCode._ECC_CODEWORDS_PER_BLOCK [self._errcorlvl.ordinal][version]
rawcodewords = QrCode._get_num_raw_data_modules(version) // 8 rawcodewords: int = QrCode._get_num_raw_data_modules(version) // 8
numshortblocks = numblocks - rawcodewords % numblocks numshortblocks: int = numblocks - rawcodewords % numblocks
shortblocklen = rawcodewords // numblocks shortblocklen: int = rawcodewords // numblocks
# Split data into blocks and append ECC to each block # Split data into blocks and append ECC to each block
blocks = [] blocks: List[bytes] = []
rsdiv = QrCode._reed_solomon_compute_divisor(blockecclen) rsdiv: bytes = QrCode._reed_solomon_compute_divisor(blockecclen)
k = 0 k: int = 0
for i in range(numblocks): for i in range(numblocks):
dat = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)] dat: bytearray = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)]
k += len(dat) k += len(dat)
ecc = QrCode._reed_solomon_compute_remainder(dat, rsdiv) ecc: bytes = QrCode._reed_solomon_compute_remainder(dat, rsdiv)
if i < numshortblocks: if i < numshortblocks:
dat.append(0) dat.append(0)
blocks.append(dat + ecc) blocks.append(dat + ecc)
assert k == len(data) assert k == len(data)
# Interleave (not concatenate) the bytes from every block into a single sequence # Interleave (not concatenate) the bytes from every block into a single sequence
result = [] result = bytearray()
for i in range(len(blocks[0])): for i in range(len(blocks[0])):
for (j, blk) in enumerate(blocks): for (j, blk) in enumerate(blocks):
# Skip the padding byte in short blocks # Skip the padding byte in short blocks
if i != shortblocklen - blockecclen or j >= numshortblocks: if (i != shortblocklen - blockecclen) or (j >= numshortblocks):
result.append(blk[i]) result.append(blk[i])
assert len(result) == rawcodewords assert len(result) == rawcodewords
return result return result
def _draw_codewords(self, data: List[int]) -> None: def _draw_codewords(self, data: bytes) -> None:
"""Draws the given sequence of 8-bit codewords (data and error correction) onto the entire """Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
data area of this QR Code. Function modules need to be marked off before this is called.""" data area of this QR Code. Function modules need to be marked off before this is called."""
assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8 assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8
i = 0 # Bit index into the data i: int = 0 # Bit index into the data
# Do the funny zigzag scan # Do the funny zigzag scan
for right in range(self._size - 1, 0, -2): # Index of right column in each column pair for right in range(self._size - 1, 0, -2): # Index of right column in each column pair
if right <= 6: if right <= 6:
right -= 1 right -= 1
for vert in range(self._size): # Vertical counter for vert in range(self._size): # Vertical counter
for j in range(2): for j in range(2):
x = right - j # Actual x coordinate x: int = right - j # Actual x coordinate
upward = (right + 1) & 2 == 0 upward: bool = (right + 1) & 2 == 0
y = (self._size - 1 - vert) if upward else vert # Actual y coordinate y: int = (self._size - 1 - vert) if upward else vert # Actual y coordinate
if not self._isfunction[y][x] and i < len(data) * 8: if (not self._isfunction[y][x]) and (i < len(data) * 8):
self._modules[y][x] = _get_bit(data[i >> 3], 7 - (i & 7)) self._modules[y][x] = _get_bit(data[i >> 3], 7 - (i & 7))
i += 1 i += 1
# If this QR Code has any remainder bits (0 to 7), they were assigned as # If this QR Code has any remainder bits (0 to 7), they were assigned as
@ -446,12 +423,12 @@ class QrCode:
def _apply_mask(self, mask: int) -> None: def _apply_mask(self, mask: int) -> None:
"""XORs the codeword modules in this QR Code with the given mask pattern. """XORs the codeword modules in this QR Code with the given mask pattern.
The function modules must be marked and the codeword bits must be drawn The function modules must be marked and the codeword bits must be drawn
before masking. Due to the arithmetic of XOR, calling applyMask() with before masking. Due to the arithmetic of XOR, calling _apply_mask() with
the same mask value a second time will undo the mask. A final well-formed the same mask value a second time will undo the mask. A final well-formed
QR Code needs exactly one (not zero, two, etc.) mask applied.""" QR Code needs exactly one (not zero, two, etc.) mask applied."""
if not (0 <= mask <= 7): if not (0 <= mask <= 7):
raise ValueError("Mask value out of range") raise ValueError("Mask value out of range")
masker = QrCode._MASK_PATTERNS[mask] masker: Callable[[int,int],int] = QrCode._MASK_PATTERNS[mask]
for y in range(self._size): for y in range(self._size):
for x in range(self._size): for x in range(self._size):
self._modules[y][x] ^= (masker(x, y) == 0) and (not self._isfunction[y][x]) self._modules[y][x] ^= (masker(x, y) == 0) and (not self._isfunction[y][x])
@ -460,14 +437,14 @@ class QrCode:
def _get_penalty_score(self) -> int: def _get_penalty_score(self) -> int:
"""Calculates and returns the penalty score based on state of this QR Code's current modules. """Calculates and returns the penalty score based on state of this QR Code's current modules.
This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.""" This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score."""
result = 0 result: int = 0
size = self._size size: int = self._size
modules = self._modules modules: List[List[bool]] = self._modules
# Adjacent modules in row having same color, and finder-like patterns # Adjacent modules in row having same color, and finder-like patterns
for y in range(size): for y in range(size):
runcolor = False runcolor: bool = False
runx = 0 runx: int = 0
runhistory = collections.deque([0] * 7, 7) runhistory = collections.deque([0] * 7, 7)
for x in range(size): for x in range(size):
if modules[y][x] == runcolor: if modules[y][x] == runcolor:
@ -510,10 +487,10 @@ class QrCode:
result += QrCode._PENALTY_N2 result += QrCode._PENALTY_N2
# Balance of black and white modules # Balance of black and white modules
black = sum((1 if cell else 0) for row in modules for cell in row) black: int = sum((1 if cell else 0) for row in modules for cell in row)
total = size**2 # Note that size is odd, so black/total != 1/2 total: int = size**2 # Note that size is odd, so black/total != 1/2
# Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% # Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)%
k = (abs(black * 20 - total * 10) + total - 1) // total - 1 k: int = (abs(black * 20 - total * 10) + total - 1) // total - 1
result += k * QrCode._PENALTY_N4 result += k * QrCode._PENALTY_N4
return result return result
@ -524,27 +501,27 @@ class QrCode:
"""Returns an ascending list of positions of alignment patterns for this version number. """Returns an ascending list of positions of alignment patterns for this version number.
Each position is in the range [0,177), and are used on both the x and y axes. Each position is in the range [0,177), and are used on both the x and y axes.
This could be implemented as lookup table of 40 variable-length lists of integers.""" This could be implemented as lookup table of 40 variable-length lists of integers."""
ver = self._version ver: int = self._version
if ver == 1: if ver == 1:
return [] return []
else: else:
numalign = ver // 7 + 2 numalign: int = ver // 7 + 2
step = 26 if (ver == 32) else \ step: int = 26 if (ver == 32) else \
(ver*4 + numalign*2 + 1) // (numalign*2 - 2) * 2 (ver*4 + numalign*2 + 1) // (numalign*2 - 2) * 2
result = [(self._size - 7 - i * step) for i in range(numalign - 1)] + [6] result: List[int] = [(self._size - 7 - i * step) for i in range(numalign - 1)] + [6]
return list(reversed(result)) return list(reversed(result))
@staticmethod @staticmethod
def _get_num_raw_data_modules(ver) -> int: def _get_num_raw_data_modules(ver: int) -> int:
"""Returns the number of data bits that can be stored in a QR Code of the given version number, after """Returns the number of data bits that can be stored in a QR Code of the given version number, after
all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.""" The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table."""
if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION): if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION):
raise ValueError("Version number out of range") raise ValueError("Version number out of range")
result = (16 * ver + 128) * ver + 64 result: int = (16 * ver + 128) * ver + 64
if ver >= 2: if ver >= 2:
numalign = ver // 7 + 2 numalign: int = ver // 7 + 2
result -= (25 * numalign - 10) * numalign - 55 result -= (25 * numalign - 10) * numalign - 55
if ver >= 7: if ver >= 7:
result -= 36 result -= 36
@ -553,7 +530,7 @@ class QrCode:
@staticmethod @staticmethod
def _get_num_data_codewords(ver, ecl: QrCode.Ecc) -> int: def _get_num_data_codewords(ver: int, ecl: QrCode.Ecc) -> int:
"""Returns the number of 8-bit data (i.e. not error correction) codewords contained in any """Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
QR Code of the given version number and error correction level, with remainder bits discarded. QR Code of the given version number and error correction level, with remainder bits discarded.
This stateless pure function could be implemented as a (40*4)-cell lookup table.""" This stateless pure function could be implemented as a (40*4)-cell lookup table."""
@ -563,19 +540,19 @@ class QrCode:
@staticmethod @staticmethod
def _reed_solomon_compute_divisor(degree: int) -> List[int]: def _reed_solomon_compute_divisor(degree: int) -> bytes:
"""Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be """Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be
implemented as a lookup table over all possible parameter values, instead of as an algorithm.""" implemented as a lookup table over all possible parameter values, instead of as an algorithm."""
if not (1 <= degree <= 255): if not (1 <= degree <= 255):
raise ValueError("Degree out of range") raise ValueError("Degree out of range")
# Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. # Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
# For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. # For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93].
result = [0] * (degree - 1) + [1] # Start off with the monomial x^0 result = bytearray([0] * (degree - 1) + [1]) # Start off with the monomial x^0
# Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), # Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
# and drop the highest monomial term which is always 1x^degree. # and drop the highest monomial term which is always 1x^degree.
# Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). # Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
root = 1 root: int = 1
for _ in range(degree): # Unused variable i for _ in range(degree): # Unused variable i
# Multiply the current product by (x - r^i) # Multiply the current product by (x - r^i)
for j in range(degree): for j in range(degree):
@ -587,11 +564,11 @@ class QrCode:
@staticmethod @staticmethod
def _reed_solomon_compute_remainder(data: List[int], divisor: List[int]) -> List[int]: def _reed_solomon_compute_remainder(data: bytes, divisor: bytes) -> bytes:
"""Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.""" """Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials."""
result = [0] * len(divisor) result = bytearray([0] * len(divisor))
for b in data: # Polynomial division for b in data: # Polynomial division
factor = b ^ result.pop(0) factor: int = b ^ result.pop(0)
result.append(0) result.append(0)
for (i, coef) in enumerate(divisor): for (i, coef) in enumerate(divisor):
result[i] ^= QrCode._reed_solomon_multiply(coef, factor) result[i] ^= QrCode._reed_solomon_multiply(coef, factor)
@ -602,10 +579,10 @@ class QrCode:
def _reed_solomon_multiply(x: int, y: int) -> int: def _reed_solomon_multiply(x: int, y: int) -> int:
"""Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result """Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result
are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.""" are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8."""
if x >> 8 != 0 or y >> 8 != 0: if (x >> 8 != 0) or (y >> 8 != 0):
raise ValueError("Byte out of range") raise ValueError("Byte out of range")
# Russian peasant multiplication # Russian peasant multiplication
z = 0 z: int = 0
for i in reversed(range(8)): for i in reversed(range(8)):
z = (z << 1) ^ ((z >> 7) * 0x11D) z = (z << 1) ^ ((z >> 7) * 0x11D)
z ^= ((y >> i) & 1) * x z ^= ((y >> i) & 1) * x
@ -616,9 +593,9 @@ class QrCode:
def _finder_penalty_count_patterns(self, runhistory: collections.deque) -> int: def _finder_penalty_count_patterns(self, runhistory: collections.deque) -> int:
"""Can only be called immediately after a white run is added, and """Can only be called immediately after a white run is added, and
returns either 0, 1, or 2. A helper function for _get_penalty_score().""" returns either 0, 1, or 2. A helper function for _get_penalty_score()."""
n = runhistory[1] n: int = runhistory[1]
assert n <= self._size * 3 assert n <= self._size * 3
core = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3 core: bool = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3
return (1 if (core and runhistory[0] >= n * 4 and runhistory[6] >= n) else 0) \ return (1 if (core and runhistory[0] >= n * 4 and runhistory[6] >= n) else 0) \
+ (1 if (core and runhistory[6] >= n * 4 and runhistory[0] >= n) else 0) + (1 if (core and runhistory[6] >= n * 4 and runhistory[0] >= n) else 0)
@ -641,16 +618,16 @@ class QrCode:
# ---- Constants and tables ---- # ---- Constants and tables ----
MIN_VERSION = 1 # The minimum version number supported in the QR Code Model 2 standard MIN_VERSION: int = 1 # The minimum version number supported in the QR Code Model 2 standard
MAX_VERSION = 40 # The maximum version number supported in the QR Code Model 2 standard MAX_VERSION: int = 40 # The maximum version number supported in the QR Code Model 2 standard
# For use in getPenaltyScore(), when evaluating which mask is best. # For use in _get_penalty_score(), when evaluating which mask is best.
_PENALTY_N1 = 3 _PENALTY_N1: int = 3
_PENALTY_N2 = 3 _PENALTY_N2: int = 3
_PENALTY_N3 = 40 _PENALTY_N3: int = 40
_PENALTY_N4 = 10 _PENALTY_N4: int = 10
_ECC_CODEWORDS_PER_BLOCK = ( _ECC_CODEWORDS_PER_BLOCK: Sequence[Sequence[int]] = (
# Version: (note that index 0 is for padding, and is set to an illegal value) # Version: (note that index 0 is for padding, and is set to an illegal value)
# 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
(-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30), # Low (-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30), # Low
@ -658,7 +635,7 @@ class QrCode:
(-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30), # Quartile (-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30), # Quartile
(-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30)) # High (-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30)) # High
_NUM_ERROR_CORRECTION_BLOCKS = ( _NUM_ERROR_CORRECTION_BLOCKS: Sequence[Sequence[int]] = (
# Version: (note that index 0 is for padding, and is set to an illegal value) # Version: (note that index 0 is for padding, and is set to an illegal value)
# 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
(-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25), # Low (-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25), # Low
@ -666,7 +643,7 @@ class QrCode:
(-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68), # Quartile (-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68), # Quartile
(-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81)) # High (-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81)) # High
_MASK_PATTERNS = ( _MASK_PATTERNS: Sequence[Callable[[int,int],int]] = (
(lambda x, y: (x + y) % 2 ), (lambda x, y: (x + y) % 2 ),
(lambda x, y: y % 2 ), (lambda x, y: y % 2 ),
(lambda x, y: x % 3 ), (lambda x, y: x % 3 ),
@ -681,11 +658,14 @@ class QrCode:
# ---- Public helper enumeration ---- # ---- Public helper enumeration ----
class Ecc: class Ecc:
ordinal: int # (Public) In the range 0 to 3 (unsigned 2-bit integer)
formatbits: int # (Package-private) In the range 0 to 3 (unsigned 2-bit integer)
"""The error correction level in a QR Code symbol. Immutable.""" """The error correction level in a QR Code symbol. Immutable."""
# Private constructor # Private constructor
def __init__(self, i: int, fb: int) -> None: def __init__(self, i: int, fb: int) -> None:
self.ordinal = i # (Public) In the range 0 to 3 (unsigned 2-bit integer) self.ordinal = i
self.formatbits = fb # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) self.formatbits = fb
# Placeholders # Placeholders
LOW : QrCode.Ecc LOW : QrCode.Ecc
@ -717,12 +697,10 @@ class QrSegment:
# ---- Static factory functions (mid level) ---- # ---- Static factory functions (mid level) ----
@staticmethod @staticmethod
def make_bytes(data) -> QrSegment: def make_bytes(data: Union[bytes,Sequence[int]]) -> QrSegment:
"""Returns a segment representing the given binary data encoded in byte mode. """Returns a segment representing the given binary data encoded in byte mode.
All input byte lists are acceptable. Any text string can be converted to All input byte lists are acceptable. Any text string can be converted to
UTF-8 bytes (s.encode("UTF-8")) and encoded as a byte mode segment.""" UTF-8 bytes (s.encode("UTF-8")) and encoded as a byte mode segment."""
if isinstance(data, str):
raise TypeError("Byte string/list expected")
bb = _BitBuffer() bb = _BitBuffer()
for b in data: for b in data:
bb.append_bits(b, 8) bb.append_bits(b, 8)
@ -735,9 +713,9 @@ class QrSegment:
if QrSegment.NUMERIC_REGEX.fullmatch(digits) is None: if QrSegment.NUMERIC_REGEX.fullmatch(digits) is None:
raise ValueError("String contains non-numeric characters") raise ValueError("String contains non-numeric characters")
bb = _BitBuffer() bb = _BitBuffer()
i = 0 i: int = 0
while i < len(digits): # Consume up to 3 digits per iteration while i < len(digits): # Consume up to 3 digits per iteration
n = min(len(digits) - i, 3) n: int = min(len(digits) - i, 3)
bb.append_bits(int(digits[i : i + n]), n * 3 + 1) bb.append_bits(int(digits[i : i + n]), n * 3 + 1)
i += n i += n
return QrSegment(QrSegment.Mode.NUMERIC, len(digits), bb) return QrSegment(QrSegment.Mode.NUMERIC, len(digits), bb)
@ -752,7 +730,7 @@ class QrSegment:
raise ValueError("String contains unencodable characters in alphanumeric mode") raise ValueError("String contains unencodable characters in alphanumeric mode")
bb = _BitBuffer() bb = _BitBuffer()
for i in range(0, len(text) - 1, 2): # Process groups of 2 for i in range(0, len(text) - 1, 2): # Process groups of 2
temp = QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45 temp: int = QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45
temp += QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i + 1]] temp += QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i + 1]]
bb.append_bits(temp, 11) bb.append_bits(temp, 11)
if len(text) % 2 > 0: # 1 character remaining if len(text) % 2 > 0: # 1 character remaining
@ -798,9 +776,24 @@ class QrSegment:
return QrSegment(QrSegment.Mode.ECI, 0, bb) return QrSegment(QrSegment.Mode.ECI, 0, bb)
# ---- Private fields ----
# The mode indicator of this segment. Accessed through get_mode().
_mode: QrSegment.Mode
# The length of this segment's unencoded data. Measured in characters for
# numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
# Always zero or positive. Not the same as the data's bit length.
# Accessed through get_num_chars().
_numchars: int
# The data bits of this segment. Accessed through get_data().
_bitdata: List[int]
# ---- Constructor (low level) ---- # ---- Constructor (low level) ----
def __init__(self, mode: QrSegment.Mode, numch: int, bitdata: List[int]) -> None: def __init__(self, mode: QrSegment.Mode, numch: int, bitdata: Sequence[int]) -> None:
"""Creates a new QR Code segment with the given attributes and data. """Creates a new QR Code segment with the given attributes and data.
The character count (numch) must agree with the mode and the bit buffer length, The character count (numch) must agree with the mode and the bit buffer length,
but the constraint isn't checked. The given bit buffer is cloned and stored.""" but the constraint isn't checked. The given bit buffer is cloned and stored."""
@ -808,17 +801,8 @@ class QrSegment:
raise TypeError("QrSegment.Mode expected") raise TypeError("QrSegment.Mode expected")
if numch < 0: if numch < 0:
raise ValueError() raise ValueError()
# The mode indicator of this segment. Accessed through get_mode().
self._mode = mode self._mode = mode
# The length of this segment's unencoded data. Measured in characters for
# numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
# Always zero or positive. Not the same as the data's bit length.
# Accessed through get_num_chars().
self._numchars = numch self._numchars = numch
# The data bits of this segment. Accessed through get_data().
self._bitdata = list(bitdata) # Make defensive copy self._bitdata = list(bitdata) # Make defensive copy
@ -845,7 +829,7 @@ class QrSegment:
returns None if a segment has too many characters to fit its length field.""" returns None if a segment has too many characters to fit its length field."""
result = 0 result = 0
for seg in segs: for seg in segs:
ccbits = seg.get_mode().num_char_count_bits(version) ccbits: int = seg.get_mode().num_char_count_bits(version)
if seg.get_num_chars() >= (1 << ccbits): if seg.get_num_chars() >= (1 << ccbits):
return None # The segment's length doesn't fit the field's bit width return None # The segment's length doesn't fit the field's bit width
result += 4 + ccbits + len(seg._bitdata) result += 4 + ccbits + len(seg._bitdata)
@ -857,17 +841,16 @@ class QrSegment:
# (Public) Describes precisely all strings that are encodable in numeric mode. # (Public) Describes precisely all strings that are encodable in numeric mode.
# To test whether a string s is encodable: ok = NUMERIC_REGEX.fullmatch(s) is not None # To test whether a string s is encodable: ok = NUMERIC_REGEX.fullmatch(s) is not None
# A string is encodable iff each character is in the range 0 to 9. # A string is encodable iff each character is in the range 0 to 9.
NUMERIC_REGEX = re.compile(r"[0-9]*") NUMERIC_REGEX: re.Pattern = re.compile(r"[0-9]*")
# (Public) Describes precisely all strings that are encodable in alphanumeric mode. # (Public) Describes precisely all strings that are encodable in alphanumeric mode.
# To test whether a string s is encodable: ok = ALPHANUMERIC_REGEX.fullmatch(s) is not None # To test whether a string s is encodable: ok = ALPHANUMERIC_REGEX.fullmatch(s) is not None
# A string is encodable iff each character is in the following set: 0 to 9, A to Z # A string is encodable iff each character is in the following set: 0 to 9, A to Z
# (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. # (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
ALPHANUMERIC_REGEX: re.Pattern = re.compile(r"[A-Z0-9 $%*+./:-]*")
ALPHANUMERIC_REGEX = re.compile(r"[A-Z0-9 $%*+./:-]*")
# (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc. # (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc.
_ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} _ALPHANUMERIC_ENCODING_TABLE: Dict[str,int] = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")}
# ---- Public helper enumeration ---- # ---- Public helper enumeration ----
@ -875,10 +858,13 @@ class QrSegment:
class Mode: class Mode:
"""Describes how a segment's data bits are interpreted. Immutable.""" """Describes how a segment's data bits are interpreted. Immutable."""
_modebits: int # The mode indicator bits, which is a uint4 value (range 0 to 15)
_charcounts: Tuple[int,int,int] # Number of character count bits for three different version ranges
# Private constructor # Private constructor
def __init__(self, modebits: int, charcounts: Tuple[int,int,int]): def __init__(self, modebits: int, charcounts: Tuple[int,int,int]):
self._modebits = modebits # The mode indicator bits, which is a uint4 value (range 0 to 15) self._modebits = modebits
self._charcounts = charcounts # Number of character count bits for three different version ranges self._charcounts = charcounts
# Package-private method # Package-private method
def get_mode_bits(self) -> int: def get_mode_bits(self) -> int:
@ -915,7 +901,7 @@ class _BitBuffer(list):
def append_bits(self, val: int, n: int) -> None: def append_bits(self, val: int, n: int) -> None:
"""Appends the given number of low-order bits of the given """Appends the given number of low-order bits of the given
value to this buffer. Requires n >= 0 and 0 <= val < 2^n.""" value to this buffer. Requires n >= 0 and 0 <= val < 2^n."""
if n < 0 or val >> n != 0: if (n < 0) or (val >> n != 0):
raise ValueError("Value out of range") raise ValueError("Value out of range")
self.extend(((val >> i) & 1) for i in reversed(range(n))) self.extend(((val >> i) & 1) for i in reversed(range(n)))

@ -29,8 +29,7 @@ use qrcodegen::Mask;
use qrcodegen::QrCode; use qrcodegen::QrCode;
use qrcodegen::QrCodeEcc; use qrcodegen::QrCodeEcc;
use qrcodegen::QrSegment; use qrcodegen::QrSegment;
use qrcodegen::QrCode_MAX_VERSION; use qrcodegen::Version;
use qrcodegen::QrCode_MIN_VERSION;
// The main application program. // The main application program.
@ -128,8 +127,8 @@ fn do_segment_demo() {
0x0000, 0x0208, 0x01FF, 0x0008, 0x0000, 0x0208, 0x01FF, 0x0008,
]; ];
let mut bb = qrcodegen::BitBuffer(Vec::new()); let mut bb = qrcodegen::BitBuffer(Vec::new());
for c in &kanjichars { for &c in &kanjichars {
bb.append_bits(*c, 13); bb.append_bits(c, 13);
} }
let segs = vec![ let segs = vec![
QrSegment::new(qrcodegen::QrSegmentMode::Kanji, kanjichars.len(), bb.0), QrSegment::new(qrcodegen::QrSegmentMode::Kanji, kanjichars.len(), bb.0),
@ -143,20 +142,20 @@ fn do_segment_demo() {
fn do_mask_demo() { fn do_mask_demo() {
// Project Nayuki URL // Project Nayuki URL
let segs = QrSegment::make_segments(&to_chars("https://www.nayuki.io/")); let segs = QrSegment::make_segments(&to_chars("https://www.nayuki.io/"));
let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true).unwrap(); // Automatic mask let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, Version::MIN, Version::MAX, None, true).unwrap(); // Automatic mask
print_qr(&qr); print_qr(&qr);
let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(3)), true).unwrap(); // Force mask 3 let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, Version::MIN, Version::MAX, Some(Mask::new(3)), true).unwrap(); // Force mask 3
print_qr(&qr); print_qr(&qr);
// Chinese text as UTF-8 // Chinese text as UTF-8
let segs = QrSegment::make_segments(&to_chars("維基百科Wikipedia聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫")); let segs = QrSegment::make_segments(&to_chars("維基百科Wikipedia聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫"));
let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(0)), true).unwrap(); // Force mask 0 let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(0)), true).unwrap(); // Force mask 0
print_qr(&qr); print_qr(&qr);
let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(1)), true).unwrap(); // Force mask 1 let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(1)), true).unwrap(); // Force mask 1
print_qr(&qr); print_qr(&qr);
let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(5)), true).unwrap(); // Force mask 5 let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(5)), true).unwrap(); // Force mask 5
print_qr(&qr); print_qr(&qr);
let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(7)), true).unwrap(); // Force mask 7 let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(7)), true).unwrap(); // Force mask 7
print_qr(&qr); print_qr(&qr);
} }

@ -59,9 +59,9 @@ fn main() {
let mask = read_int(); let mask = read_int();
let boostecl = read_int(); let boostecl = read_int();
assert!(0 <= errcorlvl && errcorlvl <= 3); assert!(0 <= errcorlvl && errcorlvl <= 3);
assert!(i16::from(qrcodegen::QrCode_MIN_VERSION.value()) <= minversion assert!(i16::from(Version::MIN.value()) <= minversion
&& minversion <= maxversion && minversion <= maxversion
&& maxversion <= i16::from(qrcodegen::QrCode_MAX_VERSION.value())); && maxversion <= i16::from(Version::MAX.value()));
assert!(-1 <= mask && mask <= 7); assert!(-1 <= mask && mask <= 7);
assert!(boostecl >> 1 == 0); assert!(boostecl >> 1 == 0);

@ -184,7 +184,7 @@ impl QrCode {
/// Returns a wrapped `QrCode` if successful, or `Err` if the /// Returns a wrapped `QrCode` if successful, or `Err` if the
/// data is too long to fit in any version at the given ECC level. /// data is too long to fit in any version at the given ECC level.
pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Result<Self,DataTooLong> { pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Result<Self,DataTooLong> {
QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) QrCode::encode_segments_advanced(segs, ecl, Version::MIN, Version::MAX, None, true)
} }
@ -207,7 +207,7 @@ impl QrCode {
assert!(minversion.value() <= maxversion.value(), "Invalid value"); assert!(minversion.value() <= maxversion.value(), "Invalid value");
// Find the minimal version number to use // Find the minimal version number to use
let mut version = minversion; let mut version: Version = minversion;
let datausedbits: usize = loop { let datausedbits: usize = loop {
// Number of data bits available // Number of data bits available
let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8;
@ -245,9 +245,9 @@ impl QrCode {
// Add terminator and pad up to a byte if applicable // Add terminator and pad up to a byte if applicable
let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8;
assert!(bb.0.len() <= datacapacitybits); assert!(bb.0.len() <= datacapacitybits);
let numzerobits = std::cmp::min(4, datacapacitybits - bb.0.len()); let numzerobits: usize = std::cmp::min(4, datacapacitybits - bb.0.len());
bb.append_bits(0, numzerobits as u8); bb.append_bits(0, numzerobits as u8);
let numzerobits = bb.0.len().wrapping_neg() & 7; let numzerobits: usize = bb.0.len().wrapping_neg() & 7;
bb.append_bits(0, numzerobits as u8); bb.append_bits(0, numzerobits as u8);
assert_eq!(bb.0.len() % 8, 0, "Assertion error"); assert_eq!(bb.0.len() % 8, 0, "Assertion error");
@ -296,7 +296,7 @@ impl QrCode {
// Do masking // Do masking
if mask.is_none() { // Automatically choose best mask if mask.is_none() { // Automatically choose best mask
let mut minpenalty: i32 = std::i32::MAX; let mut minpenalty = std::i32::MAX;
for i in 0u8 .. 8 { for i in 0u8 .. 8 {
let newmask = Mask::new(i); let newmask = Mask::new(i);
result.apply_mask(newmask); result.apply_mask(newmask);
@ -438,7 +438,7 @@ impl QrCode {
// Calculate error correction code and pack bits // Calculate error correction code and pack bits
let bits: u32 = { let bits: u32 = {
// errcorrlvl is uint2, mask is uint3 // errcorrlvl is uint2, mask is uint3
let data: u32 = self.errorcorrectionlevel.format_bits() << 3 | u32::from(mask.value()); let data: u32 = u32::from(self.errorcorrectionlevel.format_bits() << 3 | mask.value());
let mut rem: u32 = data; let mut rem: u32 = data;
for _ in 0 .. 10 { for _ in 0 .. 10 {
rem = (rem << 1) ^ ((rem >> 9) * 0x537); rem = (rem << 1) ^ ((rem >> 9) * 0x537);
@ -539,8 +539,8 @@ impl QrCode {
// Returns a new byte string representing the given data with the appropriate error correction // Returns a new byte string representing the given data with the appropriate error correction
// codewords appended to it, based on this object's version and error correction level. // codewords appended to it, based on this object's version and error correction level.
fn add_ecc_and_interleave(&self, data: &[u8]) -> Vec<u8> { fn add_ecc_and_interleave(&self, data: &[u8]) -> Vec<u8> {
let ver = self.version; let ver: Version = self.version;
let ecl = self.errorcorrectionlevel; let ecl: QrCodeEcc = self.errorcorrectionlevel;
assert_eq!(data.len(), QrCode::get_num_data_codewords(ver, ecl), "Illegal argument"); assert_eq!(data.len(), QrCode::get_num_data_codewords(ver, ecl), "Illegal argument");
// Calculate parameter numbers // Calculate parameter numbers
@ -613,14 +613,13 @@ impl QrCode {
// XORs the codeword modules in this QR Code with the given mask pattern. // XORs the codeword modules in this QR Code with the given mask pattern.
// The function modules must be marked and the codeword bits must be drawn // The function modules must be marked and the codeword bits must be drawn
// before masking. Due to the arithmetic of XOR, calling applyMask() with // before masking. Due to the arithmetic of XOR, calling apply_mask() with
// the same mask value a second time will undo the mask. A final well-formed // the same mask value a second time will undo the mask. A final well-formed
// QR Code needs exactly one (not zero, two, etc.) mask applied. // QR Code needs exactly one (not zero, two, etc.) mask applied.
fn apply_mask(&mut self, mask: Mask) { fn apply_mask(&mut self, mask: Mask) {
let mask: u8 = mask.value();
for y in 0 .. self.size { for y in 0 .. self.size {
for x in 0 .. self.size { for x in 0 .. self.size {
let invert: bool = match mask { let invert: bool = match mask.value() {
0 => (x + y) % 2 == 0, 0 => (x + y) % 2 == 0,
1 => y % 2 == 0, 1 => y % 2 == 0,
2 => x % 3 == 0, 2 => x % 3 == 0,
@ -720,7 +719,7 @@ impl QrCode {
// Each position is in the range [0,177), and are used on both the x and y axes. // Each position is in the range [0,177), and are used on both the x and y axes.
// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
fn get_alignment_pattern_positions(&self) -> Vec<i32> { fn get_alignment_pattern_positions(&self) -> Vec<i32> {
let ver = self.version.value(); let ver: u8 = self.version.value();
if ver == 1 { if ver == 1 {
vec![] vec![]
} else { } else {
@ -884,13 +883,6 @@ impl FinderPenalty {
/*---- Constants and tables ----*/ /*---- Constants and tables ----*/
/// The minimum version number supported in the QR Code Model 2 standard.
pub const QrCode_MIN_VERSION: Version = Version( 1);
/// The maximum version number supported in the QR Code Model 2 standard.
pub const QrCode_MAX_VERSION: Version = Version(40);
// For use in get_penalty_score(), when evaluating which mask is best. // For use in get_penalty_score(), when evaluating which mask is best.
const PENALTY_N1: i32 = 3; const PENALTY_N1: i32 = 3;
const PENALTY_N2: i32 = 3; const PENALTY_N2: i32 = 3;
@ -949,7 +941,7 @@ impl QrCodeEcc {
// Returns an unsigned 2-bit integer (in the range 0 to 3). // Returns an unsigned 2-bit integer (in the range 0 to 3).
fn format_bits(self) -> u32 { fn format_bits(self) -> u8 {
use QrCodeEcc::*; use QrCodeEcc::*;
match self { match self {
Low => 1, Low => 1,
@ -1047,7 +1039,7 @@ impl QrSegment {
let mut accumdata: u32 = 0; let mut accumdata: u32 = 0;
let mut accumcount: u32 = 0; let mut accumcount: u32 = 0;
for &c in text { for &c in text {
let i = ALPHANUMERIC_CHARSET.iter().position(|&x| x == c) let i: usize = ALPHANUMERIC_CHARSET.iter().position(|&x| x == c)
.expect("String contains unencodable characters in alphanumeric mode"); .expect("String contains unencodable characters in alphanumeric mode");
accumdata = accumdata * 45 + (i as u32); accumdata = accumdata * 45 + (i as u32);
accumcount += 1; accumcount += 1;
@ -1140,11 +1132,15 @@ impl QrSegment {
fn get_total_bits(segs: &[Self], version: Version) -> Option<usize> { fn get_total_bits(segs: &[Self], version: Version) -> Option<usize> {
let mut result: usize = 0; let mut result: usize = 0;
for seg in segs { for seg in segs {
let ccbits = seg.mode.num_char_count_bits(version); let ccbits: u8 = seg.mode.num_char_count_bits(version);
if seg.numchars >= 1 << ccbits { // ccbits can be as large as 16, but usize can be as small as 16
return None; // The segment's length doesn't fit the field's bit width if let Some(limit) = 1usize.checked_shl(u32::from(ccbits)) {
if seg.numchars >= limit {
return None; // The segment's length doesn't fit the field's bit width
}
} }
result = result.checked_add(4 + usize::from(ccbits) + seg.data.len())?; result = result.checked_add(4 + usize::from(ccbits))?;
result = result.checked_add(seg.data.len())?;
} }
Some(result) Some(result)
} }
@ -1249,8 +1245,8 @@ impl BitBuffer {
/// ///
/// - Decrease the error correction level if it was greater than `QrCodeEcc::Low`. /// - Decrease the error correction level if it was greater than `QrCodeEcc::Low`.
/// - If the `encode_segments_advanced()` function was called, then increase the maxversion /// - If the `encode_segments_advanced()` function was called, then increase the maxversion
/// argument if it was less than `QrCode_MAX_VERSION`. (This advice does not apply to the /// argument if it was less than `Version::MAX`. (This advice does not apply to the
/// other factory functions because they search all versions up to `QrCode_MAX_VERSION`.) /// other factory functions because they search all versions up to `Version::MAX`.)
/// - Split the text data into better or optimal segments in order to reduce the number of bits required. /// - Split the text data into better or optimal segments in order to reduce the number of bits required.
/// - Change the text or binary data to be shorter. /// - Change the text or binary data to be shorter.
/// - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). /// - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).
@ -1276,11 +1272,17 @@ impl std::fmt::Display for DataTooLong {
pub struct Version(u8); pub struct Version(u8);
impl Version { impl Version {
/// The minimum version number supported in the QR Code Model 2 standard.
pub const MIN: Version = Version( 1);
/// The maximum version number supported in the QR Code Model 2 standard.
pub const MAX: Version = Version(40);
/// Creates a version object from the given number. /// Creates a version object from the given number.
/// ///
/// Panics if the number is outside the range [1, 40]. /// Panics if the number is outside the range [1, 40].
pub fn new(ver: u8) -> Self { pub fn new(ver: u8) -> Self {
assert!(QrCode_MIN_VERSION.value() <= ver && ver <= QrCode_MAX_VERSION.value(), "Version number out of range"); assert!(Version::MIN.value() <= ver && ver <= Version::MAX.value(), "Version number out of range");
Self(ver) Self(ver)
} }

Loading…
Cancel
Save