Moved QrCode.encodeTextToSegment() to QrSegment.makeSegments() and changed the function's code somewhat, in all language versions; updated JavaScript demo script to handle new semantics.

This commit is contained in:
Nayuki Minase 2016-04-16 01:44:24 +00:00
parent 1b2a554121
commit ca7e7a60a7
9 changed files with 119 additions and 91 deletions

View File

@ -43,27 +43,11 @@ const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::HIGH (3, 2);
qrcodegen::QrCode qrcodegen::QrCode::encodeText(const char *text, const Ecc &ecl) {
std::vector<QrSegment> segs;
segs.push_back(encodeTextToSegment(text));
std::vector<QrSegment> segs(QrSegment::makeSegments(text));
return encodeSegments(segs, ecl);
}
qrcodegen::QrSegment qrcodegen::QrCode::encodeTextToSegment(const char *text) {
// Select the most efficient segment encoding automatically
if (QrSegment::isNumeric(text))
return QrSegment::makeNumeric(text);
else if (QrSegment::isAlphanumeric(text))
return QrSegment::makeAlphanumeric(text);
else {
std::vector<uint8_t> bytes;
for (; *text != '\0'; text++)
bytes.push_back(static_cast<uint8_t>(*text));
return QrSegment::makeBytes(bytes);
}
}
qrcodegen::QrCode qrcodegen::QrCode::encodeBinary(const std::vector<uint8_t> &data, const Ecc &ecl) {
std::vector<QrSegment> segs;
segs.push_back(QrSegment::makeBytes(data));

View File

@ -74,12 +74,6 @@ public:
static QrCode encodeText(const char *text, const Ecc &ecl);
/*
* Returns a QR segment representing the given Unicode text string.
*/
static QrSegment encodeTextToSegment(const char *text);
/*
* 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

View File

@ -100,6 +100,24 @@ qrcodegen::QrSegment qrcodegen::QrSegment::makeAlphanumeric(const char *text) {
}
std::vector<qrcodegen::QrSegment> qrcodegen::QrSegment::makeSegments(const char *text) {
// Select the most efficient segment encoding automatically
std::vector<QrSegment> result;
if (*text == '\0'); // Leave the vector empty
else if (QrSegment::isNumeric(text))
result.push_back(QrSegment::makeNumeric(text));
else if (QrSegment::isAlphanumeric(text))
result.push_back(QrSegment::makeAlphanumeric(text));
else {
std::vector<uint8_t> bytes;
for (; *text != '\0'; text++)
bytes.push_back(static_cast<uint8_t>(*text));
result.push_back(QrSegment::makeBytes(bytes));
}
return result;
}
qrcodegen::QrSegment::QrSegment(const Mode &md, int numCh, const std::vector<uint8_t> &b, int bitLen) :
mode(md),
numChars(numCh),

View File

@ -103,6 +103,13 @@ public:
static QrSegment makeAlphanumeric(const char *text);
/*
* Returns a list of zero or more segments to represent the given text string.
* The result may use various segment modes and switch modes to optimize the length of the bit stream.
*/
static std::vector<QrSegment> makeSegments(const char *text);
/*---- Static helper functions ----*/
public:

View File

@ -25,7 +25,6 @@
package io.nayuki.qrcodegen;
import java.awt.image.BufferedImage;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
@ -53,27 +52,8 @@ public final class QrCode {
public static QrCode encodeText(String text, Ecc ecl) {
if (text == null || ecl == null)
throw new NullPointerException();
QrSegment seg = encodeTextToSegment(text);
return encodeSegments(Arrays.asList(seg), ecl);
}
/**
* Returns a QR segment representing the specified Unicode text string.
* @param text the text to be encoded, which can be any Unicode string
* @return a QR segment containing the text
* @throws NullPointerException if the text is {@code null}
*/
public static QrSegment encodeTextToSegment(String text) {
if (text == null)
throw new NullPointerException();
// Select the most efficient segment encoding automatically
if (QrSegment.NUMERIC_REGEX.matcher(text).matches())
return QrSegment.makeNumeric(text);
else if (QrSegment.ALPHANUMERIC_REGEX.matcher(text).matches())
return QrSegment.makeAlphanumeric(text);
else
return QrSegment.makeBytes(text.getBytes(StandardCharsets.UTF_8));
List<QrSegment> segs = QrSegment.makeSegments(text);
return encodeSegments(segs, ecl);
}

View File

@ -24,7 +24,10 @@
package io.nayuki.qrcodegen;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
@ -104,6 +107,31 @@ public final class QrSegment {
}
/**
* Returns a new mutable list of zero or more segments to represent the specified Unicode text string.
* The result may use various segment modes and switch modes to optimize the length of the bit stream.
* @param text the text to be encoded, which can be any Unicode string
* @return a list of segments containing the text
* @throws NullPointerException if the text is {@code null}
*/
public static List<QrSegment> makeSegments(String text) {
if (text == null)
throw new NullPointerException();
// Select the most efficient segment encoding automatically
if (text.equals(""))
return new ArrayList<>();
QrSegment seg;
if (NUMERIC_REGEX.matcher(text).matches())
seg = makeNumeric(text);
else if (ALPHANUMERIC_REGEX.matcher(text).matches())
seg = makeAlphanumeric(text);
else
seg = makeBytes(text.getBytes(StandardCharsets.UTF_8));
return Arrays.asList(seg);
}
/*---- Instance fields ----*/

View File

@ -39,7 +39,8 @@ function redrawQrCode() {
// Get text and compute QR Code
var text = document.getElementById("text-input").value;
var qr = qrcodegen.encodeText(text, ecl);
var segs = qrcodegen.QrSegment.makeSegments(text);
var qr = qrcodegen.encodeSegments(segs, ecl);
// Get scale and border
var scale = parseInt(document.getElementById("scale-input").value, 10);
@ -67,18 +68,27 @@ function redrawQrCode() {
stats += "mask pattern = " + qr.getMask() + ", ";
stats += "character count = " + countUnicodeChars(text) + ",\n";
stats += "encoding mode = ";
var seg = qrcodegen.encodeTextToSegment(text);
if (seg.getMode() == qrcodegen.QrSegment.Mode.NUMERIC)
stats += "numeric";
else if (seg.getMode() == qrcodegen.QrSegment.Mode.ALPHANUMERIC)
stats += "alphanumeric";
else if (seg.getMode() == qrcodegen.QrSegment.Mode.BYTE)
stats += "byte";
else if (seg.getMode() == qrcodegen.QrSegment.Mode.BYTE)
stats += "kanji";
else
stats += "unknown";
stats += ", data bits = " + (4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length) + ".";
if (segs.length == 0)
stats += "none";
else if (segs.length == 1) {
var mode = segs[0].getMode();
if (mode == qrcodegen.QrSegment.Mode.NUMERIC)
stats += "numeric";
else if (mode == qrcodegen.QrSegment.Mode.ALPHANUMERIC)
stats += "alphanumeric";
else if (mode == qrcodegen.QrSegment.Mode.BYTE)
stats += "byte";
else if (mode == qrcodegen.QrSegment.Mode.BYTE)
stats += "kanji";
else
stats += "unknown";
} else
stats += "multiple";
var databits = 0;
segs.forEach(function(seg) {
databits += 4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length;
});
stats += ", data bits = " + databits + ".";
var elem = document.getElementById("statistics-output");
while (elem.firstChild != null)
elem.removeChild(elem.firstChild);

View File

@ -28,7 +28,6 @@
/*
* Module "qrcodegen". Public members inside this namespace:
* - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode
* - Function encodeTextToSegment(str text) -> QrSegment
* - Function encodeBinary(list<int> data, QrCode.Ecc ecl) -> QrCode
* - Function encodeSegments(list<QrSegment> segs, QrCode.Ecc ecl) -> QrCode
* - Class QrCode:
@ -48,6 +47,7 @@
* - Function makeBytes(list<int> data) -> QrSegment
* - Function makeNumeric(str data) -> QrSegment
* - Function makeAlphanumeric(str data) -> QrSegment
* - Function makeSegments(str text) -> list<QrSegment>
* - Constructor QrSegment(QrSegment.Mode mode, int numChars, list<int> bitData)
* - Method getMode() -> QrSegment.Mode
* - Method getNumChars() -> int
@ -67,22 +67,8 @@ var qrcodegen = new function() {
* 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 seg = this.encodeTextToSegment(text);
return this.encodeSegments([seg], ecl);
};
/*
* Returns a single QR segment representing the given Unicode text string.
*/
this.encodeTextToSegment = function(text) {
// Select the most efficient segment encoding automatically
if (QrSegment.NUMERIC_REGEX.test(text))
return this.QrSegment.makeNumeric(text);
else if (QrSegment.ALPHANUMERIC_REGEX.test(text))
return this.QrSegment.makeAlphanumeric(text);
else
return this.QrSegment.makeBytes(toUtf8ByteArray(text));
var segs = this.QrSegment.makeSegments(text);
return this.encodeSegments(segs, ecl);
};
@ -773,6 +759,22 @@ var qrcodegen = new function() {
return new this(this.Mode.ALPHANUMERIC, text.length, bb.getBits());
};
/*
* Returns a new mutable list of zero or more segments to represent the given Unicode text string.
* The result may use various segment modes and switch modes to optimize the length of the bit stream.
*/
this.QrSegment.makeSegments = function(text) {
// Select the most efficient segment encoding automatically
if (text == "")
return [];
else if (QrSegment.NUMERIC_REGEX.test(text))
return [this.makeNumeric(text)];
else if (QrSegment.ALPHANUMERIC_REGEX.test(text))
return [this.makeAlphanumeric(text)];
else
return [this.makeBytes(toUtf8ByteArray(text))];
};
/*-- Constants --*/
var QrSegment = {}; // Private object to assign properties to

View File

@ -28,7 +28,6 @@ import itertools, re, sys
"""
Public members inside this module "qrcodegen":
- Function encode_text(str text, QrCode.Ecc ecl) -> QrCode
- Function encode_text_to_segment(str text) -> QrSegment
- Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode
- Function encode_segments(list<QrSegment> segs, QrCode.Ecc ecl) -> QrCode
- Class QrCode:
@ -47,6 +46,7 @@ Public members inside this module "qrcodegen":
- 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>
- Constructor QrSegment(QrSegment.Mode mode, int numch, list<int> bitdata)
- Method get_mode() -> QrSegment.Mode
- Method get_num_chars() -> int
@ -64,21 +64,8 @@ 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."""
seg = encode_text_to_segment(text)
return encode_segments([seg], ecl)
def encode_text_to_segment(text):
"""Returns a QR segment representing the given Unicode text string."""
if not isinstance(text, str) and (sys.version_info[0] >= 3 or not isinstance(text, unicode)):
raise TypeError("Text string expected")
# Select the most efficient segment encoding automatically
if QrSegment.NUMERIC_REGEX.match(text) is not None:
return QrSegment.make_numeric(text)
elif QrSegment.ALPHANUMERIC_REGEX.match(text) is not None:
return QrSegment.make_alphanumeric(text)
else:
return QrSegment.make_bytes(text.encode("UTF-8"))
segs = QrSegment.make_segments(text)
return encode_segments(segs, ecl)
def encode_binary(data, ecl):
@ -659,6 +646,24 @@ class QrSegment(object):
return QrSegment(QrSegment.Mode.ALPHANUMERIC, len(text), bb.get_bits())
@staticmethod
def make_segments(text):
"""Returns a new mutable list of zero or more segments to represent the given Unicode text string.
The result may use various segment modes and switch modes to optimize the length of the bit stream."""
if not isinstance(text, str) and (sys.version_info[0] >= 3 or not isinstance(text, unicode)):
raise TypeError("Text string expected")
# Select the most efficient segment encoding automatically
if text == "":
return []
elif QrSegment.NUMERIC_REGEX.match(text) is not None:
return [QrSegment.make_numeric(text)]
elif QrSegment.ALPHANUMERIC_REGEX.match(text) is not None:
return [QrSegment.make_alphanumeric(text)]
else:
return [QrSegment.make_bytes(text.encode("UTF-8"))]
# -- Constructor --
def __init__(self, mode, numch, bitdata):