Moved {encodeText(), encodeBinary(), encodeSegments()} into class QrCode in Python and JavaScript implementations, for consistency with Java implementation.

This commit is contained in:
Nayuki Minase 2016-04-18 18:40:14 +00:00
parent de261ca62d
commit f325bfe638
4 changed files with 167 additions and 169 deletions

View File

@ -42,7 +42,7 @@ function redrawQrCode() {
var ecl = getInputErrorCorrectionLevel(); var ecl = getInputErrorCorrectionLevel();
var text = document.getElementById("text-input").value; var text = document.getElementById("text-input").value;
var segs = qrcodegen.QrSegment.makeSegments(text); var segs = qrcodegen.QrSegment.makeSegments(text);
var qr = qrcodegen.encodeSegments(segs, ecl); var qr = qrcodegen.QrCode.encodeSegments(segs, ecl);
// Get scale and border // Get scale and border
var scale = parseInt(document.getElementById("scale-input").value, 10); var scale = parseInt(document.getElementById("scale-input").value, 10);

View File

@ -27,11 +27,11 @@
/* /*
* Module "qrcodegen". Public members inside this namespace: * Module "qrcodegen". Public members inside this namespace:
* - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode
* - Function encodeBinary(list<int> data, QrCode.Ecc ecl) -> QrCode
* - Function encodeSegments(list<QrSegment> segs, QrCode.Ecc ecl,
* int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode
* - Class QrCode: * - Class QrCode:
* - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode
* - Function encodeBinary(list<int> data, QrCode.Ecc ecl) -> QrCode
* - Function encodeSegments(list<QrSegment> segs, QrCode.Ecc ecl,
* int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode
* - Constructor QrCode(QrCode qr, int mask) * - Constructor QrCode(QrCode qr, int mask)
* - Constructor QrCode(list<int> bytes, int mask, int version, QrCode.Ecc ecl) * - Constructor QrCode(list<int> bytes, int mask, int version, QrCode.Ecc ecl)
* - Method getVersion() -> int * - Method getVersion() -> int
@ -60,87 +60,6 @@
*/ */
var qrcodegen = new function() { var qrcodegen = new function() {
/*---- Public static factory functions for QrCode ----*/
/*
* Returns a QR Code symbol representing the given Unicode text string at the given error correction level.
* As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode
* code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.
*/
this.encodeText = function(text, ecl) {
var segs = this.QrSegment.makeSegments(text);
return this.encodeSegments(segs, ecl);
};
/*
* Returns a QR Code symbol representing the given binary data string at the given error correction level.
* 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.
*/
this.encodeBinary = function(data, ecl) {
var seg = this.QrSegment.makeBytes(data);
return this.encodeSegments([seg], ecl);
};
/*
* Returns a QR Code symbol representing the specified data segments with the specified encoding parameters.
* The smallest possible QR Code version within the specified range is automatically chosen for the output.
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and binary) to encode text more efficiently.
* This function is considered to be lower level than simply encoding text or binary data.
*/
this.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) {
if (minVersion == undefined) minVersion = 1;
if (maxVersion == undefined) maxVersion = 40;
if (mask == undefined) mask = -1;
if (boostEcl == undefined) boostEcl = true;
if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7)
throw "Invalid value";
// Find the minimal version number to use
var version, dataUsedBits;
for (version = minVersion; ; version++) {
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available
dataUsedBits = this.QrSegment.getTotalBits(segs, version);
if (dataUsedBits != null && dataUsedBits <= dataCapacityBits)
break; // This version number is found to be suitable
if (version >= maxVersion) // All versions in the range could not fit the given data
throw "Data too long";
}
// Increase the error correction level while the data still fits in the current version number
[this.QrCode.Ecc.MEDIUM, this.QrCode.Ecc.QUARTILE, this.QrCode.Ecc.HIGH].forEach(function(newEcl) {
if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8)
ecl = newEcl;
});
// Create the data bit string by concatenating all segments
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8;
var bb = new BitBuffer();
segs.forEach(function(seg) {
bb.appendBits(seg.getMode().getModeBits(), 4);
bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version));
bb.appendData(seg);
});
// Add terminator and pad up to a byte if applicable
bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength()));
bb.appendBits(0, (8 - bb.bitLength() % 8) % 8);
// Pad with alternate bytes until data capacity is reached
for (var padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
bb.appendBits(padByte, 8);
if (bb.bitLength() % 8 != 0)
throw "Assertion error";
// Create the QR Code symbol
return new this.QrCode(bb.getBytes(), mask, version, ecl);
};
/*---- QR Code symbol class ----*/ /*---- QR Code symbol class ----*/
/* /*
@ -594,6 +513,85 @@ var qrcodegen = new function() {
}; };
/*---- Public static factory functions for QrCode ----*/
/*
* Returns a QR Code symbol representing the given Unicode text string at the given error correction level.
* As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode
* code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.
*/
this.QrCode.encodeText = function(text, ecl) {
var segs = qrcodegen.QrSegment.makeSegments(text);
return this.encodeSegments(segs, ecl);
};
/*
* Returns a QR Code symbol representing the given binary data string at the given error correction level.
* 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.
*/
this.QrCode.encodeBinary = function(data, ecl) {
var seg = qrcodegen.QrSegment.makeBytes(data);
return this.encodeSegments([seg], ecl);
};
/*
* Returns a QR Code symbol representing the specified data segments with the specified encoding parameters.
* The smallest possible QR Code version within the specified range is automatically chosen for the output.
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and binary) to encode text more efficiently.
* This function is considered to be lower level than simply encoding text or binary data.
*/
this.QrCode.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) {
if (minVersion == undefined) minVersion = 1;
if (maxVersion == undefined) maxVersion = 40;
if (mask == undefined) mask = -1;
if (boostEcl == undefined) boostEcl = true;
if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7)
throw "Invalid value";
// Find the minimal version number to use
var version, dataUsedBits;
for (version = minVersion; ; version++) {
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available
dataUsedBits = qrcodegen.QrSegment.getTotalBits(segs, version);
if (dataUsedBits != null && dataUsedBits <= dataCapacityBits)
break; // This version number is found to be suitable
if (version >= maxVersion) // All versions in the range could not fit the given data
throw "Data too long";
}
// Increase the error correction level while the data still fits in the current version number
[this.Ecc.MEDIUM, this.Ecc.QUARTILE, this.Ecc.HIGH].forEach(function(newEcl) {
if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8)
ecl = newEcl;
});
// Create the data bit string by concatenating all segments
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8;
var bb = new BitBuffer();
segs.forEach(function(seg) {
bb.appendBits(seg.getMode().getModeBits(), 4);
bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version));
bb.appendData(seg);
});
// Add terminator and pad up to a byte if applicable
bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength()));
bb.appendBits(0, (8 - bb.bitLength() % 8) % 8);
// Pad with alternate bytes until data capacity is reached
for (var padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
bb.appendBits(padByte, 8);
if (bb.bitLength() % 8 != 0)
throw "Assertion error";
// Create the QR Code symbol
return new this(bb.getBytes(), mask, version, ecl);
};
/*---- Private static helper functions ----*/ /*---- Private static helper functions ----*/
var QrCode = {}; // Private object to assign properties to var QrCode = {}; // Private object to assign properties to

View File

@ -39,7 +39,7 @@ def do_basic_demo():
"""Creates a single QR Code, then prints it to the console.""" """Creates a single QR Code, then prints it to the console."""
text = u"Hello, world!" # User-supplied Unicode text text = u"Hello, world!" # User-supplied Unicode text
errcorlvl = qrcodegen.QrCode.Ecc.LOW # Error correction level errcorlvl = qrcodegen.QrCode.Ecc.LOW # Error correction level
qr = qrcodegen.encode_text(text, errcorlvl) qr = qrcodegen.QrCode.encode_text(text, errcorlvl)
print_qr(qr) print_qr(qr)
@ -47,27 +47,27 @@ def do_variety_demo():
"""Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console.""" """Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console."""
# Project Nayuki URL # Project Nayuki URL
qr = qrcodegen.encode_text("https://www.nayuki.io/", qrcodegen.QrCode.Ecc.HIGH) qr = qrcodegen.QrCode.encode_text("https://www.nayuki.io/", qrcodegen.QrCode.Ecc.HIGH)
qr = qrcodegen.QrCode(qrcode=qr, mask=3) # Change mask, forcing to mask #3 qr = qrcodegen.QrCode(qrcode=qr, mask=3) # Change mask, forcing to mask #3
print_qr(qr) print_qr(qr)
# Numeric mode encoding (3.33 bits per digit) # Numeric mode encoding (3.33 bits per digit)
qr = qrcodegen.encode_text("314159265358979323846264338327950288419716939937510", qrcodegen.QrCode.Ecc.MEDIUM) qr = qrcodegen.QrCode.encode_text("314159265358979323846264338327950288419716939937510", qrcodegen.QrCode.Ecc.MEDIUM)
print_qr(qr) print_qr(qr)
# Alphanumeric mode encoding (5.5 bits per character) # Alphanumeric mode encoding (5.5 bits per character)
qr = qrcodegen.encode_text("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcodegen.QrCode.Ecc.HIGH) qr = qrcodegen.QrCode.encode_text("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcodegen.QrCode.Ecc.HIGH)
print_qr(qr) print_qr(qr)
# Unicode text as UTF-8, and different masks # Unicode text as UTF-8, and different masks
qr = qrcodegen.encode_text(u"\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", qrcodegen.QrCode.Ecc.QUARTILE) qr = qrcodegen.QrCode.encode_text(u"\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", qrcodegen.QrCode.Ecc.QUARTILE)
print_qr(qrcodegen.QrCode(qrcode=qr, mask=0)) print_qr(qrcodegen.QrCode(qrcode=qr, mask=0))
print_qr(qrcodegen.QrCode(qrcode=qr, mask=1)) print_qr(qrcodegen.QrCode(qrcode=qr, mask=1))
print_qr(qrcodegen.QrCode(qrcode=qr, mask=5)) print_qr(qrcodegen.QrCode(qrcode=qr, mask=5))
print_qr(qrcodegen.QrCode(qrcode=qr, mask=7)) print_qr(qrcodegen.QrCode(qrcode=qr, mask=7))
# Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) # Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland)
qr = qrcodegen.encode_text("Alice was beginning to get very tired of sitting by her sister on the bank, " qr = qrcodegen.QrCode.encode_text("Alice was beginning to get very tired of sitting by her sister on the bank, "
"and of having nothing to do: once or twice she had peeped into the book her sister was reading, " "and of having nothing to do: once or twice she had peeped into the book her sister was reading, "
"but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice "
"'without pictures or conversations?' So she was considering in her own mind (as well as she could, " "'without pictures or conversations?' So she was considering in her own mind (as well as she could, "
@ -83,27 +83,27 @@ def do_segment_demo():
# Illustration "silver" # Illustration "silver"
silver0 = "THE SQUARE ROOT OF 2 IS 1." silver0 = "THE SQUARE ROOT OF 2 IS 1."
silver1 = "41421356237309504880168872420969807856967187537694807317667973799" silver1 = "41421356237309504880168872420969807856967187537694807317667973799"
qr = qrcodegen.encode_text(silver0 + silver1, qrcodegen.QrCode.Ecc.LOW) qr = qrcodegen.QrCode.encode_text(silver0 + silver1, qrcodegen.QrCode.Ecc.LOW)
print_qr(qr) print_qr(qr)
segs = [ segs = [
qrcodegen.QrSegment.make_alphanumeric(silver0), qrcodegen.QrSegment.make_alphanumeric(silver0),
qrcodegen.QrSegment.make_numeric(silver1)] qrcodegen.QrSegment.make_numeric(silver1)]
qr = qrcodegen.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW) qr = qrcodegen.QrCode.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW)
print_qr(qr) print_qr(qr)
# Illustration "golden" # Illustration "golden"
golden0 = u"Golden ratio \u03C6 = 1." golden0 = u"Golden ratio \u03C6 = 1."
golden1 = u"6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374" golden1 = u"6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"
golden2 = u"......" golden2 = u"......"
qr = qrcodegen.encode_text(golden0 + golden1 + golden2, qrcodegen.QrCode.Ecc.LOW) qr = qrcodegen.QrCode.encode_text(golden0 + golden1 + golden2, qrcodegen.QrCode.Ecc.LOW)
print_qr(qr) print_qr(qr)
segs = [ segs = [
qrcodegen.QrSegment.make_bytes(golden0.encode("UTF-8")), qrcodegen.QrSegment.make_bytes(golden0.encode("UTF-8")),
qrcodegen.QrSegment.make_numeric(golden1), qrcodegen.QrSegment.make_numeric(golden1),
qrcodegen.QrSegment.make_alphanumeric(golden2)] qrcodegen.QrSegment.make_alphanumeric(golden2)]
qr = qrcodegen.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW) qr = qrcodegen.QrCode.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW)
print_qr(qr) print_qr(qr)

View File

@ -27,11 +27,11 @@ import itertools, re, sys
""" """
Public members inside this module "qrcodegen": Public members inside this module "qrcodegen":
- 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
- Class QrCode: - 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
- Constructor QrCode(QrCode qr, int mask) - Constructor QrCode(QrCode qr, int mask)
- Constructor QrCode(bytes bytes, int mask, int version, QrCode.Ecc ecl) - Constructor QrCode(bytes bytes, int mask, int version, QrCode.Ecc ecl)
- Method get_version() -> int - Method get_version() -> int
@ -59,81 +59,81 @@ Public members inside this module "qrcodegen":
""" """
# ---- Public static factory functions for QrCode ----
def encode_text(text, ecl):
"""Returns a QR Code symbol representing the given Unicode text string at the given error correction level.
As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode
code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output."""
segs = QrSegment.make_segments(text)
return encode_segments(segs, ecl)
def encode_binary(data, ecl):
"""Returns a QR Code symbol representing the given binary data string at the given error correction level.
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."""
if not isinstance(data, bytes):
raise TypeError("Binary array expected")
return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl)
def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True):
"""Returns a QR Code symbol representing the specified data segments with the specified encoding parameters.
The smallest possible QR Code version within the specified range is automatically chosen for the output.
This function allows the user to create a custom sequence of segments that switches
between modes (such as alphanumeric and binary) to encode text more efficiently.
This function is considered to be lower level than simply encoding text or binary data."""
if not 1 <= minversion <= maxversion <= 40 or not -1 <= mask <= 7:
raise ValueError("Invalid value")
# Find the minimal version number to use
for version in range(minversion, maxversion + 1):
datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available
datausedbits = QrSegment.get_total_bits(segs, version)
if datausedbits is not None and datausedbits <= datacapacitybits:
break # This version number is found to be suitable
if version >= maxversion: # All versions in the range could not fit the given data
raise ValueError("Data too long")
if datausedbits is None:
raise AssertionError()
# 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):
if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8:
ecl = newecl
# Create the data bit string by concatenating all segments
datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8
bb = _BitBuffer()
for seg in segs:
bb.append_bits(seg.get_mode().get_mode_bits(), 4)
bb.append_bits(seg.get_num_chars(), seg.get_mode().num_char_count_bits(version))
bb.append_all(seg)
# Add terminator and pad up to a byte if applicable
bb.append_bits(0, min(4, datacapacitybits - bb.bit_length()))
bb.append_bits(0, -bb.bit_length() % 8)
# Pad with alternate bytes until data capacity is reached
for padbyte in itertools.cycle((0xEC, 0x11)):
if bb.bit_length() >= datacapacitybits:
break
bb.append_bits(padbyte, 8)
assert bb.bit_length() % 8 == 0
# Create the QR Code symbol
return QrCode(None, bb.get_bytes(), mask, version, ecl)
# ---- QR Code symbol class ---- # ---- QR Code symbol class ----
class QrCode(object): class QrCode(object):
"""Represents an immutable square grid of black or white cells for a QR Code symbol. This class covers the """Represents an immutable square grid of black or white cells for a QR Code symbol. This class covers the
QR Code model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels.""" QR Code model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels."""
# ---- Public static factory functions ----
def encode_text(text, ecl):
"""Returns a QR Code symbol representing the given Unicode text string at the given error correction level.
As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode
code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output."""
segs = QrSegment.make_segments(text)
return QrCode.encode_segments(segs, ecl)
def encode_binary(data, ecl):
"""Returns a QR Code symbol representing the given binary data string at the given error correction level.
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."""
if not isinstance(data, bytes):
raise TypeError("Binary array expected")
return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl)
@staticmethod
def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True):
"""Returns a QR Code symbol representing the specified data segments with the specified encoding parameters.
The smallest possible QR Code version within the specified range is automatically chosen for the output.
This function allows the user to create a custom sequence of segments that switches
between modes (such as alphanumeric and binary) to encode text more efficiently.
This function is considered to be lower level than simply encoding text or binary data."""
if not 1 <= minversion <= maxversion <= 40 or not -1 <= mask <= 7:
raise ValueError("Invalid value")
# Find the minimal version number to use
for version in range(minversion, maxversion + 1):
datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available
datausedbits = QrSegment.get_total_bits(segs, version)
if datausedbits is not None and datausedbits <= datacapacitybits:
break # This version number is found to be suitable
if version >= maxversion: # All versions in the range could not fit the given data
raise ValueError("Data too long")
if datausedbits is None:
raise AssertionError()
# 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):
if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8:
ecl = newecl
# Create the data bit string by concatenating all segments
datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8
bb = _BitBuffer()
for seg in segs:
bb.append_bits(seg.get_mode().get_mode_bits(), 4)
bb.append_bits(seg.get_num_chars(), seg.get_mode().num_char_count_bits(version))
bb.append_all(seg)
# Add terminator and pad up to a byte if applicable
bb.append_bits(0, min(4, datacapacitybits - bb.bit_length()))
bb.append_bits(0, -bb.bit_length() % 8)
# Pad with alternate bytes until data capacity is reached
for padbyte in itertools.cycle((0xEC, 0x11)):
if bb.bit_length() >= datacapacitybits:
break
bb.append_bits(padbyte, 8)
assert bb.bit_length() % 8 == 0
# Create the QR Code symbol
return QrCode(None, bb.get_bytes(), mask, version, ecl)
# ---- Constructor ---- # ---- Constructor ----
def __init__(self, qrcode=None, datacodewords=None, mask=None, version=None, errcorlvl=None): def __init__(self, qrcode=None, datacodewords=None, mask=None, version=None, errcorlvl=None):