diff --git a/cpp/BitBuffer.cpp b/cpp/BitBuffer.cpp deleted file mode 100644 index e22e9d3..0000000 --- a/cpp/BitBuffer.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include "BitBuffer.hpp" - - -namespace qrcodegen { - -BitBuffer::BitBuffer() - : std::vector() {} - - -void BitBuffer::appendBits(std::uint32_t val, int len) { - if (len < 0 || len > 31 || val >> len != 0) - throw std::domain_error("Value out of range"); - for (int i = len - 1; i >= 0; i--) // Append bit by bit - this->push_back(((val >> i) & 1) != 0); -} - -} diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp deleted file mode 100644 index f30913a..0000000 --- a/cpp/BitBuffer.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include - - -namespace qrcodegen { - -/* - * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. - */ -class BitBuffer final : public std::vector { - - /*---- Constructor ----*/ - - // Creates an empty bit buffer (length 0). - public: BitBuffer(); - - - - /*---- Method ----*/ - - // Appends the given number of low-order bits of the given value - // to this buffer. Requires 0 <= len <= 31 and val < 2^len. - public: void appendBits(std::uint32_t val, int len); - -}; - -} diff --git a/cpp/Makefile b/cpp/Makefile index 1d32943..f83c512 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -52,7 +52,7 @@ CXXFLAGS += -std=c++11 -O LIB = qrcodegen LIBFILE = lib$(LIB).a -LIBOBJ = BitBuffer.o QrCode.o QrSegment.o +LIBOBJ = QrCode.o MAINS = QrCodeGeneratorDemo QrCodeGeneratorWorker # Build all binaries diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 503f39c..ad1fa74 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -25,9 +25,10 @@ #include #include #include +#include #include +#include #include -#include "BitBuffer.hpp" #include "QrCode.hpp" using std::int8_t; @@ -38,6 +39,196 @@ using std::vector; namespace qrcodegen { +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int QrSegment::Mode::getModeBits() const { + return modeBits; +} + + +int QrSegment::Mode::numCharCountBits(int ver) const { + return numBitsCharCount[(ver + 7) / 17]; +} + + +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); + + +QrSegment QrSegment::makeBytes(const vector &data) { + if (data.size() > static_cast(INT_MAX)) + throw std::length_error("Data too long"); + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); +} + + +QrSegment QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw std::domain_error("String contains non-numeric characters"); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(static_cast(accumData), 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(static_cast(accumData), accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + + +QrSegment QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); + accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(static_cast(accumData), 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(static_cast(accumData), 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + + +vector QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + vector result; + if (*text == '\0'); // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + + +QrSegment QrSegment::makeEci(long assignVal) { + BitBuffer bb; + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) + bb.appendBits(static_cast(assignVal), 8); + else if (assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(static_cast(assignVal), 14); + } else if (assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(static_cast(assignVal), 21); + } else + throw std::domain_error("ECI assignment value out of range"); + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + + +QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : + mode(md), + numChars(numCh), + data(dt) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : + mode(md), + numChars(numCh), + data(std::move(dt)) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +int QrSegment::getTotalBits(const vector &segs, int version) { + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode.numCharCountBits(version); + if (seg.numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + if (4 + ccbits > INT_MAX - result) + return -1; // The sum will overflow an int type + result += 4 + ccbits; + if (seg.data.size() > static_cast(INT_MAX - result)) + return -1; // The sum will overflow an int type + result += static_cast(seg.data.size()); + } + return result; +} + + +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +bool QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +QrSegment::Mode QrSegment::getMode() const { + return mode; +} + + +int QrSegment::getNumChars() const { + return numChars; +} + + +const std::vector &QrSegment::getData() const { + return data; +} + + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + int QrCode::getFormatBits(Ecc ecl) { switch (ecl) { case Ecc::LOW : return 1; @@ -657,4 +848,17 @@ const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { data_too_long::data_too_long(const std::string &msg) : std::length_error(msg) {} + + +BitBuffer::BitBuffer() + : std::vector() {} + + +void BitBuffer::appendBits(std::uint32_t val, int len) { + if (len < 0 || len > 31 || val >> len != 0) + throw std::domain_error("Value out of range"); + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 6babfdf..bcc0426 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -28,11 +28,195 @@ #include #include #include -#include "QrSegment.hpp" namespace qrcodegen { +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * Describes how a segment's data bits are interpreted. Immutable. + */ + public: class Mode final { + + /*-- Constants --*/ + + public: static const Mode NUMERIC; + public: static const Mode ALPHANUMERIC; + public: static const Mode BYTE; + public: static const Mode KANJI; + public: static const Mode ECI; + + + /*-- Fields --*/ + + // The mode indicator bits, which is a uint4 value (range 0 to 15). + private: int modeBits; + + // Number of character count bits for three different version ranges. + private: int numBitsCharCount[3]; + + + /*-- Constructor --*/ + + private: Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: int getModeBits() const; + + /* + * (Package-private) Returns the bit width of the character count field for a segment in + * this mode in a QR Code at the given version number. The result is in the range [0, 16]. + */ + public: int numCharCountBits(int ver) const; + + }; + + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ + public: static QrSegment makeBytes(const std::vector &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + public: static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + 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. + */ + 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 ----*/ + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static bool isAlphanumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ + public: static bool isNumeric(const char *text); + + + + /*---- Instance fields ----*/ + + /* The mode indicator of this segment. Accessed through getMode(). */ + private: Mode mode; + + /* The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ + private: int numChars; + + /* The data bits of this segment. Accessed through getData(). */ + private: std::vector data; + + + /*---- Constructors (low level) ----*/ + + /* + * Creates a new QR Code segment with the given attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. + */ + public: QrSegment(Mode md, int numCh, const std::vector &dt); + + + /* + * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. + */ + public: QrSegment(Mode md, int numCh, std::vector &&dt); + + + /*---- Methods ----*/ + + /* + * Returns the mode field of this segment. + */ + public: Mode getMode() const; + + + /* + * Returns the character count field of this segment. + */ + public: int getNumChars() const; + + + /* + * Returns the data bits of this segment. + */ + public: const std::vector &getData() const; + + + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. + public: static int getTotalBits(const std::vector &segs, int version); + + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ + private: static const char *ALPHANUMERIC_CHARSET; + +}; + + + /* * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. @@ -347,4 +531,26 @@ class data_too_long : public std::length_error { }; + + +/* + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + */ +class BitBuffer final : public std::vector { + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public: BitBuffer(); + + + + /*---- Method ----*/ + + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. + public: void appendBits(std::uint32_t val, int len); + +}; + } diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 8284cfa..017dcbe 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -30,7 +30,6 @@ #include #include #include -#include "BitBuffer.hpp" #include "QrCode.hpp" using std::uint8_t; diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp deleted file mode 100644 index 45ea843..0000000 --- a/cpp/QrSegment.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include -#include -#include -#include "QrSegment.hpp" - -using std::uint8_t; -using std::vector; - - -namespace qrcodegen { - -QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : - modeBits(mode) { - numBitsCharCount[0] = cc0; - numBitsCharCount[1] = cc1; - numBitsCharCount[2] = cc2; -} - - -int QrSegment::Mode::getModeBits() const { - return modeBits; -} - - -int QrSegment::Mode::numCharCountBits(int ver) const { - return numBitsCharCount[(ver + 7) / 17]; -} - - -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); - - - -QrSegment QrSegment::makeBytes(const vector &data) { - if (data.size() > static_cast(INT_MAX)) - throw std::length_error("Data too long"); - BitBuffer bb; - for (uint8_t b : data) - bb.appendBits(b, 8); - return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); -} - - -QrSegment QrSegment::makeNumeric(const char *digits) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *digits != '\0'; digits++, charCount++) { - char c = *digits; - if (c < '0' || c > '9') - throw std::domain_error("String contains non-numeric characters"); - accumData = accumData * 10 + (c - '0'); - accumCount++; - if (accumCount == 3) { - bb.appendBits(static_cast(accumData), 10); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 or 2 digits remaining - bb.appendBits(static_cast(accumData), accumCount * 3 + 1); - return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); -} - - -QrSegment QrSegment::makeAlphanumeric(const char *text) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *text != '\0'; text++, charCount++) { - const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); - if (temp == nullptr) - throw std::domain_error("String contains unencodable characters in alphanumeric mode"); - accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); - accumCount++; - if (accumCount == 2) { - bb.appendBits(static_cast(accumData), 11); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 character remaining - bb.appendBits(static_cast(accumData), 6); - return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); -} - - -vector QrSegment::makeSegments(const char *text) { - // Select the most efficient segment encoding automatically - vector result; - if (*text == '\0'); // Leave result empty - else if (isNumeric(text)) - result.push_back(makeNumeric(text)); - else if (isAlphanumeric(text)) - result.push_back(makeAlphanumeric(text)); - else { - vector bytes; - for (; *text != '\0'; text++) - bytes.push_back(static_cast(*text)); - result.push_back(makeBytes(bytes)); - } - return result; -} - - -QrSegment QrSegment::makeEci(long assignVal) { - BitBuffer bb; - if (assignVal < 0) - throw std::domain_error("ECI assignment value out of range"); - else if (assignVal < (1 << 7)) - bb.appendBits(static_cast(assignVal), 8); - else if (assignVal < (1 << 14)) { - bb.appendBits(2, 2); - bb.appendBits(static_cast(assignVal), 14); - } else if (assignVal < 1000000L) { - bb.appendBits(6, 3); - bb.appendBits(static_cast(assignVal), 21); - } else - throw std::domain_error("ECI assignment value out of range"); - return QrSegment(Mode::ECI, 0, std::move(bb)); -} - - -QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : - mode(md), - numChars(numCh), - data(dt) { - if (numCh < 0) - throw std::domain_error("Invalid value"); -} - - -QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : - mode(md), - numChars(numCh), - data(std::move(dt)) { - if (numCh < 0) - throw std::domain_error("Invalid value"); -} - - -int QrSegment::getTotalBits(const vector &segs, int version) { - int result = 0; - for (const QrSegment &seg : segs) { - int ccbits = seg.mode.numCharCountBits(version); - if (seg.numChars >= (1L << ccbits)) - return -1; // The segment's length doesn't fit the field's bit width - if (4 + ccbits > INT_MAX - result) - return -1; // The sum will overflow an int type - result += 4 + ccbits; - if (seg.data.size() > static_cast(INT_MAX - result)) - return -1; // The sum will overflow an int type - result += static_cast(seg.data.size()); - } - return result; -} - - -bool QrSegment::isAlphanumeric(const char *text) { - for (; *text != '\0'; text++) { - if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) - return false; - } - return true; -} - - -bool QrSegment::isNumeric(const char *text) { - for (; *text != '\0'; text++) { - char c = *text; - if (c < '0' || c > '9') - return false; - } - return true; -} - - -QrSegment::Mode QrSegment::getMode() const { - return mode; -} - - -int QrSegment::getNumChars() const { - return numChars; -} - - -const std::vector &QrSegment::getData() const { - return data; -} - - -const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; - -} diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp deleted file mode 100644 index 663b8bb..0000000 --- a/cpp/QrSegment.hpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include -#include "BitBuffer.hpp" - - -namespace qrcodegen { - -/* - * A segment of character/binary/control data in a QR Code symbol. - * Instances of this class are immutable. - * The mid-level way to create a segment is to take the payload data - * and call a static factory function such as QrSegment::makeNumeric(). - * The low-level way to create a segment is to custom-make the bit buffer - * and call the QrSegment() constructor with appropriate values. - * This segment class imposes no length restrictions, but QR Codes have restrictions. - * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes. - */ -class QrSegment final { - - /*---- Public helper enumeration ----*/ - - /* - * Describes how a segment's data bits are interpreted. Immutable. - */ - public: class Mode final { - - /*-- Constants --*/ - - public: static const Mode NUMERIC; - public: static const Mode ALPHANUMERIC; - public: static const Mode BYTE; - public: static const Mode KANJI; - public: static const Mode ECI; - - - /*-- Fields --*/ - - // The mode indicator bits, which is a uint4 value (range 0 to 15). - private: int modeBits; - - // Number of character count bits for three different version ranges. - private: int numBitsCharCount[3]; - - - /*-- Constructor --*/ - - private: Mode(int mode, int cc0, int cc1, int cc2); - - - /*-- Methods --*/ - - /* - * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). - */ - public: int getModeBits() const; - - /* - * (Package-private) Returns the bit width of the character count field for a segment in - * this mode in a QR Code at the given version number. The result is in the range [0, 16]. - */ - public: int numCharCountBits(int ver) const; - - }; - - - - /*---- Static factory functions (mid level) ----*/ - - /* - * Returns a segment representing the given binary data encoded in - * byte mode. All input byte vectors are acceptable. Any text string - * can be converted to UTF-8 bytes and encoded as a byte mode segment. - */ - public: static QrSegment makeBytes(const std::vector &data); - - - /* - * Returns a segment representing the given string of decimal digits encoded in numeric mode. - */ - public: static QrSegment makeNumeric(const char *digits); - - - /* - * Returns a segment representing the given text string encoded in alphanumeric mode. - * The characters allowed are: 0 to 9, A to Z (uppercase only), space, - * dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - 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. - */ - 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 ----*/ - - /* - * Tests whether the given string can be encoded as a segment in alphanumeric mode. - * A string is encodable iff each character is in the following set: 0 to 9, A to Z - * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - public: static bool isAlphanumeric(const char *text); - - - /* - * Tests whether the given string can be encoded as a segment in numeric mode. - * A string is encodable iff each character is in the range 0 to 9. - */ - public: static bool isNumeric(const char *text); - - - - /*---- Instance fields ----*/ - - /* The mode indicator of this segment. Accessed through getMode(). */ - private: Mode mode; - - /* The length of this segment's unencoded data. Measured in characters for - * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - * Always zero or positive. Not the same as the data's bit length. - * Accessed through getNumChars(). */ - private: int numChars; - - /* The data bits of this segment. Accessed through getData(). */ - private: std::vector data; - - - /*---- Constructors (low level) ----*/ - - /* - * Creates a new QR Code segment with the given attributes and data. - * The character count (numCh) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is copied and stored. - */ - public: QrSegment(Mode md, int numCh, const std::vector &dt); - - - /* - * Creates a new QR Code segment with the given parameters and data. - * The character count (numCh) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is moved and stored. - */ - public: QrSegment(Mode md, int numCh, std::vector &&dt); - - - /*---- Methods ----*/ - - /* - * Returns the mode field of this segment. - */ - public: Mode getMode() const; - - - /* - * Returns the character count field of this segment. - */ - public: int getNumChars() const; - - - /* - * Returns the data bits of this segment. - */ - public: const std::vector &getData() const; - - - // (Package-private) Calculates the number of bits needed to encode the given segments at - // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a - // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. - public: static int getTotalBits(const std::vector &segs, int version); - - - /*---- Private constant ----*/ - - /* The set of all legal characters in alphanumeric mode, where - * each character value maps to the index in the string. */ - private: static const char *ALPHANUMERIC_CHARSET; - -}; - -}