From a610a3d8df7589c5347738999b5b8adda178973a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 8 May 2017 18:04:10 +0000 Subject: [PATCH] Added support for making Extended Channel Interpretation (ECI) segments - in Java, C++, JavaScript, and Python language versions. --- cpp/QrSegment.cpp | 15 +++++++++++++++ cpp/QrSegment.hpp | 7 +++++++ java/io/nayuki/qrcodegen/QrSegment.java | 23 ++++++++++++++++++++++- javascript/qrcodegen.js | 23 ++++++++++++++++++++++- python/qrcodegen.py | 22 +++++++++++++++++++++- 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index b2cec89..1d31746 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -53,6 +53,7 @@ const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); @@ -127,6 +128,20 @@ vector QrSegment::makeSegments(const char *text) { } +QrSegment QrSegment::makeEci(long assignVal) { + vector data; + if (0 <= assignVal && assignVal < (1 << 7)) + data = {static_cast(assignVal)}; + else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) + data = {static_cast(0x80 | (assignVal >> 8)), static_cast(assignVal)}; + else if ((1 << 14) <= assignVal && assignVal < 999999L) + data = {static_cast(0xC0 | (assignVal >> 16)), static_cast(assignVal >> 8), static_cast(assignVal)}; + else + throw "ECI assignment value out of range"; + return QrSegment(Mode::ECI, 0, data, data.size() * 8); +} + + QrSegment::QrSegment(const Mode &md, int numCh, const vector &b, int bitLen) : mode(md), numChars(numCh), diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 65a416f..01e413a 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -52,6 +52,7 @@ class QrSegment final { public: static const Mode ALPHANUMERIC; public: static const Mode BYTE; public: static const Mode KANJI; + public: static const Mode ECI; /*-- Fields --*/ @@ -106,6 +107,12 @@ class QrSegment final { public: static std::vector makeSegments(const char *text); + /* + * Returns a segment representing an Extended Channel Interpretation (ECI) designator with the given assignment value. + */ + public: static QrSegment makeEci(long assignVal); + + /*---- Public static helper functions ----*/ /* diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 19662bf..e68a944 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -128,6 +128,26 @@ public final class QrSegment { } + /** + * Returns a segment representing an Extended Channel Interpretation (ECI) designator with the specified assignment value. + * @param assignVal the ECI assignment number (see the AIM ECI specification) + * @return a segment containing the data + * @throws IllegalArgumentException if the value is outside the range [0, 106) + */ + public static QrSegment makeEci(int assignVal) { + byte[] data; + if (0 <= assignVal && assignVal < (1 << 7)) + data = new byte[]{(byte)assignVal}; + else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) + data = new byte[]{(byte)(0x80 | (assignVal >>> 8)), (byte)assignVal}; + else if ((1 << 14) <= assignVal && assignVal < 999999) + data = new byte[]{(byte)(0xC0 | (assignVal >>> 16)), (byte)(assignVal >>> 8), (byte)assignVal}; + else + throw new IllegalArgumentException("ECI assignment value out of range"); + return new QrSegment(Mode.ECI, 0, data, data.length * 8); + } + + /*---- Instance fields ----*/ @@ -228,7 +248,8 @@ public final class QrSegment { NUMERIC (0x1, 10, 12, 14), ALPHANUMERIC(0x2, 9, 11, 13), BYTE (0x4, 8, 16, 16), - KANJI (0x8, 8, 10, 12); + KANJI (0x8, 8, 10, 12), + ECI (0x7, 0, 0, 0); /*-- Fields --*/ diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 08c817c..f3cb9fa 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -46,13 +46,14 @@ * - Function makeNumeric(str data) -> QrSegment * - Function makeAlphanumeric(str data) -> QrSegment * - Function makeSegments(str text) -> list + * - Function makeEci(int assignVal) -> QrSegment * - Constructor QrSegment(QrSegment.Mode mode, int numChars, list bitData) * - Field QrSegment.Mode mode * - Field int numChars * - Method getBits() -> list * - Constants RegExp NUMERIC_REGEX, ALPHANUMERIC_REGEX * - Enum Mode: - * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI + * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI */ var qrcodegen = new function() { @@ -815,6 +816,25 @@ var qrcodegen = new function() { }; + /* + * Returns a segment representing an Extended Channel Interpretation (ECI) designator with the given assignment value. + */ + this.QrSegment.makeEci = function(assignVal) { + var bb = new BitBuffer(); + if (0 <= assignVal && assignVal < (1 << 7)) + bb.appendBits(assignVal, 8); + else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(assignVal, 14); + } else if ((1 << 14) <= assignVal && assignVal < 999999) { + bb.appendBits(6, 3); + bb.appendBits(assignVal, 21); + } else + throw "ECI assignment value out of range"; + return new this(this.Mode.ECI, 0, bb.getBits()); + }; + + // Package-private helper function. this.QrSegment.getTotalBits = function(segs, version) { if (version < 1 || version > 40) @@ -856,6 +876,7 @@ var qrcodegen = new function() { ALPHANUMERIC: new Mode(0x2, [ 9, 11, 13]), BYTE : new Mode(0x4, [ 8, 16, 16]), KANJI : new Mode(0x8, [ 8, 10, 12]), + ECI : new Mode(0x7, [ 0, 0, 0]), }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index cb37db2..9e05db8 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -47,13 +47,14 @@ This module "qrcodegen", public members: - Function make_numeric(str digits) -> QrSegment - Function make_alphanumeric(str text) -> QrSegment - Function make_segments(str text) -> list + - Function make_eci(int assignval) -> QrSegment - Constructor QrSegment(QrSegment.Mode mode, int numch, list bitdata) - Method get_mode() -> QrSegment.Mode - Method get_num_chars() -> int - Method get_bits() -> list - Constants regex NUMERIC_REGEX, ALPHANUMERIC_REGEX - Enum Mode: - - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI + - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI """ @@ -669,6 +670,24 @@ class QrSegment(object): return [QrSegment.make_bytes(text.encode("UTF-8"))] + @staticmethod + def make_eci(assignval): + """Returns a segment representing an Extended Channel Interpretation + (ECI) designator with the given assignment value.""" + bb = _BitBuffer() + if 0 <= assignval < (1 << 7): + bb.append_bits(assignval, 8) + elif (1 << 7) <= assignval < (1 << 14): + bb.append_bits(2, 2) + bb.append_bits(assignval, 14) + elif (1 << 14) <= assignval < 999999: + bb.append_bits(6, 3) + bb.append_bits(assignval, 21) + else: + raise ValueError("ECI assignment value out of range") + return QrSegment(QrSegment.Mode.ECI, 0, bb.get_bits()) + + # ---- Constructor ---- def __init__(self, mode, numch, bitdata): @@ -746,6 +765,7 @@ class QrSegment(object): Mode.ALPHANUMERIC = Mode(0x2, ( 9, 11, 13)) Mode.BYTE = Mode(0x4, ( 8, 16, 16)) Mode.KANJI = Mode(0x8, ( 8, 10, 12)) + Mode.ECI = Mode(0x7, ( 0, 0, 0))