@juit/qrcode 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +211 -0
- package/NOTICE.md +13 -0
- package/README.md +175 -0
- package/dist/encode.cjs +147 -0
- package/dist/encode.cjs.map +6 -0
- package/dist/encode.d.ts +11 -0
- package/dist/encode.mjs +122 -0
- package/dist/encode.mjs.map +6 -0
- package/dist/images/path.cjs +145 -0
- package/dist/images/path.cjs.map +6 -0
- package/dist/images/path.d.ts +6 -0
- package/dist/images/path.mjs +120 -0
- package/dist/images/path.mjs.map +6 -0
- package/dist/images/pdf.cjs +96 -0
- package/dist/images/pdf.cjs.map +6 -0
- package/dist/images/pdf.d.ts +3 -0
- package/dist/images/pdf.mjs +71 -0
- package/dist/images/pdf.mjs.map +6 -0
- package/dist/images/png.cjs +86 -0
- package/dist/images/png.cjs.map +6 -0
- package/dist/images/png.d.ts +3 -0
- package/dist/images/png.mjs +61 -0
- package/dist/images/png.mjs.map +6 -0
- package/dist/images/svg.cjs +64 -0
- package/dist/images/svg.cjs.map +6 -0
- package/dist/images/svg.d.ts +54 -0
- package/dist/images/svg.mjs +38 -0
- package/dist/images/svg.mjs.map +6 -0
- package/dist/index.cjs +110 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.mjs +74 -0
- package/dist/index.mjs.map +6 -0
- package/dist/matrix.cjs +360 -0
- package/dist/matrix.cjs.map +6 -0
- package/dist/matrix.d.ts +3 -0
- package/dist/matrix.mjs +335 -0
- package/dist/matrix.mjs.map +6 -0
- package/dist/qrcode.cjs +183 -0
- package/dist/qrcode.cjs.map +6 -0
- package/dist/qrcode.d.ts +20 -0
- package/dist/qrcode.mjs +158 -0
- package/dist/qrcode.mjs.map +6 -0
- package/dist/utils/crc32.cjs +53 -0
- package/dist/utils/crc32.cjs.map +6 -0
- package/dist/utils/crc32.d.ts +9 -0
- package/dist/utils/crc32.mjs +28 -0
- package/dist/utils/crc32.mjs.map +6 -0
- package/dist/utils/dataurl.cjs +35 -0
- package/dist/utils/dataurl.cjs.map +6 -0
- package/dist/utils/dataurl.d.ts +1 -0
- package/dist/utils/dataurl.mjs +10 -0
- package/dist/utils/dataurl.mjs.map +6 -0
- package/dist/utils/deflate.cjs +39 -0
- package/dist/utils/deflate.cjs.map +6 -0
- package/dist/utils/deflate.d.ts +2 -0
- package/dist/utils/deflate.mjs +14 -0
- package/dist/utils/deflate.mjs.map +6 -0
- package/dist/utils/ecc.cjs +93 -0
- package/dist/utils/ecc.cjs.map +6 -0
- package/dist/utils/ecc.d.ts +2 -0
- package/dist/utils/ecc.mjs +68 -0
- package/dist/utils/ecc.mjs.map +6 -0
- package/dist/utils/merge.cjs +41 -0
- package/dist/utils/merge.cjs.map +6 -0
- package/dist/utils/merge.d.ts +2 -0
- package/dist/utils/merge.mjs +16 -0
- package/dist/utils/merge.mjs.map +6 -0
- package/package.json +61 -0
- package/src/encode.ts +180 -0
- package/src/images/path.ts +131 -0
- package/src/images/pdf.ts +76 -0
- package/src/images/png.ts +105 -0
- package/src/images/svg.ts +102 -0
- package/src/index.ts +147 -0
- package/src/matrix.ts +392 -0
- package/src/qrcode.ts +217 -0
- package/src/utils/crc32.ts +50 -0
- package/src/utils/dataurl.ts +7 -0
- package/src/utils/deflate.ts +17 -0
- package/src/utils/ecc.ts +95 -0
- package/src/utils/merge.ts +13 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { encodeQrCodeMessage } from './encode'
|
|
2
|
+
import { generatePdf } from './images/pdf'
|
|
3
|
+
import { generatePng } from './images/png'
|
|
4
|
+
import { generateSvg } from './images/svg'
|
|
5
|
+
import { generateQrCodeMatrix } from './matrix'
|
|
6
|
+
import { generateQrCodeData } from './qrcode'
|
|
7
|
+
import { generateDataUrl } from './utils/dataurl'
|
|
8
|
+
|
|
9
|
+
export { generatePdf } from './images/pdf'
|
|
10
|
+
export { generatePng } from './images/png'
|
|
11
|
+
export { generateSvg, generateSvgPath } from './images/svg'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The error correction level for QR codes (`L`, `M`, `Q`, or `H`)
|
|
15
|
+
*
|
|
16
|
+
* * `L`: approximately 7% of error correction capability
|
|
17
|
+
* * `M`: approximately 15% of error correction capability
|
|
18
|
+
* * `Q`: approximately 25% of error correction capability
|
|
19
|
+
* * `H`: approximately 30% of error correction capability
|
|
20
|
+
*/
|
|
21
|
+
export type ECLevel = 'L' | 'M' | 'Q' | 'H'
|
|
22
|
+
|
|
23
|
+
/** The structure of a _generated_ QR code */
|
|
24
|
+
export interface QrCode {
|
|
25
|
+
/** The version of the QR code (1...40) */
|
|
26
|
+
readonly version: number,
|
|
27
|
+
/** The error correction level for the QR code */
|
|
28
|
+
readonly ecLevel: ECLevel,
|
|
29
|
+
/** The size (in pixels) of the QR code */
|
|
30
|
+
readonly size: number,
|
|
31
|
+
/** The QR code matrix */
|
|
32
|
+
readonly matrix: readonly boolean[][]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Options for the generation of a {@link QrCode} */
|
|
36
|
+
export interface QrCodeGenerationOptions {
|
|
37
|
+
/** The error correction level for the QR code (default: `M`) */
|
|
38
|
+
ecLevel?: ECLevel,
|
|
39
|
+
/** Whether to optimize URLs in QR codes (default: `false`) */
|
|
40
|
+
url?: boolean,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Options to render a {@link QrCode} into an image */
|
|
44
|
+
export interface QrCodeImageOptions {
|
|
45
|
+
/**
|
|
46
|
+
* The number of pixels used for each dot in the matrix
|
|
47
|
+
* (default: `1` for PNG/SVG and `9` for PDF)
|
|
48
|
+
*/
|
|
49
|
+
scale?: number,
|
|
50
|
+
/**
|
|
51
|
+
* The size of the margin around the QR code in matrix modules
|
|
52
|
+
* (default: `4` as per the QR code specification - the _quiet area_)
|
|
53
|
+
*/
|
|
54
|
+
margin?: number,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** QR code options */
|
|
58
|
+
export interface QrCodeOptions extends QrCodeGenerationOptions, QrCodeImageOptions {}
|
|
59
|
+
|
|
60
|
+
/** Generate a {@link QrCode} from a string or binary message */
|
|
61
|
+
export function generateQrCode(message: string | Uint8Array, options?: QrCodeGenerationOptions): QrCode {
|
|
62
|
+
const { ecLevel = 'M', url = false } = { ...options }
|
|
63
|
+
|
|
64
|
+
const encoded = encodeQrCodeMessage(message, url)
|
|
65
|
+
const qrcode = generateQrCodeData(encoded, ecLevel)
|
|
66
|
+
const matrix = generateQrCodeMatrix(qrcode)
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
version: qrcode.version,
|
|
70
|
+
ecLevel: qrcode.ecLevel,
|
|
71
|
+
size: matrix.length,
|
|
72
|
+
matrix,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ===== SINGLE FORMAT (MORE "BUNDLE FRIENDLY" FOR TREE SHAKING) ============ */
|
|
77
|
+
|
|
78
|
+
/** Generate a QR code in PNG format from a string or binary message */
|
|
79
|
+
export async function generatePngQrCode(message: string | Uint8Array, options?: QrCodeOptions): Promise<Uint8Array> {
|
|
80
|
+
return await generatePng(generateQrCode(message, options), options)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Generate a QR code in PDF format from a string or binary message */
|
|
84
|
+
export async function generatePdfQrCode(message: string | Uint8Array, options?: QrCodeOptions): Promise<Uint8Array> {
|
|
85
|
+
return await generatePdf(generateQrCode(message, options), options)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Generate a QR code in SVG format from a string or binary message */
|
|
89
|
+
export function generateSvgQrCode(message: string | Uint8Array, options?: QrCodeOptions): string {
|
|
90
|
+
return generateSvg(generateQrCode(message, options), options)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Generate a QR code as a PNG data URL from a string or binary message */
|
|
94
|
+
export async function generatePngDataQrCode(message: string | Uint8Array, options?: QrCodeOptions): Promise<string> {
|
|
95
|
+
return generateDataUrl(await generatePng(generateQrCode(message, options), options), 'image/png')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Generate a QR code as a PDF data URL from a string or binary message */
|
|
99
|
+
export async function generatePdfDataQrCode(message: string | Uint8Array, options?: QrCodeOptions): Promise<string> {
|
|
100
|
+
return generateDataUrl(await generatePdf(generateQrCode(message, options), options), 'application/pdf')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Generate a QR code as a SVG data URL from a string or binary message */
|
|
104
|
+
export function generateSvgDataQrCode(message: string | Uint8Array, options?: QrCodeOptions): string {
|
|
105
|
+
return generateDataUrl(generateSvg(generateQrCode(message, options), options), 'image/svg+xml')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* ===== DEFINITELY NOT "BUNDLE FRIENDLY" =================================== */
|
|
109
|
+
|
|
110
|
+
/** Generate a QR code in PNG format from a string or binary message */
|
|
111
|
+
export function generate(message: string | Uint8Array, format: 'png', options?: QrCodeOptions): Promise<Uint8Array>
|
|
112
|
+
/** Generate a QR code in PDF format from a string or binary message */
|
|
113
|
+
export function generate(message: string | Uint8Array, format: 'pdf', options?: QrCodeOptions): Promise<Uint8Array>
|
|
114
|
+
/** Generate a QR code in SVG format from a string or binary message */
|
|
115
|
+
export function generate(message: string | Uint8Array, format: 'svg', options?: QrCodeOptions): Promise<string>
|
|
116
|
+
/** Generate a QR code as a PNG data URL from a string or binary message */
|
|
117
|
+
export function generate(message: string | Uint8Array, format: 'pngData', options?: QrCodeOptions): Promise<string>
|
|
118
|
+
/** Generate a QR code as a PDF data URL from a string or binary message */
|
|
119
|
+
export function generate(message: string | Uint8Array, format: 'pdfData', options?: QrCodeOptions): Promise<string>
|
|
120
|
+
/** Generate a QR code as a SVG data URL from a string or binary message */
|
|
121
|
+
export function generate(message: string | Uint8Array, format: 'svgData', options?: QrCodeOptions): Promise<string>
|
|
122
|
+
/** Generate a QR code as an image */
|
|
123
|
+
export async function generate<Format extends 'png' | 'pdf' | 'svg' | 'pngData' | 'pdfData' | 'svgData'>(
|
|
124
|
+
message: string | Uint8Array,
|
|
125
|
+
format: Format,
|
|
126
|
+
options?: QrCodeOptions,
|
|
127
|
+
): Promise<Format extends 'png' ? Uint8Array : Format extends 'pdf' ? Uint8Array : string>
|
|
128
|
+
|
|
129
|
+
// Overloaded function implementations
|
|
130
|
+
export async function generate(
|
|
131
|
+
message: string | Uint8Array,
|
|
132
|
+
format: 'png' | 'pdf' | 'svg' | 'pngData' | 'pdfData' | 'svgData',
|
|
133
|
+
options?: QrCodeOptions,
|
|
134
|
+
): Promise<Uint8Array | string> {
|
|
135
|
+
switch (format) {
|
|
136
|
+
// plain images
|
|
137
|
+
case 'png': return await generatePngQrCode(message, options)
|
|
138
|
+
case 'pdf': return await generatePdfQrCode(message, options)
|
|
139
|
+
case 'svg': return generateSvgQrCode(message, options)
|
|
140
|
+
// images as data URLs
|
|
141
|
+
case 'pngData': return await generatePngDataQrCode(message, options)
|
|
142
|
+
case 'pdfData': return await generatePdfDataQrCode(message, options)
|
|
143
|
+
case 'svgData': return generateSvgDataQrCode(message, options)
|
|
144
|
+
// coverage ignore next
|
|
145
|
+
default: throw new Error(`Unsupported format "${format}"`)
|
|
146
|
+
}
|
|
147
|
+
}
|
package/src/matrix.ts
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import type { ECLevel } from './index'
|
|
2
|
+
import type { QrCodeData } from './qrcode'
|
|
3
|
+
|
|
4
|
+
/* ========================================================================== *
|
|
5
|
+
* INTERNALS *
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
// Initialize a matrix with all zeros
|
|
9
|
+
function init(version: number): number[][] {
|
|
10
|
+
const n = (version * 4) + 17
|
|
11
|
+
const matrix = new Array<number[]>(n)
|
|
12
|
+
for (let i = 0; i < n; i++) {
|
|
13
|
+
matrix[i] = new Array<number>(n).fill(0)
|
|
14
|
+
}
|
|
15
|
+
return matrix
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* ========================================================================== */
|
|
19
|
+
|
|
20
|
+
// Put finders into matrix
|
|
21
|
+
function fillFinders(matrix: number[][]): void {
|
|
22
|
+
const n = matrix.length
|
|
23
|
+
for (let i = -3; i <= 3; i++) {
|
|
24
|
+
for (let j = -3; j <= 3; j++) {
|
|
25
|
+
const max = Math.max(i, j)
|
|
26
|
+
const min = Math.min(i, j)
|
|
27
|
+
const pixel = (max == 2 && min >= -2) || (min == -2 && max <= 2) ? 0x80 : 0x81
|
|
28
|
+
matrix[3 + i]![3 + j] = pixel
|
|
29
|
+
matrix[3 + i]![n - 4 + j] = pixel
|
|
30
|
+
matrix[n - 4 + i]![3 + j] = pixel
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
for (let i = 0; i < 8; i++) {
|
|
34
|
+
matrix[7]![i] = matrix[i]![7] =
|
|
35
|
+
matrix[7]![n - i - 1] = matrix[i]![n - 8] =
|
|
36
|
+
matrix[n - 8]![i] = matrix[n - 1 - i]![7] = 0x80
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ========================================================================== */
|
|
41
|
+
|
|
42
|
+
// Put align and timinig
|
|
43
|
+
function fillAlignAndTiming(matrix: number[][]): void {
|
|
44
|
+
const n = matrix.length
|
|
45
|
+
if (n > 21) {
|
|
46
|
+
const len = n - 13
|
|
47
|
+
let delta = Math.round(len / Math.ceil(len / 28))
|
|
48
|
+
// coverage ignore if // can't seem to cover it...
|
|
49
|
+
if (delta % 2) delta++
|
|
50
|
+
const res = []
|
|
51
|
+
for (let p = len + 6; p > 10; p -= delta) {
|
|
52
|
+
res.unshift(p)
|
|
53
|
+
}
|
|
54
|
+
res.unshift(6)
|
|
55
|
+
for (let i = 0; i < res.length; i++) {
|
|
56
|
+
for (let j = 0; j < res.length; j++) {
|
|
57
|
+
const x = res[i]!
|
|
58
|
+
const y = res[j]!
|
|
59
|
+
if (matrix[x]![y]) continue
|
|
60
|
+
for (let r = -2; r <=2; r++) {
|
|
61
|
+
for (let c = -2; c <=2; c++) {
|
|
62
|
+
const max = Math.max(r, c)
|
|
63
|
+
const min = Math.min(r, c)
|
|
64
|
+
const pixel = (max == 1 && min >= -1) || (min == -1 && max <= 1) ? 0x80 : 0x81
|
|
65
|
+
matrix[x + r]![y + c] = pixel
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (let i = 8; i < n - 8; i++) {
|
|
72
|
+
matrix[6]![i] = matrix[i]![6] = i % 2 ? 0x80 : 0x81
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ========================================================================== */
|
|
77
|
+
|
|
78
|
+
// Fill reserved areas with zeroes
|
|
79
|
+
function fillStub(matrix: number[][]): void {
|
|
80
|
+
const n = matrix.length
|
|
81
|
+
for (let i = 0; i < 8; i++) {
|
|
82
|
+
if (i != 6) {
|
|
83
|
+
matrix[8]![i] = matrix[i]![8] = 0x80
|
|
84
|
+
}
|
|
85
|
+
matrix[8]![n - 1 - i] = 0x80
|
|
86
|
+
matrix[n - 1 - i]![8] = 0x80
|
|
87
|
+
}
|
|
88
|
+
matrix[8]![8] = 0x80
|
|
89
|
+
matrix[n - 8]![8] = 0x81
|
|
90
|
+
|
|
91
|
+
if (n < 45) return
|
|
92
|
+
|
|
93
|
+
for (let i = n - 11; i < n - 8; i++) {
|
|
94
|
+
for (let j = 0; j < 6; j++) {
|
|
95
|
+
matrix[i]![j] = matrix[j]![i] = 0x80
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ========================================================================== */
|
|
101
|
+
|
|
102
|
+
// Fill reserved areas
|
|
103
|
+
const fillReserved = ((): ((matrix: number[][], ec_level: ECLevel, mask: number) => void) => {
|
|
104
|
+
const FORMATS = new Array<number>(32)
|
|
105
|
+
const VERSIONS = new Array<number>(40)
|
|
106
|
+
|
|
107
|
+
const gf15 = 0x0537
|
|
108
|
+
const gf18 = 0x1f25
|
|
109
|
+
const formatsMask = 0x5412
|
|
110
|
+
|
|
111
|
+
for (let format = 0; format < 32; format++) {
|
|
112
|
+
let res = format << 10
|
|
113
|
+
for (let i = 5; i > 0; i--) {
|
|
114
|
+
if (res >>> (9 + i)) {
|
|
115
|
+
res = res ^ (gf15 << (i - 1))
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
FORMATS[format] = (res | (format << 10)) ^ formatsMask
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (let version = 7; version <= 40; version++) {
|
|
122
|
+
let res = version << 12
|
|
123
|
+
for (let i = 6; i > 0; i--) {
|
|
124
|
+
if (res >>> (11 + i)) {
|
|
125
|
+
res = res ^ (gf18 << (i - 1))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
VERSIONS[version] = (res | (version << 12))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const EC_LEVELS = { L: 1, M: 0, Q: 3, H: 2 }
|
|
132
|
+
|
|
133
|
+
return function fillReserved(matrix: number[][], ecLevel: ECLevel, mask: number) {
|
|
134
|
+
const N = matrix.length
|
|
135
|
+
const format = FORMATS[EC_LEVELS[ecLevel] << 3 | mask]!
|
|
136
|
+
function _f(k: number): number {
|
|
137
|
+
return format >> k & 1 ? 0x81 : 0x80
|
|
138
|
+
}
|
|
139
|
+
for (let i = 0; i < 8; i++) {
|
|
140
|
+
matrix[8]![N - 1 - i] = _f(i)
|
|
141
|
+
if (i < 6) matrix[i]![8] = _f(i)
|
|
142
|
+
}
|
|
143
|
+
for (let i = 8; i < 15; i++) {
|
|
144
|
+
matrix[N - 15 + i]![8] = _f(i)
|
|
145
|
+
if (i > 8) matrix[8]![14 - i] = _f(i)
|
|
146
|
+
}
|
|
147
|
+
matrix[7]![8] = _f(6)
|
|
148
|
+
matrix[8]![8] = _f(7)
|
|
149
|
+
matrix[8]![7] = _f(8)
|
|
150
|
+
|
|
151
|
+
const version = VERSIONS[(N - 17)/4]!
|
|
152
|
+
if (!version) return
|
|
153
|
+
|
|
154
|
+
function _v(k: number): number {
|
|
155
|
+
return version >> k & 1 ? 0x81 : 0x80
|
|
156
|
+
}
|
|
157
|
+
for (let i = 0; i < 6; i++) {
|
|
158
|
+
for (let j = 0; j < 3; j++) {
|
|
159
|
+
matrix[N - 11 + j]![i] = matrix[i]![N - 11 + j] = _v(i * 3 + j)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
})()
|
|
164
|
+
|
|
165
|
+
/* ========================================================================== */
|
|
166
|
+
|
|
167
|
+
// Fill data
|
|
168
|
+
const fillData = ((): ((matrix: number[][], data: any, mask: number) => void) => {
|
|
169
|
+
const MASK_FUNCTIONS = [
|
|
170
|
+
function(i: number, j: number): boolean {
|
|
171
|
+
return (i + j) % 2 == 0
|
|
172
|
+
},
|
|
173
|
+
function(i: number, j: number): boolean {
|
|
174
|
+
void j
|
|
175
|
+
return i % 2 == 0
|
|
176
|
+
},
|
|
177
|
+
function(i: number, j: number): boolean {
|
|
178
|
+
void i
|
|
179
|
+
return j % 3 == 0
|
|
180
|
+
},
|
|
181
|
+
function(i: number, j: number): boolean {
|
|
182
|
+
return (i + j) % 3 == 0
|
|
183
|
+
},
|
|
184
|
+
function(i: number, j: number): boolean {
|
|
185
|
+
return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0
|
|
186
|
+
},
|
|
187
|
+
function(i: number, j: number): boolean {
|
|
188
|
+
return (i * j) % 2 + (i * j) % 3 == 0
|
|
189
|
+
},
|
|
190
|
+
function(i: number, j: number): boolean {
|
|
191
|
+
return ( (i * j) % 2 + (i * j) % 3) % 2 == 0
|
|
192
|
+
},
|
|
193
|
+
function(i: number, j: number): boolean {
|
|
194
|
+
return ( (i * j) % 3 + (i + j) % 2) % 2 == 0
|
|
195
|
+
},
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
return function fillData(matrix: number[][], data: QrCodeData, mask: number): void {
|
|
199
|
+
const N = matrix.length
|
|
200
|
+
let row: number
|
|
201
|
+
let col: number
|
|
202
|
+
let dir = -1
|
|
203
|
+
row = col = N - 1
|
|
204
|
+
const maskFn = MASK_FUNCTIONS[mask]!
|
|
205
|
+
let len = data.blockData[data.blockData.length - 1]!.length
|
|
206
|
+
|
|
207
|
+
for (let i = 0; i < len; i++) {
|
|
208
|
+
for (let b = 0; b < data.blockData.length; b++) {
|
|
209
|
+
if (data.blockData[b]!.length <= i) continue
|
|
210
|
+
put(data.blockData[b]![i]!)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
len = data.ecData[0]!.length
|
|
215
|
+
for (let i = 0; i < len; i++) {
|
|
216
|
+
for (let b = 0; b < data.ecData.length; b++) {
|
|
217
|
+
put(data.ecData[b]![i]!)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (col > -1) {
|
|
222
|
+
do {
|
|
223
|
+
matrix[row]![col] = maskFn(row, col) ? 1 : 0
|
|
224
|
+
} while (next())
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function put(byte: number): void {
|
|
228
|
+
for (let mask = 0x80; mask; mask = mask >> 1) {
|
|
229
|
+
let pixel = !!(mask & byte)
|
|
230
|
+
if (maskFn(row, col)) pixel = !pixel
|
|
231
|
+
matrix[row]![col] = pixel ? 1 : 0
|
|
232
|
+
next()
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function next(): boolean {
|
|
237
|
+
do {
|
|
238
|
+
if ((col % 2) ^ (col < 6 ? 1 : 0)) {
|
|
239
|
+
if (dir < 0 && row == 0 || dir > 0 && row == N - 1) {
|
|
240
|
+
col--
|
|
241
|
+
dir = -dir
|
|
242
|
+
} else {
|
|
243
|
+
col++
|
|
244
|
+
row += dir
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
col--
|
|
248
|
+
}
|
|
249
|
+
if (col == 6) {
|
|
250
|
+
col--
|
|
251
|
+
}
|
|
252
|
+
if (col < 0) {
|
|
253
|
+
return false
|
|
254
|
+
}
|
|
255
|
+
} while (matrix[row]![col]! & 0xf0)
|
|
256
|
+
return true
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
})()
|
|
260
|
+
|
|
261
|
+
/* ========================================================================== */
|
|
262
|
+
|
|
263
|
+
// Calculate penalty
|
|
264
|
+
function calculatePenalty(matrix: number[][]): number {
|
|
265
|
+
const N = matrix.length
|
|
266
|
+
let penalty = 0
|
|
267
|
+
// Rule 1
|
|
268
|
+
for (let i = 0; i < N; i++) {
|
|
269
|
+
let pixel = matrix[i]![0]! & 1
|
|
270
|
+
let len = 1
|
|
271
|
+
for (let j = 1; j < N; j++) {
|
|
272
|
+
const p = matrix[i]![j]! & 1
|
|
273
|
+
if (p == pixel) {
|
|
274
|
+
len++
|
|
275
|
+
continue
|
|
276
|
+
}
|
|
277
|
+
if (len >= 5) {
|
|
278
|
+
penalty += len - 2
|
|
279
|
+
}
|
|
280
|
+
pixel = p
|
|
281
|
+
len = 1
|
|
282
|
+
}
|
|
283
|
+
if (len >= 5) {
|
|
284
|
+
penalty += len - 2
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
for (let j = 0; j < N; j++) {
|
|
288
|
+
let pixel = matrix[0]![j]! & 1
|
|
289
|
+
let len = 1
|
|
290
|
+
for (let i = 1; i < N; i++) {
|
|
291
|
+
const p = matrix[i]![j]! & 1
|
|
292
|
+
if (p == pixel) {
|
|
293
|
+
len++
|
|
294
|
+
continue
|
|
295
|
+
}
|
|
296
|
+
if (len >= 5) {
|
|
297
|
+
penalty += len - 2
|
|
298
|
+
}
|
|
299
|
+
pixel = p
|
|
300
|
+
len = 1
|
|
301
|
+
}
|
|
302
|
+
if (len >= 5) {
|
|
303
|
+
penalty += len - 2
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Rule 2
|
|
308
|
+
for (let i = 0; i < N - 1; i++) {
|
|
309
|
+
for (let j = 0; j < N - 1; j++) {
|
|
310
|
+
const s = matrix[i]![j]! + matrix[i]![j + 1]! + matrix[i + 1]![j]! + matrix[i + 1]![j + 1]! & 7
|
|
311
|
+
if (s == 0 || s == 4) {
|
|
312
|
+
penalty += 3
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Rule 3
|
|
318
|
+
let i: number
|
|
319
|
+
let j: number
|
|
320
|
+
|
|
321
|
+
function _i(k: number): number {
|
|
322
|
+
return matrix[i]![j + k]! & 1
|
|
323
|
+
}
|
|
324
|
+
function _j(k: number): number {
|
|
325
|
+
return matrix[i + k]![j]! & 1
|
|
326
|
+
}
|
|
327
|
+
for (i = 0; i < N; i++) {
|
|
328
|
+
for (j = 0; j < N; j++) {
|
|
329
|
+
if (j < N - 6 && _i(0) && !_i(1) && _i(2) && _i(3) && _i(4) && !_i(5) && _i(6)) {
|
|
330
|
+
if (j >= 4 && !(_i(-4) || _i(-3) || _i(-2) || _i(-1))) {
|
|
331
|
+
penalty += 40
|
|
332
|
+
}
|
|
333
|
+
if (j < N - 10 && !(_i(7) || _i(8) || _i(9) || _i(10))) {
|
|
334
|
+
penalty += 40
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (i < N - 6 && _j(0) && !_j(1) && _j(2) && _j(3) && _j(4) && !_j(5) && _j(6)) {
|
|
339
|
+
if (i >= 4 && !(_j(-4) || _j(-3) || _j(-2) || _j(-1))) {
|
|
340
|
+
penalty += 40
|
|
341
|
+
}
|
|
342
|
+
if (i < N - 10 && !(_j(7) || _j(8) || _j(9) || _j(10))) {
|
|
343
|
+
penalty += 40
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Rule 4
|
|
350
|
+
let numDark = 0
|
|
351
|
+
for (let i = 0; i < N; i++) {
|
|
352
|
+
for (let j = 0; j < N; j++) {
|
|
353
|
+
if (matrix[i]![j]! & 1) numDark++
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
penalty += 10 * Math.floor(Math.abs(10 - 20 * numDark/(N * N)))
|
|
357
|
+
|
|
358
|
+
return penalty
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* ========================================================================== *
|
|
362
|
+
* EXPORTED *
|
|
363
|
+
* ========================================================================== */
|
|
364
|
+
|
|
365
|
+
/** Generate a matrix from a QR code data structure */
|
|
366
|
+
export function generateQrCodeMatrix(code: QrCodeData): boolean[][] {
|
|
367
|
+
const matrix = init(code.version)
|
|
368
|
+
fillFinders(matrix)
|
|
369
|
+
fillAlignAndTiming(matrix)
|
|
370
|
+
fillStub(matrix)
|
|
371
|
+
|
|
372
|
+
let penalty = Infinity
|
|
373
|
+
let bestMask = 0
|
|
374
|
+
for (let mask = 0; mask < 8; mask++) {
|
|
375
|
+
fillData(matrix, code, mask)
|
|
376
|
+
fillReserved(matrix, code.ecLevel, mask)
|
|
377
|
+
const p = calculatePenalty(matrix)
|
|
378
|
+
if (p < penalty) {
|
|
379
|
+
penalty = p
|
|
380
|
+
bestMask = mask
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
fillData(matrix, code, bestMask)
|
|
385
|
+
fillReserved(matrix, code.ecLevel, bestMask)
|
|
386
|
+
|
|
387
|
+
return matrix.map((row) => {
|
|
388
|
+
return row.map((cell) => {
|
|
389
|
+
return !! (cell & 1)
|
|
390
|
+
})
|
|
391
|
+
})
|
|
392
|
+
}
|