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 <lizhiqi1@huawei.com>
This commit is contained in:
pssea 2021-12-22 21:00:55 +08:00
parent 67161552fb
commit e9104a60b9
39 changed files with 1082 additions and 1686 deletions

View File

@ -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" ]

View File

@ -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)

View File

@ -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"

View File

@ -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<QrSegment> 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<QrSegment> 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 <string>
#include <vector>
#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<QrSegment> 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 <string>
#include <vector>
#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<QrSegment> 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 <stdbool.h>
#include <stdint.h>
#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 <stdbool.h>
#include <stdint.h>
#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<char> = "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<char> = "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

View File

@ -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)

View File

@ -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++;

View File

@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -24,13 +24,15 @@
* Software.
*/
#include <climits>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 ";
sb << (qr.getSize() + border * 2) << " " << (qr.getSize() + border * 2) << "\" stroke=\"none\">\n";
sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
sb << "\t<path d=\"";
for (int y = 0; y < qr.getSize(); y++) {
for (int x = 0; x < qr.getSize(); x++) {
if (qr.getModule(x, y)) {
if (x != 0 || y != 0)
sb << " ";
sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z";
}
}
}
sb << "\" fill=\"#000000\"/>\n";
sb << "</svg>\n";
return sb.str();
}
// Prints the given QrCode object to the console.
static void printQr(const QrCode &qr) {
int border = 4;

View File

@ -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 <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <vector>
#include "QrCode.hpp"
using qrcodegen::QrCode;
using qrcodegen::QrSegment;
static const std::vector<QrCode::Ecc> 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<uint8_t> data;
for (int i = 0; i < length; i++) {
int b;
std::cin >> b;
data.push_back(static_cast<uint8_t>(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<QrSegment> segs;
if (isAscii) {
std::vector<char> 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<std::size_t>(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;
}

98
cpp/QrCode.cpp → cpp/qrcodegen.cpp Executable file → Normal file
View File

@ -29,7 +29,7 @@
#include <sstream>
#include <stdexcept>
#include <utility>
#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<bool> &dt) :
mode(md),
QrSegment::QrSegment(const Mode &md, int numCh, const std::vector<bool> &dt) :
mode(&md),
numChars(numCh),
data(dt) {
if (numCh < 0)
@ -165,8 +165,8 @@ QrSegment::QrSegment(Mode md, int numCh, const std::vector<bool> &dt) :
}
QrSegment::QrSegment(Mode md, int numCh, std::vector<bool> &&dt) :
mode(md),
QrSegment::QrSegment(const Mode &md, int numCh, std::vector<bool> &&dt) :
mode(&md),
numChars(numCh),
data(std::move(dt)) {
if (numCh < 0)
@ -177,7 +177,7 @@ QrSegment::QrSegment(Mode md, int numCh, std::vector<bool> &&dt) :
int QrSegment::getTotalBits(const vector<QrSegment> &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<QrSegment> &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<QrSegment> &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>{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<QrSegment> &segs, Ecc ecl,
// Pack bits into bytes in big endian
vector<uint8_t> 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<uint8_t> &dataCodewords, int msk)
throw std::domain_error("Mask value out of range");
size = ver * 4 + 17;
size_t sz = static_cast<size_t>(size);
modules = vector<vector<bool> >(sz, vector<bool>(sz)); // Initially all white
modules = vector<vector<bool> >(sz, vector<bool>(sz)); // Initially all light
isFunction = vector<vector<bool> >(sz, vector<bool>(sz));
// Compute ECC, draw modules
@ -351,7 +351,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 ";
sb << (size + border * 2) << " " << (size + border * 2) << "\" stroke=\"none\">\n";
sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
sb << "\t<path d=\"";
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
if (getModule(x, y)) {
if (x != 0 || y != 0)
sb << " ";
sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z";
}
}
}
sb << "\" fill=\"#000000\"/>\n";
sb << "</svg>\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<size_t>(x);
size_t uy = static_cast<size_t>(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<uint8_t> &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<bool> &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<int>((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<int>((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1;
result += k * PENALTY_N4;
return result;
}
@ -698,7 +670,7 @@ vector<int> 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<int> 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<int,7> &runHistory) cons
int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &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<int,7> &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;
}

41
cpp/QrCode.hpp → cpp/qrcodegen.hpp Executable file → Normal file
View File

@ -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<bool> &dt);
public: QrSegment(const Mode &md, int numCh, const std::vector<bool> &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<bool> &&dt);
public: QrSegment(const Mode &md, int numCh, std::vector<bool> &&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<std::vector<bool> > 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<int,7> &runHistory) const;

View File

@ -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.
* <p>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&#xD7;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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n")
.append(String.format("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 %1$d %1$d\" stroke=\"none\">\n",
qr.size + brd * 2))
.append("\t<rect width=\"100%\" height=\"100%\" fill=\"" + lightColor + "\"/>\n")
.append("\t<path d=\"");
for (int y = 0; y < qr.size; y++) {
for (int x = 0; x < qr.size; x++) {
if (qr.getModule(x, y)) {
if (x != 0 || y != 0)
sb.append(" ");
sb.append(String.format("M%d,%dh1v1h-1z", x + brd, y + brd));
}
}
}
return sb
.append("\" fill=\"" + darkColor + "\"/>\n")
.append("</svg>\n")
.toString();
}
}

View File

@ -4,16 +4,43 @@
<groupId>io.nayuki</groupId>
<artifactId>qrcodegen</artifactId>
<version>1.6.0</version>
<version>1.7.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<release>9</release>
</configuration>
</execution>
<execution>
<id>java8-compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>1.8</source>
<target>1.8</target>
<excludes>
<exclude>module-info.java</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
@ -23,6 +50,7 @@
</checkModificationExcludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
@ -36,6 +64,7 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
@ -49,6 +78,7 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
@ -63,9 +93,11 @@
</execution>
</executions>
</plugin>
</plugins>
</build>
<name>QR Code generator library</name>
<description>High quality QR Code generator library</description>
<url>https://www.nayuki.io/page/qr-code-generator-library</url>

View File

@ -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.
* <p>Instances of this class represent an immutable square grid of black and white cells.
* <p>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.</p>
@ -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&#x2212;1 is the right edge
* @param y the y coordinate, where 0 is the top edge and size&#x2212;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.
* <p>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&#xD7;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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n")
.append(String.format("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 %1$d %1$d\" stroke=\"none\">\n",
size + brd * 2))
.append("\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n")
.append("\t<path d=\"");
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
if (getModule(x, y)) {
if (x != 0 || y != 0)
sb.append(" ");
sb.append(String.format("M%d,%dh1v1h-1z", x + brd, y + brd));
}
}
}
return sb
.append("\" fill=\"#000000\"/>\n")
.append("</svg>\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;
}

View File

@ -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<QrSegment> 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;
}
}

View File

@ -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<QrSegment> 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.

View File

@ -10,7 +10,7 @@
* <li><p>Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C</p></li>
* <li><p>Significantly shorter code but more documentation comments compared to competing libraries</p></li>
* <li><p>Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard</p></li>
* <li><p>Output formats: Raw modules/pixels of the QR symbol, SVG XML string, {@code BufferedImage} raster bitmap</p></li>
* <li><p>Output format: Raw modules/pixels of the QR symbol</p></li>
* <li><p>Encodes numeric and special-alphanumeric text in less space than general text</p></li>
* <li><p>Open source code under the permissive MIT License</p></li>
* </ul>
@ -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"));</pre>
* <p>Manual operation:</p>
* <pre style="margin-left:2em">import java.util.List;

View File

@ -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;
}

View File

@ -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()

View File

@ -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 """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none">
<rect width="100%" height="100%" fill="#FFFFFF"/>
<path d="{1}" fill="#000000"/>
</svg>
""".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()

View File

@ -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()

View File

@ -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<QrSegment> 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<QrSegment>
- Function make_eci(int assignval) -> QrSegment
- Constructor QrSegment(QrSegment.Mode mode, int numch, list<int> bitdata)
- Method get_mode() -> QrSegment.Mode
- Method get_num_chars() -> int
- Method get_data() -> list<int>
- 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 """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none">
<rect width="100%" height="100%" fill="#FFFFFF"/>
<path d="{1}" fill="#000000"/>
</svg>
""".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

View File

@ -1,2 +0,0 @@
[bdist_wheel]
universal = 1

View File

@ -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")

View File

@ -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/*"]

View File

@ -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<char> = "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) ...)

View File

@ -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 += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
result += "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
let dimension = qr.size().checked_add(border.checked_mul(2).unwrap()).unwrap();
result += &format!(
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {0} {0}\" stroke=\"none\">\n", dimension);
result += "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
result += "\t<path d=\"";
for y in 0 .. qr.size() {
for x in 0 .. qr.size() {
if qr.get_module(x, y) {
if x != 0 || y != 0 {
result += " ";
}
result += &format!("M{},{}h1v1h-1z", x + border, y + border);
}
}
}
result += "\" fill=\"#000000\"/>\n";
result += "</svg>\n";
result
}
// Prints the given QrCode object to the console.
fn print_qr(qr: &QrCode) {
let border: i32 = 4;

View File

@ -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::<u8>::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<QrSegment> = if isascii {
let chrs: Vec<char> = 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<char> = line.chars().collect();
assert_eq!(chrs.pop().unwrap(), '\n');
let line: String = chrs.iter().cloned().collect();
line.parse::<i16>().expect("Invalid number")
}
static ECC_LEVELS: [QrCodeEcc; 4] = [
QrCodeEcc::Low,
QrCodeEcc::Medium,
QrCodeEcc::Quartile,
QrCodeEcc::High,
];

View File

@ -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<char> = "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<bool>,
@ -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<Self,DataTooLong> {
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 += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
result += "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
let dimension = self.size.checked_add(border.checked_mul(2).unwrap()).unwrap();
result += &format!(
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {0} {0}\" stroke=\"none\">\n", dimension);
result += "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
result += "\t<path d=\"";
for y in 0 .. self.size {
for x in 0 .. self.size {
if self.get_module(x, y) {
if x != 0 || y != 0 {
result += " ";
}
result += &format!("M{},{}h1v1h-1z", x + border, y + border);
}
}
}
result += "\" fill=\"#000000\"/>\n";
result += "</svg>\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<u8> {
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<i32> {
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<i32> = (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<usize> {
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)
}

View File

@ -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

View File

@ -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;
}
</style>
</head>
@ -56,12 +71,12 @@
<p>(Are the JavaScript files missing?)</p>
<p>(The JavaScript code needs to be compiled from the TypeScript code.)</p>
</div>
<form id="loaded" style="display:none" action="#" method="get" onsubmit="return false;">
<table class="noborder" style="width:100%">
<form id="loaded" style="display:none" onsubmit="event.preventDefault();">
<table>
<tbody>
<tr>
<td><strong>Text string:</strong></td>
<td style="width:100%"><textarea placeholder="Enter your text to be put into the QR Code" id="text-input" style="width:100%; max-width:30em; height:5em; font-family:inherit"></textarea></td>
<td><textarea placeholder="Enter your text to be put into the QR Code" id="text-input" style="width:30em; height:5em"></textarea></td>
</tr>
<tr>
<td><strong>QR Code:</strong></td>
@ -97,6 +112,13 @@
<td>Scale:</td>
<td><input type="number" value="8" min="1" max="30" step="1" id="scale-input" style="width:4em"> pixels per module</td>
</tr>
<tr>
<td>Colors:</td>
<td>
Light = <input type="text" value="#FFFFFF" id="light-color-input" style="width:6em">,
dark = <input type="text" value="#000000" id="dark-color-input" style="width:6em">
</td>
</tr>
<tr>
<td>Version range:</td>
<td>

View File

@ -29,7 +29,7 @@ namespace app {
function initialize(): void {
getElem("loading").style.display = "none";
getElem("loaded").style.removeProperty("display");
let elems = document.querySelectorAll("input[type=number], textarea");
let elems = document.querySelectorAll("input[type=number], input[type=text], textarea");
for (let el of elems) {
if (el.id.indexOf("version-") != 0)
(el as any).oninput = redrawQrCode;
@ -86,22 +86,26 @@ namespace app {
// Draw image output
const border: number = parseInt(getInput("border-input").value, 10);
const lightColor: string = getInput("light-color-input").value;
const darkColor : string = getInput("dark-color-input" ).value;
if (border < 0 || border > 100)
return;
if (bitmapOutput) {
const scale: number = parseInt(getInput("scale-input").value, 10);
if (scale <= 0 || scale > 30)
return;
qr.drawCanvas(scale, border, canvas);
drawCanvas(qr, scale, border, lightColor, darkColor, canvas);
canvas.style.removeProperty("display");
} else {
const code: string = qr.toSvgString(border);
const code: string = toSvgString(qr, border, lightColor, darkColor);
const viewBox: string = (/ viewBox="([^"]*)"/.exec(code) as RegExpExecArray)[1];
const pathD: string = (/ d="([^"]*)"/.exec(code) as RegExpExecArray)[1];
svg.setAttribute("viewBox", viewBox);
(svg.querySelector("path") as Element).setAttribute("d", pathD);
(svg.querySelector("rect") as Element).setAttribute("fill", lightColor);
(svg.querySelector("path") as Element).setAttribute("fill", darkColor);
svg.style.removeProperty("display");
svgXml.value = qr.toSvgString(border);
svgXml.value = code;
}
// Returns a string to describe the given list of segments.
@ -148,6 +152,48 @@ namespace app {
}
// Draws the given QR Code, with the given module scale and border modules, onto the given HTML
// canvas element. The canvas's width and height is resized to (qr.size + border * 2) * scale.
// The drawn image is purely dark and light, and fully opaque.
// The scale must be a positive integer and the border must be a non-negative integer.
function drawCanvas(qr: qrcodegen.QrCode, scale: number, border: number, lightColor: string, darkColor: string, canvas: HTMLCanvasElement): void {
if (scale <= 0 || border < 0)
throw "Value out of range";
const width: number = (qr.size + border * 2) * scale;
canvas.width = width;
canvas.height = width;
let ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
for (let y = -border; y < qr.size + border; y++) {
for (let x = -border; x < qr.size + border; x++) {
ctx.fillStyle = qr.getModule(x, y) ? darkColor : lightColor;
ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale);
}
}
}
// 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.
function toSvgString(qr: qrcodegen.QrCode, border: number, lightColor: string, darkColor: string): string {
if (border < 0)
throw "Border must be non-negative";
let parts: Array<string> = [];
for (let y = 0; y < qr.size; y++) {
for (let x = 0; x < qr.size; x++) {
if (qr.getModule(x, y))
parts.push(`M${x + border},${y + border}h1v1h-1z`);
}
}
return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 ${qr.size + border * 2} ${qr.size + border * 2}" stroke="none">
<rect width="100%" height="100%" fill="${lightColor}"/>
<path d="${parts.join(" ")}" fill="${darkColor}"/>
</svg>
`
}
export function handleVersionMinMax(which: "min"|"max"): void {
const minElem: HTMLInputElement = getInput("version-min-input");
const maxElem: HTMLInputElement = getInput("version-max-input");

View File

@ -29,6 +29,9 @@
html {
font-family: sans-serif;
}
h1 {
text-align: center;
}
#output p {
margin-top: 0.5em;
margin-bottom: 0.5em;
@ -36,7 +39,13 @@
#output canvas {
display: block;
margin-bottom: 1.5em;
border: 1px solid #C0C0C0;
border: 0.2em solid #D0D0D0;
border-radius: 0.4em;
}
hr {
margin: 2em 0em;
border: none;
border-top: 0.1em solid #A0A0A0;
}
</style>
</head>

View File

@ -46,7 +46,7 @@ namespace app {
const text: string = "Hello, world!"; // User-supplied Unicode text
const errCorLvl: qrcodegen.QrCode.Ecc = qrcodegen.QrCode.Ecc.LOW; // Error correction level
const qr: qrcodegen.QrCode = qrcodegen.QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol
qr.drawCanvas(10, 4, appendCanvas("hello-world-QR")); // Draw it on screen
drawCanvas(qr, 10, 4, "#FFFFFF", "#000000", appendCanvas("hello-world-QR")); // Draw it on screen
}
@ -58,15 +58,15 @@ namespace app {
// Numeric mode encoding (3.33 bits per digit)
qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM);
qr.drawCanvas(13, 1, appendCanvas("pi-digits-QR"));
drawCanvas(qr, 13, 1, "#FFFFFF", "#000000", appendCanvas("pi-digits-QR"));
// Alphanumeric mode encoding (5.5 bits per character)
qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH);
qr.drawCanvas(10, 2, appendCanvas("alphanumeric-QR"));
drawCanvas(qr, 10, 2, "#FFFFFF", "#000000", appendCanvas("alphanumeric-QR"));
// Unicode text as UTF-8
qr = QrCode.encodeText("\u3053\u3093\u306B\u3061wa\u3001\u4E16\u754C\uFF01 \u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE);
qr.drawCanvas(10, 3, appendCanvas("unicode-QR"));
drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-QR"));
// Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland)
qr = QrCode.encodeText(
@ -77,7 +77,7 @@ namespace app {
+ "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);
qr.drawCanvas(6, 10, appendCanvas("alice-wonderland-QR"));
drawCanvas(qr, 6, 10, "#FFFFFF", "#000000", appendCanvas("alice-wonderland-QR"));
}
@ -93,32 +93,32 @@ namespace app {
const silver0: string = "THE SQUARE ROOT OF 2 IS 1.";
const silver1: string = "41421356237309504880168872420969807856967187537694807317667973799";
qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW);
qr.drawCanvas(10, 3, appendCanvas("sqrt2-monolithic-QR"));
drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("sqrt2-monolithic-QR"));
segs = [
QrSegment.makeAlphanumeric(silver0),
QrSegment.makeNumeric(silver1)];
qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW);
qr.drawCanvas(10, 3, appendCanvas("sqrt2-segmented-QR"));
drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("sqrt2-segmented-QR"));
// Illustration "golden"
const golden0: string = "Golden ratio \u03C6 = 1.";
const golden1: string = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374";
const golden2: string = "......";
qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW);
qr.drawCanvas(8, 5, appendCanvas("phi-monolithic-QR"));
drawCanvas(qr, 8, 5, "#FFFFFF", "#000000", appendCanvas("phi-monolithic-QR"));
segs = [
QrSegment.makeBytes(toUtf8ByteArray(golden0)),
QrSegment.makeNumeric(golden1),
QrSegment.makeAlphanumeric(golden2)];
qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW);
qr.drawCanvas(8, 5, appendCanvas("phi-segmented-QR"));
drawCanvas(qr, 8, 5, "#FFFFFF", "#000000", appendCanvas("phi-segmented-QR"));
// Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters
const madoka: string = "\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.encodeText(madoka, QrCode.Ecc.LOW);
qr.drawCanvas(9, 4, appendCanvas("madoka-utf8-QR"));
drawCanvas(qr, 9, 4, "#FFFFE0", "#303080", appendCanvas("madoka-utf8-QR"));
const kanjiCharBits: Array<number> = [ // Kanji mode encoding (13 bits per character)
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1,
@ -153,7 +153,7 @@ namespace app {
];
segs = [new QrSegment(QrSegment.Mode.KANJI, kanjiCharBits.length / 13, kanjiCharBits)];
qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW);
qr.drawCanvas(9, 4, appendCanvas("madoka-kanji-QR"));
drawCanvas(qr, 9, 4, "#E0F0FF", "#404040", appendCanvas("madoka-kanji-QR"));
}
@ -167,21 +167,21 @@ namespace app {
// Project Nayuki URL
segs = qrcodegen.QrSegment.makeSegments("https://www.nayuki.io/");
qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask
qr.drawCanvas(8, 6, appendCanvas("project-nayuki-automask-QR"));
drawCanvas(qr, 8, 6, "#E0FFE0", "#206020", appendCanvas("project-nayuki-automask-QR"));
qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3
qr.drawCanvas(8, 6, appendCanvas("project-nayuki-mask3-QR"));
drawCanvas(qr, 8, 6, "#FFE0E0", "#602020", appendCanvas("project-nayuki-mask3-QR"));
// Chinese text as UTF-8
segs = qrcodegen.QrSegment.makeSegments("\u7DAD\u57FA\u767E\u79D1\uFF08Wikipedia\uFF0C\u8046\u807Di/\u02CCw\u026Ak\u1D7B\u02C8pi\u02D0di.\u0259/\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");
qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0
qr.drawCanvas(10, 3, appendCanvas("unicode-mask0-QR"));
drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-mask0-QR"));
qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1
qr.drawCanvas(10, 3, appendCanvas("unicode-mask1-QR"));
drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-mask1-QR"));
qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5
qr.drawCanvas(10, 3, appendCanvas("unicode-mask5-QR"));
drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-mask5-QR"));
qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7
qr.drawCanvas(10, 3, appendCanvas("unicode-mask7-QR"));
drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-mask7-QR"));
}
@ -200,6 +200,26 @@ namespace app {
}
// Draws the given QR Code, with the given module scale and border modules, onto the given HTML
// canvas element. The canvas's width and height is resized to (qr.size + border * 2) * scale.
// The drawn image is purely dark and light, and fully opaque.
// The scale must be a positive integer and the border must be a non-negative integer.
function drawCanvas(qr: qrcodegen.QrCode, scale: number, border: number, lightColor: string, darkColor: string, canvas: HTMLCanvasElement): void {
if (scale <= 0 || border < 0)
throw "Value out of range";
const width: number = (qr.size + border * 2) * scale;
canvas.width = width;
canvas.height = width;
let ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
for (let y = -border; y < qr.size + border; y++) {
for (let x = -border; x < qr.size + border; x++) {
ctx.fillStyle = qr.getModule(x, y) ? darkColor : lightColor;
ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale);
}
}
}
function toUtf8ByteArray(str: string): Array<number> {
str = encodeURI(str);
let result: Array<number> = [];

View File

@ -1,130 +0,0 @@
/*
* QR Code generator test worker (TypeScript)
*
* 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.
*/
"use strict";
async function main(): Promise<void> {
while (true) {
// Read data or exit
const length: number = await input.readInt();
if (length == -1)
break;
let data: Array<number> = [];
for (let i = 0; i < length; i++)
data.push(await input.readInt());
// Read encoding parameters
const errCorLvl : number = await input.readInt();
const minVersion: number = await input.readInt();
const maxVersion: number = await input.readInt();
const mask : number = await input.readInt();
const boostEcl : number = await input.readInt();
// Make segments for encoding
let segs: Array<qrcodegen.QrSegment>;
if (data.every(b => b < 128)) { // Is ASCII
const s: string = data.map(b => String.fromCharCode(b)).join("");
segs = qrcodegen.QrSegment.makeSegments(s);
} else
segs = [qrcodegen.QrSegment.makeBytes(data)];
try { // Try to make QR Code symbol
const qr = qrcodegen.QrCode.encodeSegments(
segs, ECC_LEVELS[errCorLvl], minVersion, maxVersion, mask, boostEcl != 0);
// Print grid of modules
await printLine(qr.version);
for (let y = 0; y < qr.size; y++) {
for (let x = 0; x < qr.size; x++)
await printLine(qr.getModule(x, y) ? 1 : 0);
}
} catch (e) {
if (e == "Data too long")
await printLine(-1);
}
}
}
namespace input {
let queue: Array<string> = [];
let callback: ((line:string)=>void)|null = null;
const readline = require("readline");
let reader = readline.createInterface({
input: process.stdin,
terminal: false,
});
reader.on("line", (line: string) => {
queue.push(line);
if (callback !== null) {
callback(queue.shift() as string);
callback = null;
}
});
async function readLine(): Promise<string> {
return new Promise(resolve => {
if (callback !== null)
throw "Illegal state";
if (queue.length > 0)
resolve(queue.shift() as string);
else
callback = resolve;
});
}
export async function readInt(): Promise<number> {
let s = await readLine();
if (!/^-?\d+$/.test(s))
throw "Invalid number syntax";
return parseInt(s, 10);
}
}
async function printLine(x: Object): Promise<void> {
return new Promise(resolve =>
process.stdout.write(x + "\n", "utf-8", ()=>resolve()));
}
const ECC_LEVELS: Array<qrcodegen.QrCode.Ecc> = [
qrcodegen.QrCode.Ecc.LOW,
qrcodegen.QrCode.Ecc.MEDIUM,
qrcodegen.QrCode.Ecc.QUARTILE,
qrcodegen.QrCode.Ecc.HIGH,
];
main();

View File

@ -36,7 +36,7 @@ namespace qrcodegen {
/*
* 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.
@ -155,7 +155,7 @@ namespace qrcodegen {
// 21 and 177 (inclusive). This is equal to version * 4 + 17.
public readonly size: int;
// 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 readonly modules : Array<Array<boolean>> = [];
@ -196,7 +196,7 @@ namespace qrcodegen {
for (let i = 0; i < this.size; i++)
row.push(false);
for (let i = 0; i < this.size; i++) {
this.modules .push(row.slice()); // Initially all white
this.modules .push(row.slice()); // Initially all light
this.isFunction.push(row.slice());
}
@ -232,57 +232,13 @@ namespace qrcodegen {
/*-- Accessor methods --*/
// 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 getModule(x: int, y: int): boolean {
return 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x];
}
/*-- Public instance methods --*/
// Draws this QR Code, with the given module scale and border modules, onto the given HTML
// canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale.
// The drawn image is be purely black and white, and fully opaque.
// The scale must be a positive integer and the border must be a non-negative integer.
public drawCanvas(scale: int, border: int, canvas: HTMLCanvasElement): void {
if (scale <= 0 || border < 0)
throw "Value out of range";
const width: int = (this.size + border * 2) * scale;
canvas.width = width;
canvas.height = width;
let ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
for (let y = -border; y < this.size + border; y++) {
for (let x = -border; x < this.size + border; x++) {
ctx.fillStyle = this.getModule(x, y) ? "#000000" : "#FFFFFF";
ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale);
}
}
}
// 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 toSvgString(border: int): string {
if (border < 0)
throw "Border must be non-negative";
let parts: Array<string> = [];
for (let y = 0; y < this.size; y++) {
for (let x = 0; x < this.size; x++) {
if (this.getModule(x, y))
parts.push(`M${x + border},${y + border}h1v1h-1z`);
}
}
return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 ${this.size + border * 2} ${this.size + border * 2}" stroke="none">
<rect width="100%" height="100%" fill="#FFFFFF"/>
<path d="${parts.join(" ")}" fill="#000000"/>
</svg>
`
}
/*-- Private helper methods for constructor: Drawing function modules --*/
// Reads this object's version field, and draws and marks all function modules.
@ -341,7 +297,7 @@ namespace qrcodegen {
this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i));
for (let i = 8; i < 15; i++)
this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i));
this.setFunctionModule(8, this.size - 8, true); // Always black
this.setFunctionModule(8, this.size - 8, true); // Always dark
}
@ -397,8 +353,8 @@ namespace qrcodegen {
// Sets the color of a module and marks it as a function module.
// Only used by the constructor. Coordinates must be in bounds.
private setFunctionModule(x: int, y: int, isBlack: boolean): void {
this.modules[y][x] = isBlack;
private setFunctionModule(x: int, y: int, isDark: boolean): void {
this.modules[y][x] = isDark;
this.isFunction[y][x] = true;
}
@ -467,7 +423,7 @@ namespace qrcodegen {
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
}
}
}
@ -566,13 +522,13 @@ namespace qrcodegen {
}
}
// Balance of black and white modules
let black: int = 0;
// Balance of dark and light modules
let dark: int = 0;
for (const row of this.modules)
black = row.reduce((sum, color) => sum + (color ? 1 : 0), black);
const total: int = this.size * this.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)%
const k: int = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1;
dark = row.reduce((sum, color) => sum + (color ? 1 : 0), dark);
const total: int = this.size * this.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)%
const k: int = Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1;
result += k * QrCode.PENALTY_N4;
return result;
}
@ -589,7 +545,7 @@ namespace qrcodegen {
else {
const numAlign: int = Math.floor(this.version / 7) + 2;
const step: int = (this.version == 32) ? 26 :
Math.ceil((this.size - 13) / (numAlign*2 - 2)) * 2;
Math.ceil((this.version * 4 + 4) / (numAlign * 2 - 2)) * 2;
let result: Array<int> = [6];
for (let pos = this.size - 7; result.length < numAlign; pos -= step)
result.splice(1, 0, pos);
@ -686,7 +642,7 @@ namespace qrcodegen {
}
// 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 finderPenaltyCountPatterns(runHistory: Array<int>): int {
const n: int = runHistory[1];
@ -700,11 +656,11 @@ namespace qrcodegen {
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
private finderPenaltyTerminateAndCount(currentRunColor: boolean, currentRunLength: int, runHistory: Array<int>): int {
if (currentRunColor) { // Terminate black run
if (currentRunColor) { // Terminate dark run
this.finderPenaltyAddHistory(currentRunLength, runHistory);
currentRunLength = 0;
}
currentRunLength += this.size; // Add white border to final run
currentRunLength += this.size; // Add light border to final run
this.finderPenaltyAddHistory(currentRunLength, runHistory);
return this.finderPenaltyCountPatterns(runHistory);
}
@ -713,7 +669,7 @@ namespace qrcodegen {
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
private finderPenaltyAddHistory(currentRunLength: int, runHistory: Array<int>): void {
if (runHistory[0] == 0)
currentRunLength += this.size; // Add white border to initial run
currentRunLength += this.size; // Add light border to initial run
runHistory.pop();
runHistory.unshift(currentRunLength);
}
@ -800,7 +756,7 @@ namespace qrcodegen {
// Returns a segment representing the given string of decimal digits encoded in numeric mode.
public static makeNumeric(digits: string): QrSegment {
if (!this.NUMERIC_REGEX.test(digits))
if (!QrSegment.isNumeric(digits))
throw "String contains non-numeric characters";
let bb: Array<bit> = []
for (let i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration
@ -816,7 +772,7 @@ namespace qrcodegen {
// The characters allowed are: 0 to 9, A to Z (uppercase only), space,
// dollar, percent, asterisk, plus, hyphen, period, slash, colon.
public static makeAlphanumeric(text: string): QrSegment {
if (!this.ALPHANUMERIC_REGEX.test(text))
if (!QrSegment.isAlphanumeric(text))
throw "String contains unencodable characters in alphanumeric mode";
let bb: Array<bit> = []
let i: int;
@ -837,9 +793,9 @@ namespace qrcodegen {
// Select the most efficient segment encoding automatically
if (text == "")
return [];
else if (this.NUMERIC_REGEX.test(text))
else if (QrSegment.isNumeric(text))
return [QrSegment.makeNumeric(text)];
else if (this.ALPHANUMERIC_REGEX.test(text))
else if (QrSegment.isAlphanumeric(text))
return [QrSegment.makeAlphanumeric(text)];
else
return [QrSegment.makeBytes(QrSegment.toUtf8ByteArray(text))];
@ -866,6 +822,21 @@ namespace qrcodegen {
}
// 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 isNumeric(text: string): boolean {
return QrSegment.NUMERIC_REGEX.test(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
// (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
public static isAlphanumeric(text: string): boolean {
return QrSegment.ALPHANUMERIC_REGEX.test(text);
}
/*-- Constructor (low level) and fields --*/
// Creates a new QR Code segment with the given attributes and data.
@ -929,16 +900,11 @@ namespace qrcodegen {
/*-- Constants --*/
// Describes precisely all strings that are encodable in numeric mode. To test
// whether a string s is encodable: let ok: boolean = NUMERIC_REGEX.test(s);
// A string is encodable iff each character is in the range 0 to 9.
public static readonly NUMERIC_REGEX: RegExp = /^[0-9]*$/;
// Describes precisely all strings that are encodable in numeric mode.
private static readonly NUMERIC_REGEX: RegExp = /^[0-9]*$/;
// Describes precisely all strings that are encodable in alphanumeric mode. To test
// whether a string s is encodable: let ok: boolean = ALPHANUMERIC_REGEX.test(s);
// A string is encodable iff each character is in the following set: 0 to 9, A to Z
// (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
public static readonly ALPHANUMERIC_REGEX: RegExp = /^[A-Z0-9 $%*+.\/:-]*$/;
// Describes precisely all strings that are encodable in alphanumeric mode.
private static readonly ALPHANUMERIC_REGEX: RegExp = /^[A-Z0-9 $%*+.\/:-]*$/;
// The set of all legal characters in alphanumeric mode,
// where each character value maps to the index in the string.