From ca7e7a60a7ca4b1c14a686f79b958b4a1ec9a268 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sat, 16 Apr 2016 01:44:24 +0000 Subject: [PATCH] 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. --- cpp/QrCode.cpp | 18 +----------- cpp/QrCode.hpp | 6 ---- cpp/QrSegment.cpp | 18 ++++++++++++ cpp/QrSegment.hpp | 7 +++++ java/io/nayuki/qrcodegen/QrCode.java | 24 ++-------------- java/io/nayuki/qrcodegen/QrSegment.java | 28 +++++++++++++++++++ javascript/qrcodegen-demo.js | 36 +++++++++++++++--------- javascript/qrcodegen.js | 36 ++++++++++++------------ python/qrcodegen.py | 37 ++++++++++++++----------- 9 files changed, 119 insertions(+), 91 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index c0de087..273a688 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -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 segs; - segs.push_back(encodeTextToSegment(text)); + std::vector 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 bytes; - for (; *text != '\0'; text++) - bytes.push_back(static_cast(*text)); - return QrSegment::makeBytes(bytes); - } -} - - qrcodegen::QrCode qrcodegen::QrCode::encodeBinary(const std::vector &data, const Ecc &ecl) { std::vector segs; segs.push_back(QrSegment::makeBytes(data)); diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 82107a5..9463279 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -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 diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index debb41a..0c48908 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -100,6 +100,24 @@ qrcodegen::QrSegment qrcodegen::QrSegment::makeAlphanumeric(const char *text) { } +std::vector qrcodegen::QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + std::vector 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 bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + result.push_back(QrSegment::makeBytes(bytes)); + } + return result; +} + + qrcodegen::QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen) : mode(md), numChars(numCh), diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 20e5120..18f7a51 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -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 makeSegments(const char *text); + + /*---- Static helper functions ----*/ public: diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index d04cf7b..3fc5581 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -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 segs = QrSegment.makeSegments(text); + return encodeSegments(segs, ecl); } diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 20c0609..8ebc224 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -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 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 ----*/ diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index b48d505..e4a21b6 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -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); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 898c38c..77da0c7 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -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 data, QrCode.Ecc ecl) -> QrCode * - Function encodeSegments(list segs, QrCode.Ecc ecl) -> QrCode * - Class QrCode: @@ -48,6 +47,7 @@ * - Function makeBytes(list data) -> QrSegment * - Function makeNumeric(str data) -> QrSegment * - Function makeAlphanumeric(str data) -> QrSegment + * - Function makeSegments(str text) -> list * - Constructor QrSegment(QrSegment.Mode mode, int numChars, list 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 diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8de6d8e..1aefcda 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -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 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 - Constructor QrSegment(QrSegment.Mode mode, int numch, list 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):