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:
Nayuki Minase
2016-04-16 03:53:58 +00:00
parent ca7e7a60a7
commit 5692e951dd
9 changed files with 205 additions and 113 deletions
+2 -1
View File
@@ -88,7 +88,8 @@ function redrawQrCode() {
segs.forEach(function(seg) {
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");
while (elem.firstChild != null)
elem.removeChild(elem.firstChild);
+44 -29
View File
@@ -29,7 +29,8 @@
* 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) -> QrCode
* - Function encodeSegments(list<QrSegment> segs, QrCode.Ecc ecl,
* int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode
* - Class QrCode:
* - Constructor QrCode(QrCode qr, int mask)
* - 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
* correction level. The smallest possible QR Code version is automatically chosen for the output.
* 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.
* 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) {
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, dataCapacityBits;
outer:
for (version = 1; ; version++) { // Increment until the data fits in the QR Code
if (version > 40) // All versions could not fit the given data
throw "Data too long";
dataCapacityBits = QrCode.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)
var dataUsedBits = 0;
for (var i = 0; i < segs.length; i++) {
var seg = segs[i];
if (seg.numChars < 0)
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)
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);
@@ -136,7 +136,7 @@ var qrcodegen = new function() {
throw "Assertion error";
// 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))];
};
// 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 --*/
var QrSegment = {}; // Private object to assign properties to