From e9104a60b94c7c9f2c2177aa1dc0cbc43fb32a49 Mon Sep 17 00:00:00 2001 From: pssea Date: Wed, 22 Dec 2021 21:00:55 +0800 Subject: [PATCH] Description: upgrade qrcode from 1.6.0 to 1.7.0 IssueNo: https://gitee.com/openharmony/graphic_ui/issues/I4NPYK Feature or Bugfix: Feature Binary Source:No Signed-off-by: lizhiqi --- BUILD.gn | 4 +- CMakeLists.txt | 2 +- README.OpenSource | 2 +- Readme.markdown | 230 ++++---- c/Makefile | 6 +- c/qrcodegen-test.c | 34 +- c/qrcodegen-worker.c | 118 ---- c/qrcodegen.c | 72 +-- c/qrcodegen.h | 28 +- cpp/Makefile | 8 +- cpp/QrCodeGeneratorDemo.cpp | 37 +- cpp/QrCodeGeneratorWorker.cpp | 101 ---- cpp/{QrCode.cpp => qrcodegen.cpp} | 98 ++-- cpp/{QrCode.hpp => qrcodegen.hpp} | 41 +- .../qrcodegen => }/QrCodeGeneratorDemo.java | 125 ++++- java/pom.xml | 38 +- .../main/java/io/nayuki/qrcodegen/QrCode.java | 111 +--- .../qrcodegen/QrCodeGeneratorWorker.java | 100 ---- .../java/io/nayuki/qrcodegen/QrSegment.java | 50 +- .../io/nayuki/qrcodegen/package-info.java | 4 +- java/src/main/java/module-info.java | 29 + python/qrcodegen-batch-test.py | 137 ----- python/qrcodegen-demo.py | 60 ++- python/qrcodegen-worker.py | 82 --- python/qrcodegen.py | 503 +++++++++--------- python/setup.cfg | 2 - python/setup.py | 12 +- rust/Cargo.toml | 3 +- rust/Readme.markdown | 10 +- rust/examples/qrcodegen-demo.rs | 50 +- rust/examples/qrcodegen-worker.rs | 113 ---- rust/src/lib.rs | 156 +++--- typescript-javascript/build.sh | 5 - .../qrcodegen-input-demo.html | 28 +- typescript-javascript/qrcodegen-input-demo.ts | 54 +- .../qrcodegen-output-demo.html | 11 +- .../qrcodegen-output-demo.ts | 54 +- typescript-javascript/qrcodegen-worker.ts | 130 ----- typescript-javascript/qrcodegen.ts | 120 ++--- 39 files changed, 1082 insertions(+), 1686 deletions(-) delete mode 100755 c/qrcodegen-worker.c delete mode 100755 cpp/QrCodeGeneratorWorker.cpp rename cpp/{QrCode.cpp => qrcodegen.cpp} (92%) mode change 100755 => 100644 rename cpp/{QrCode.hpp => qrcodegen.hpp} (96%) mode change 100755 => 100644 rename java/{src/main/java/io/nayuki/qrcodegen => }/QrCodeGeneratorDemo.java (56%) mode change 100755 => 100644 delete mode 100755 java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java create mode 100644 java/src/main/java/module-info.java delete mode 100755 python/qrcodegen-batch-test.py delete mode 100755 python/qrcodegen-worker.py delete mode 100755 python/setup.cfg delete mode 100755 rust/examples/qrcodegen-worker.rs delete mode 100755 typescript-javascript/qrcodegen-worker.ts diff --git a/BUILD.gn b/BUILD.gn index 942f1eb..40cc7f3 100755 --- a/BUILD.gn +++ b/BUILD.gn @@ -25,7 +25,7 @@ if (defined(ohos_lite)) { } else { target_type = "shared_library" } - sources = [ "cpp/QrCode.cpp" ] + sources = [ "cpp/qrcodegen.cpp" ] include_dirs = [ "//third_party/qrcodegen/cpp" ] cflags = [ "-Wall" ] cflags_cc = cflags @@ -37,7 +37,7 @@ if (defined(ohos_lite)) { } } else { ohos_static_library("qrcodegen_static") { - sources = [ "cpp/QrCode.cpp" ] + sources = [ "cpp/qrcodegen.cpp" ] include_dirs = [ "//third_party/qrcodegen/cpp" ] configs = [ ":qrcodegen_config" ] public_configs = [ ":libqrcodegen_config" ] diff --git a/CMakeLists.txt b/CMakeLists.txt index 8157912..9cb5190 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set(QrcodegenSrc "${PROJECT_SOURCE_DIR}/third_party/qrcodegen") include_directories(${QrcodegenSrc}) add_library(qrcodegen STATIC - ${QrcodegenSrc}/cpp/QrCode.cpp + ${QrcodegenSrc}/cpp/qrcodegen.cpp ) target_include_directories(qrcodegen PUBLIC ${QrcodegenSrc}/cpp) diff --git a/README.OpenSource b/README.OpenSource index 931f898..8539e9d 100755 --- a/README.OpenSource +++ b/README.OpenSource @@ -3,7 +3,7 @@ "Name": "QR-Code-generator", "License": "MIT License", "License File": "LICENSE", - "Version Number": " 1.6.0", + "Version Number": " 1.7.0", "Owner": "guorensong1@huawei.com", "Upstream URL": "https://www.nayuki.io/page/qr-code-generator-library", "Description": "QR-Code-generator" diff --git a/Readme.markdown b/Readme.markdown index d28e31c..e4a28b8 100755 --- a/Readme.markdown +++ b/Readme.markdown @@ -18,7 +18,7 @@ Core features: * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard -* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (TypeScript/JavaScript only) +* Output format: Raw modules/pixels of the QR symbol * Detects finder-like penalty patterns more accurately than other implementations * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License @@ -43,139 +43,151 @@ Examples Java language: - import java.awt.image.BufferedImage; - import java.io.File; - import java.util.List; - import javax.imageio.ImageIO; - import io.nayuki.qrcodegen.*; - - // Simple operation - QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); - BufferedImage img = qr0.toImage(4, 10); - ImageIO.write(img, "png", new File("qr-code.png")); - - // Manual operation - List segs = QrSegment.makeSegments("3141592653589793238462643383"); - QrCode qr1 = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false); - for (int y = 0; y < qr1.size; y++) { - for (int x = 0; x < qr1.size; x++) { - (... paint qr1.getModule(x, y) ...) - } +```java +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.List; +import javax.imageio.ImageIO; +import io.nayuki.qrcodegen.*; + +// Simple operation +QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); +BufferedImage img = toImage(qr0, 4, 10); // See QrCodeGeneratorDemo +ImageIO.write(img, "png", new File("qr-code.png")); + +// Manual operation +List segs = QrSegment.makeSegments("3141592653589793238462643383"); +QrCode qr1 = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false); +for (int y = 0; y < qr1.size; y++) { + for (int x = 0; x < qr1.size; x++) { + (... paint qr1.getModule(x, y) ...) } +} +``` TypeScript/JavaScript languages: - // Name abbreviated for the sake of these examples here - var QRC = qrcodegen.QrCode; - - // Simple operation - var qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM); - var svg = qr0.toSvgString(4); - - // Manual operation - var segs = qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); - var qr1 = QRC.encodeSegments(segs, QRC.Ecc.HIGH, 5, 5, 2, false); - for (var y = 0; y < qr1.size; y++) { - for (var x = 0; x < qr1.size; x++) { - (... paint qr1.getModule(x, y) ...) - } +```typescript +// Name abbreviated for the sake of these examples here +const QRC = qrcodegen.QrCode; + +// Simple operation +const qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM); +const svg = toSvgString(qr0, 4); // See qrcodegen-input-demo + +// Manual operation +const segs = qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); +const qr1 = QRC.encodeSegments(segs, QRC.Ecc.HIGH, 5, 5, 2, false); +for (let y = 0; y < qr1.size; y++) { + for (let x = 0; x < qr1.size; x++) { + (... paint qr1.getModule(x, y) ...) } +} +``` Python language: - from qrcodegen import * - - # Simple operation - qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) - svg = qr0.to_svg_str(4) - - # Manual operation - segs = QrSegment.make_segments("3141592653589793238462643383") - qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) - for y in range(qr1.get_size()): - for x in range(qr1.get_size()): - (... paint qr1.get_module(x, y) ...) +```python +from qrcodegen import * + +# Simple operation +qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) +svg = to_svg_str(qr0, 4) # See qrcodegen-demo + +# Manual operation +segs = QrSegment.make_segments("3141592653589793238462643383") +qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) +for y in range(qr1.get_size()): + for x in range(qr1.get_size()): + (... paint qr1.get_module(x, y) ...) +``` C++ language: - #include - #include - #include "QrCode.hpp" - using namespace qrcodegen; - - // Simple operation - QrCode qr0 = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM); - std::string svg = qr0.toSvgString(4); - - // Manual operation - std::vector segs = - QrSegment::makeSegments("3141592653589793238462643383"); - QrCode qr1 = QrCode::encodeSegments( - segs, QrCode::Ecc::HIGH, 5, 5, 2, false); - for (int y = 0; y < qr1.getSize(); y++) { - for (int x = 0; x < qr1.getSize(); x++) { - (... paint qr1.getModule(x, y) ...) - } +```c++ +#include +#include +#include "QrCode.hpp" +using namespace qrcodegen; + +// Simple operation +QrCode qr0 = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM); +std::string svg = toSvgString(qr0, 4); // See QrCodeGeneratorDemo + +// Manual operation +std::vector segs = + QrSegment::makeSegments("3141592653589793238462643383"); +QrCode qr1 = QrCode::encodeSegments( + segs, QrCode::Ecc::HIGH, 5, 5, 2, false); +for (int y = 0; y < qr1.getSize(); y++) { + for (int x = 0; x < qr1.getSize(); x++) { + (... paint qr1.getModule(x, y) ...) } +} +``` C language: - #include - #include - #include "qrcodegen.h" - - // Text data - uint8_t qr0[qrcodegen_BUFFER_LEN_MAX]; - uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; - bool ok = qrcodegen_encodeText("Hello, world!", - tempBuffer, qr0, qrcodegen_Ecc_MEDIUM, - qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, - qrcodegen_Mask_AUTO, true); - if (!ok) - return; - - int size = qrcodegen_getSize(qr0); - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) { - (... paint qrcodegen_getModule(qr0, x, y) ...) - } +```c +#include +#include +#include "qrcodegen.h" + +// Text data +uint8_t qr0[qrcodegen_BUFFER_LEN_MAX]; +uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; +bool ok = qrcodegen_encodeText("Hello, world!", + tempBuffer, qr0, qrcodegen_Ecc_MEDIUM, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, + qrcodegen_Mask_AUTO, true); +if (!ok) + return; + +int size = qrcodegen_getSize(qr0); +for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + (... paint qrcodegen_getModule(qr0, x, y) ...) } - - // Binary data - uint8_t dataAndTemp[qrcodegen_BUFFER_LEN_FOR_VERSION(7)] - = {0xE3, 0x81, 0x82}; - uint8_t qr1[qrcodegen_BUFFER_LEN_FOR_VERSION(7)]; - ok = qrcodegen_encodeBinary(dataAndTemp, 3, qr1, - qrcodegen_Ecc_HIGH, 2, 7, qrcodegen_Mask_4, false); +} + +// Binary data +uint8_t dataAndTemp[qrcodegen_BUFFER_LEN_FOR_VERSION(7)] + = {0xE3, 0x81, 0x82}; +uint8_t qr1[qrcodegen_BUFFER_LEN_FOR_VERSION(7)]; +ok = qrcodegen_encodeBinary(dataAndTemp, 3, qr1, + qrcodegen_Ecc_HIGH, 2, 7, qrcodegen_Mask_4, false); +``` Rust language: - extern crate qrcodegen; - use qrcodegen::QrCode; - use qrcodegen::QrCodeEcc; - use qrcodegen::QrSegment; - - // Simple operation - let qr = QrCode::encode_text("Hello, world!", - QrCodeEcc::Medium).unwrap(); - let svg = qr.to_svg_string(4); - - // Manual operation - let chrs: Vec = "3141592653589793238462643383".chars().collect(); - let segs = QrSegment::make_segments(&chrs); - let qr = QrCode::encode_segments_advanced( - &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap(); - for y in 0 .. qr.size() { - for x in 0 .. qr.size() { - (... paint qr.get_module(x, y) ...) - } +```rust +extern crate qrcodegen; +use qrcodegen::QrCode; +use qrcodegen::QrCodeEcc; +use qrcodegen::QrSegment; + +// Simple operation +let qr = QrCode::encode_text("Hello, world!", + QrCodeEcc::Medium).unwrap(); +let svg = to_svg_string(&qr, 4); // See qrcodegen-demo + +// Manual operation +let chrs: Vec = "3141592653589793238462643383".chars().collect(); +let segs = QrSegment::make_segments(&chrs); +let qr = QrCode::encode_segments_advanced( + &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap(); +for y in 0 .. qr.size() { + for x in 0 .. qr.size() { + (... paint qr.get_module(x, y) ...) } +} +``` License ------- -Copyright © 2020 Project Nayuki. (MIT License) +Copyright © 2021 Project Nayuki. (MIT License) [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/c/Makefile b/c/Makefile index fd0c367..661b1f7 100755 --- a/c/Makefile +++ b/c/Makefile @@ -53,7 +53,7 @@ CFLAGS += -std=c99 -O LIB = qrcodegen LIBFILE = lib$(LIB).a LIBOBJ = qrcodegen.o -MAINS = qrcodegen-demo qrcodegen-test qrcodegen-worker +MAINS = qrcodegen-demo qrcodegen-test # Build all binaries all: $(LIBFILE) $(MAINS) @@ -65,11 +65,11 @@ clean: # Executable files %: %.o $(LIBFILE) - $(CC) $(CFLAGS) -o $@ $< -L . -l $(LIB) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -L . -l $(LIB) # Special executable qrcodegen-test: qrcodegen-test.c $(LIBOBJ:%.o=%.c) - $(CC) $(CFLAGS) -DQRCODEGEN_TEST -o $@ $^ + $(CC) $(CFLAGS) $(LDFLAGS) -DQRCODEGEN_TEST -o $@ $^ # The library $(LIBFILE): $(LIBOBJ) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 37612d7..fd57380 100755 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -55,8 +55,8 @@ uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); void initializeFunctionModules(int version, uint8_t qrcode[]); int getAlignmentPatternPositions(int version, uint8_t result[7]); bool getModule(const uint8_t qrcode[], int x, int y); -void setModule(uint8_t qrcode[], int x, int y, bool isBlack); -void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack); +void setModule(uint8_t qrcode[], int x, int y, bool isDark); +void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark); int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); @@ -374,18 +374,18 @@ static void testInitializeFunctionModulesEtc(void) { else assert(size == ver * 4 + 17); - bool hasWhite = false; - bool hasBlack = false; + bool hasLight = false; + bool hasDark = false; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { bool color = qrcodegen_getModule(qrcode, x, y); if (color) - hasBlack = true; + hasDark = true; else - hasWhite = true; + hasLight = true; } } - assert(hasWhite && hasBlack); + assert(hasLight && hasDark); free(qrcode); numTestCases++; } @@ -424,42 +424,42 @@ static void testGetSetModule(void) { initializeFunctionModules(23, qrcode); int size = qrcodegen_getSize(qrcode); - for (int y = 0; y < size; y++) { // Clear all to white + for (int y = 0; y < size; y++) { // Clear all to light for (int x = 0; x < size; x++) setModule(qrcode, x, y, false); } - for (int y = 0; y < size; y++) { // Check all white + for (int y = 0; y < size; y++) { // Check all light for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == false); } - for (int y = 0; y < size; y++) { // Set all to black + for (int y = 0; y < size; y++) { // Set all to dark for (int x = 0; x < size; x++) setModule(qrcode, x, y, true); } - for (int y = 0; y < size; y++) { // Check all black + for (int y = 0; y < size; y++) { // Check all dark for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == true); } - // Set some out of bounds modules to white + // Set some out of bounds modules to light setModuleBounded(qrcode, -1, -1, false); setModuleBounded(qrcode, -1, 0, false); setModuleBounded(qrcode, 0, -1, false); setModuleBounded(qrcode, size, 5, false); setModuleBounded(qrcode, 72, size, false); setModuleBounded(qrcode, size, size, false); - for (int y = 0; y < size; y++) { // Check all black + for (int y = 0; y < size; y++) { // Check all dark for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == true); } - // Set some modules to white + // Set some modules to light setModule(qrcode, 3, 8, false); setModule(qrcode, 61, 49, false); - for (int y = 0; y < size; y++) { // Check most black + for (int y = 0; y < size; y++) { // Check most dark for (int x = 0; x < size; x++) { - bool white = (x == 3 && y == 8) || (x == 61 && y == 49); - assert(qrcodegen_getModule(qrcode, x, y) != white); + bool light = (x == 3 && y == 8) || (x == 61 && y == 49); + assert(qrcodegen_getModule(qrcode, x, y) != light); } } numTestCases++; diff --git a/c/qrcodegen-worker.c b/c/qrcodegen-worker.c deleted file mode 100755 index dc16888..0000000 --- a/c/qrcodegen-worker.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * QR Code generator test worker (C) - * - * This program reads data and encoding parameters from standard input and writes - * QR Code bitmaps to standard output. The I/O format is one integer per line. - * Run with no command line arguments. The program is intended for automated - * batch testing of end-to-end functionality of this QR Code generator library. - * - * 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 -#include -#include "qrcodegen.h" - - -int main(void) { - while (true) { - - // Read data length or exit - size_t length; - { - int temp; - if (scanf("%d", &temp) != 1) - return EXIT_FAILURE; - if (temp == -1) - break; - length = (size_t)temp; - } - - // Read data bytes - bool isAscii = true; - uint8_t *data = malloc(length * sizeof(uint8_t)); - if (data == NULL) { - perror("malloc"); - return EXIT_FAILURE; - } - for (size_t i = 0; i < length; i++) { - int b; - if (scanf("%d", &b) != 1) - return EXIT_FAILURE; - data[i] = (uint8_t)b; - isAscii &= 0 < b && b < 128; - } - - // Read encoding parameters - int errCorLvl, minVersion, maxVersion, mask, boostEcl; - if (scanf("%d %d %d %d %d", &errCorLvl, &minVersion, &maxVersion, &mask, &boostEcl) != 5) - return EXIT_FAILURE; - - // Allocate memory for QR Code - size_t bufferLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); - uint8_t *qrcode = malloc(bufferLen * sizeof(uint8_t)); - uint8_t *tempBuffer = malloc(bufferLen * sizeof(uint8_t)); - if (qrcode == NULL || tempBuffer == NULL) { - perror("malloc"); - return EXIT_FAILURE; - } - - // Try to make QR Code symbol - bool ok; - if (isAscii) { - char *text = malloc((length + 1) * sizeof(char)); - if (text == NULL) { - perror("malloc"); - return EXIT_FAILURE; - } - for (size_t i = 0; i < length; i++) - text[i] = (char)data[i]; - text[length] = '\0'; - ok = qrcodegen_encodeText(text, tempBuffer, qrcode, (enum qrcodegen_Ecc)errCorLvl, - minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); - free(text); - } else if (length <= bufferLen) { - memcpy(tempBuffer, data, length * sizeof(data[0])); - ok = qrcodegen_encodeBinary(tempBuffer, length, qrcode, (enum qrcodegen_Ecc)errCorLvl, - minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); - } else - ok = false; - free(data); - free(tempBuffer); - - if (ok) { - // Print grid of modules - int size = qrcodegen_getSize(qrcode); - printf("%d\n", (size - 17) / 4); - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) - printf("%d\n", qrcodegen_getModule(qrcode, x, y) ? 1 : 0); - } - } else - printf("-1\n"); - free(qrcode); - fflush(stdout); - } - return EXIT_SUCCESS; -} diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 31219a7..2e28a4f 100755 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -64,7 +64,7 @@ testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); testable void initializeFunctionModules(int version, uint8_t qrcode[]); -static void drawWhiteFunctionModules(uint8_t qrcode[], int version); +static void drawLightFunctionModules(uint8_t qrcode[], int version); static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); testable int getAlignmentPatternPositions(int version, uint8_t result[7]); static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); @@ -77,8 +77,8 @@ static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLe static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize); testable bool getModule(const uint8_t qrcode[], int x, int y); -testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); -testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack); +testable void setModule(uint8_t qrcode[], int x, int y, bool isDark); +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark); static bool getBit(int x, int i); testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); @@ -259,7 +259,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz addEccAndInterleave(qrcode, version, ecl, tempBuffer); initializeFunctionModules(version, qrcode); drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); - drawWhiteFunctionModules(qrcode, version); + drawLightFunctionModules(qrcode, version); initializeFunctionModules(version, tempBuffer); // Handle masking @@ -413,8 +413,8 @@ testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { /*---- Drawing function modules ----*/ -// Clears the given QR Code grid with white modules for the given -// version's size, then marks every function module as black. +// Clears the given QR Code grid with light modules for the given +// version's size, then marks every function module as dark. testable void initializeFunctionModules(int version, uint8_t qrcode[]) { // Initialize QR Code int qrsize = version * 4 + 17; @@ -449,10 +449,10 @@ testable void initializeFunctionModules(int version, uint8_t qrcode[]) { } -// Draws white function modules and possibly some black modules onto the given QR Code, without changing +// Draws light function modules and possibly some dark modules onto the given QR Code, without changing // non-function modules. This does not draw the format bits. This requires all function modules to be previously -// marked black (namely by initializeFunctionModules()), because this may skip redrawing black function modules. -static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { +// marked dark (namely by initializeFunctionModules()), because this may skip redrawing dark function modules. +static void drawLightFunctionModules(uint8_t qrcode[], int version) { // Draw horizontal and vertical timing patterns int qrsize = qrcodegen_getSize(qrcode); for (int i = 7; i < qrsize - 7; i += 2) { @@ -512,7 +512,7 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { // Draws two copies of the format bits (with its own error correction code) based // on the given mask and error correction level. This always draws all modules of -// the format bits, unlike drawWhiteFunctionModules() which might skip black modules. +// the format bits, unlike drawLightFunctionModules() which might skip dark modules. static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { // Calculate error correction code and pack bits assert(0 <= (int)mask && (int)mask <= 7); @@ -539,7 +539,7 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin setModule(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setModule(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); - setModule(qrcode, 8, qrsize - 8, true); // Always black + setModule(qrcode, 8, qrsize - 8, true); // Always dark } @@ -552,7 +552,7 @@ testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { return 0; int numAlign = version / 7 + 2; int step = (version == 32) ? 26 : - (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) result[i] = (uint8_t)pos; result[0] = 6; @@ -560,7 +560,7 @@ testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { } -// Sets every pixel in the range [left : left + width] * [top : top + height] to black. +// Sets every pixel in the range [left : left + width] * [top : top + height] to dark. static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { for (int dy = 0; dy < height; dy++) { for (int dx = 0; dx < width; dx++) @@ -573,7 +573,7 @@ static void fillRectangle(int left, int top, int width, int height, uint8_t qrco /*---- Drawing data modules and masking ----*/ // Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of -// the QR Code to be black at function modules and white at codeword modules (including unused remainder bits). +// the QR Code to be dark at function modules and light at codeword modules (including unused remainder bits). static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { int qrsize = qrcodegen_getSize(qrcode); int i = 0; // Bit index into the data @@ -587,12 +587,12 @@ static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { bool upward = ((right + 1) & 2) == 0; int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate if (!getModule(qrcode, x, y) && i < dataLen * 8) { - bool black = getBit(data[i >> 3], 7 - (i & 7)); - setModule(qrcode, x, y, black); + bool dark = getBit(data[i >> 3], 7 - (i & 7)); + setModule(qrcode, x, y, dark); i++; } // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method + // 0/false/light by the constructor and are left unchanged by this method } } } @@ -693,29 +693,29 @@ static long getPenaltyScore(const uint8_t qrcode[]) { } } - // Balance of black and white modules - int black = 0; + // Balance of dark and light modules + int dark = 0; for (int y = 0; y < qrsize; y++) { for (int x = 0; x < qrsize; x++) { if (getModule(qrcode, x, y)) - black++; + dark++; } } - int total = qrsize * qrsize; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - int k = (int)((labs(black * 20L - total * 10L) + total - 1) / total) - 1; + int total = qrsize * qrsize; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = (int)((labs(dark * 20L - total * 10L) + total - 1) / total) - 1; result += k * PENALTY_N4; return result; } -// Can only be called immediately after a white run is added, and +// Can only be called immediately after a light run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { int n = runHistory[1]; assert(n <= qrsize * 3); bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; - // The maximum QR Code size is 177, hence the black run length n <= 177. + // The maximum QR Code size is 177, hence the dark run length n <= 177. // Arithmetic is promoted to int, so n*4 will not overflow. return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); @@ -724,11 +724,11 @@ static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) { - if (currentRunColor) { // Terminate black run + if (currentRunColor) { // Terminate dark run finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); currentRunLength = 0; } - currentRunLength += qrsize; // Add white border to final run + currentRunLength += qrsize; // Add light border to final run finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); return finderPenaltyCountPatterns(runHistory, qrsize); } @@ -737,7 +737,7 @@ static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLe // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize) { if (runHistory[0] == 0) - currentRunLength += qrsize; // Add white border to initial run + currentRunLength += qrsize; // Add light border to initial run memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0])); runHistory[0] = currentRunLength; } @@ -774,13 +774,13 @@ testable bool getModule(const uint8_t qrcode[], int x, int y) { // Sets the module at the given coordinates, which must be in bounds. -testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack) { +testable void setModule(uint8_t qrcode[], int x, int y, bool isDark) { int qrsize = qrcode[0]; assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); int index = y * qrsize + x; int bitIndex = index & 7; int byteIndex = (index >> 3) + 1; - if (isBlack) + if (isDark) qrcode[byteIndex] |= 1 << bitIndex; else qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; @@ -788,10 +788,10 @@ testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack) { // Sets the module at the given coordinates, doing nothing if out of bounds. -testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack) { +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark) { int qrsize = qrcode[0]; if (0 <= x && x < qrsize && 0 <= y && y < qrsize) - setModule(qrcode, x, y, isBlack); + setModule(qrcode, x, y, isDark); } @@ -805,10 +805,10 @@ static bool getBit(int x, int i) { /*---- Segment handling ----*/ // Public function - see documentation comment in header file. -bool qrcodegen_isAlphanumeric(const char *text) { +bool qrcodegen_isNumeric(const char *text) { assert(text != NULL); for (; *text != '\0'; text++) { - if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) + if (*text < '0' || *text > '9') return false; } return true; @@ -816,10 +816,10 @@ bool qrcodegen_isAlphanumeric(const char *text) { // Public function - see documentation comment in header file. -bool qrcodegen_isNumeric(const char *text) { +bool qrcodegen_isAlphanumeric(const char *text) { assert(text != NULL); for (; *text != '\0'; text++) { - if (*text < '0' || *text > '9') + if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) return false; } return true; diff --git a/c/qrcodegen.h b/c/qrcodegen.h index a99b94d..e6da033 100755 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -36,7 +36,7 @@ extern "C" { /* * This library creates QR Code symbols, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * A QR Code structure is an immutable square grid of black and white cells. + * A QR Code structure is an immutable square grid of dark and light cells. * The library provides functions to create a QR Code from text or binary data. * The library covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -154,8 +154,8 @@ struct qrcodegen_Segment { * - The input text must be encoded in UTF-8 and contain no NULs. * - The variables ecl and mask must correspond to enum constant values. * - Requires 1 <= minVersion <= maxVersion <= 40. - * - The arrays tempBuffer and qrcode must each have a length - * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). + * - The arrays tempBuffer and qrcode must each have a length of at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion), and cannot overlap. * - After the function returns, tempBuffer contains no useful data. * - If successful, the resulting QR Code may use numeric, * alphanumeric, or byte mode to encode the text. @@ -178,8 +178,8 @@ bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode * valid UTF-8 text, but is not required by the QR Code standard. * - The variables ecl and mask must correspond to enum constant values. * - Requires 1 <= minVersion <= maxVersion <= 40. - * - The arrays dataAndTemp and qrcode must each have a length - * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). + * - The arrays dataAndTemp and qrcode must each have a length of at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion), and cannot overlap. * - After the function returns, the contents of dataAndTemp may have changed, * and does not represent useful data anymore. * - If successful, the resulting QR Code will use byte mode to encode the data. @@ -229,6 +229,13 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); +/* + * 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. + */ +bool qrcodegen_isNumeric(const char *text); + + /* * 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 @@ -237,13 +244,6 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz bool qrcodegen_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. - */ -bool qrcodegen_isNumeric(const char *text); - - /* * Returns the number of bytes (uint8_t) needed for the data buffer of a segment * containing the given number of characters using the given mode. Notes: @@ -300,8 +300,8 @@ int qrcodegen_getSize(const uint8_t qrcode[]); /* * Returns the color of the module (pixel) at the given coordinates, which is false - * for white or true for black. The top left corner has the coordinates (x=0, y=0). - * If the given coordinates are out of bounds, then false (white) is returned. + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. */ bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); diff --git a/cpp/Makefile b/cpp/Makefile index f83c512..57dc0bf 100755 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -50,10 +50,10 @@ CXXFLAGS += -std=c++11 -O # ---- Targets to build ---- -LIB = qrcodegen +LIB = qrcodegencpp LIBFILE = lib$(LIB).a -LIBOBJ = QrCode.o -MAINS = QrCodeGeneratorDemo QrCodeGeneratorWorker +LIBOBJ = qrcodegen.o +MAINS = QrCodeGeneratorDemo # Build all binaries all: $(LIBFILE) $(MAINS) @@ -65,7 +65,7 @@ clean: # Executable files %: %.o $(LIBFILE) - $(CXX) $(CXXFLAGS) -o $@ $< -L . -l $(LIB) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $< -L . -l $(LIB) # The library $(LIBFILE): $(LIBOBJ) diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 017dcbe..b64bf5e 100755 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -24,13 +24,15 @@ * Software. */ +#include #include #include #include #include +#include #include #include -#include "QrCode.hpp" +#include "qrcodegen.hpp" using std::uint8_t; using qrcodegen::QrCode; @@ -42,6 +44,7 @@ static void doBasicDemo(); static void doVarietyDemo(); static void doSegmentDemo(); static void doMaskDemo(); +static std::string toSvgString(const QrCode &qr, int border); static void printQr(const QrCode &qr); @@ -66,7 +69,7 @@ static void doBasicDemo() { // Make and print the QR Code symbol const QrCode qr = QrCode::encodeText(text, errCorLvl); printQr(qr); - std::cout << qr.toSvgString(4) << std::endl; + std::cout << toSvgString(qr, 4) << std::endl; } @@ -186,6 +189,36 @@ static void doMaskDemo() { /*---- Utilities ----*/ +// Returns a string of SVG code for an image depicting the given QR Code, with the given number +// of border modules. The string always uses Unix newlines (\n), regardless of the platform. +static std::string toSvgString(const QrCode &qr, int border) { + if (border < 0) + throw std::domain_error("Border must be non-negative"); + if (border > INT_MAX / 2 || border * 2 > INT_MAX - qr.getSize()) + throw std::overflow_error("Border too large"); + + std::ostringstream sb; + sb << "\n"; + sb << "\n"; + sb << "\n"; + sb << "\t\n"; + sb << "\t\n"; + sb << "\n"; + return sb.str(); +} + + // Prints the given QrCode object to the console. static void printQr(const QrCode &qr) { int border = 4; diff --git a/cpp/QrCodeGeneratorWorker.cpp b/cpp/QrCodeGeneratorWorker.cpp deleted file mode 100755 index 073107f..0000000 --- a/cpp/QrCodeGeneratorWorker.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * QR Code generator test worker (C++) - * - * This program reads data and encoding parameters from standard input and writes - * QR Code bitmaps to standard output. The I/O format is one integer per line. - * Run with no command line arguments. The program is intended for automated - * batch testing of end-to-end functionality of this QR Code generator library. - * - * 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 -#include -#include "QrCode.hpp" - -using qrcodegen::QrCode; -using qrcodegen::QrSegment; - - -static const std::vector ECC_LEVELS{ - QrCode::Ecc::LOW, - QrCode::Ecc::MEDIUM, - QrCode::Ecc::QUARTILE, - QrCode::Ecc::HIGH, -}; - - -int main() { - while (true) { - - // Read data length or exit - int length; - std::cin >> length; - if (length == -1) - break; - - // Read data bytes - bool isAscii = true; - std::vector data; - for (int i = 0; i < length; i++) { - int b; - std::cin >> b; - data.push_back(static_cast(b)); - isAscii &= 0 < b && b < 128; - } - - // Read encoding parameters - int errCorLvl, minVersion, maxVersion, mask, boostEcl; - std::cin >> errCorLvl; - std::cin >> minVersion; - std::cin >> maxVersion; - std::cin >> mask; - std::cin >> boostEcl; - - // Make list of segments - std::vector segs; - if (isAscii) { - std::vector text(data.cbegin(), data.cend()); - text.push_back('\0'); - segs = QrSegment::makeSegments(text.data()); - } else - segs.push_back(QrSegment::makeBytes(data)); - - try { // Try to make QR Code symbol - const QrCode qr = QrCode::encodeSegments(segs, - ECC_LEVELS.at(static_cast(errCorLvl)), minVersion, maxVersion, mask, boostEcl == 1); - // Print grid of modules - std::cout << qr.getVersion() << std::endl; - for (int y = 0; y < qr.getSize(); y++) { - for (int x = 0; x < qr.getSize(); x++) - std::cout << (qr.getModule(x, y) ? 1 : 0) << std::endl; - } - - } catch (const qrcodegen::data_too_long &ex) { - std::cout << -1 << std::endl; - } - std::cout << std::flush; - } - return EXIT_SUCCESS; -} diff --git a/cpp/QrCode.cpp b/cpp/qrcodegen.cpp old mode 100755 new mode 100644 similarity index 92% rename from cpp/QrCode.cpp rename to cpp/qrcodegen.cpp index b9de862..e52dd50 --- a/cpp/QrCode.cpp +++ b/cpp/qrcodegen.cpp @@ -29,7 +29,7 @@ #include #include #include -#include "QrCode.hpp" +#include "qrcodegen.hpp" using std::int8_t; using std::uint8_t; @@ -156,8 +156,8 @@ QrSegment QrSegment::makeEci(long assignVal) { } -QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : - mode(md), +QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) : + mode(&md), numChars(numCh), data(dt) { if (numCh < 0) @@ -165,8 +165,8 @@ QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : } -QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : - mode(md), +QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) : + mode(&md), numChars(numCh), data(std::move(dt)) { if (numCh < 0) @@ -177,7 +177,7 @@ QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : int QrSegment::getTotalBits(const vector &segs, int version) { int result = 0; for (const QrSegment &seg : segs) { - int ccbits = seg.mode.numCharCountBits(version); + 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) @@ -191,15 +191,6 @@ int QrSegment::getTotalBits(const vector &segs, int version) { } -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; @@ -210,8 +201,17 @@ bool QrSegment::isNumeric(const char *text) { } -QrSegment::Mode QrSegment::getMode() const { - return mode; +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +const QrSegment::Mode &QrSegment::getMode() const { + return *mode; } @@ -279,7 +279,7 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, throw std::logic_error("Assertion error"); // Increase the error correction level while the data still fits in the current version number - for (Ecc newEcl : vector{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high + for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; } @@ -310,7 +310,7 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, // Pack bits into bytes in big endian vector dataCodewords(bb.size() / 8); for (size_t i = 0; i < bb.size(); i++) - dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); // Create the QR Code object return QrCode(version, ecl, dataCodewords, mask); @@ -327,7 +327,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) throw std::domain_error("Mask value out of range"); size = ver * 4 + 17; size_t sz = static_cast(size); - modules = vector >(sz, vector(sz)); // Initially all white + modules = vector >(sz, vector(sz)); // Initially all light isFunction = vector >(sz, vector(sz)); // Compute ECC, draw modules @@ -351,7 +351,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) } if (msk < 0 || msk > 7) throw std::logic_error("Assertion error"); - this->mask = msk; + mask = msk; applyMask(msk); // Apply the final choice of mask drawFormatBits(msk); // Overwrite old format bits @@ -385,34 +385,6 @@ bool QrCode::getModule(int x, int y) const { } -std::string QrCode::toSvgString(int border) const { - if (border < 0) - throw std::domain_error("Border must be non-negative"); - if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) - throw std::overflow_error("Border too large"); - - std::ostringstream sb; - sb << "\n"; - sb << "\n"; - sb << "\n"; - sb << "\t\n"; - sb << "\t\n"; - sb << "\n"; - return sb.str(); -} - - void QrCode::drawFunctionPatterns() { // Draw horizontal and vertical timing patterns for (int i = 0; i < size; i++) { @@ -466,7 +438,7 @@ void QrCode::drawFormatBits(int msk) { setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(bits, i)); - setFunctionModule(8, size - 8, true); // Always black + setFunctionModule(8, size - 8, true); // Always dark } @@ -513,10 +485,10 @@ void QrCode::drawAlignmentPattern(int x, int y) { } -void QrCode::setFunctionModule(int x, int y, bool isBlack) { +void QrCode::setFunctionModule(int x, int y, bool isDark) { size_t ux = static_cast(x); size_t uy = static_cast(y); - modules .at(uy).at(ux) = isBlack; + modules .at(uy).at(ux) = isDark; isFunction.at(uy).at(ux) = true; } @@ -584,7 +556,7 @@ void QrCode::drawCodewords(const vector &data) { i++; } // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method + // 0/false/light by the constructor and are left unchanged by this method } } } @@ -676,17 +648,17 @@ long QrCode::getPenaltyScore() const { } } - // Balance of black and white modules - int black = 0; + // Balance of dark and light modules + int dark = 0; for (const vector &row : modules) { for (bool color : row) { if (color) - black++; + dark++; } } - int total = size * size; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - int k = static_cast((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1; + int total = size * size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = static_cast((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1; result += k * PENALTY_N4; return result; } @@ -698,7 +670,7 @@ vector QrCode::getAlignmentPatternPositions() const { else { int numAlign = version / 7 + 2; int step = (version == 32) ? 26 : - (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; vector result; for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) result.insert(result.begin(), pos); @@ -793,11 +765,11 @@ int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) cons int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const { - if (currentRunColor) { // Terminate black run + if (currentRunColor) { // Terminate dark run finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } - currentRunLength += size; // Add white border to final run + currentRunLength += size; // Add light border to final run finderPenaltyAddHistory(currentRunLength, runHistory); return finderPenaltyCountPatterns(runHistory); } @@ -805,7 +777,7 @@ int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunL void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const { if (runHistory.at(0) == 0) - currentRunLength += size; // Add white border to initial run + currentRunLength += size; // Add light border to initial run std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); runHistory.at(0) = currentRunLength; } diff --git a/cpp/QrCode.hpp b/cpp/qrcodegen.hpp old mode 100755 new mode 100644 similarity index 96% rename from cpp/QrCode.hpp rename to cpp/qrcodegen.hpp index 7341e41..9448982 --- a/cpp/QrCode.hpp +++ b/cpp/qrcodegen.hpp @@ -132,6 +132,13 @@ class QrSegment final { /*---- Public static helper functions ----*/ + /* + * 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); + + /* * 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 @@ -140,18 +147,11 @@ class QrSegment final { 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; + private: const 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. @@ -170,7 +170,7 @@ class QrSegment final { * 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); + public: QrSegment(const Mode &md, int numCh, const std::vector &dt); /* @@ -178,7 +178,7 @@ class QrSegment final { * 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); + public: QrSegment(const Mode &md, int numCh, std::vector &&dt); /*---- Methods ----*/ @@ -186,7 +186,7 @@ class QrSegment final { /* * Returns the mode field of this segment. */ - public: Mode getMode() const; + public: const Mode &getMode() const; /* @@ -220,7 +220,7 @@ class QrSegment final { /* * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this class represent an immutable square grid of black and white cells. + * Instances of this class represent an immutable square grid of dark and light cells. * The class provides static factory functions to create a QR Code from text or binary data. * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -314,7 +314,7 @@ class QrCode final { // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = white, true = black). + // The modules of this QR Code (false = light, true = dark). // Immutable after constructor finishes. Accessed through getModule(). private: std::vector > modules; @@ -363,19 +363,12 @@ class QrCode final { /* * Returns the color of the module (pixel) at the given coordinates, which is false - * for white or true for black. The top left corner has the coordinates (x=0, y=0). - * If the given coordinates are out of bounds, then false (white) is returned. + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. */ public: bool getModule(int x, int y) const; - /* - * Returns a string of SVG code for an image depicting this QR Code, with the given number - * of border modules. The string always uses Unix newlines (\n), regardless of the platform. - */ - public: std::string toSvgString(int border) const; - - /*---- Private helper methods for constructor: Drawing function modules ----*/ @@ -405,7 +398,7 @@ class QrCode final { // Sets the color of a module and marks it as a function module. // Only used by the constructor. Coordinates must be in bounds. - private: void setFunctionModule(int x, int y, bool isBlack); + private: void setFunctionModule(int x, int y, bool isDark); // Returns the color of the module at the given coordinates, which must be in range. @@ -472,7 +465,7 @@ class QrCode final { private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); - // Can only be called immediately after a white run is added, and + // Can only be called immediately after a light run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). private: int finderPenaltyCountPatterns(const std::array &runHistory) const; diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/QrCodeGeneratorDemo.java old mode 100755 new mode 100644 similarity index 56% rename from java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java rename to java/QrCodeGeneratorDemo.java index 3c50bda..5a5035d --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/QrCodeGeneratorDemo.java @@ -24,8 +24,6 @@ * Software. */ -package io.nayuki.qrcodegen; - import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -33,7 +31,11 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Arrays; import java.util.List; +import java.util.Objects; import javax.imageio.ImageIO; +import io.nayuki.qrcodegen.QrCode; +import io.nayuki.qrcodegen.QrSegment; +import io.nayuki.qrcodegen.QrSegmentAdvanced; public final class QrCodeGeneratorDemo { @@ -57,13 +59,13 @@ public final class QrCodeGeneratorDemo { QrCode qr = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol - BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image + BufferedImage img = toImage(qr, 10, 4); // Convert to bitmap image File imgFile = new File("hello-world-QR.png"); // File path for output ImageIO.write(img, "png", imgFile); // Write image to file - String svg = qr.toSvgString(4); // Convert to SVG XML code - File svgFile = new File("hello-world-QR.svg"); // File path for output - Files.write(svgFile.toPath(), // Write image to file + String svg = toSvgString(qr, 4, "#FFFFFF", "#000000"); // Convert to SVG XML code + File svgFile = new File("hello-world-QR.svg"); // File path for output + Files.write(svgFile.toPath(), // Write image to file svg.getBytes(StandardCharsets.UTF_8)); } @@ -74,15 +76,15 @@ public final class QrCodeGeneratorDemo { // Numeric mode encoding (3.33 bits per digit) qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM); - writePng(qr.toImage(13, 1), "pi-digits-QR.png"); + writePng(toImage(qr, 13, 1), "pi-digits-QR.png"); // Alphanumeric mode encoding (5.5 bits per character) qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH); - writePng(qr.toImage(10, 2), "alphanumeric-QR.png"); + writePng(toImage(qr, 10, 2), "alphanumeric-QR.png"); // Unicode text as UTF-8 qr = QrCode.encodeText("こんにちwa、世界! αβγδ", QrCode.Ecc.QUARTILE); - writePng(qr.toImage(10, 3), "unicode-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-QR.png"); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) qr = QrCode.encodeText( @@ -93,7 +95,7 @@ public final class QrCodeGeneratorDemo { + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + "a White Rabbit with pink eyes ran close by her.", QrCode.Ecc.HIGH); - writePng(qr.toImage(6, 10), "alice-wonderland-QR.png"); + writePng(toImage(qr, 6, 10), "alice-wonderland-QR.png"); } @@ -106,36 +108,36 @@ public final class QrCodeGeneratorDemo { String silver0 = "THE SQUARE ROOT OF 2 IS 1."; String silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); - writePng(qr.toImage(10, 3), "sqrt2-monolithic-QR.png"); + writePng(toImage(qr, 10, 3), "sqrt2-monolithic-QR.png"); segs = Arrays.asList( QrSegment.makeAlphanumeric(silver0), QrSegment.makeNumeric(silver1)); qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - writePng(qr.toImage(10, 3), "sqrt2-segmented-QR.png"); + writePng(toImage(qr, 10, 3), "sqrt2-segmented-QR.png"); // Illustration "golden" String golden0 = "Golden ratio φ = 1."; String golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; String golden2 = "......"; qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW); - writePng(qr.toImage(8, 5), "phi-monolithic-QR.png"); + writePng(toImage(qr, 8, 5), "phi-monolithic-QR.png"); segs = Arrays.asList( QrSegment.makeBytes(golden0.getBytes(StandardCharsets.UTF_8)), QrSegment.makeNumeric(golden1), QrSegment.makeAlphanumeric(golden2)); qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - writePng(qr.toImage(8, 5), "phi-segmented-QR.png"); + writePng(toImage(qr, 8, 5), "phi-segmented-QR.png"); // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters String madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); - writePng(qr.toImage(9, 4), "madoka-utf8-QR.png"); + writePng(toImage(qr, 9, 4, 0xFFFFE0, 0x303080), "madoka-utf8-QR.png"); segs = Arrays.asList(QrSegmentAdvanced.makeKanji(madoka)); qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - writePng(qr.toImage(9, 4), "madoka-kanji-QR.png"); + writePng(toImage(qr, 9, 4, 0xE0F0FF, 0x404040), "madoka-kanji-QR.png"); } @@ -147,29 +149,108 @@ public final class QrCodeGeneratorDemo { // Project Nayuki URL segs = QrSegment.makeSegments("https://www.nayuki.io/"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask - writePng(qr.toImage(8, 6), "project-nayuki-automask-QR.png"); + writePng(toImage(qr, 8, 6, 0xE0FFE0, 0x206020), "project-nayuki-automask-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 - writePng(qr.toImage(8, 6), "project-nayuki-mask3-QR.png"); + writePng(toImage(qr, 8, 6, 0xFFE0E0, 0x602020), "project-nayuki-mask3-QR.png"); // Chinese text as UTF-8 segs = QrSegment.makeSegments("維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0 - writePng(qr.toImage(10, 3), "unicode-mask0-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-mask0-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1 - writePng(qr.toImage(10, 3), "unicode-mask1-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-mask1-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5 - writePng(qr.toImage(10, 3), "unicode-mask5-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-mask5-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7 - writePng(qr.toImage(10, 3), "unicode-mask7-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-mask7-QR.png"); } /*---- Utilities ----*/ + private static BufferedImage toImage(QrCode qr, int scale, int border) { + return toImage(qr, scale, border, 0xFFFFFF, 0x000000); + } + + + /** + * Returns a raster image depicting the specified QR Code, with + * the specified module scale, border modules, and module colors. + *

For example, scale=10 and border=4 means to pad the QR Code with 4 light border + * modules on all four sides, and use 10×10 pixels to represent each module. + * @param qr the QR Code to render (not {@code null}) + * @param scale the side length (measured in pixels, must be positive) of each module + * @param border the number of border modules to add, which must be non-negative + * @param lightColor the color to use for light modules, in 0xRRGGBB format + * @param darkColor the color to use for dark modules, in 0xRRGGBB format + * @return a new image representing the QR Code, with padding and scaling + * @throws NullPointerException if the QR Code is {@code null} + * @throws IllegalArgumentException if the scale or border is out of range, or if + * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE + */ + private static BufferedImage toImage(QrCode qr, int scale, int border, int lightColor, int darkColor) { + Objects.requireNonNull(qr); + if (scale <= 0 || border < 0) + throw new IllegalArgumentException("Value out of range"); + if (border > Integer.MAX_VALUE / 2 || qr.size + border * 2L > Integer.MAX_VALUE / scale) + throw new IllegalArgumentException("Scale or border too large"); + + BufferedImage result = new BufferedImage((qr.size + border * 2) * scale, (qr.size + border * 2) * scale, BufferedImage.TYPE_INT_RGB); + for (int y = 0; y < result.getHeight(); y++) { + for (int x = 0; x < result.getWidth(); x++) { + boolean color = qr.getModule(x / scale - border, y / scale - border); + result.setRGB(x, y, color ? darkColor : lightColor); + } + } + return result; + } + + // Helper function to reduce code duplication. private static void writePng(BufferedImage img, String filepath) throws IOException { ImageIO.write(img, "png", new File(filepath)); } + + /** + * Returns a string of SVG code for an image depicting the specified QR Code, with the specified + * number of border modules. The string always uses Unix newlines (\n), regardless of the platform. + * @param qr the QR Code to render (not {@code null}) + * @param border the number of border modules to add, which must be non-negative + * @param lightColor the color to use for light modules, in any format supported by CSS, not {@code null} + * @param darkColor the color to use for dark modules, in any format supported by CSS, not {@code null} + * @return a string representing the QR Code as an SVG XML document + * @throws NullPointerException if any object is {@code null} + * @throws IllegalArgumentException if the border is negative + */ + private static String toSvgString(QrCode qr, int border, String lightColor, String darkColor) { + Objects.requireNonNull(qr); + Objects.requireNonNull(lightColor); + Objects.requireNonNull(darkColor); + if (border < 0) + throw new IllegalArgumentException("Border must be non-negative"); + long brd = border; + StringBuilder sb = new StringBuilder() + .append("\n") + .append("\n") + .append(String.format("\n", + qr.size + brd * 2)) + .append("\t\n") + .append("\t\n") + .append("\n") + .toString(); + } + } diff --git a/java/pom.xml b/java/pom.xml index 0645a73..66b0343 100755 --- a/java/pom.xml +++ b/java/pom.xml @@ -4,16 +4,43 @@ io.nayuki qrcodegen - 1.6.0 + 1.7.0 jar UTF-8 - 1.8 - 1.8 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + default-compile + + 9 + + + + java8-compile + + compile + + + 1.8 + 1.8 + + module-info.java + + + + + + org.apache.maven.plugins maven-release-plugin @@ -23,6 +50,7 @@ + org.apache.maven.plugins maven-source-plugin @@ -36,6 +64,7 @@ + org.apache.maven.plugins maven-javadoc-plugin @@ -49,6 +78,7 @@ + org.apache.maven.plugins maven-gpg-plugin @@ -63,9 +93,11 @@ + + QR Code generator library High quality QR Code generator library https://www.nayuki.io/page/qr-code-generator-library diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index ea0196c..8e6ef09 100755 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -23,7 +23,6 @@ package io.nayuki.qrcodegen; -import java.awt.image.BufferedImage; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -32,7 +31,7 @@ import java.util.Objects; /** * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - *

Instances of this class represent an immutable square grid of black and white cells. + *

Instances of this class represent an immutable square grid of dark and light cells. * The class provides static factory functions to create a QR Code from text or binary data. * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.

@@ -222,7 +221,7 @@ public final class QrCode { // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = white, true = black). + // The modules of this QR Code (false = light, true = dark). // Immutable after constructor finishes. Accessed through getModule(). private boolean[][] modules; @@ -256,14 +255,14 @@ public final class QrCode { size = ver * 4 + 17; errorCorrectionLevel = Objects.requireNonNull(ecl); Objects.requireNonNull(dataCodewords); - modules = new boolean[size][size]; // Initially all white + modules = new boolean[size][size]; // Initially all light isFunction = new boolean[size][size]; // Compute ECC, draw modules, do masking drawFunctionPatterns(); byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); - this.mask = handleConstructorMasking(msk); + mask = handleConstructorMasking(msk); isFunction = null; } @@ -273,80 +272,18 @@ public final class QrCode { /** * Returns the color of the module (pixel) at the specified coordinates, which is {@code false} - * for white or {@code true} for black. The top left corner has the coordinates (x=0, y=0). - * If the specified coordinates are out of bounds, then {@code false} (white) is returned. + * for light or {@code true} for dark. The top left corner has the coordinates (x=0, y=0). + * If the specified coordinates are out of bounds, then {@code false} (light) is returned. * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge * @return {@code true} if the coordinates are in bounds and the module - * at that location is black, or {@code false} (white) otherwise + * at that location is dark, or {@code false} (light) otherwise */ public boolean getModule(int x, int y) { return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; } - /** - * Returns a raster image depicting this QR Code, with the specified module scale and border modules. - *

For example, toImage(scale=10, border=4) means to pad the QR Code with 4 white - * border modules on all four sides, and use 10×10 pixels to represent each module. - * The resulting image only contains the hex colors 000000 and FFFFFF. - * @param scale the side length (measured in pixels, must be positive) of each module - * @param border the number of border modules to add, which must be non-negative - * @return a new image representing this QR Code, with padding and scaling - * @throws IllegalArgumentException if the scale or border is out of range, or if - * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE - */ - public BufferedImage toImage(int scale, int border) { - if (scale <= 0 || border < 0) - throw new IllegalArgumentException("Value out of range"); - if (border > Integer.MAX_VALUE / 2 || size + border * 2L > Integer.MAX_VALUE / scale) - throw new IllegalArgumentException("Scale or border too large"); - - BufferedImage result = new BufferedImage((size + border * 2) * scale, (size + border * 2) * scale, BufferedImage.TYPE_INT_RGB); - for (int y = 0; y < result.getHeight(); y++) { - for (int x = 0; x < result.getWidth(); x++) { - boolean color = getModule(x / scale - border, y / scale - border); - result.setRGB(x, y, color ? 0x000000 : 0xFFFFFF); - } - } - return result; - } - - - /** - * Returns a string of SVG code for an image depicting this QR Code, with the specified number - * of border modules. The string always uses Unix newlines (\n), regardless of the platform. - * @param border the number of border modules to add, which must be non-negative - * @return a string representing this QR Code as an SVG XML document - * @throws IllegalArgumentException if the border is negative - */ - public String toSvgString(int border) { - if (border < 0) - throw new IllegalArgumentException("Border must be non-negative"); - long brd = border; - StringBuilder sb = new StringBuilder() - .append("\n") - .append("\n") - .append(String.format("\n", - size + brd * 2)) - .append("\t\n") - .append("\t\n") - .append("\n") - .toString(); - } - - /*---- Private helper methods for constructor: Drawing function modules ----*/ @@ -405,7 +342,7 @@ public final class QrCode { setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(bits, i)); - setFunctionModule(8, size - 8, true); // Always black + setFunctionModule(8, size - 8, true); // Always dark } @@ -459,8 +396,8 @@ public final class QrCode { // Sets the color of a module and marks it as a function module. // Only used by the constructor. Coordinates must be in bounds. - private void setFunctionModule(int x, int y, boolean isBlack) { - modules[y][x] = isBlack; + private void setFunctionModule(int x, int y, boolean isDark) { + modules[y][x] = isDark; isFunction[y][x] = true; } @@ -530,7 +467,7 @@ public final class QrCode { i++; } // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method + // 0/false/light by the constructor and are left unchanged by this method } } } @@ -651,17 +588,17 @@ public final class QrCode { } } - // Balance of black and white modules - int black = 0; + // Balance of dark and light modules + int dark = 0; for (boolean[] row : modules) { for (boolean color : row) { if (color) - black++; + dark++; } } - int total = size * size; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - int k = (Math.abs(black * 20 - total * 10) + total - 1) / total - 1; + int total = size * size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = (Math.abs(dark * 20 - total * 10) + total - 1) / total - 1; result += k * PENALTY_N4; return result; } @@ -681,8 +618,8 @@ public final class QrCode { int step; if (version == 32) // Special snowflake step = 26; - else // step = ceil[(size - 13) / (numAlign*2 - 2)] * 2 - step = (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + else // step = ceil[(size - 13) / (numAlign * 2 - 2)] * 2 + step = (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; int[] result = new int[numAlign]; result[0] = 6; for (int i = result.length - 1, pos = size - 7; i >= 1; i--, pos -= step) @@ -702,7 +639,7 @@ public final class QrCode { int size = ver * 4 + 17; int result = size * size; // Number of modules in the whole QR Code square result -= 8 * 8 * 3; // Subtract the three finders with separators - result -= 15 * 2 + 1; // Subtract the format information and black module + result -= 15 * 2 + 1; // Subtract the format information and dark module result -= (size - 16) * 2; // Subtract the timing patterns (excluding finders) // The five lines above are equivalent to: int result = (16 * ver + 128) * ver + 64; if (ver >= 2) { @@ -786,7 +723,7 @@ public final class QrCode { } - // Can only be called immediately after a white run is added, and + // Can only be called immediately after a light run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). private int finderPenaltyCountPatterns(int[] runHistory) { int n = runHistory[1]; @@ -799,11 +736,11 @@ public final class QrCode { // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). private int finderPenaltyTerminateAndCount(boolean currentRunColor, int currentRunLength, int[] runHistory) { - if (currentRunColor) { // Terminate black run + if (currentRunColor) { // Terminate dark run finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } - currentRunLength += size; // Add white border to final run + currentRunLength += size; // Add light border to final run finderPenaltyAddHistory(currentRunLength, runHistory); return finderPenaltyCountPatterns(runHistory); } @@ -812,7 +749,7 @@ public final class QrCode { // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). private void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) { if (runHistory[0] == 0) - currentRunLength += size; // Add white border to initial run + currentRunLength += size; // Add light border to initial run System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1); runHistory[0] = currentRunLength; } diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java deleted file mode 100755 index 4cf9de3..0000000 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * QR Code generator test worker (Java) - * - * This program reads data and encoding parameters from standard input and writes - * QR Code bitmaps to standard output. The I/O format is one integer per line. - * Run with no command line arguments. The program is intended for automated - * batch testing of end-to-end functionality of this QR Code generator library. - * - * 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. - */ - -package io.nayuki.qrcodegen; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.List; -import java.util.Scanner; - - -public final class QrCodeGeneratorWorker { - - public static void main(String[] args) { - // Set up input stream and start loop - try (Scanner input = new Scanner(System.in, "US-ASCII")) { - input.useDelimiter("\r\n|\n|\r"); - while (processCase(input)); - } - } - - - private static boolean processCase(Scanner input) { - // Read data length or exit - int length = input.nextInt(); - if (length == -1) - return false; - if (length > Short.MAX_VALUE) - throw new RuntimeException(); - - // Read data bytes - boolean isAscii = true; - byte[] data = new byte[length]; - for (int i = 0; i < data.length; i++) { - int b = input.nextInt(); - if (b < 0 || b > 255) - throw new RuntimeException(); - data[i] = (byte)b; - isAscii &= b < 128; - } - - // Read encoding parameters - int errCorLvl = input.nextInt(); - int minVersion = input.nextInt(); - int maxVersion = input.nextInt(); - int mask = input.nextInt(); - int boostEcl = input.nextInt(); - if (!(0 <= errCorLvl && errCorLvl <= 3) || !(-1 <= mask && mask <= 7) || (boostEcl >>> 1) != 0 - || !(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)) - throw new RuntimeException(); - - // Make segments for encoding - List segs; - if (isAscii) - segs = QrSegment.makeSegments(new String(data, StandardCharsets.US_ASCII)); - else - segs = Arrays.asList(QrSegment.makeBytes(data)); - - try { // Try to make QR Code symbol - QrCode qr = QrCode.encodeSegments(segs, QrCode.Ecc.values()[errCorLvl], minVersion, maxVersion, mask, boostEcl != 0); - // Print grid of modules - System.out.println(qr.version); - for (int y = 0; y < qr.size; y++) { - for (int x = 0; x < qr.size; x++) - System.out.println(qr.getModule(x, y) ? 1 : 0); - } - - } catch (DataTooLongException e) { - System.out.println(-1); - } - System.out.flush(); - return true; - } - -} diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrSegment.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegment.java index f416200..73ccbfa 100755 --- a/java/src/main/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrSegment.java @@ -74,7 +74,7 @@ public final class QrSegment { */ public static QrSegment makeNumeric(String digits) { Objects.requireNonNull(digits); - if (!NUMERIC_REGEX.matcher(digits).matches()) + if (!isNumeric(digits)) throw new IllegalArgumentException("String contains non-numeric characters"); BitBuffer bb = new BitBuffer(); @@ -98,7 +98,7 @@ public final class QrSegment { */ public static QrSegment makeAlphanumeric(String text) { Objects.requireNonNull(text); - if (!ALPHANUMERIC_REGEX.matcher(text).matches()) + if (!isAlphanumeric(text)) throw new IllegalArgumentException("String contains unencodable characters in alphanumeric mode"); BitBuffer bb = new BitBuffer(); @@ -127,9 +127,9 @@ public final class QrSegment { // Select the most efficient segment encoding automatically List result = new ArrayList<>(); if (text.equals("")); // Leave result empty - else if (NUMERIC_REGEX.matcher(text).matches()) + else if (isNumeric(text)) result.add(makeNumeric(text)); - else if (ALPHANUMERIC_REGEX.matcher(text).matches()) + else if (isAlphanumeric(text)) result.add(makeAlphanumeric(text)); else result.add(makeBytes(text.getBytes(StandardCharsets.UTF_8))); @@ -162,6 +162,33 @@ public final class QrSegment { } + /** + * Tests whether the specified string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + * @param text the string to test for encodability (not {@code null}) + * @return {@code true} iff each character is in the range 0 to 9. + * @throws NullPointerException if the string is {@code null} + * @see #makeNumeric(String) + */ + public static boolean isNumeric(String text) { + return NUMERIC_REGEX.matcher(text).matches(); + } + + + /** + * Tests whether the specified 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. + * @param text the string to test for encodability (not {@code null}) + * @return {@code true} iff each character is in the alphanumeric mode character set + * @throws NullPointerException if the string is {@code null} + * @see #makeAlphanumeric(String) + */ + public static boolean isAlphanumeric(String text) { + return ALPHANUMERIC_REGEX.matcher(text).matches(); + } + + /*---- Instance fields ----*/ @@ -231,18 +258,11 @@ public final class QrSegment { /*---- Constants ----*/ - /** Describes precisely all strings that are encodable in numeric mode. To test whether a - * string {@code s} is encodable: {@code boolean ok = NUMERIC_REGEX.matcher(s).matches();}. - * A string is encodable iff each character is in the range 0 to 9. - * @see #makeNumeric(String) */ - public static final Pattern NUMERIC_REGEX = Pattern.compile("[0-9]*"); + // Describes precisely all strings that are encodable in numeric mode. + private static final Pattern NUMERIC_REGEX = Pattern.compile("[0-9]*"); - /** Describes precisely all strings that are encodable in alphanumeric mode. To test whether a - * string {@code s} is encodable: {@code boolean ok = ALPHANUMERIC_REGEX.matcher(s).matches();}. - * 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. - * @see #makeAlphanumeric(String) */ - public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); + // Describes precisely all strings that are encodable in alphanumeric mode. + private static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); // The set of all legal characters in alphanumeric mode, where // each character value maps to the index in the string. diff --git a/java/src/main/java/io/nayuki/qrcodegen/package-info.java b/java/src/main/java/io/nayuki/qrcodegen/package-info.java index 124b438..04ff2cd 100755 --- a/java/src/main/java/io/nayuki/qrcodegen/package-info.java +++ b/java/src/main/java/io/nayuki/qrcodegen/package-info.java @@ -10,7 +10,7 @@ *

  • Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C

  • *
  • Significantly shorter code but more documentation comments compared to competing libraries

  • *
  • Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard

  • - *
  • Output formats: Raw modules/pixels of the QR symbol, SVG XML string, {@code BufferedImage} raster bitmap

  • + *
  • Output format: Raw modules/pixels of the QR symbol

  • *
  • Encodes numeric and special-alphanumeric text in less space than general text

  • *
  • Open source code under the permissive MIT License

  • * @@ -35,7 +35,7 @@ *import io.nayuki.qrcodegen.*; * *QrCode qr = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); - *BufferedImage img = qr.toImage(4, 10); + *BufferedImage img = toImage(qr, 4, 10); // See QrCodeGeneratorDemo *ImageIO.write(img, "png", new File("qr-code.png")); *

    Manual operation:

    *
    import java.util.List;
    diff --git a/java/src/main/java/module-info.java b/java/src/main/java/module-info.java
    new file mode 100644
    index 0000000..ded220a
    --- /dev/null
    +++ b/java/src/main/java/module-info.java
    @@ -0,0 +1,29 @@
    +/* 
    + * QR Code generator library (Java)
    + * 
    + * 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.
    + */
    +
    +
    +module io.nayuki.qrcodegen {
    +	
    +	exports io.nayuki.qrcodegen;
    +	
    +}
    diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py
    deleted file mode 100755
    index ef56b76..0000000
    --- a/python/qrcodegen-batch-test.py
    +++ /dev/null
    @@ -1,137 +0,0 @@
    -# 
    -# QR Code generator batch test (Python 3)
    -# 
    -# Runs various versions of the QR Code generator test worker as subprocesses,
    -# feeds each one the same random input, and compares their output for equality.
    -# 
    -# 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.
    -# 
    -
    -import itertools, random, subprocess, sys, time
    -if sys.version_info.major < 3:
    -	raise RuntimeError("Requires Python 3+")
    -
    -
    -CHILD_PROGRAMS = [
    -	["python2", "-B", "../python/qrcodegen-worker.py"],  # Python 2 program
    -	["python3", "-B", "../python/qrcodegen-worker.py"],  # Python 3 program
    -	["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"],  # Java program
    -	["node", "../typescript-javascript/qrcodegen-worker.js"],  # TypeScript program
    -	["../c/qrcodegen-worker"],  # C program
    -	["../cpp/QrCodeGeneratorWorker"],  # C++ program
    -	["../rust/target/debug/examples/qrcodegen-worker"],  # Rust program
    -]
    -
    -
    -subprocs = []
    -
    -def main():
    -	# Launch workers
    -	global subprocs
    -	try:
    -		for args in CHILD_PROGRAMS:
    -			subprocs.append(subprocess.Popen(args, universal_newlines=True,
    -				stdin=subprocess.PIPE, stdout=subprocess.PIPE))
    -	except FileNotFoundError:
    -		write_all(-1)
    -		raise
    -	
    -	# Check if any died
    -	time.sleep(0.3)
    -	if any(proc.poll() is not None for proc in subprocs):
    -		for proc in subprocs:
    -			if proc.poll() is None:
    -				print(-1, file=proc.stdin)
    -				proc.stdin.flush()
    -		sys.exit("Error: One or more workers failed to start")
    -	
    -	# Do tests
    -	for i in itertools.count():
    -		print("Trial {}: ".format(i), end="")
    -		do_trial()
    -		print()
    -
    -
    -def do_trial():
    -	mode = random.randrange(4)
    -	if mode == 0:  # Numeric
    -		length = round((2 * 7089) ** random.random())
    -		data = [random.randrange(48, 58) for _ in range(length)]
    -	elif mode == 1:  # Alphanumeric
    -		length = round((2 * 4296) ** random.random())
    -		data = [ord(random.choice("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")) for _ in range(length)]
    -	elif mode == 2:  # ASCII
    -		length = round((2 * 2953) ** random.random())
    -		data = [random.randrange(128) for _ in range(length)]
    -	elif mode == 3:  # Byte
    -		length = round((2 * 2953) ** random.random())
    -		data = [random.randrange(256) for _ in range(length)]
    -	else:
    -		raise AssertionError()
    -	
    -	write_all(length)
    -	for b in data:
    -		write_all(b)
    -	
    -	errcorlvl = random.randrange(4)
    -	minversion = random.randint(1, 40)
    -	maxversion = random.randint(1, 40)
    -	if minversion > maxversion:
    -		minversion, maxversion = maxversion, minversion
    -	mask = -1
    -	if random.random() < 0.5:
    -		mask = random.randrange(8)
    -	boostecl = int(random.random() < 0.2)
    -	print("mode={} len={} ecl={} minv={} maxv={} mask={} boost={}".format(mode, length, errcorlvl, minversion, maxversion, mask, boostecl), end="")
    -	
    -	write_all(errcorlvl)
    -	write_all(minversion)
    -	write_all(maxversion)
    -	write_all(mask)
    -	write_all(boostecl)
    -	flush_all()
    -	
    -	version = read_verify()
    -	print(" version={}".format(version), end="")
    -	if version == -1:
    -		return
    -	size = version * 4 + 17
    -	for _ in range(size**2):
    -		read_verify()
    -
    -
    -def write_all(val):
    -	for proc in subprocs:
    -		print(val, file=proc.stdin)
    -
    -def flush_all():
    -	for proc in subprocs:
    -		proc.stdin.flush()
    -
    -def read_verify():
    -	val = subprocs[0].stdout.readline().rstrip("\r\n")
    -	for proc in subprocs[1 : ]:
    -		if proc.stdout.readline().rstrip("\r\n") != val:
    -			raise ValueError("Mismatch")
    -	return int(val)
    -
    -
    -if __name__ == "__main__":
    -	main()
    diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py
    index e809a6f..ef6c098 100755
    --- a/python/qrcodegen-demo.py
    +++ b/python/qrcodegen-demo.py
    @@ -1,5 +1,5 @@
     # 
    -# QR Code generator demo (Python 2, 3)
    +# QR Code generator demo (Python)
     # 
     # Run this command-line program with no arguments. The program computes a bunch of demonstration
     # QR Codes and prints them to the console. Also, the SVG code for one QR Code is printed as a sample.
    @@ -24,11 +24,10 @@
     #   Software.
     # 
     
    -from __future__ import print_function
     from qrcodegen import QrCode, QrSegment
     
     
    -def main():
    +def main() -> None:
     	"""The main application program."""
     	do_basic_demo()
     	do_variety_demo()
    @@ -39,18 +38,18 @@ def main():
     
     # ---- Demo suite ----
     
    -def do_basic_demo():
    +def do_basic_demo() -> None:
     	"""Creates a single QR Code, then prints it to the console."""
    -	text = u"Hello, world!"     # User-supplied Unicode text
    +	text = "Hello, world!"      # User-supplied Unicode text
     	errcorlvl = QrCode.Ecc.LOW  # Error correction level
     	
     	# Make and print the QR Code symbol
     	qr = QrCode.encode_text(text, errcorlvl)
     	print_qr(qr)
    -	print(qr.to_svg_str(4))
    +	print(to_svg_str(qr, 4))
     
     
    -def do_variety_demo():
    +def do_variety_demo() -> None:
     	"""Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console."""
     	
     	# Numeric mode encoding (3.33 bits per digit)
    @@ -62,7 +61,7 @@ def do_variety_demo():
     	print_qr(qr)
     	
     	# Unicode text as UTF-8
    -	qr = QrCode.encode_text(u"\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE)
    +	qr = QrCode.encode_text("\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE)
     	print_qr(qr)
     	
     	# Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland)
    @@ -77,7 +76,7 @@ def do_variety_demo():
     	print_qr(qr)
     
     
    -def do_segment_demo():
    +def do_segment_demo() -> None:
     	"""Creates QR Codes with manually specified segments for better compactness."""
     	
     	# Illustration "silver"
    @@ -93,9 +92,9 @@ def do_segment_demo():
     	print_qr(qr)
     	
     	# Illustration "golden"
    -	golden0 = u"Golden ratio \u03C6 = 1."
    -	golden1 = u"6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"
    -	golden2 = u"......"
    +	golden0 = "Golden ratio \u03C6 = 1."
    +	golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"
    +	golden2 = "......"
     	qr = QrCode.encode_text(golden0 + golden1 + golden2, QrCode.Ecc.LOW)
     	print_qr(qr)
     	
    @@ -107,7 +106,7 @@ def do_segment_demo():
     	print_qr(qr)
     	
     	# Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters
    -	madoka = u"\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F"
    +	madoka = "\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F"
     	qr = QrCode.encode_text(madoka, QrCode.Ecc.LOW)
     	print_qr(qr)
     	
    @@ -147,7 +146,7 @@ def do_segment_demo():
     	print_qr(qr)
     
     
    -def do_mask_demo():
    +def do_mask_demo() -> None:
     	"""Creates QR Codes with the same size and contents but different mask patterns."""
     	
     	# Project Nayuki URL
    @@ -157,11 +156,11 @@ def do_mask_demo():
     	
     	# Chinese text as UTF-8
     	segs = QrSegment.make_segments(
    -		u"\u7DAD\u57FA\u767E\u79D1\uFF08\u0057\u0069\u006B\u0069\u0070\u0065\u0064\u0069\u0061\uFF0C"
    -		 "\u8046\u807D\u0069\u002F\u02CC\u0077\u026A\u006B\u1D7B\u02C8\u0070\u0069\u02D0\u0064\u0069"
    -		 "\u002E\u0259\u002F\uFF09\u662F\u4E00\u500B\u81EA\u7531\u5167\u5BB9\u3001\u516C\u958B\u7DE8"
    -		 "\u8F2F\u4E14\u591A\u8A9E\u8A00\u7684\u7DB2\u8DEF\u767E\u79D1\u5168\u66F8\u5354\u4F5C\u8A08"
    -		 "\u756B")
    +		"\u7DAD\u57FA\u767E\u79D1\uFF08\u0057\u0069\u006B\u0069\u0070\u0065\u0064\u0069\u0061\uFF0C"
    +		"\u8046\u807D\u0069\u002F\u02CC\u0077\u026A\u006B\u1D7B\u02C8\u0070\u0069\u02D0\u0064\u0069"
    +		"\u002E\u0259\u002F\uFF09\u662F\u4E00\u500B\u81EA\u7531\u5167\u5BB9\u3001\u516C\u958B\u7DE8"
    +		"\u8F2F\u4E14\u591A\u8A9E\u8A00\u7684\u7DB2\u8DEF\u767E\u79D1\u5168\u66F8\u5354\u4F5C\u8A08"
    +		"\u756B")
     	print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=0))  # Force mask 0
     	print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=1))  # Force mask 1
     	print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=5))  # Force mask 5
    @@ -171,12 +170,31 @@ def do_mask_demo():
     
     # ---- Utilities ----
     
    -def print_qr(qrcode):
    +def to_svg_str(qr: QrCode, border: int) -> str:
    +	"""Returns a string of SVG code for an image depicting the given QR Code, with the given number
    +	of border modules. The string always uses Unix newlines (\n), regardless of the platform."""
    +	if border < 0:
    +		raise ValueError("Border must be non-negative")
    +	parts: List[str] = []
    +	for y in range(qr.get_size()):
    +		for x in range(qr.get_size()):
    +			if qr.get_module(x, y):
    +				parts.append("M{},{}h1v1h-1z".format(x + border, y + border))
    +	return """
    +
    +
    +	
    +	
    +
    +""".format(qr.get_size() + border * 2, " ".join(parts))
    +
    +
    +def print_qr(qrcode: QrCode) -> None:
     	"""Prints the given QrCode object to the console."""
     	border = 4
     	for y in range(-border, qrcode.get_size() + border):
     		for x in range(-border, qrcode.get_size() + border):
    -			print(u"\u2588 "[1 if qrcode.get_module(x,y) else 0] * 2, end="")
    +			print("\u2588 "[1 if qrcode.get_module(x,y) else 0] * 2, end="")
     		print()
     	print()
     
    diff --git a/python/qrcodegen-worker.py b/python/qrcodegen-worker.py
    deleted file mode 100755
    index b159bb6..0000000
    --- a/python/qrcodegen-worker.py
    +++ /dev/null
    @@ -1,82 +0,0 @@
    -# 
    -# QR Code generator test worker (Python 2, 3)
    -# 
    -# This program reads data and encoding parameters from standard input and writes
    -# QR Code bitmaps to standard output. The I/O format is one integer per line.
    -# Run with no command line arguments. The program is intended for automated
    -# batch testing of end-to-end functionality of this QR Code generator library.
    -# 
    -# 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.
    -# 
    -
    -import sys
    -import qrcodegen
    -py3 = sys.version_info.major >= 3
    -
    -
    -def read_int():
    -	return int((input if py3 else raw_input)())
    -
    -
    -def main():
    -	while True:
    -		
    -		# Read data or exit
    -		length = read_int()
    -		if length == -1:
    -			break
    -		data = bytearray(read_int() for _ in range(length))
    -		
    -		# Read encoding parameters
    -		errcorlvl  = read_int()
    -		minversion = read_int()
    -		maxversion = read_int()
    -		mask       = read_int()
    -		boostecl   = read_int()
    -		
    -		# Make segments for encoding
    -		if all((b < 128) for b in data):  # Is ASCII
    -			segs = qrcodegen.QrSegment.make_segments(data.decode("ASCII"))
    -		else:
    -			segs = [qrcodegen.QrSegment.make_bytes(data)]
    -		
    -		try:  # Try to make QR Code symbol
    -			qr = qrcodegen.QrCode.encode_segments(segs, ECC_LEVELS[errcorlvl], minversion, maxversion, mask, boostecl != 0)
    -			# Print grid of modules
    -			print(qr.get_version())
    -			for y in range(qr.get_size()):
    -				for x in range(qr.get_size()):
    -					print(1 if qr.get_module(x, y) else 0)
    -			
    -		except qrcodegen.DataTooLongError:
    -			print(-1)
    -		sys.stdout.flush()
    -
    -
    -ECC_LEVELS = (
    -	qrcodegen.QrCode.Ecc.LOW,
    -	qrcodegen.QrCode.Ecc.MEDIUM,
    -	qrcodegen.QrCode.Ecc.QUARTILE,
    -	qrcodegen.QrCode.Ecc.HIGH,
    -)
    -
    -
    -if __name__ == "__main__":
    -	main()
    diff --git a/python/qrcodegen.py b/python/qrcodegen.py
    index 260ace8..dba11ca 100755
    --- a/python/qrcodegen.py
    +++ b/python/qrcodegen.py
    @@ -1,5 +1,5 @@
     # 
    -# QR Code generator library (Python 2, 3)
    +# QR Code generator library (Python)
     # 
     # Copyright (c) Project Nayuki. (MIT License)
     # https://www.nayuki.io/page/qr-code-generator-library
    @@ -21,49 +21,18 @@
     #   Software.
     # 
     
    -import collections, itertools, re, sys
    -
    -
    -"""
    -This module "qrcodegen", public members:
    -- Class QrCode:
    -  - Function encode_text(str text, QrCode.Ecc ecl) -> QrCode
    -  - Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode
    -  - Function encode_segments(list segs, QrCode.Ecc ecl,
    -        int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode
    -  - Constants int MIN_VERSION, MAX_VERSION
    -  - Constructor QrCode(int version, QrCode.Ecc ecl, bytes datacodewords, int mask)
    -  - Method get_version() -> int
    -  - Method get_size() -> int
    -  - Method get_error_correction_level() -> QrCode.Ecc
    -  - Method get_mask() -> int
    -  - Method get_module(int x, int y) -> bool
    -  - Method to_svg_str(int border) -> str
    -  - Enum Ecc:
    -    - Constants LOW, MEDIUM, QUARTILE, HIGH
    -    - Field int ordinal
    -- Class QrSegment:
    -  - Function make_bytes(bytes data) -> QrSegment
    -  - 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_data() -> list
    -  - Constants regex NUMERIC_REGEX, ALPHANUMERIC_REGEX
    -  - Enum Mode:
    -    - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI
    -"""
    +from __future__ import annotations
    +import collections, itertools, re
    +from collections.abc import Sequence
    +from typing import Callable, Dict, List, Optional, Tuple, Union
     
     
     # ---- QR Code symbol class ----
     
    -class QrCode(object):
    +class QrCode:
     	"""A QR Code symbol, which is a type of two-dimension barcode.
     	Invented by Denso Wave and described in the ISO/IEC 18004 standard.
    -	Instances of this class represent an immutable square grid of black and white cells.
    +	Instances of this class represent an immutable square grid of dark and light cells.
     	The class provides static factory functions to create a QR Code from text or binary data.
     	The class covers the QR Code Model 2 specification, supporting all versions (sizes)
     	from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
    @@ -79,31 +48,29 @@ class QrCode(object):
     	# ---- Static factory functions (high level) ----
     	
     	@staticmethod
    -	def encode_text(text, ecl):
    +	def encode_text(text: str, ecl: QrCode.Ecc) -> QrCode:
     		"""Returns a QR Code 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) if the low error correction level is used. The smallest possible
     		QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the
     		ecl argument if it can be done without increasing the version."""
    -		segs = QrSegment.make_segments(text)
    +		segs: List[QrSegment] = QrSegment.make_segments(text)
     		return QrCode.encode_segments(segs, ecl)
     	
     	
     	@staticmethod
    -	def encode_binary(data, ecl):
    +	def encode_binary(data: Union[bytes,Sequence[int]], ecl: QrCode.Ecc) -> QrCode:
     		"""Returns a QR Code representing the given binary data at the given error correction level.
     		This function always encodes using the binary segment mode, not any text mode. The maximum number of
     		bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
     		The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version."""
    -		if not isinstance(data, (bytes, bytearray)):
    -			raise TypeError("Byte string/list expected")
     		return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl)
     	
     	
     	# ---- Static factory functions (mid level) ----
     	
     	@staticmethod
    -	def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True):
    +	def encode_segments(segs: Sequence[QrSegment], ecl: QrCode.Ecc, minversion: int = 1, maxversion: int = 40, mask: int = -1, boostecl: bool = True) -> QrCode:
     		"""Returns a QR Code representing the given segments with the given encoding parameters.
     		The smallest possible QR Code version within the given range is automatically
     		chosen for the output. Iff boostecl is true, then the ECC level of the result
    @@ -119,12 +86,12 @@ class QrCode(object):
     		
     		# Find the minimal version number to use
     		for version in range(minversion, maxversion + 1):
    -			datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8  # Number of data bits available
    -			datausedbits = QrSegment.get_total_bits(segs, version)
    -			if datausedbits is not None and datausedbits <= datacapacitybits:
    +			datacapacitybits: int = QrCode._get_num_data_codewords(version, ecl) * 8  # Number of data bits available
    +			datausedbits: Optional[int] = QrSegment.get_total_bits(segs, version)
    +			if (datausedbits is not None) and (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
    -				msg = "Segment too long"
    +				msg: str = "Segment too long"
     				if datausedbits is not None:
     					msg = "Data length = {} bits, Max capacity = {} bits".format(datausedbits, datacapacitybits)
     				raise DataTooLongError(msg)
    @@ -133,7 +100,7 @@ class QrCode(object):
     		
     		# Increase the error correction level while the data still fits in the current version number
     		for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH):  # From low to high
    -			if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8:
    +			if boostecl and (datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8):
     				ecl = newecl
     		
     		# Concatenate all segments to create the data bit string
    @@ -158,7 +125,7 @@ class QrCode(object):
     			bb.append_bits(padbyte, 8)
     		
     		# Pack bits into bytes in big endian
    -		datacodewords = [0] * (len(bb) // 8)
    +		datacodewords = bytearray([0] * (len(bb) // 8))
     		for (i, bit) in enumerate(bb):
     			datacodewords[i >> 3] |= bit << (7 - (i & 7))
     		
    @@ -166,9 +133,35 @@ class QrCode(object):
     		return QrCode(version, ecl, datacodewords, mask)
     	
     	
    +	# ---- Private fields ----
    +	
    +	# The version number of this QR Code, which is between 1 and 40 (inclusive).
    +	# This determines the size of this barcode.
    +	_version: int
    +	
    +	# The width and height of this QR Code, measured in modules, between
    +	# 21 and 177 (inclusive). This is equal to version * 4 + 17.
    +	_size: int
    +	
    +	# The error correction level used in this QR Code.
    +	_errcorlvl: QrCode.Ecc
    +	
    +	# The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
    +	# Even if a QR Code is created with automatic masking requested (mask = -1),
    +	# the resulting object still has a mask value between 0 and 7.
    +	_mask: int
    +	
    +	# The modules of this QR Code (False = light, True = dark).
    +	# Immutable after constructor finishes. Accessed through get_module().
    +	_modules: List[List[bool]]
    +	
    +	# Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
    +	_isfunction: List[List[bool]]
    +	
    +	
     	# ---- Constructor (low level) ----
     	
    -	def __init__(self, version, errcorlvl, datacodewords, mask):
    +	def __init__(self, version: int, errcorlvl: QrCode.Ecc, datacodewords: Union[bytes,Sequence[int]], mask: int) -> None:
     		"""Creates a new QR Code with the given version number,
     		error correction level, data codeword bytes, and mask number.
     		This is a low-level API that most users should not use directly.
    @@ -182,32 +175,22 @@ class QrCode(object):
     		if not isinstance(errcorlvl, QrCode.Ecc):
     			raise TypeError("QrCode.Ecc expected")
     		
    -		# The version number of this QR Code, which is between 1 and 40 (inclusive).
    -		# This determines the size of this barcode.
     		self._version = version
    -		
    -		# The width and height of this QR Code, measured in modules, between
    -		# 21 and 177 (inclusive). This is equal to version * 4 + 17.
     		self._size = version * 4 + 17
    -		
    -		# The error correction level used in this QR Code.
     		self._errcorlvl = errcorlvl
     		
     		# Initialize both grids to be size*size arrays of Boolean false
    -		# The modules of this QR Code (False = white, True = black).
    -		# Immutable after constructor finishes. Accessed through get_module().
    -		self._modules    = [[False] * self._size for _ in range(self._size)]  # Initially all white
    -		# Indicates function modules that are not subjected to masking. Discarded when constructor finishes
    +		self._modules    = [[False] * self._size for _ in range(self._size)]  # Initially all light
     		self._isfunction = [[False] * self._size for _ in range(self._size)]
     		
     		# Compute ECC, draw modules
     		self._draw_function_patterns()
    -		allcodewords = self._add_ecc_and_interleave(datacodewords)
    +		allcodewords: bytes = self._add_ecc_and_interleave(bytearray(datacodewords))
     		self._draw_codewords(allcodewords)
     		
     		# Do masking
     		if mask == -1:  # Automatically choose best mask
    -			minpenalty = 1 << 32
    +			minpenalty: int = 1 << 32
     			for i in range(8):
     				self._apply_mask(i)
     				self._draw_format_bits(i)
    @@ -219,10 +202,6 @@ class QrCode(object):
     		assert 0 <= mask <= 7
     		self._apply_mask(mask)  # Apply the final choice of mask
     		self._draw_format_bits(mask)  # Overwrite old format bits
    -		
    -		# The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
    -		# Even if a QR Code is created with automatic masking requested (mask = -1),
    -		# the resulting object still has a mask value between 0 and 7.
     		self._mask = mask
     		
     		del self._isfunction
    @@ -230,53 +209,32 @@ class QrCode(object):
     	
     	# ---- Accessor methods ----
     	
    -	def get_version(self):
    +	def get_version(self) -> int:
     		"""Returns this QR Code's version number, in the range [1, 40]."""
     		return self._version
     	
    -	def get_size(self):
    +	def get_size(self) -> int:
     		"""Returns this QR Code's size, in the range [21, 177]."""
     		return self._size
     	
    -	def get_error_correction_level(self):
    +	def get_error_correction_level(self) -> QrCode.Ecc:
     		"""Returns this QR Code's error correction level."""
     		return self._errcorlvl
     	
    -	def get_mask(self):
    +	def get_mask(self) -> int:
     		"""Returns this QR Code's mask, in the range [0, 7]."""
     		return self._mask
     	
    -	def get_module(self, x, y):
    +	def get_module(self, x: int, y: int) -> bool:
     		"""Returns the color of the module (pixel) at the given coordinates, which is False
    -		for white or True for black. The top left corner has the coordinates (x=0, y=0).
    -		If the given coordinates are out of bounds, then False (white) is returned."""
    +		for light or True for dark. The top left corner has the coordinates (x=0, y=0).
    +		If the given coordinates are out of bounds, then False (light) is returned."""
     		return (0 <= x < self._size) and (0 <= y < self._size) and self._modules[y][x]
     	
     	
    -	# ---- Public instance methods ----
    -	
    -	def to_svg_str(self, border):
    -		"""Returns a string of SVG code for an image depicting this QR Code, with the given number
    -		of border modules. The string always uses Unix newlines (\n), regardless of the platform."""
    -		if border < 0:
    -			raise ValueError("Border must be non-negative")
    -		parts = []
    -		for y in range(self._size):
    -			for x in range(self._size):
    -				if self.get_module(x, y):
    -					parts.append("M{},{}h1v1h-1z".format(x + border, y + border))
    -		return """
    -
    -
    -	
    -	
    -
    -""".format(self._size + border * 2, " ".join(parts))
    -	
    -	
     	# ---- Private helper methods for constructor: Drawing function modules ----
     	
    -	def _draw_function_patterns(self):
    +	def _draw_function_patterns(self) -> None:
     		"""Reads this object's version field, and draws and marks all function modules."""
     		# Draw horizontal and vertical timing patterns
     		for i in range(self._size):
    @@ -289,9 +247,9 @@ class QrCode(object):
     		self._draw_finder_pattern(3, self._size - 4)
     		
     		# Draw numerous alignment patterns
    -		alignpatpos = self._get_alignment_pattern_positions()
    -		numalign = len(alignpatpos)
    -		skips = ((0, 0), (0, numalign - 1), (numalign - 1, 0))
    +		alignpatpos: List[int] = self._get_alignment_pattern_positions()
    +		numalign: int = len(alignpatpos)
    +		skips: Sequence[Tuple[int,int]] = ((0, 0), (0, numalign - 1), (numalign - 1, 0))
     		for i in range(numalign):
     			for j in range(numalign):
     				if (i, j) not in skips:  # Don't draw on the three finder corners
    @@ -302,15 +260,15 @@ class QrCode(object):
     		self._draw_version()
     	
     	
    -	def _draw_format_bits(self, mask):
    +	def _draw_format_bits(self, mask: int) -> None:
     		"""Draws two copies of the format bits (with its own error correction code)
     		based on the given mask and this object's error correction level field."""
     		# Calculate error correction code and pack bits
    -		data = self._errcorlvl.formatbits << 3 | mask  # errCorrLvl is uint2, mask is uint3
    -		rem = data
    +		data: int = self._errcorlvl.formatbits << 3 | mask  # errCorrLvl is uint2, mask is uint3
    +		rem: int = data
     		for _ in range(10):
     			rem = (rem << 1) ^ ((rem >> 9) * 0x537)
    -		bits = (data << 10 | rem) ^ 0x5412  # uint15
    +		bits: int = (data << 10 | rem) ^ 0x5412  # uint15
     		assert bits >> 15 == 0
     		
     		# Draw first copy
    @@ -327,32 +285,32 @@ class QrCode(object):
     			self._set_function_module(self._size - 1 - i, 8, _get_bit(bits, i))
     		for i in range(8, 15):
     			self._set_function_module(8, self._size - 15 + i, _get_bit(bits, i))
    -		self._set_function_module(8, self._size - 8, True)  # Always black
    +		self._set_function_module(8, self._size - 8, True)  # Always dark
     	
     	
    -	def _draw_version(self):
    +	def _draw_version(self) -> None:
     		"""Draws two copies of the version bits (with its own error correction code),
     		based on this object's version field, iff 7 <= version <= 40."""
     		if self._version < 7:
     			return
     		
     		# Calculate error correction code and pack bits
    -		rem = self._version  # version is uint6, in the range [7, 40]
    +		rem: int = self._version  # version is uint6, in the range [7, 40]
     		for _ in range(12):
     			rem = (rem << 1) ^ ((rem >> 11) * 0x1F25)
    -		bits = self._version << 12 | rem  # uint18
    +		bits: int = self._version << 12 | rem  # uint18
     		assert bits >> 18 == 0
     		
     		# Draw two copies
     		for i in range(18):
    -			bit = _get_bit(bits, i)
    -			a = self._size - 11 + i % 3
    -			b = i // 3
    +			bit: bool = _get_bit(bits, i)
    +			a: int = self._size - 11 + i % 3
    +			b: int = i // 3
     			self._set_function_module(a, b, bit)
     			self._set_function_module(b, a, bit)
     	
     	
    -	def _draw_finder_pattern(self, x, y):
    +	def _draw_finder_pattern(self, x: int, y: int) -> None:
     		"""Draws a 9*9 finder pattern including the border separator,
     		with the center module at (x, y). Modules can be out of bounds."""
     		for dy in range(-4, 5):
    @@ -363,7 +321,7 @@ class QrCode(object):
     					self._set_function_module(xx, yy, max(abs(dx), abs(dy)) not in (2, 4))
     	
     	
    -	def _draw_alignment_pattern(self, x, y):
    +	def _draw_alignment_pattern(self, x: int, y: int) -> None:
     		"""Draws a 5*5 alignment pattern, with the center module
     		at (x, y). All modules must be in bounds."""
     		for dy in range(-2, 3):
    @@ -371,101 +329,101 @@ class QrCode(object):
     				self._set_function_module(x + dx, y + dy, max(abs(dx), abs(dy)) != 1)
     	
     	
    -	def _set_function_module(self, x, y, isblack):
    +	def _set_function_module(self, x: int, y: int, isdark: bool) -> None:
     		"""Sets the color of a module and marks it as a function module.
     		Only used by the constructor. Coordinates must be in bounds."""
    -		assert type(isblack) is bool
    -		self._modules[y][x] = isblack
    +		assert type(isdark) is bool
    +		self._modules[y][x] = isdark
     		self._isfunction[y][x] = True
     	
     	
     	# ---- Private helper methods for constructor: Codewords and masking ----
     	
    -	def _add_ecc_and_interleave(self, data):
    +	def _add_ecc_and_interleave(self, data: bytearray) -> bytes:
     		"""Returns a new byte string representing the given data with the appropriate error correction
     		codewords appended to it, based on this object's version and error correction level."""
    -		version = self._version
    +		version: int = self._version
     		assert len(data) == QrCode._get_num_data_codewords(version, self._errcorlvl)
     		
     		# Calculate parameter numbers
    -		numblocks = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version]
    -		blockecclen = QrCode._ECC_CODEWORDS_PER_BLOCK  [self._errcorlvl.ordinal][version]
    -		rawcodewords = QrCode._get_num_raw_data_modules(version) // 8
    -		numshortblocks = numblocks - rawcodewords % numblocks
    -		shortblocklen = rawcodewords // numblocks
    +		numblocks: int = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version]
    +		blockecclen: int = QrCode._ECC_CODEWORDS_PER_BLOCK  [self._errcorlvl.ordinal][version]
    +		rawcodewords: int = QrCode._get_num_raw_data_modules(version) // 8
    +		numshortblocks: int = numblocks - rawcodewords % numblocks
    +		shortblocklen: int = rawcodewords // numblocks
     		
     		# Split data into blocks and append ECC to each block
    -		blocks = []
    -		rsdiv = QrCode._reed_solomon_compute_divisor(blockecclen)
    -		k = 0
    +		blocks: List[bytes] = []
    +		rsdiv: bytes = QrCode._reed_solomon_compute_divisor(blockecclen)
    +		k: int = 0
     		for i in range(numblocks):
    -			dat = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)]
    +			dat: bytearray = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)]
     			k += len(dat)
    -			ecc = QrCode._reed_solomon_compute_remainder(dat, rsdiv)
    +			ecc: bytes = QrCode._reed_solomon_compute_remainder(dat, rsdiv)
     			if i < numshortblocks:
     				dat.append(0)
     			blocks.append(dat + ecc)
     		assert k == len(data)
     		
     		# Interleave (not concatenate) the bytes from every block into a single sequence
    -		result = []
    +		result = bytearray()
     		for i in range(len(blocks[0])):
     			for (j, blk) in enumerate(blocks):
     				# Skip the padding byte in short blocks
    -				if i != shortblocklen - blockecclen or j >= numshortblocks:
    +				if (i != shortblocklen - blockecclen) or (j >= numshortblocks):
     					result.append(blk[i])
     		assert len(result) == rawcodewords
     		return result
     	
     	
    -	def _draw_codewords(self, data):
    +	def _draw_codewords(self, data: bytes) -> None:
     		"""Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
     		data area of this QR Code. Function modules need to be marked off before this is called."""
     		assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8
     		
    -		i = 0  # Bit index into the data
    +		i: int = 0  # Bit index into the data
     		# Do the funny zigzag scan
     		for right in range(self._size - 1, 0, -2):  # Index of right column in each column pair
     			if right <= 6:
     				right -= 1
     			for vert in range(self._size):  # Vertical counter
     				for j in range(2):
    -					x = right - j  # Actual x coordinate
    -					upward = (right + 1) & 2 == 0
    -					y = (self._size - 1 - vert) if upward else vert  # Actual y coordinate
    -					if not self._isfunction[y][x] and i < len(data) * 8:
    +					x: int = right - j  # Actual x coordinate
    +					upward: bool = (right + 1) & 2 == 0
    +					y: int = (self._size - 1 - vert) if upward else vert  # Actual y coordinate
    +					if (not self._isfunction[y][x]) and (i < len(data) * 8):
     						self._modules[y][x] = _get_bit(data[i >> 3], 7 - (i & 7))
     						i += 1
     					# If this QR Code has any remainder bits (0 to 7), they were assigned as
    -					# 0/false/white by the constructor and are left unchanged by this method
    +					# 0/false/light by the constructor and are left unchanged by this method
     		assert i == len(data) * 8
     	
     	
    -	def _apply_mask(self, mask):
    +	def _apply_mask(self, mask: int) -> None:
     		"""XORs the codeword modules in this QR Code with the given mask pattern.
     		The function modules must be marked and the codeword bits must be drawn
    -		before masking. Due to the arithmetic of XOR, calling applyMask() with
    +		before masking. Due to the arithmetic of XOR, calling _apply_mask() with
     		the same mask value a second time will undo the mask. A final well-formed
     		QR Code needs exactly one (not zero, two, etc.) mask applied."""
     		if not (0 <= mask <= 7):
     			raise ValueError("Mask value out of range")
    -		masker = QrCode._MASK_PATTERNS[mask]
    +		masker: Callable[[int,int],int] = QrCode._MASK_PATTERNS[mask]
     		for y in range(self._size):
     			for x in range(self._size):
     				self._modules[y][x] ^= (masker(x, y) == 0) and (not self._isfunction[y][x])
     	
     	
    -	def _get_penalty_score(self):
    +	def _get_penalty_score(self) -> int:
     		"""Calculates and returns the penalty score based on state of this QR Code's current modules.
     		This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score."""
    -		result = 0
    -		size = self._size
    -		modules = self._modules
    +		result: int = 0
    +		size: int = self._size
    +		modules: List[List[bool]] = self._modules
     		
     		# Adjacent modules in row having same color, and finder-like patterns
     		for y in range(size):
    -			runcolor = False
    -			runx = 0
    +			runcolor: bool = False
    +			runx: int = 0
     			runhistory = collections.deque([0] * 7, 7)
     			for x in range(size):
     				if modules[y][x] == runcolor:
    @@ -507,42 +465,42 @@ class QrCode(object):
     				if modules[y][x] == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]:
     					result += QrCode._PENALTY_N2
     		
    -		# Balance of black and white modules
    -		black = sum((1 if cell else 0) for row in modules for cell in row)
    -		total = size**2  # Note that size is odd, so black/total != 1/2
    -		# Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)%
    -		k = (abs(black * 20 - total * 10) + total - 1) // total - 1
    +		# Balance of dark and light modules
    +		dark: int = sum((1 if cell else 0) for row in modules for cell in row)
    +		total: int = size**2  # Note that size is odd, so dark/total != 1/2
    +		# Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%
    +		k: int = (abs(dark * 20 - total * 10) + total - 1) // total - 1
     		result += k * QrCode._PENALTY_N4
     		return result
     	
     	
     	# ---- Private helper functions ----
     	
    -	def _get_alignment_pattern_positions(self):
    +	def _get_alignment_pattern_positions(self) -> List[int]:
     		"""Returns an ascending list of positions of alignment patterns for this version number.
     		Each position is in the range [0,177), and are used on both the x and y axes.
     		This could be implemented as lookup table of 40 variable-length lists of integers."""
    -		ver = self._version
    +		ver: int = self._version
     		if ver == 1:
     			return []
     		else:
    -			numalign = ver // 7 + 2
    -			step = 26 if (ver == 32) else \
    -				(ver*4 + numalign*2 + 1) // (numalign*2 - 2) * 2
    -			result = [(self._size - 7 - i * step) for i in range(numalign - 1)] + [6]
    +			numalign: int = ver // 7 + 2
    +			step: int = 26 if (ver == 32) else \
    +				(ver * 4 + numalign * 2 + 1) // (numalign * 2 - 2) * 2
    +			result: List[int] = [(self._size - 7 - i * step) for i in range(numalign - 1)] + [6]
     			return list(reversed(result))
     	
     	
     	@staticmethod
    -	def _get_num_raw_data_modules(ver):
    +	def _get_num_raw_data_modules(ver: int) -> int:
     		"""Returns the number of data bits that can be stored in a QR Code of the given version number, after
     		all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
     		The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table."""
     		if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION):
     			raise ValueError("Version number out of range")
    -		result = (16 * ver + 128) * ver + 64
    +		result: int = (16 * ver + 128) * ver + 64
     		if ver >= 2:
    -			numalign = ver // 7 + 2
    +			numalign: int = ver // 7 + 2
     			result -= (25 * numalign - 10) * numalign - 55
     			if ver >= 7:
     				result -= 36
    @@ -551,7 +509,7 @@ class QrCode(object):
     	
     	
     	@staticmethod
    -	def _get_num_data_codewords(ver, ecl):
    +	def _get_num_data_codewords(ver: int, ecl: QrCode.Ecc) -> int:
     		"""Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
     		QR Code of the given version number and error correction level, with remainder bits discarded.
     		This stateless pure function could be implemented as a (40*4)-cell lookup table."""
    @@ -561,19 +519,19 @@ class QrCode(object):
     	
     	
     	@staticmethod
    -	def _reed_solomon_compute_divisor(degree):
    +	def _reed_solomon_compute_divisor(degree: int) -> bytes:
     		"""Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be
     		implemented as a lookup table over all possible parameter values, instead of as an algorithm."""
     		if not (1 <= degree <= 255):
     			raise ValueError("Degree out of range")
     		# Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
     		# For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93].
    -		result = [0] * (degree - 1) + [1]  # Start off with the monomial x^0
    +		result = bytearray([0] * (degree - 1) + [1])  # Start off with the monomial x^0
     		
     		# Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
     		# and drop the highest monomial term which is always 1x^degree.
     		# Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
    -		root = 1
    +		root: int = 1
     		for _ in range(degree):  # Unused variable i
     			# Multiply the current product by (x - r^i)
     			for j in range(degree):
    @@ -585,11 +543,11 @@ class QrCode(object):
     	
     	
     	@staticmethod
    -	def _reed_solomon_compute_remainder(data, divisor):
    +	def _reed_solomon_compute_remainder(data: bytes, divisor: bytes) -> bytes:
     		"""Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials."""
    -		result = [0] * len(divisor)
    +		result = bytearray([0] * len(divisor))
     		for b in data:  # Polynomial division
    -			factor = b ^ result.pop(0)
    +			factor: int = b ^ result.pop(0)
     			result.append(0)
     			for (i, coef) in enumerate(divisor):
     				result[i] ^= QrCode._reed_solomon_multiply(coef, factor)
    @@ -597,13 +555,13 @@ class QrCode(object):
     	
     	
     	@staticmethod
    -	def _reed_solomon_multiply(x, y):
    +	def _reed_solomon_multiply(x: int, y: int) -> int:
     		"""Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result
     		are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8."""
    -		if x >> 8 != 0 or y >> 8 != 0:
    +		if (x >> 8 != 0) or (y >> 8 != 0):
     			raise ValueError("Byte out of range")
     		# Russian peasant multiplication
    -		z = 0
    +		z: int = 0
     		for i in reversed(range(8)):
     			z = (z << 1) ^ ((z >> 7) * 0x11D)
     			z ^= ((y >> i) & 1) * x
    @@ -611,60 +569,60 @@ class QrCode(object):
     		return z
     	
     	
    -	def _finder_penalty_count_patterns(self, runhistory):
    -		"""Can only be called immediately after a white run is added, and
    +	def _finder_penalty_count_patterns(self, runhistory: collections.deque) -> int:
    +		"""Can only be called immediately after a light run is added, and
     		returns either 0, 1, or 2. A helper function for _get_penalty_score()."""
    -		n = runhistory[1]
    +		n: int = runhistory[1]
     		assert n <= self._size * 3
    -		core = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3
    +		core: bool = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3
     		return (1 if (core and runhistory[0] >= n * 4 and runhistory[6] >= n) else 0) \
     		     + (1 if (core and runhistory[6] >= n * 4 and runhistory[0] >= n) else 0)
     	
     	
    -	def _finder_penalty_terminate_and_count(self, currentruncolor, currentrunlength, runhistory):
    +	def _finder_penalty_terminate_and_count(self, currentruncolor: bool, currentrunlength: int, runhistory: collections.deque) -> int:
     		"""Must be called at the end of a line (row or column) of modules. A helper function for _get_penalty_score()."""
    -		if currentruncolor:  # Terminate black run
    +		if currentruncolor:  # Terminate dark run
     			self._finder_penalty_add_history(currentrunlength, runhistory)
     			currentrunlength = 0
    -		currentrunlength += self._size  # Add white border to final run
    +		currentrunlength += self._size  # Add light border to final run
     		self._finder_penalty_add_history(currentrunlength, runhistory)
     		return self._finder_penalty_count_patterns(runhistory)
     	
     	
    -	def _finder_penalty_add_history(self, currentrunlength, runhistory):
    +	def _finder_penalty_add_history(self, currentrunlength: int, runhistory: collections.deque) -> None:
     		if runhistory[0] == 0:
    -			currentrunlength += self._size  # Add white border to initial run
    +			currentrunlength += self._size  # Add light border to initial run
     		runhistory.appendleft(currentrunlength)
     	
     	
     	# ---- Constants and tables ----
     	
    -	MIN_VERSION =  1  # The minimum version number supported in the QR Code Model 2 standard
    -	MAX_VERSION = 40  # The maximum version number supported in the QR Code Model 2 standard
    +	MIN_VERSION: int =  1  # The minimum version number supported in the QR Code Model 2 standard
    +	MAX_VERSION: int = 40  # The maximum version number supported in the QR Code Model 2 standard
     	
    -	# For use in getPenaltyScore(), when evaluating which mask is best.
    -	_PENALTY_N1 =  3
    -	_PENALTY_N2 =  3
    -	_PENALTY_N3 = 40
    -	_PENALTY_N4 = 10
    +	# For use in _get_penalty_score(), when evaluating which mask is best.
    +	_PENALTY_N1: int =  3
    +	_PENALTY_N2: int =  3
    +	_PENALTY_N3: int = 40
    +	_PENALTY_N4: int = 10
     	
    -	_ECC_CODEWORDS_PER_BLOCK = (
    +	_ECC_CODEWORDS_PER_BLOCK: Sequence[Sequence[int]] = (
     		# Version: (note that index 0 is for padding, and is set to an illegal value)
    -		#   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
    -		(None,  7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30),  # Low
    -		(None, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28),  # Medium
    -		(None, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30),  # Quartile
    -		(None, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30))  # High
    +		# 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
    +		(-1,  7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30),  # Low
    +		(-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28),  # Medium
    +		(-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30),  # Quartile
    +		(-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30))  # High
     	
    -	_NUM_ERROR_CORRECTION_BLOCKS = (
    +	_NUM_ERROR_CORRECTION_BLOCKS: Sequence[Sequence[int]] = (
     		# Version: (note that index 0 is for padding, and is set to an illegal value)
    -		#   0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
    -		(None, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4,  4,  4,  4,  4,  6,  6,  6,  6,  7,  8,  8,  9,  9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25),  # Low
    -		(None, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5,  5,  8,  9,  9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49),  # Medium
    -		(None, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8,  8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68),  # Quartile
    -		(None, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81))  # High
    +		# 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40    Error correction level
    +		(-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4,  4,  4,  4,  4,  6,  6,  6,  6,  7,  8,  8,  9,  9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25),  # Low
    +		(-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5,  5,  8,  9,  9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49),  # Medium
    +		(-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8,  8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68),  # Quartile
    +		(-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81))  # High
     	
    -	_MASK_PATTERNS = (
    +	_MASK_PATTERNS: Sequence[Callable[[int,int],int]] = (
     		(lambda x, y:  (x + y) % 2                  ),
     		(lambda x, y:  y % 2                        ),
     		(lambda x, y:  x % 3                        ),
    @@ -678,12 +636,21 @@ class QrCode(object):
     	
     	# ---- Public helper enumeration ----
     	
    -	class Ecc(object):
    +	class Ecc:
    +		ordinal: int  # (Public) In the range 0 to 3 (unsigned 2-bit integer)
    +		formatbits: int  # (Package-private) In the range 0 to 3 (unsigned 2-bit integer)
    +		
     		"""The error correction level in a QR Code symbol. Immutable."""
     		# Private constructor
    -		def __init__(self, i, fb):
    -			self.ordinal = i  # (Public) In the range 0 to 3 (unsigned 2-bit integer)
    -			self.formatbits = fb  # (Package-private) In the range 0 to 3 (unsigned 2-bit integer)
    +		def __init__(self, i: int, fb: int) -> None:
    +			self.ordinal = i
    +			self.formatbits = fb
    +		
    +		# Placeholders
    +		LOW     : QrCode.Ecc
    +		MEDIUM  : QrCode.Ecc
    +		QUARTILE: QrCode.Ecc
    +		HIGH    : QrCode.Ecc
     	
     	# Public constants. Create them outside the class.
     	Ecc.LOW      = Ecc(0, 1)  # The QR Code can tolerate about  7% erroneous codewords
    @@ -695,7 +662,7 @@ class QrCode(object):
     
     # ---- Data segment class ----
     
    -class QrSegment(object):
    +class QrSegment:
     	"""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
    @@ -709,15 +676,10 @@ class QrSegment(object):
     	# ---- Static factory functions (mid level) ----
     	
     	@staticmethod
    -	def make_bytes(data):
    +	def make_bytes(data: Union[bytes,Sequence[int]]) -> QrSegment:
     		"""Returns a segment representing the given binary data encoded in byte mode.
     		All input byte lists are acceptable. Any text string can be converted to
     		UTF-8 bytes (s.encode("UTF-8")) and encoded as a byte mode segment."""
    -		py3 = sys.version_info.major >= 3
    -		if (py3 and isinstance(data, str)) or (not py3 and isinstance(data, unicode)):
    -			raise TypeError("Byte string/list expected")
    -		if not py3 and isinstance(data, str):
    -			data = bytearray(data)
     		bb = _BitBuffer()
     		for b in data:
     			bb.append_bits(b, 8)
    @@ -725,29 +687,29 @@ class QrSegment(object):
     	
     	
     	@staticmethod
    -	def make_numeric(digits):
    +	def make_numeric(digits: str) -> QrSegment:
     		"""Returns a segment representing the given string of decimal digits encoded in numeric mode."""
    -		if QrSegment.NUMERIC_REGEX.match(digits) is None:
    +		if not QrSegment.is_numeric(digits):
     			raise ValueError("String contains non-numeric characters")
     		bb = _BitBuffer()
    -		i = 0
    +		i: int = 0
     		while i < len(digits):  # Consume up to 3 digits per iteration
    -			n = min(len(digits) - i, 3)
    +			n: int = min(len(digits) - i, 3)
     			bb.append_bits(int(digits[i : i + n]), n * 3 + 1)
     			i += n
     		return QrSegment(QrSegment.Mode.NUMERIC, len(digits), bb)
     	
     	
     	@staticmethod
    -	def make_alphanumeric(text):
    +	def make_alphanumeric(text: str) -> QrSegment:
     		"""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."""
    -		if QrSegment.ALPHANUMERIC_REGEX.match(text) is None:
    +		if not QrSegment.is_alphanumeric(text):
     			raise ValueError("String contains unencodable characters in alphanumeric mode")
     		bb = _BitBuffer()
     		for i in range(0, len(text) - 1, 2):  # Process groups of 2
    -			temp = QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45
    +			temp: int = QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45
     			temp += QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i + 1]]
     			bb.append_bits(temp, 11)
     		if len(text) % 2 > 0:  # 1 character remaining
    @@ -756,25 +718,25 @@ class QrSegment(object):
     	
     	
     	@staticmethod
    -	def make_segments(text):
    +	def make_segments(text: str) -> List[QrSegment]:
     		"""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) or (sys.version_info.major < 3 and isinstance(text, unicode))):
    +		if not isinstance(text, str):
     			raise TypeError("Text string expected")
     		
     		# Select the most efficient segment encoding automatically
     		if text == "":
     			return []
    -		elif QrSegment.NUMERIC_REGEX.match(text) is not None:
    +		elif QrSegment.is_numeric(text):
     			return [QrSegment.make_numeric(text)]
    -		elif QrSegment.ALPHANUMERIC_REGEX.match(text) is not None:
    +		elif QrSegment.is_alphanumeric(text):
     			return [QrSegment.make_alphanumeric(text)]
     		else:
     			return [QrSegment.make_bytes(text.encode("UTF-8"))]
     	
     	
     	@staticmethod
    -	def make_eci(assignval):
    +	def make_eci(assignval: int) -> QrSegment:
     		"""Returns a segment representing an Extended Channel Interpretation
     		(ECI) designator with the given assignment value."""
     		bb = _BitBuffer()
    @@ -793,9 +755,39 @@ class QrSegment(object):
     		return QrSegment(QrSegment.Mode.ECI, 0, bb)
     	
     	
    +	# 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.
    +	@staticmethod
    +	def is_numeric(text: str) -> bool:
    +		return QrSegment._NUMERIC_REGEX.fullmatch(text) is not None
    +	
    +	
    +	# 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.
    +	@staticmethod
    +	def is_alphanumeric(text: str) -> bool:
    +		return QrSegment._ALPHANUMERIC_REGEX.fullmatch(text) is not None
    +	
    +	
    +	# ---- Private fields ----
    +	
    +	# The mode indicator of this segment. Accessed through get_mode().
    +	_mode: QrSegment.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 get_num_chars().
    +	_numchars: int
    +	
    +	# The data bits of this segment. Accessed through get_data().
    +	_bitdata: List[int]
    +	
    +	
     	# ---- Constructor (low level) ----
     	
    -	def __init__(self, mode, numch, bitdata):
    +	def __init__(self, mode: QrSegment.Mode, numch: int, bitdata: Sequence[int]) -> None:
     		"""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 cloned and stored."""
    @@ -803,44 +795,35 @@ class QrSegment(object):
     			raise TypeError("QrSegment.Mode expected")
     		if numch < 0:
     			raise ValueError()
    -		
    -		# The mode indicator of this segment. Accessed through get_mode().
     		self._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 get_num_chars().
     		self._numchars = numch
    -		
    -		# The data bits of this segment. Accessed through get_data().
     		self._bitdata = list(bitdata)  # Make defensive copy
     	
     	
     	# ---- Accessor methods ----
     	
    -	def get_mode(self):
    +	def get_mode(self) -> QrSegment.Mode:
     		"""Returns the mode field of this segment."""
     		return self._mode
     	
    -	def get_num_chars(self):
    +	def get_num_chars(self) -> int:
     		"""Returns the character count field of this segment."""
     		return self._numchars
     	
    -	def get_data(self):
    +	def get_data(self) -> List[int]:
     		"""Returns a new copy of the data bits of this segment."""
     		return list(self._bitdata)  # Make defensive copy
     	
     	
     	# Package-private function
     	@staticmethod
    -	def get_total_bits(segs, version):
    +	def get_total_bits(segs, version: int) -> Optional[int]:
     		"""Calculates the number of bits needed to encode the given segments at
     		the given version. Returns a non-negative number if successful. Otherwise
     		returns None if a segment has too many characters to fit its length field."""
     		result = 0
     		for seg in segs:
    -			ccbits = seg.get_mode().num_char_count_bits(version)
    +			ccbits: int = seg.get_mode().num_char_count_bits(version)
     			if seg.get_num_chars() >= (1 << ccbits):
     				return None  # The segment's length doesn't fit the field's bit width
     			result += 4 + ccbits + len(seg._bitdata)
    @@ -849,42 +832,46 @@ class QrSegment(object):
     	
     	# ---- Constants ----
     	
    -	# (Public) Describes precisely all strings that are encodable in numeric mode.
    -	# To test whether a string s is encodable: ok = NUMERIC_REGEX.fullmatch(s) is not None
    -	# A string is encodable iff each character is in the range 0 to 9.
    -	NUMERIC_REGEX = re.compile(r"[0-9]*\Z")
    +	# Describes precisely all strings that are encodable in numeric mode.
    +	_NUMERIC_REGEX: re.Pattern = re.compile(r"[0-9]*")
     	
    -	# (Public) Describes precisely all strings that are encodable in alphanumeric mode.
    -	# To test whether a string s is encodable: ok = ALPHANUMERIC_REGEX.fullmatch(s) is not None
    -	# 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.
    -
    -	ALPHANUMERIC_REGEX = re.compile(r"[A-Z0-9 $%*+./:-]*\Z")
    +	# Describes precisely all strings that are encodable in alphanumeric mode.
    +	_ALPHANUMERIC_REGEX: re.Pattern = re.compile(r"[A-Z0-9 $%*+./:-]*")
     	
    -	# (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc.
    -	_ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")}
    +	# Dictionary of "0"->0, "A"->10, "$"->37, etc.
    +	_ALPHANUMERIC_ENCODING_TABLE: Dict[str,int] = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")}
     	
     	
     	# ---- Public helper enumeration ----
     	
    -	class Mode(object):
    +	class Mode:
     		"""Describes how a segment's data bits are interpreted. Immutable."""
     		
    +		_modebits: int  # The mode indicator bits, which is a uint4 value (range 0 to 15)
    +		_charcounts: Tuple[int,int,int]  # Number of character count bits for three different version ranges
    +		
     		# Private constructor
    -		def __init__(self, modebits, charcounts):
    -			self._modebits = modebits  # The mode indicator bits, which is a uint4 value (range 0 to 15)
    -			self._charcounts = charcounts  # Number of character count bits for three different version ranges
    +		def __init__(self, modebits: int, charcounts: Tuple[int,int,int]):
    +			self._modebits = modebits
    +			self._charcounts = charcounts
     		
     		# Package-private method
    -		def get_mode_bits(self):
    +		def get_mode_bits(self) -> int:
     			"""Returns an unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object."""
     			return self._modebits
     		
     		# Package-private method
    -		def num_char_count_bits(self, ver):
    +		def num_char_count_bits(self, ver: int) -> int:
     			"""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]."""
     			return self._charcounts[(ver + 7) // 17]
    +		
    +		# Placeholders
    +		NUMERIC     : QrSegment.Mode
    +		ALPHANUMERIC: QrSegment.Mode
    +		BYTE        : QrSegment.Mode
    +		KANJI       : QrSegment.Mode
    +		ECI         : QrSegment.Mode
     	
     	# Public constants. Create them outside the class.
     	Mode.NUMERIC      = Mode(0x1, (10, 12, 14))
    @@ -900,15 +887,15 @@ class QrSegment(object):
     class _BitBuffer(list):
     	"""An appendable sequence of bits (0s and 1s). Mainly used by QrSegment."""
     	
    -	def append_bits(self, val, n):
    +	def append_bits(self, val: int, n: int) -> None:
     		"""Appends the given number of low-order bits of the given
     		value to this buffer. Requires n >= 0 and 0 <= val < 2^n."""
    -		if n < 0 or val >> n != 0:
    +		if (n < 0) or (val >> n != 0):
     			raise ValueError("Value out of range")
     		self.extend(((val >> i) & 1) for i in reversed(range(n)))
     
     
    -def _get_bit(x, i):
    +def _get_bit(x: int, i: int) -> bool:
     	"""Returns true iff the i'th bit of x is set to 1."""
     	return (x >> i) & 1 != 0
     
    diff --git a/python/setup.cfg b/python/setup.cfg
    deleted file mode 100755
    index 2a9acf1..0000000
    --- a/python/setup.cfg
    +++ /dev/null
    @@ -1,2 +0,0 @@
    -[bdist_wheel]
    -universal = 1
    diff --git a/python/setup.py b/python/setup.py
    index 23ca70e..7e4b4d4 100755
    --- a/python/setup.py
    +++ b/python/setup.py
    @@ -1,5 +1,5 @@
     # 
    -# QR Code generator Distutils script (Python 2, 3)
    +# QR Code generator Distutils script (Python)
     # 
     # Copyright (c) Project Nayuki. (MIT License)
     # https://www.nayuki.io/page/qr-code-generator-library
    @@ -26,9 +26,10 @@ import setuptools
     
     setuptools.setup(
     	name = "qrcodegen",
    -	description = "High quality QR Code generator library for Python 2 and 3",
    -	version = "1.6.0",
    +	description = "High quality QR Code generator library for Python",
    +	version = "1.7.0",
     	platforms = "OS Independent",
    +	python_requires = '>=3',
     	license = "MIT License",
     	
     	author = "Project Nayuki",
    @@ -42,7 +43,6 @@ setuptools.setup(
     		"License :: OSI Approved :: MIT License",
     		"Operating System :: OS Independent",
     		"Programming Language :: Python",
    -		"Programming Language :: Python :: 2",
     		"Programming Language :: Python :: 3",
     		"Topic :: Multimedia :: Graphics",
     		"Topic :: Software Development :: Libraries :: Python Modules",
    @@ -69,7 +69,7 @@ Core features:
     * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C
     * Significantly shorter code but more documentation comments compared to competing libraries
     * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard
    -* Output formats: Raw modules/pixels of the QR symbol, SVG XML string
    +* Output format: Raw modules/pixels of the QR symbol
     * Encodes numeric and special-alphanumeric text in less space than general text
     * Open source code under the permissive MIT License
     
    @@ -94,7 +94,7 @@ Examples:
         
         # Simple operation
         qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM)
    -    svg = qr0.to_svg_str(4)
    +    svg = to_svg_str(qr0, 4)  # See qrcodegen-demo
         
         # Manual operation
         segs = QrSegment.make_segments("3141592653589793238462643383")
    diff --git a/rust/Cargo.toml b/rust/Cargo.toml
    index 251cbbb..d3e155b 100755
    --- a/rust/Cargo.toml
    +++ b/rust/Cargo.toml
    @@ -1,6 +1,6 @@
     [package]
     name = "qrcodegen"
    -version = "1.6.0"
    +version = "1.7.0"
     authors = ["Project Nayuki"]
     description = "High-quality QR Code generator library"
     homepage = "https://www.nayuki.io/page/qr-code-generator-library"
    @@ -9,3 +9,4 @@ readme = "Readme.markdown"
     keywords = ["qr-code", "barcode", "encoder", "image"]
     categories = ["encoding", "multimedia::images"]
     license = "MIT"
    +exclude = ["examples/*"]
    diff --git a/rust/Readme.markdown b/rust/Readme.markdown
    index 7a20c4a..f14a66d 100755
    --- a/rust/Readme.markdown
    +++ b/rust/Readme.markdown
    @@ -18,7 +18,7 @@ Core features:
     * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C
     * Significantly shorter code but more documentation comments compared to competing libraries
     * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard
    -* Output formats: Raw modules/pixels of the QR symbol, SVG XML string
    +* Output format: Raw modules/pixels of the QR symbol
     * Detects finder-like penalty patterns more accurately than other implementations
     * Encodes numeric and special-alphanumeric text in less space than general text
     * Open source code under the permissive MIT License
    @@ -35,20 +35,22 @@ Examples
     --------
     
         extern crate qrcodegen;
    +    use qrcodegen::Mask;
         use qrcodegen::QrCode;
         use qrcodegen::QrCodeEcc;
         use qrcodegen::QrSegment;
    +    use qrcodegen::Version;
         
         // Simple operation
         let qr = QrCode::encode_text("Hello, world!",
             QrCodeEcc::Medium).unwrap();
    -    let svg = qr.to_svg_string(4);
    +    let svg = to_svg_string(&qr, 4);  // See qrcodegen-demo
         
         // Manual operation
         let chrs: Vec = "3141592653589793238462643383".chars().collect();
         let segs = QrSegment::make_segments(&chrs);
    -    let qr = QrCode::encode_segments_advanced(
    -        &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap();
    +    let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High,
    +        Version::new(5), Version::new(5), Some(Mask::new(2)), false).unwrap();
         for y in 0 .. qr.size() {
             for x in 0 .. qr.size() {
                 (... paint qr.get_module(x, y) ...)
    diff --git a/rust/examples/qrcodegen-demo.rs b/rust/examples/qrcodegen-demo.rs
    index 80b2b7a..ed37c65 100755
    --- a/rust/examples/qrcodegen-demo.rs
    +++ b/rust/examples/qrcodegen-demo.rs
    @@ -29,8 +29,7 @@ use qrcodegen::Mask;
     use qrcodegen::QrCode;
     use qrcodegen::QrCodeEcc;
     use qrcodegen::QrSegment;
    -use qrcodegen::QrCode_MAX_VERSION;
    -use qrcodegen::QrCode_MIN_VERSION;
    +use qrcodegen::Version;
     
     
     // The main application program.
    @@ -53,7 +52,7 @@ fn do_basic_demo() {
     	// Make and print the QR Code symbol
     	let qr: QrCode = QrCode::encode_text(text, errcorlvl).unwrap();
     	print_qr(&qr);
    -	println!("{}", qr.to_svg_string(4));
    +	println!("{}", to_svg_string(&qr, 4));
     }
     
     
    @@ -128,8 +127,8 @@ fn do_segment_demo() {
     		0x0000, 0x0208, 0x01FF, 0x0008,
     	];
     	let mut bb = qrcodegen::BitBuffer(Vec::new());
    -	for c in &kanjichars {
    -		bb.append_bits(*c, 13);
    +	for &c in &kanjichars {
    +		bb.append_bits(c, 13);
     	}
     	let segs = vec![
     		QrSegment::new(qrcodegen::QrSegmentMode::Kanji, kanjichars.len(), bb.0),
    @@ -143,20 +142,20 @@ fn do_segment_demo() {
     fn do_mask_demo() {
     	// Project Nayuki URL
     	let segs = QrSegment::make_segments(&to_chars("https://www.nayuki.io/"));
    -	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true).unwrap();  // Automatic mask
    +	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, Version::MIN, Version::MAX, None, true).unwrap();  // Automatic mask
     	print_qr(&qr);
    -	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(3)), true).unwrap();  // Force mask 3
    +	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, Version::MIN, Version::MAX, Some(Mask::new(3)), true).unwrap();  // Force mask 3
     	print_qr(&qr);
     	
     	// Chinese text as UTF-8
     	let segs = QrSegment::make_segments(&to_chars("維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫"));
    -	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(0)), true).unwrap();  // Force mask 0
    +	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(0)), true).unwrap();  // Force mask 0
     	print_qr(&qr);
    -	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(1)), true).unwrap();  // Force mask 1
    +	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(1)), true).unwrap();  // Force mask 1
     	print_qr(&qr);
    -	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(5)), true).unwrap();  // Force mask 5
    +	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(5)), true).unwrap();  // Force mask 5
     	print_qr(&qr);
    -	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(7)), true).unwrap();  // Force mask 7
    +	let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(7)), true).unwrap();  // Force mask 7
     	print_qr(&qr);
     }
     
    @@ -164,6 +163,35 @@ fn do_mask_demo() {
     
     /*---- Utilities ----*/
     
    +// Returns a string of SVG code for an image depicting
    +// the given QR Code, with the given number of border modules.
    +// The string always uses Unix newlines (\n), regardless of the platform.
    +fn to_svg_string(qr: &QrCode, border: i32) -> String {
    +	assert!(border >= 0, "Border must be non-negative");
    +	let mut result = String::new();
    +	result += "\n";
    +	result += "\n";
    +	let dimension = qr.size().checked_add(border.checked_mul(2).unwrap()).unwrap();
    +	result += &format!(
    +		"\n", dimension);
    +	result += "\t\n";
    +	result += "\t\n";
    +	result += "\n";
    +	result
    +}
    +
    +
     // Prints the given QrCode object to the console.
     fn print_qr(qr: &QrCode) {
     	let border: i32 = 4;
    diff --git a/rust/examples/qrcodegen-worker.rs b/rust/examples/qrcodegen-worker.rs
    deleted file mode 100755
    index da9a0a3..0000000
    --- a/rust/examples/qrcodegen-worker.rs
    +++ /dev/null
    @@ -1,113 +0,0 @@
    -/* 
    - * QR Code generator test worker (Rust)
    - * 
    - * This program reads data and encoding parameters from standard input and writes
    - * QR Code bitmaps to standard output. The I/O format is one integer per line.
    - * Run with no command line arguments. The program is intended for automated
    - * batch testing of end-to-end functionality of this QR Code generator library.
    - * 
    - * 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.
    - */
    -
    -extern crate qrcodegen;
    -use qrcodegen::Mask;
    -use qrcodegen::QrCode;
    -use qrcodegen::QrCodeEcc;
    -use qrcodegen::QrSegment;
    -use qrcodegen::Version;
    -
    -
    -fn main() {
    -	loop {
    -		
    -		// Read data length or exit
    -		let length: i16 = read_int();
    -		if length == -1 {
    -			break;
    -		}
    -		
    -		// Read data bytes
    -		let mut data = Vec::::with_capacity(length as usize);
    -		for _ in 0 .. length {
    -			let b: i16 = read_int();
    -			assert_eq!(i16::from(b as u8), b, "Byte value out of range");
    -			data.push(b as u8);
    -		}
    -		let isascii: bool = data.iter().all(|b| *b < 128);
    -		
    -		// Read encoding parameters
    -		let errcorlvl  = read_int();
    -		let minversion = read_int();
    -		let maxversion = read_int();
    -		let mask       = read_int();
    -		let boostecl   = read_int();
    -		assert!(0 <= errcorlvl && errcorlvl <= 3);
    -		assert!(i16::from(qrcodegen::QrCode_MIN_VERSION.value()) <= minversion
    -			&& minversion <= maxversion
    -			&& maxversion <= i16::from(qrcodegen::QrCode_MAX_VERSION.value()));
    -		assert!(-1 <= mask && mask <= 7);
    -		assert!(boostecl >> 1 == 0);
    -		
    -		// Make segments for encoding
    -		let segs: Vec = if isascii {
    -			let chrs: Vec = std::str::from_utf8(&data).unwrap().chars().collect();
    -			QrSegment::make_segments(&chrs)
    -		} else {
    -			vec![QrSegment::make_bytes(&data)]
    -		};
    -		
    -		// Try to make QR Code symbol
    -		let msk = if mask == -1 { None } else { Some(Mask::new(mask as u8)) };
    -		match QrCode::encode_segments_advanced(&segs, ECC_LEVELS[errcorlvl as usize],
    -				Version::new(minversion as u8), Version::new(maxversion as u8), msk, boostecl != 0) {
    -		
    -			Ok(qr) => {
    -				// Print grid of modules
    -				println!("{}", qr.version().value());
    -				for y in 0 .. qr.size() {
    -					for x in 0 .. qr.size() {
    -						println!("{}", qr.get_module(x, y) as i8);
    -					}
    -				}
    -			},
    -			Err(_) => println!("-1"),
    -		}
    -		use std::io::Write;
    -		std::io::stdout().flush().unwrap();
    -	}
    -}
    -
    -
    -fn read_int() -> i16 {
    -	let mut line = String::new();
    -	std::io::stdin().read_line(&mut line).unwrap();
    -	let mut chrs: Vec = line.chars().collect();
    -	assert_eq!(chrs.pop().unwrap(), '\n');
    -	let line: String = chrs.iter().cloned().collect();
    -	line.parse::().expect("Invalid number")
    -}
    -
    -
    -static ECC_LEVELS: [QrCodeEcc; 4] = [
    -	QrCodeEcc::Low,
    -	QrCodeEcc::Medium,
    -	QrCodeEcc::Quartile,
    -	QrCodeEcc::High,
    -];
    diff --git a/rust/src/lib.rs b/rust/src/lib.rs
    index 6a90c81..eded602 100755
    --- a/rust/src/lib.rs
    +++ b/rust/src/lib.rs
    @@ -38,7 +38,7 @@
     //! - Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C
     //! - Significantly shorter code but more documentation comments compared to competing libraries
     //! - Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard
    -//! - Output formats: Raw modules/pixels of the QR symbol, SVG XML string
    +//! - Output format: Raw modules/pixels of the QR symbol
     //! - Detects finder-like penalty patterns more accurately than other implementations
     //! - Encodes numeric and special-alphanumeric text in less space than general text
     //! - Open source code under the permissive MIT License
    @@ -54,9 +54,11 @@
     //! 
     //! ```
     //! extern crate qrcodegen;
    +//! use qrcodegen::Mask;
     //! use qrcodegen::QrCode;
     //! use qrcodegen::QrCodeEcc;
     //! use qrcodegen::QrSegment;
    +//! use qrcodegen::Version;
     //! ```
     //! 
     //! Simple operation:
    @@ -64,7 +66,7 @@
     //! ```
     //! let qr = QrCode::encode_text("Hello, world!",
     //!     QrCodeEcc::Medium).unwrap();
    -//! let svg = qr.to_svg_string(4);
    +//! let svg = to_svg_string(&qr, 4);  // See qrcodegen-demo
     //! ```
     //! 
     //! Manual operation:
    @@ -72,8 +74,8 @@
     //! ```
     //! let chrs: Vec = "3141592653589793238462643383".chars().collect();
     //! let segs = QrSegment::make_segments(&chrs);
    -//! let qr = QrCode::encode_segments_advanced(
    -//!     &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap();
    +//! let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High,
    +//!     Version::new(5), Version::new(5), Some(Mask::new(2)), false).unwrap();
     //! for y in 0 .. qr.size() {
     //!     for x in 0 .. qr.size() {
     //!         (... paint qr.get_module(x, y) ...)
    @@ -88,7 +90,7 @@
     /// 
     /// Invented by Denso Wave and described in the ISO/IEC 18004 standard.
     /// 
    -/// Instances of this struct represent an immutable square grid of black and white cells.
    +/// Instances of this struct represent an immutable square grid of dark and light cells.
     /// The impl provides static factory functions to create a QR Code from text or binary data.
     /// The struct and impl cover the QR Code Model 2 specification, supporting all versions
     /// (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
    @@ -126,7 +128,7 @@ pub struct QrCode {
     	
     	// Grids of modules/pixels, with dimensions of size*size:
     	
    -	// The modules of this QR Code (false = white, true = black).
    +	// The modules of this QR Code (false = light, true = dark).
     	// Immutable after constructor finishes. Accessed through get_module().
     	modules: Vec,
     	
    @@ -184,7 +186,7 @@ impl QrCode {
     	/// Returns a wrapped `QrCode` if successful, or `Err` if the
     	/// data is too long to fit in any version at the given ECC level.
     	pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Result {
    -		QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true)
    +		QrCode::encode_segments_advanced(segs, ecl, Version::MIN, Version::MAX, None, true)
     	}
     	
     	
    @@ -207,7 +209,7 @@ impl QrCode {
     		assert!(minversion.value() <= maxversion.value(), "Invalid value");
     		
     		// Find the minimal version number to use
    -		let mut version = minversion;
    +		let mut version: Version = minversion;
     		let datausedbits: usize = loop {
     			// Number of data bits available
     			let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8;
    @@ -245,9 +247,9 @@ impl QrCode {
     		// Add terminator and pad up to a byte if applicable
     		let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8;
     		assert!(bb.0.len() <= datacapacitybits);
    -		let numzerobits = std::cmp::min(4, datacapacitybits - bb.0.len());
    +		let numzerobits: usize = std::cmp::min(4, datacapacitybits - bb.0.len());
     		bb.append_bits(0, numzerobits as u8);
    -		let numzerobits = bb.0.len().wrapping_neg() & 7;
    +		let numzerobits: usize = bb.0.len().wrapping_neg() & 7;
     		bb.append_bits(0, numzerobits as u8);
     		assert_eq!(bb.0.len() % 8, 0, "Assertion error");
     		
    @@ -285,7 +287,7 @@ impl QrCode {
     			size: size as i32,
     			mask: Mask::new(0),  // Dummy value
     			errorcorrectionlevel: ecl,
    -			modules   : vec![false; size * size],  // Initially all white
    +			modules   : vec![false; size * size],  // Initially all light
     			isfunction: vec![false; size * size],
     		};
     		
    @@ -296,7 +298,7 @@ impl QrCode {
     		
     		// Do masking
     		if mask.is_none() {  // Automatically choose best mask
    -			let mut minpenalty: i32 = std::i32::MAX;
    +			let mut minpenalty = std::i32::MAX;
     			for i in 0u8 .. 8 {
     				let newmask = Mask::new(i);
     				result.apply_mask(newmask);
    @@ -347,10 +349,10 @@ impl QrCode {
     	
     	
     	/// Returns the color of the module (pixel) at the given coordinates,
    -	/// which is `false` for white or `true` for black.
    +	/// which is `false` for light or `true` for dark.
     	/// 
     	/// The top left corner has the coordinates (x=0, y=0). If the given
    -	/// coordinates are out of bounds, then `false` (white) is returned.
    +	/// coordinates are out of bounds, then `false` (light) is returned.
     	pub fn get_module(&self, x: i32, y: i32) -> bool {
     		0 <= x && x < self.size && 0 <= y && y < self.size && self.module(x, y)
     	}
    @@ -368,36 +370,6 @@ impl QrCode {
     	}
     	
     	
    -	/// Returns a string of SVG code for an image depicting
    -	/// this QR Code, with the given number of border modules.
    -	/// 
    -	/// The string always uses Unix newlines (\n), regardless of the platform.
    -	pub fn to_svg_string(&self, border: i32) -> String {
    -		assert!(border >= 0, "Border must be non-negative");
    -		let mut result = String::new();
    -		result += "\n";
    -		result += "\n";
    -		let dimension = self.size.checked_add(border.checked_mul(2).unwrap()).unwrap();
    -		result += &format!(
    -			"\n", dimension);
    -		result += "\t\n";
    -		result += "\t\n";
    -		result += "\n";
    -		result
    -	}
    -	
    -	
     	/*---- Private helper methods for constructor: Drawing function modules ----*/
     	
     	// Reads this object's version field, and draws and marks all function modules.
    @@ -438,7 +410,7 @@ impl QrCode {
     		// Calculate error correction code and pack bits
     		let bits: u32 = {
     			// errcorrlvl is uint2, mask is uint3
    -			let data: u32 = self.errorcorrectionlevel.format_bits() << 3 | u32::from(mask.value());
    +			let data: u32 = u32::from(self.errorcorrectionlevel.format_bits() << 3 | mask.value());
     			let mut rem: u32 = data;
     			for _ in 0 .. 10 {
     				rem = (rem << 1) ^ ((rem >> 9) * 0x537);
    @@ -466,7 +438,7 @@ impl QrCode {
     		for i in 8 .. 15 {
     			self.set_function_module(8, size - 15 + i, get_bit(bits, i));
     		}
    -		self.set_function_module(8, size - 8, true);  // Always black
    +		self.set_function_module(8, size - 8, true);  // Always dark
     	}
     	
     	
    @@ -528,8 +500,8 @@ impl QrCode {
     	
     	// Sets the color of a module and marks it as a function module.
     	// Only used by the constructor. Coordinates must be in bounds.
    -	fn set_function_module(&mut self, x: i32, y: i32, isblack: bool) {
    -		*self.module_mut(x, y) = isblack;
    +	fn set_function_module(&mut self, x: i32, y: i32, isdark: bool) {
    +		*self.module_mut(x, y) = isdark;
     		self.isfunction[(y * self.size + x) as usize] = true;
     	}
     	
    @@ -539,8 +511,8 @@ impl QrCode {
     	// Returns a new byte string representing the given data with the appropriate error correction
     	// codewords appended to it, based on this object's version and error correction level.
     	fn add_ecc_and_interleave(&self, data: &[u8]) -> Vec {
    -		let ver = self.version;
    -		let ecl = self.errorcorrectionlevel;
    +		let ver: Version = self.version;
    +		let ecl: QrCodeEcc = self.errorcorrectionlevel;
     		assert_eq!(data.len(), QrCode::get_num_data_codewords(ver, ecl), "Illegal argument");
     		
     		// Calculate parameter numbers
    @@ -602,7 +574,7 @@ impl QrCode {
     						i += 1;
     					}
     					// If this QR Code has any remainder bits (0 to 7), they were assigned as
    -					// 0/false/white by the constructor and are left unchanged by this method
    +					// 0/false/light by the constructor and are left unchanged by this method
     				}
     			}
     			right -= 2;
    @@ -613,14 +585,13 @@ impl QrCode {
     	
     	// XORs the codeword modules in this QR Code with the given mask pattern.
     	// The function modules must be marked and the codeword bits must be drawn
    -	// before masking. Due to the arithmetic of XOR, calling applyMask() with
    +	// before masking. Due to the arithmetic of XOR, calling apply_mask() with
     	// the same mask value a second time will undo the mask. A final well-formed
     	// QR Code needs exactly one (not zero, two, etc.) mask applied.
     	fn apply_mask(&mut self, mask: Mask) {
    -		let mask: u8 = mask.value();
     		for y in 0 .. self.size {
     			for x in 0 .. self.size {
    -				let invert: bool = match mask {
    +				let invert: bool = match mask.value() {
     					0 => (x + y) % 2 == 0,
     					1 => y % 2 == 0,
     					2 => x % 3 == 0,
    @@ -704,11 +675,11 @@ impl QrCode {
     			}
     		}
     		
    -		// Balance of black and white modules
    -		let black: i32 = self.modules.iter().copied().map(i32::from).sum();
    -		let total: i32 = size * size;  // Note that size is odd, so black/total != 1/2
    -		// Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)%
    -		let k: i32 = ((black * 20 - total * 10).abs() + total - 1) / total - 1;
    +		// Balance of dark and light modules
    +		let dark: i32 = self.modules.iter().copied().map(i32::from).sum();
    +		let total: i32 = size * size;  // Note that size is odd, so dark/total != 1/2
    +		// Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%
    +		let k: i32 = ((dark * 20 - total * 10).abs() + total - 1) / total - 1;
     		result += k * PENALTY_N4;
     		result
     	}
    @@ -720,13 +691,13 @@ impl QrCode {
     	// Each position is in the range [0,177), and are used on both the x and y axes.
     	// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
     	fn get_alignment_pattern_positions(&self) -> Vec {
    -		let ver = self.version.value();
    +		let ver: u8 = self.version.value();
     		if ver == 1 {
     			vec![]
     		} else {
     			let numalign = i32::from(ver) / 7 + 2;
     			let step: i32 = if ver == 32 { 26 } else
    -				{(i32::from(ver)*4 + numalign*2 + 1) / (numalign*2 - 2) * 2};
    +				{(i32::from(ver) * 4 + numalign * 2 + 1) / (numalign * 2 - 2) * 2};
     			let mut result: Vec = (0 .. numalign - 1).map(
     				|i| self.size - 7 - i * step).collect();
     			result.push(6);
    @@ -847,7 +818,7 @@ impl FinderPenalty {
     	// Pushes the given value to the front and drops the last value.
     	pub fn add_history(&mut self, mut currentrunlength: i32) {
     		if self.run_history[0] == 0 {
    -			currentrunlength += self.qr_size;  // Add white border to initial run
    +			currentrunlength += self.qr_size;  // Add light border to initial run
     		}
     		let rh = &mut self.run_history;
     		for i in (0 .. rh.len()-1).rev() {
    @@ -857,7 +828,7 @@ impl FinderPenalty {
     	}
     	
     	
    -	// Can only be called immediately after a white run is added, and returns either 0, 1, or 2.
    +	// Can only be called immediately after a light run is added, and returns either 0, 1, or 2.
     	pub fn count_patterns(&self) -> i32 {
     		let rh = &self.run_history;
     		let n = rh[1];
    @@ -870,11 +841,11 @@ impl FinderPenalty {
     	
     	// Must be called at the end of a line (row or column) of modules.
     	pub fn terminate_and_count(mut self, currentruncolor: bool, mut currentrunlength: i32) -> i32 {
    -		if currentruncolor {  // Terminate black run
    +		if currentruncolor {  // Terminate dark run
     			self.add_history(currentrunlength);
     			currentrunlength = 0;
     		}
    -		currentrunlength += self.qr_size;  // Add white border to final run
    +		currentrunlength += self.qr_size;  // Add light border to final run
     		self.add_history(currentrunlength);
     		self.count_patterns()
     	}
    @@ -884,13 +855,6 @@ impl FinderPenalty {
     
     /*---- Constants and tables ----*/
     
    -/// The minimum version number supported in the QR Code Model 2 standard.
    -pub const QrCode_MIN_VERSION: Version = Version( 1);
    -
    -/// The maximum version number supported in the QR Code Model 2 standard.
    -pub const QrCode_MAX_VERSION: Version = Version(40);
    -
    -
     // For use in get_penalty_score(), when evaluating which mask is best.
     const PENALTY_N1: i32 =  3;
     const PENALTY_N2: i32 =  3;
    @@ -949,7 +913,7 @@ impl QrCodeEcc {
     	
     	
     	// Returns an unsigned 2-bit integer (in the range 0 to 3).
    -	fn format_bits(self) -> u32 {
    +	fn format_bits(self) -> u8 {
     		use QrCodeEcc::*;
     		match self {
     			Low      => 1,
    @@ -1047,7 +1011,7 @@ impl QrSegment {
     		let mut accumdata: u32 = 0;
     		let mut accumcount: u32 = 0;
     		for &c in text {
    -			let i = ALPHANUMERIC_CHARSET.iter().position(|&x| x == c)
    +			let i: usize = ALPHANUMERIC_CHARSET.iter().position(|&x| x == c)
     				.expect("String contains unencodable characters in alphanumeric mode");
     			accumdata = accumdata * 45 + (i as u32);
     			accumcount += 1;
    @@ -1140,28 +1104,34 @@ impl QrSegment {
     	fn get_total_bits(segs: &[Self], version: Version) -> Option {
     		let mut result: usize = 0;
     		for seg in segs {
    -			let ccbits = seg.mode.num_char_count_bits(version);
    -			if seg.numchars >= 1 << ccbits {
    -				return None;  // The segment's length doesn't fit the field's bit width
    +			let ccbits: u8 = seg.mode.num_char_count_bits(version);
    +			// ccbits can be as large as 16, but usize can be as small as 16
    +			if let Some(limit) = 1usize.checked_shl(u32::from(ccbits)) {
    +				if seg.numchars >= limit {
    +					return None;  // The segment's length doesn't fit the field's bit width
    +				}
     			}
    -			result = result.checked_add(4 + usize::from(ccbits) + seg.data.len())?;
    +			result = result.checked_add(4 + usize::from(ccbits))?;
    +			result = result.checked_add(seg.data.len())?;
     		}
     		Some(result)
     	}
     	
     	
    -	// 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.
    -	fn is_alphanumeric(text: &[char]) -> bool {
    -		text.iter().all(|c| ALPHANUMERIC_CHARSET.contains(c))
    +	/// 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.
    +	pub fn is_numeric(text: &[char]) -> bool {
    +		text.iter().all(|&c| '0' <= c && c <= '9')
     	}
     	
     	
    -	// 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.
    -	fn is_numeric(text: &[char]) -> bool {
    -		text.iter().all(|&c| '0' <= c && c <= '9')
    +	/// 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.
    +	pub fn is_alphanumeric(text: &[char]) -> bool {
    +		text.iter().all(|c| ALPHANUMERIC_CHARSET.contains(c))
     	}
     	
     }
    @@ -1249,8 +1219,8 @@ impl BitBuffer {
     /// 
     /// - Decrease the error correction level if it was greater than `QrCodeEcc::Low`.
     /// - If the `encode_segments_advanced()` function was called, then increase the maxversion
    -///   argument if it was less than `QrCode_MAX_VERSION`. (This advice does not apply to the
    -///   other factory functions because they search all versions up to `QrCode_MAX_VERSION`.)
    +///   argument if it was less than `Version::MAX`. (This advice does not apply to the
    +///   other factory functions because they search all versions up to `Version::MAX`.)
     /// - Split the text data into better or optimal segments in order to reduce the number of bits required.
     /// - Change the text or binary data to be shorter.
     /// - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).
    @@ -1276,11 +1246,17 @@ impl std::fmt::Display for DataTooLong {
     pub struct Version(u8);
     
     impl Version {
    +	/// The minimum version number supported in the QR Code Model 2 standard.
    +	pub const MIN: Version = Version( 1);
    +	
    +	/// The maximum version number supported in the QR Code Model 2 standard.
    +	pub const MAX: Version = Version(40);
    +	
     	/// Creates a version object from the given number.
     	/// 
     	/// Panics if the number is outside the range [1, 40].
     	pub fn new(ver: u8) -> Self {
    -		assert!(QrCode_MIN_VERSION.value() <= ver && ver <= QrCode_MAX_VERSION.value(), "Version number out of range");
    +		assert!(Version::MIN.value() <= ver && ver <= Version::MAX.value(), "Version number out of range");
     		Self(ver)
     	}
     	
    diff --git a/typescript-javascript/build.sh b/typescript-javascript/build.sh
    index f1689aa..5875c15 100755
    --- a/typescript-javascript/build.sh
    +++ b/typescript-javascript/build.sh
    @@ -23,8 +23,3 @@
     
     tsc --strict --lib DOM,DOM.Iterable,ES6 --target ES6 qrcodegen.ts qrcodegen-input-demo.ts
     tsc --strict --lib DOM,DOM.Iterable,ES6 --target ES6 qrcodegen.ts qrcodegen-output-demo.ts
    -
    -if [ '!' -d node_modules ]; then
    -	npm install @types/node
    -fi
    -tsc --strict --target ES2017 --outFile qrcodegen-worker.js qrcodegen.ts qrcodegen-worker.ts
    diff --git a/typescript-javascript/qrcodegen-input-demo.html b/typescript-javascript/qrcodegen-input-demo.html
    index d136412..cc46d6e 100755
    --- a/typescript-javascript/qrcodegen-input-demo.html
    +++ b/typescript-javascript/qrcodegen-input-demo.html
    @@ -29,6 +29,12 @@
     			html {
     				font-family: sans-serif;
     			}
    +			h1 {
    +				text-align: center;
    +			}
    +			table {
    +				border-collapse: collapse;
    +			}
     			td {
     				vertical-align: top;
     				padding-top: 0.2em;
    @@ -38,6 +44,10 @@
     				white-space: pre;
     				padding-right: 0.5em;
     			}
    +			input[type=number], input[type=text], textarea {
    +				font-size: inherit;
    +				font-family: inherit;
    +			}
     			input[type=radio], input[type=checkbox] {
     				margin: 0em;
     				padding: 0em;
    @@ -46,6 +56,11 @@
     				margin-right: 0.8em;
     				padding-left: 0.2em;
     			}
    +			hr {
    +				margin: 2em 0em;
    +				border: none;
    +				border-top: 0.1em solid #A0A0A0;
    +			}
     		
     	
     	
    @@ -56,12 +71,12 @@
     			

    (Are the JavaScript files missing?)

    (The JavaScript code needs to be compiled from the TypeScript code.)

    -