function renderQRCode(qrVersion = 1) {
const maxSize = 300;
const svg = getSVG({ width: maxSize, height: maxSize });
const dim = 4 * qrVersion + 17 + QUIET_ZONE_SIZE * 2;
const rectSize = parseInt(maxSize / dim);
let data = d3.range(0, dim).map(function(i) {
return d3.range(0, dim).map(function(j) {
return ['gray', Math.random() < 0.5 ? 0 : 1];
});
});
function drawQRLine([i1, j1], [i2, j2], getColor) {
d3.range(i1, i2).map(function(i) {
d3.range(j1, j2).map(function(j) {
data[i][j] = getColor(i, j);
});
});
}
function drawQRRect([i, j], [rectWidth, rectHeight], getColor, strokeWidth) {
drawQRLine([i, j], [i + strokeWidth, j + rectHeight], getColor);
drawQRLine([i, j], [i + rectWidth, j + strokeWidth], getColor);
drawQRLine(
[i + rectWidth - strokeWidth, j],
[i + rectWidth, j + rectHeight],
getColor
);
drawQRLine(
[i, j + rectHeight - strokeWidth],
[i + rectWidth, j + rectHeight],
getColor
);
}
function drawQRSquaare([i, j], squareSize, getColor, strokeWidth) {
drawQRRect([i, j], [squareSize, squareSize], getColor, strokeWidth);
}
// Quiet Zone
drawQRRect([0, 0], [dim, dim], () => [COLOR.QUIET_ZONE, 0], QUIET_ZONE_SIZE);
// Position Detection Patterns (3) & Seperators for Position Detection Patterns (3)
const positionBoxGap = dim - QUIET_ZONE_SIZE * 2 - 7;
[[0, 0], [1, 0], [0, 1]].forEach(function([bi, bj]) {
const squareSize = 7;
const [i0, j0] = [
QUIET_ZONE_SIZE + bi * (positionBoxGap - 1) + 4 - (squareSize + 1) / 2,
QUIET_ZONE_SIZE + bj * (positionBoxGap - 1) + 4 - (squareSize + 1) / 2
];
drawQRSquaare([i0, j0], squareSize + 1, () => [COLOR.SEPERATOR, 0], 1);
[1, 3, 5, 7].forEach(function(squareSize) {
const [i0, j0] = [
QUIET_ZONE_SIZE + bi * positionBoxGap + 4 - (squareSize + 1) / 2,
QUIET_ZONE_SIZE + bj * positionBoxGap + 4 - (squareSize + 1) / 2
];
drawQRSquaare(
[i0, j0],
squareSize,
() => [COLOR.POSITION_BOX, squareSize === 5 ? 0 : 1],
1
);
});
});
const innerSize = dim - QUIET_ZONE_SIZE * 2 - 7 * 2 + 1;
const nAlignment = getAlignmentPatternCount(qrVersion);
d3.range(0, nAlignment).map(function(bi) {
d3.range(0, nAlignment).map(function(bj) {
if (
(bi === 0 && bj === 0) ||
(bi === 0 && bj === nAlignment - 1) ||
(bi === nAlignment - 1 && bj === 0)
) {
return;
}
[1, 3, 5].forEach(function(squareSize) {
const [i0, j0] = [
QUIET_ZONE_SIZE +
7 +
(innerSize * bi) / (nAlignment - 1) -
(squareSize + 1) / 2,
QUIET_ZONE_SIZE +
7 +
(innerSize * bj) / (nAlignment - 1) -
(squareSize + 1) / 2
];
drawQRSquaare(
[i0, j0],
squareSize,
() => [COLOR.ALIGNMENT, squareSize === 3 ? 0 : 1],
1
);
});
});
});
// Timing Patterns
const spanTiming = dim - QUIET_ZONE_SIZE * 2 - 17;
[[0, 1], [1, 0]].forEach(function([bi, bj]) {
const [i0, j0] = [
QUIET_ZONE_SIZE + 6 + bi * 2,
QUIET_ZONE_SIZE + 6 + bj * 2
];
drawQRLine(
[i0, j0],
[i0 + spanTiming * bi + 1, j0 + spanTiming * bj + 1],
(i, j) => [COLOR.TIMING, (i + j + 1) % 2]
);
});
// Format Information
const eclBits = [0, 0];
const maskBits = [1, 0, 1];
const dataBits = [].concat(eclBits, maskBits);
const gX10 = [1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1];
const bchR = div(pow(dataBits, 10), gX10)[1];
const totalBits = [].concat(dataBits, pad(smul(bchR, -1), 10));
const mask = [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0];
const formatBits = xor(totalBits, mask);
drawQRLine(
[QUIET_ZONE_SIZE + 8, QUIET_ZONE_SIZE],
[QUIET_ZONE_SIZE + 9, QUIET_ZONE_SIZE + 6],
(i, j) => [COLOR.FORMAT_INFO, formatBits[j - QUIET_ZONE_SIZE]]
);
drawQRLine(
[QUIET_ZONE_SIZE + 8, QUIET_ZONE_SIZE + 7],
[QUIET_ZONE_SIZE + 9, QUIET_ZONE_SIZE + 9],
(i, j) => [COLOR.FORMAT_INFO, formatBits[j - QUIET_ZONE_SIZE]]
);
drawQRLine(
[QUIET_ZONE_SIZE + 7, QUIET_ZONE_SIZE + 8],
[QUIET_ZONE_SIZE + 8, QUIET_ZONE_SIZE + 9],
(i, j) => [COLOR.FORMAT_INFO, formatBits[9]]
);
drawQRLine(
[QUIET_ZONE_SIZE, QUIET_ZONE_SIZE + 8],
[QUIET_ZONE_SIZE + 6, QUIET_ZONE_SIZE + 9],
(i, j) => [COLOR.FORMAT_INFO, formatBits[14 - (i - QUIET_ZONE_SIZE)]]
);
drawQRLine(
[dim - QUIET_ZONE_SIZE - 9, QUIET_ZONE_SIZE + 8],
[dim - QUIET_ZONE_SIZE, QUIET_ZONE_SIZE + 9],
(i, j) => [COLOR.FORMAT_INFO, formatBits[dim - QUIET_ZONE_SIZE - 1 - i]]
);
drawQRLine(
[QUIET_ZONE_SIZE + 8, dim - QUIET_ZONE_SIZE - 9],
[QUIET_ZONE_SIZE + 9, dim - QUIET_ZONE_SIZE],
(i, j) => [COLOR.FORMAT_INFO, formatBits[j - dim + QUIET_ZONE_SIZE + 15]]
);
// Version Information
const versionDataBits = pad(numToP(qrVersion), 6);
const gX12 = [1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1];
const bchRVersion = div(pow(versionDataBits, 12), gX12)[1];
const versionBits = [].concat(
versionDataBits,
pad(smul(bchRVersion, -1), 12)
);
drawQRLine(
[QUIET_ZONE_SIZE, dim - QUIET_ZONE_SIZE - 8 - 3],
[QUIET_ZONE_SIZE + 6, dim - QUIET_ZONE_SIZE - 8],
(i, j) => [
COLOR.VERSION_INFO,
versionBits[
(i - QUIET_ZONE_SIZE) * 3 + (j - (dim - QUIET_ZONE_SIZE - 8 - 3))
]
]
);
drawQRLine(
[dim - QUIET_ZONE_SIZE - 8 - 3, QUIET_ZONE_SIZE],
[dim - QUIET_ZONE_SIZE - 8, QUIET_ZONE_SIZE + 6],
(i, j) => [
COLOR.VERSION_INFO,
versionBits[
(j - QUIET_ZONE_SIZE) * 3 + (i - (dim - QUIET_ZONE_SIZE - 8 - 3))
]
]
);
// Data
const dataNumber = 123;
const dataNumberBits = pad(numToP(dataNumber), 10);
const charCountBits = pad(numToP(dataNumber.toString().length), 10);
const payloadDataBits = [].concat(
MODE_INDICATORS.Numeric,
charCountBits,
dataNumberBits,
MODE_INDICATORS.Terminator
);
console.debug('dataBits', payloadDataBits);
// Finalize
const IS_TEST_MODE = true;
d3.range(0, dim).map(function(i) {
d3.range(0, dim).map(function(j) {
const [x, y] = [i * rectSize, j * rectSize];
const [color, value] = data[i][j];
const style = IS_TEST_MODE
? {
fill: color,
stroke: 'white',
fillOpacity: 0.2 + value * 0.8
}
: { fill: 'black', stroke: 'none', fillOpacity: value };
drawRect(svg, [x, y], [rectSize, rectSize], style);
});
});
return svg.node();
}