Revamped QrCode.encodeSegments() to add parameters to make a much richer API, in all language versions; updated JavaScript demo script to handle new semantics.
This commit is contained in:
parent
ca7e7a60a7
commit
5692e951dd
|
@ -55,34 +55,34 @@ qrcodegen::QrCode qrcodegen::QrCode::encodeBinary(const std::vector<uint8_t> &da
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector<QrSegment> &segs, const Ecc &ecl) {
|
qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector<QrSegment> &segs, const Ecc &ecl,
|
||||||
// Find the minimal version number to use
|
int minVersion, int maxVersion, int mask, bool boostEcl) {
|
||||||
int version, dataCapacityBits;
|
if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7)
|
||||||
for (version = 1; ; version++) { // Increment until the data fits in the QR Code
|
throw "Invalid value";
|
||||||
if (version > 40) // All versions could not fit the given data
|
|
||||||
throw "Data too long";
|
|
||||||
dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available
|
|
||||||
|
|
||||||
// Calculate the total number of bits needed at this version number
|
// Find the minimal version number to use
|
||||||
// to encode all the segments (i.e. segment metadata and payloads)
|
int version, dataUsedBits;
|
||||||
int dataUsedBits = 0;
|
for (version = minVersion; ; version++) {
|
||||||
for (size_t i = 0; i < segs.size(); i++) {
|
int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available
|
||||||
const QrSegment &seg(segs.at(i));
|
dataUsedBits = QrSegment::getTotalBits(segs, version);
|
||||||
if (seg.numChars < 0)
|
if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits)
|
||||||
throw "Assertion error";
|
|
||||||
int ccbits = seg.mode.numCharCountBits(version);
|
|
||||||
if (seg.numChars >= (1 << ccbits)) {
|
|
||||||
// Segment length value doesn't fit in the length field's bit-width, so fail immediately
|
|
||||||
goto continueOuter;
|
|
||||||
}
|
|
||||||
dataUsedBits += 4 + ccbits + seg.bitLength;
|
|
||||||
}
|
|
||||||
if (dataUsedBits <= dataCapacityBits)
|
|
||||||
break; // This version number is found to be suitable
|
break; // This version number is found to be suitable
|
||||||
continueOuter:;
|
if (version >= maxVersion) // All versions in the range could not fit the given data
|
||||||
|
throw "Data too long";
|
||||||
|
}
|
||||||
|
if (dataUsedBits == -1)
|
||||||
|
throw "Assertion error";
|
||||||
|
|
||||||
|
// Increase the error correction level while the data still fits in the current version number
|
||||||
|
const Ecc *newEcl = &ecl;
|
||||||
|
if (boostEcl) {
|
||||||
|
if (dataUsedBits <= getNumDataCodewords(version, Ecc::MEDIUM ) * 8) newEcl = &Ecc::MEDIUM ;
|
||||||
|
if (dataUsedBits <= getNumDataCodewords(version, Ecc::QUARTILE) * 8) newEcl = &Ecc::QUARTILE;
|
||||||
|
if (dataUsedBits <= getNumDataCodewords(version, Ecc::HIGH ) * 8) newEcl = &Ecc::HIGH ;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the data bit string by concatenating all segments
|
// Create the data bit string by concatenating all segments
|
||||||
|
int dataCapacityBits = getNumDataCodewords(version, *newEcl) * 8;
|
||||||
BitBuffer bb;
|
BitBuffer bb;
|
||||||
for (size_t i = 0; i < segs.size(); i++) {
|
for (size_t i = 0; i < segs.size(); i++) {
|
||||||
const QrSegment &seg(segs.at(i));
|
const QrSegment &seg(segs.at(i));
|
||||||
|
@ -102,7 +102,7 @@ qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector<QrSegment>
|
||||||
throw "Assertion error";
|
throw "Assertion error";
|
||||||
|
|
||||||
// Create the QR Code symbol
|
// Create the QR Code symbol
|
||||||
return QrCode(version, ecl, bb.getBytes(), -1);
|
return QrCode(version, *newEcl, bb.getBytes(), mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -83,13 +83,14 @@ public:
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns a QR Code symbol representing the given data segments at the given error
|
* Returns a QR Code symbol representing the specified data segments with the specified encoding parameters.
|
||||||
* correction level. The smallest possible QR Code version is automatically chosen for the output.
|
* 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
|
* 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
|
* between modes (such as alphanumeric and binary) to encode text more efficiently.
|
||||||
* function is considered to be lower level than simply encoding text or binary data.
|
* This function is considered to be lower level than simply encoding text or binary data.
|
||||||
*/
|
*/
|
||||||
static QrCode encodeSegments(const std::vector<QrSegment> &segs, const Ecc &ecl);
|
static QrCode encodeSegments(const std::vector<QrSegment> &segs, const Ecc &ecl,
|
||||||
|
int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Software.
|
* Software.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include "BitBuffer.hpp"
|
#include "BitBuffer.hpp"
|
||||||
#include "QrSegment.hpp"
|
#include "QrSegment.hpp"
|
||||||
|
|
||||||
|
@ -128,6 +129,22 @@ qrcodegen::QrSegment::QrSegment(const Mode &md, int numCh, const std::vector<uin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int qrcodegen::QrSegment::getTotalBits(const std::vector<QrSegment> &segs, int version) {
|
||||||
|
if (version < 1 || version > 40)
|
||||||
|
throw "Version number out of range";
|
||||||
|
int result = 0;
|
||||||
|
for (size_t i = 0; i < segs.size(); i++) {
|
||||||
|
const QrSegment &seg(segs.at(i));
|
||||||
|
int ccbits = seg.mode.numCharCountBits(version);
|
||||||
|
// Fail if segment length value doesn't fit in the length field's bit-width
|
||||||
|
if (seg.numChars >= (1 << ccbits))
|
||||||
|
return -1;
|
||||||
|
result += 4 + ccbits + seg.bitLength;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool qrcodegen::QrSegment::isAlphanumeric(const char *text) {
|
bool qrcodegen::QrSegment::isAlphanumeric(const char *text) {
|
||||||
for (; *text != '\0'; text++) {
|
for (; *text != '\0'; text++) {
|
||||||
char c = *text;
|
char c = *text;
|
||||||
|
|
|
@ -151,6 +151,10 @@ public:
|
||||||
QrSegment(const Mode &md, int numCh, const std::vector<uint8_t> &b, int bitLen);
|
QrSegment(const Mode &md, int numCh, const std::vector<uint8_t> &b, int bitLen);
|
||||||
|
|
||||||
|
|
||||||
|
// Package-private helper function.
|
||||||
|
static int getTotalBits(const std::vector<QrSegment> &segs, int version);
|
||||||
|
|
||||||
|
|
||||||
/*---- Constant ----*/
|
/*---- Constant ----*/
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -76,49 +76,66 @@ public final class QrCode {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a QR Code symbol representing the specified data segments at the specified error
|
* Returns a QR Code symbol representing the specified data segments at the specified error correction
|
||||||
* correction level. The smallest possible QR Code version is automatically chosen for the output.
|
* level or higher. The smallest possible QR Code version is automatically chosen for the output.
|
||||||
* <p>This function allows the user to create a custom sequence of segments that switches
|
* <p>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
|
* 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.</p>
|
* function is considered to be lower level than simply encoding text or binary data.</p>
|
||||||
* @param segs the segments to encode
|
* @param segs the segments to encode
|
||||||
* @param ecl the error correction level to use
|
* @param ecl the error correction level to use (will be boosted)
|
||||||
* @return a QR Code representing the segments
|
* @return a QR Code representing the segments
|
||||||
* @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null}
|
* @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null}
|
||||||
* @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long
|
* @throws IllegalArgumentException if the data is too long to fit in the largest version QR Code at the ECL
|
||||||
*/
|
*/
|
||||||
public static QrCode encodeSegments(List<QrSegment> segs, Ecc ecl) {
|
public static QrCode encodeSegments(List<QrSegment> segs, Ecc ecl) {
|
||||||
|
return encodeSegments(segs, ecl, 1, 40, -1, 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.
|
||||||
|
* <p>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.</p>
|
||||||
|
* @param segs the segments to encode
|
||||||
|
* @param ecl the error correction level to use (may be boosted)
|
||||||
|
* @param minVersion the minimum allowed version of the QR symbol (at least 1)
|
||||||
|
* @param maxVersion the maximum allowed version of the QR symbol (at most 40)
|
||||||
|
* @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice
|
||||||
|
* @param boostEcl increases the error correction level if it can be done without increasing the version number
|
||||||
|
* @return a QR Code representing the segments
|
||||||
|
* @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null}
|
||||||
|
* @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, or if mask
|
||||||
|
* < −1 or mask > 7, or if the data is too long to fit in a QR Code at maxVersion at the ECL
|
||||||
|
*/
|
||||||
|
public static QrCode encodeSegments(List<QrSegment> segs, Ecc ecl, int minVersion, int maxVersion, int mask, boolean boostEcl) {
|
||||||
if (segs == null || ecl == null)
|
if (segs == null || ecl == null)
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
|
if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7)
|
||||||
|
throw new IllegalArgumentException("Invalid value");
|
||||||
|
|
||||||
// Find the minimal version number to use
|
// Find the minimal version number to use
|
||||||
int version, dataCapacityBits;
|
int version, dataUsedBits;
|
||||||
outer:
|
for (version = minVersion; ; version++) {
|
||||||
for (version = 1; ; version++) { // Increment until the data fits in the QR Code
|
int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available
|
||||||
if (version > 40) // All versions could not fit the given data
|
dataUsedBits = QrSegment.getTotalBits(segs, version);
|
||||||
throw new IllegalArgumentException("Data too long");
|
if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits)
|
||||||
dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available
|
|
||||||
|
|
||||||
// Calculate the total number of bits needed at this version number
|
|
||||||
// to encode all the segments (i.e. segment metadata and payloads)
|
|
||||||
int dataUsedBits = 0;
|
|
||||||
for (QrSegment seg : segs) {
|
|
||||||
if (seg == null)
|
|
||||||
throw new NullPointerException();
|
|
||||||
if (seg.numChars < 0)
|
|
||||||
throw new AssertionError();
|
|
||||||
int ccbits = seg.mode.numCharCountBits(version);
|
|
||||||
if (seg.numChars >= (1 << ccbits)) {
|
|
||||||
// Segment length value doesn't fit in the length field's bit-width, so fail immediately
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
dataUsedBits += 4 + ccbits + seg.bitLength;
|
|
||||||
}
|
|
||||||
if (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
|
||||||
|
throw new IllegalArgumentException("Data too long");
|
||||||
|
}
|
||||||
|
if (dataUsedBits == -1)
|
||||||
|
throw new AssertionError();
|
||||||
|
|
||||||
|
// Increase the error correction level while the data still fits in the current version number
|
||||||
|
for (Ecc newEcl : Ecc.values()) {
|
||||||
|
if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8)
|
||||||
|
ecl = newEcl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the data bit string by concatenating all segments
|
// Create the data bit string by concatenating all segments
|
||||||
|
int dataCapacityBits = getNumDataCodewords(version, ecl) * 8;
|
||||||
BitBuffer bb = new BitBuffer();
|
BitBuffer bb = new BitBuffer();
|
||||||
for (QrSegment seg : segs) {
|
for (QrSegment seg : segs) {
|
||||||
bb.appendBits(seg.mode.modeBits, 4);
|
bb.appendBits(seg.mode.modeBits, 4);
|
||||||
|
@ -137,7 +154,7 @@ public final class QrCode {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
|
|
||||||
// Create the QR Code symbol
|
// Create the QR Code symbol
|
||||||
return new QrCode(version, ecl, bb.getBytes(), -1);
|
return new QrCode(version, ecl, bb.getBytes(), mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -732,7 +749,8 @@ public final class QrCode {
|
||||||
* Represents the error correction level used in a QR Code symbol.
|
* Represents the error correction level used in a QR Code symbol.
|
||||||
*/
|
*/
|
||||||
public enum Ecc {
|
public enum Ecc {
|
||||||
// Constants declared in ascending order of error protection.
|
// These enum constants must be declared in ascending order of error protection,
|
||||||
|
// for the sake of the implicit ordinal() method and values() function.
|
||||||
LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2);
|
LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2);
|
||||||
|
|
||||||
// In the range 0 to 3 (unsigned 2-bit integer).
|
// In the range 0 to 3 (unsigned 2-bit integer).
|
||||||
|
|
|
@ -186,6 +186,27 @@ public final class QrSegment {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Package-private helper function.
|
||||||
|
static int getTotalBits(List<QrSegment> segs, int version) {
|
||||||
|
if (segs == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
if (version < 1 || version > 40)
|
||||||
|
throw new IllegalArgumentException("Version number out of range");
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
for (QrSegment seg : segs) {
|
||||||
|
if (seg == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
int ccbits = seg.mode.numCharCountBits(version);
|
||||||
|
// Fail if segment length value doesn't fit in the length field's bit-width
|
||||||
|
if (seg.numChars >= (1 << ccbits))
|
||||||
|
return -1;
|
||||||
|
result += 4 + ccbits + seg.bitLength;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*---- Constants ----*/
|
/*---- Constants ----*/
|
||||||
|
|
||||||
/** Can test whether a string is encodable in numeric mode (such as by using {@link #makeNumeric(String)}). */
|
/** Can test whether a string is encodable in numeric mode (such as by using {@link #makeNumeric(String)}). */
|
||||||
|
|
|
@ -88,7 +88,8 @@ function redrawQrCode() {
|
||||||
segs.forEach(function(seg) {
|
segs.forEach(function(seg) {
|
||||||
databits += 4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length;
|
databits += 4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length;
|
||||||
});
|
});
|
||||||
stats += ", data bits = " + databits + ".";
|
stats += ", error correction = level " + "LMQH".charAt(qr.getErrorCorrectionLevel().ordinal) + ", ";
|
||||||
|
stats += "data bits = " + databits + ".";
|
||||||
var elem = document.getElementById("statistics-output");
|
var elem = document.getElementById("statistics-output");
|
||||||
while (elem.firstChild != null)
|
while (elem.firstChild != null)
|
||||||
elem.removeChild(elem.firstChild);
|
elem.removeChild(elem.firstChild);
|
||||||
|
|
|
@ -29,7 +29,8 @@
|
||||||
* Module "qrcodegen". Public members inside this namespace:
|
* Module "qrcodegen". Public members inside this namespace:
|
||||||
* - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode
|
* - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode
|
||||||
* - Function encodeBinary(list<int> data, QrCode.Ecc ecl) -> QrCode
|
* - Function encodeBinary(list<int> data, QrCode.Ecc ecl) -> QrCode
|
||||||
* - Function encodeSegments(list<QrSegment> segs, 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:
|
||||||
* - 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)
|
||||||
|
@ -84,40 +85,39 @@ var qrcodegen = new function() {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns a QR Code symbol representing the given data segments at the given error
|
* Returns a QR Code symbol representing the specified data segments with the specified encoding parameters.
|
||||||
* correction level. The smallest possible QR Code version is automatically chosen for the output.
|
* 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
|
* 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
|
* between modes (such as alphanumeric and binary) to encode text more efficiently.
|
||||||
* function is considered to be lower level than simply encoding text or binary data.
|
* This function is considered to be lower level than simply encoding text or binary data.
|
||||||
*/
|
*/
|
||||||
this.encodeSegments = function(segs, ecl) {
|
this.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) {
|
||||||
// Find the minimal version number to use
|
if (minVersion == undefined) minVersion = 1;
|
||||||
var version, dataCapacityBits;
|
if (maxVersion == undefined) maxVersion = 40;
|
||||||
outer:
|
if (mask == undefined) mask = -1;
|
||||||
for (version = 1; ; version++) { // Increment until the data fits in the QR Code
|
if (boostEcl == undefined) boostEcl = true;
|
||||||
if (version > 40) // All versions could not fit the given data
|
if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7)
|
||||||
throw "Data too long";
|
throw "Invalid value";
|
||||||
dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available
|
|
||||||
|
|
||||||
// Calculate the total number of bits needed at this version number
|
// Find the minimal version number to use
|
||||||
// to encode all the segments (i.e. segment metadata and payloads)
|
var version, dataUsedBits;
|
||||||
var dataUsedBits = 0;
|
for (version = minVersion; ; version++) {
|
||||||
for (var i = 0; i < segs.length; i++) {
|
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available
|
||||||
var seg = segs[i];
|
dataUsedBits = this.QrSegment.getTotalBits(segs, version);
|
||||||
if (seg.numChars < 0)
|
if (dataUsedBits != null && dataUsedBits <= dataCapacityBits)
|
||||||
throw "Assertion error";
|
|
||||||
var ccbits = seg.getMode().numCharCountBits(version);
|
|
||||||
if (seg.getNumChars() >= (1 << ccbits)) {
|
|
||||||
// Segment length value doesn't fit in the length field's bit-width, so fail immediately
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
dataUsedBits += 4 + ccbits + seg.getBits().length;
|
|
||||||
}
|
|
||||||
if (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
|
||||||
|
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
|
// Create the data bit string by concatenating all segments
|
||||||
|
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8;
|
||||||
var bb = new BitBuffer();
|
var bb = new BitBuffer();
|
||||||
segs.forEach(function(seg) {
|
segs.forEach(function(seg) {
|
||||||
bb.appendBits(seg.getMode().getModeBits(), 4);
|
bb.appendBits(seg.getMode().getModeBits(), 4);
|
||||||
|
@ -136,7 +136,7 @@ var qrcodegen = new function() {
|
||||||
throw "Assertion error";
|
throw "Assertion error";
|
||||||
|
|
||||||
// Create the QR Code symbol
|
// Create the QR Code symbol
|
||||||
return new this.QrCode(bb.getBytes(), -1, version, ecl);
|
return new this.QrCode(bb.getBytes(), mask, version, ecl);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -775,6 +775,21 @@ var qrcodegen = new function() {
|
||||||
return [this.makeBytes(toUtf8ByteArray(text))];
|
return [this.makeBytes(toUtf8ByteArray(text))];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Package-private helper function.
|
||||||
|
this.QrSegment.getTotalBits = function(segs, version) {
|
||||||
|
if (version < 1 || version > 40)
|
||||||
|
throw "Version number out of range";
|
||||||
|
var result = 0;
|
||||||
|
segs.forEach(function(seg) {
|
||||||
|
var ccbits = seg.getMode().numCharCountBits(version);
|
||||||
|
// Fail if segment length value doesn't fit in the length field's bit-width
|
||||||
|
if (seg.getNumChars() >= (1 << ccbits))
|
||||||
|
return null;
|
||||||
|
result += 4 + ccbits + seg.getBits().length;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
/*-- Constants --*/
|
/*-- Constants --*/
|
||||||
|
|
||||||
var QrSegment = {}; // Private object to assign properties to
|
var QrSegment = {}; // Private object to assign properties to
|
||||||
|
|
|
@ -29,7 +29,8 @@ 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_text(str text, QrCode.Ecc ecl) -> QrCode
|
||||||
- Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode
|
- Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode
|
||||||
- Function encode_segments(list<QrSegment> segs, 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:
|
||||||
- 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)
|
||||||
|
@ -77,35 +78,34 @@ def encode_binary(data, ecl):
|
||||||
return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl)
|
return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl)
|
||||||
|
|
||||||
|
|
||||||
def encode_segments(segs, ecl):
|
def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True):
|
||||||
"""Returns a QR Code symbol representing the given data segments at the given error
|
"""Returns a QR Code symbol representing the specified data segments with the specified encoding parameters.
|
||||||
correction level. The smallest possible QR Code version is automatically chosen for the output.
|
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
|
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
|
between modes (such as alphanumeric and binary) to encode text more efficiently.
|
||||||
function is considered to be lower level than simply encoding text or binary data."""
|
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
|
# Find the minimal version number to use
|
||||||
for version in itertools.count(1): # Increment until the data fits in the QR Code
|
for version in range(minversion, maxversion + 1):
|
||||||
if version > 40: # All versions could not fit the given data
|
|
||||||
raise ValueError("Data too long")
|
|
||||||
datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available
|
datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available
|
||||||
|
datausedbits = QrSegment.get_total_bits(segs, version)
|
||||||
# Calculate the total number of bits needed at this version number
|
if datausedbits is not None and datausedbits <= datacapacitybits:
|
||||||
# to encode all the segments (i.e. segment metadata and payloads)
|
|
||||||
datausedbits = 0
|
|
||||||
for seg in segs:
|
|
||||||
if seg.get_num_chars() < 0:
|
|
||||||
raise AssertionError()
|
|
||||||
ccbits = seg.get_mode().num_char_count_bits(version)
|
|
||||||
if seg.get_num_chars() >= (1 << ccbits):
|
|
||||||
# Segment length value doesn't fit in the length field's bit-width, so fail immediately
|
|
||||||
break
|
|
||||||
datausedbits += 4 + ccbits + len(seg.get_bits())
|
|
||||||
else: # If the loop above did not break
|
|
||||||
if 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
|
||||||
|
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
|
# Create the data bit string by concatenating all segments
|
||||||
|
datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8
|
||||||
bb = _BitBuffer()
|
bb = _BitBuffer()
|
||||||
for seg in segs:
|
for seg in segs:
|
||||||
bb.append_bits(seg.get_mode().get_mode_bits(), 4)
|
bb.append_bits(seg.get_mode().get_mode_bits(), 4)
|
||||||
|
@ -124,7 +124,7 @@ def encode_segments(segs, ecl):
|
||||||
assert bb.bit_length() % 8 == 0
|
assert bb.bit_length() % 8 == 0
|
||||||
|
|
||||||
# Create the QR Code symbol
|
# Create the QR Code symbol
|
||||||
return QrCode(datacodewords=bb.get_bytes(), mask=-1, version=version, errcorlvl=ecl)
|
return QrCode(None, bb.get_bytes(), mask, version, ecl)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -686,6 +686,21 @@ class QrSegment(object):
|
||||||
return list(self._bitdata) # Defensive copy
|
return list(self._bitdata) # Defensive copy
|
||||||
|
|
||||||
|
|
||||||
|
# Package-private helper function.
|
||||||
|
@staticmethod
|
||||||
|
def get_total_bits(segs, version):
|
||||||
|
if not 1 <= version <= 40:
|
||||||
|
raise ValueError("Version number out of range")
|
||||||
|
result = 0
|
||||||
|
for seg in segs:
|
||||||
|
ccbits = seg.get_mode().num_char_count_bits(version)
|
||||||
|
# Fail if segment length value doesn't fit in the length field's bit-width
|
||||||
|
if seg.get_num_chars() >= (1 << ccbits):
|
||||||
|
return None
|
||||||
|
result += 4 + ccbits + len(seg.get_bits())
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
# -- Constants --
|
# -- Constants --
|
||||||
|
|
||||||
# Can test whether a string is encodable in numeric mode (such as by using make_numeric())
|
# Can test whether a string is encodable in numeric mode (such as by using make_numeric())
|
||||||
|
|
Loading…
Reference in New Issue