mirror of
https://gitee.com/openharmony/third_party_qrcodegen
synced 2024-11-27 01:11:45 +00:00
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:
parent
67161552fb
commit
e9104a60b9
4
BUILD.gn
4
BUILD.gn
@ -25,7 +25,7 @@ if (defined(ohos_lite)) {
|
||||
} else {
|
||||
target_type = "shared_library"
|
||||
}
|
||||
sources = [ "cpp/QrCode.cpp" ]
|
||||
sources = [ "cpp/qrcodegen.cpp" ]
|
||||
include_dirs = [ "//third_party/qrcodegen/cpp" ]
|
||||
cflags = [ "-Wall" ]
|
||||
cflags_cc = cflags
|
||||
@ -37,7 +37,7 @@ if (defined(ohos_lite)) {
|
||||
}
|
||||
} else {
|
||||
ohos_static_library("qrcodegen_static") {
|
||||
sources = [ "cpp/QrCode.cpp" ]
|
||||
sources = [ "cpp/qrcodegen.cpp" ]
|
||||
include_dirs = [ "//third_party/qrcodegen/cpp" ]
|
||||
configs = [ ":qrcodegen_config" ]
|
||||
public_configs = [ ":libqrcodegen_config" ]
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
204
Readme.markdown
204
Readme.markdown
@ -18,7 +18,7 @@ Core features:
|
||||
* Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C
|
||||
* Significantly shorter code but more documentation comments compared to competing libraries
|
||||
* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard
|
||||
* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (TypeScript/JavaScript only)
|
||||
* Output format: Raw modules/pixels of the QR symbol
|
||||
* Detects finder-like penalty patterns more accurately than other implementations
|
||||
* Encodes numeric and special-alphanumeric text in less space than general text
|
||||
* Open source code under the permissive MIT License
|
||||
@ -43,139 +43,151 @@ Examples
|
||||
|
||||
Java language:
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import javax.imageio.ImageIO;
|
||||
import io.nayuki.qrcodegen.*;
|
||||
```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 = qr0.toImage(4, 10);
|
||||
ImageIO.write(img, "png", new File("qr-code.png"));
|
||||
// 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) ...)
|
||||
}
|
||||
// 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;
|
||||
```typescript
|
||||
// Name abbreviated for the sake of these examples here
|
||||
const QRC = qrcodegen.QrCode;
|
||||
|
||||
// Simple operation
|
||||
var qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM);
|
||||
var svg = qr0.toSvgString(4);
|
||||
// Simple operation
|
||||
const qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM);
|
||||
const svg = toSvgString(qr0, 4); // See qrcodegen-input-demo
|
||||
|
||||
// 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) ...)
|
||||
}
|
||||
// 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 *
|
||||
```python
|
||||
from qrcodegen import *
|
||||
|
||||
# Simple operation
|
||||
qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM)
|
||||
svg = qr0.to_svg_str(4)
|
||||
# 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) ...)
|
||||
# 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;
|
||||
```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 = qr0.toSvgString(4);
|
||||
// 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) ...)
|
||||
}
|
||||
// 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"
|
||||
```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;
|
||||
// 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) ...)
|
||||
}
|
||||
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;
|
||||
```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 = qr.to_svg_string(4);
|
||||
// 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) ...)
|
||||
}
|
||||
// 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
|
||||
|
@ -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)
|
||||
|
@ -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++;
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
98
cpp/QrCode.cpp → cpp/qrcodegen.cpp
Executable file → Normal 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
41
cpp/QrCode.hpp → cpp/qrcodegen.hpp
Executable file → Normal 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;
|
||||
|
125
java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java → java/QrCodeGeneratorDemo.java
Executable file → Normal file
125
java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java → java/QrCodeGeneratorDemo.java
Executable file → Normal 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×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();
|
||||
}
|
||||
|
||||
}
|
38
java/pom.xml
38
java/pom.xml
@ -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>
|
||||
|
@ -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−1 is the right edge
|
||||
* @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge
|
||||
* @return {@code true} if the coordinates are in bounds and the module
|
||||
* at that location is black, or {@code false} (white) otherwise
|
||||
* at that location is dark, or {@code false} (light) otherwise
|
||||
*/
|
||||
public boolean getModule(int x, int y) {
|
||||
return 0 <= x && x < size && 0 <= y && y < size && modules[y][x];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a raster image depicting this QR Code, with the specified module scale and border modules.
|
||||
* <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×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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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.
|
||||
|
@ -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;
|
||||
|
29
java/src/main/java/module-info.java
Normal file
29
java/src/main/java/module-info.java
Normal 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;
|
||||
|
||||
}
|
@ -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()
|
@ -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()
|
||||
|
||||
|
@ -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()
|
@ -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,43 +832,47 @@ 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.
|
||||
# Describes precisely all strings that are encodable in alphanumeric mode.
|
||||
_ALPHANUMERIC_REGEX: re.Pattern = re.compile(r"[A-Z0-9 $%*+./:-]*")
|
||||
|
||||
ALPHANUMERIC_REGEX = re.compile(r"[A-Z0-9 $%*+./:-]*\Z")
|
||||
|
||||
# (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))
|
||||
Mode.ALPHANUMERIC = Mode(0x2, ( 9, 11, 13))
|
||||
@ -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
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
[bdist_wheel]
|
||||
universal = 1
|
@ -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")
|
||||
|
@ -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/*"]
|
||||
|
@ -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) ...)
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
];
|
156
rust/src/lib.rs
156
rust/src/lib.rs
@ -38,7 +38,7 @@
|
||||
//! - Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C
|
||||
//! - Significantly shorter code but more documentation comments compared to competing libraries
|
||||
//! - Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard
|
||||
//! - Output formats: Raw modules/pixels of the QR symbol, SVG XML string
|
||||
//! - Output format: Raw modules/pixels of the QR symbol
|
||||
//! - Detects finder-like penalty patterns more accurately than other implementations
|
||||
//! - Encodes numeric and special-alphanumeric text in less space than general text
|
||||
//! - Open source code under the permissive MIT License
|
||||
@ -54,9 +54,11 @@
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate qrcodegen;
|
||||
//! use qrcodegen::Mask;
|
||||
//! use qrcodegen::QrCode;
|
||||
//! use qrcodegen::QrCodeEcc;
|
||||
//! use qrcodegen::QrSegment;
|
||||
//! use qrcodegen::Version;
|
||||
//! ```
|
||||
//!
|
||||
//! Simple operation:
|
||||
@ -64,7 +66,7 @@
|
||||
//! ```
|
||||
//! let qr = QrCode::encode_text("Hello, world!",
|
||||
//! QrCodeEcc::Medium).unwrap();
|
||||
//! let svg = qr.to_svg_string(4);
|
||||
//! let svg = to_svg_string(&qr, 4); // See qrcodegen-demo
|
||||
//! ```
|
||||
//!
|
||||
//! Manual operation:
|
||||
@ -72,8 +74,8 @@
|
||||
//! ```
|
||||
//! let chrs: Vec<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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
|
@ -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>
|
||||
|
@ -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> = [];
|
||||
|
@ -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();
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user