From 8be33ed3f6a65909f9a00d8b3f8f7d4fe31f12f6 Mon Sep 17 00:00:00 2001 From: florian Date: Thu, 27 Aug 2026 15:19:52 +0200 Subject: [PATCH] =?UTF-8?q?Initial=20commit:=20QR=20code=20generator=20(en?= =?UTF-8?q?codage=20et=20rendu=20c=C3=B4t=C3=A9=20navigateur)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + Dockerfile | 16 + LICENSE | 21 + README.md | 42 + app/app.js | 129 +++ app/index.html | 81 ++ app/style.css | 237 ++++ app/vendor/qrcode.min.js | 2297 ++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 16 + docs/screenshot.png | Bin 0 -> 73204 bytes nginx.conf | 23 + 11 files changed, 2864 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/app.js create mode 100644 app/index.html create mode 100644 app/style.css create mode 100644 app/vendor/qrcode.min.js create mode 100644 docker-compose.yml create mode 100644 docs/screenshot.png create mode 100644 nginx.conf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9fa88ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bbb54bb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +# Image "unprivileged" officielle : nginx tourne nativement en non-root, écoute sur un port >1024 +FROM nginxinc/nginx-unprivileged:1.27-alpine + +USER root +RUN rm -rf /usr/share/nginx/html/* /etc/nginx/conf.d/default.conf + +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY app/ /usr/share/nginx/html/ + +RUN chown -R nginx:nginx /usr/share/nginx/html + +USER nginx +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget -qO- http://127.0.0.1:8080/healthz || exit 1 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7d3a67a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Florian Utiel + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..773fa4d --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# QR Code Generator + +Générateur de QR codes entièrement statique, sans backend. + +L'encodage et le rendu du QR code se font **intégralement dans le navigateur** : aucun texte, lien ou logo n'est jamais envoyé à un serveur. + +## Aperçu + +![Aperçu du générateur](docs/screenshot.png) + +## Fonctionnalités + +- Encodage de texte libre ou d'URL en QR code, côté client, via [qrcode-generator](https://github.com/kazuhikoarase/qrcode-generator) +- Niveaux de correction d'erreur configurables (L / M / Q / H) +- Couleurs de modules et de fond personnalisables +- Superposition d'un logo au centre du QR code (force automatiquement la correction d'erreur au niveau H pour rester scannable) +- Export du QR code en PNG +- Aucune dépendance réseau au runtime (CSP `connect-src 'none'`) + +## Déploiement + +```bash +docker compose up -d --build +``` + +L'application est alors disponible sur `http://:8091/`. + +## Stack technique + +- HTML / CSS / JavaScript vanilla, aucun build step +- [qrcode-generator](https://github.com/kazuhikoarase/qrcode-generator) pour l'encodage QR +- Rendu et superposition du logo via l'API Canvas du navigateur +- Servi par `nginx-unprivileged` (image non-root) +- Conteneur durci : `read_only`, `cap_drop: ALL`, `no-new-privileges`, en-têtes de sécurité (CSP, X-Frame-Options, etc.) + +## Sécurité + +Le contenu encodé et les images de logo restent uniquement dans le navigateur (traitement via l'API Canvas / FileReader). Le serveur ne fait que servir des fichiers statiques. + +## Licence + +Ce projet est distribué sous licence [MIT](LICENSE). diff --git a/app/app.js b/app/app.js new file mode 100644 index 0000000..233416f --- /dev/null +++ b/app/app.js @@ -0,0 +1,129 @@ +(() => { + "use strict"; + + const textEl = document.getElementById("qr-text"); + const cellSizeEl = document.getElementById("qr-cell-size"); + const correctionEl = document.getElementById("qr-correction"); + const fgColorEl = document.getElementById("qr-fg-color"); + const bgColorEl = document.getElementById("qr-bg-color"); + const logoInputEl = document.getElementById("qr-logo-input"); + const logoClearBtn = document.getElementById("qr-logo-clear"); + const generateBtn = document.getElementById("generate-btn"); + const downloadBtn = document.getElementById("download-btn"); + const canvas = document.getElementById("qr-canvas"); + const statusEl = document.getElementById("qr-status"); + const ctx = canvas.getContext("2d"); + + let logoImage = null; + + function readLogoFile(file) { + if (!file) return; + const reader = new FileReader(); + reader.onload = () => { + const img = new Image(); + img.onload = () => { + logoImage = img; + render(); + }; + img.src = reader.result; + }; + reader.readAsDataURL(file); + } + + logoInputEl.addEventListener("change", (e) => { + readLogoFile(e.target.files[0]); + }); + + logoClearBtn.addEventListener("click", () => { + logoImage = null; + logoInputEl.value = ""; + render(); + }); + + function render() { + const text = textEl.value; + statusEl.classList.remove("error"); + + if (!text) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + canvas.width = 280; + canvas.height = 280; + statusEl.textContent = "Saisissez un texte ou une URL pour générer le QR code."; + return; + } + + const cellSize = parseInt(cellSizeEl.value, 10) || 8; + const requestedLevel = correctionEl.value; + const level = logoImage ? "H" : requestedLevel; + const fgColor = fgColorEl.value || "#000000"; + const bgColor = bgColorEl.value || "#ffffff"; + const margin = 4; // modules + + try { + const qr = qrcode(0, level); + qr.addData(text); + qr.make(); + + const moduleCount = qr.getModuleCount(); + const size = (moduleCount + margin * 2) * cellSize; + canvas.width = size; + canvas.height = size; + + ctx.imageSmoothingEnabled = false; + ctx.fillStyle = bgColor; + ctx.fillRect(0, 0, size, size); + + ctx.fillStyle = fgColor; + for (let row = 0; row < moduleCount; row++) { + for (let col = 0; col < moduleCount; col++) { + if (qr.isDark(row, col)) { + ctx.fillRect((col + margin) * cellSize, (row + margin) * cellSize, cellSize, cellSize); + } + } + } + + if (logoImage) { + const logoSize = size * 0.22; + const pad = logoSize * 0.14; + const x = (size - logoSize) / 2; + const y = (size - logoSize) / 2; + + ctx.fillStyle = bgColor; + ctx.fillRect(x - pad, y - pad, logoSize + pad * 2, logoSize + pad * 2); + + ctx.imageSmoothingEnabled = true; + ctx.drawImage(logoImage, x, y, logoSize, logoSize); + } + + statusEl.textContent = logoImage + ? `QR code généré (correction ${level}, forcée par le logo).` + : `QR code généré (correction ${level}).`; + } catch (err) { + statusEl.textContent = "Erreur : " + (err.message || "contenu trop long pour être encodé."); + statusEl.classList.add("error"); + } + } + + function downloadPng() { + canvas.toBlob((blob) => { + if (!blob) return; + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "qrcode.png"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, "image/png"); + } + + [textEl, cellSizeEl, correctionEl, fgColorEl, bgColorEl].forEach((el) => { + el.addEventListener("input", render); + }); + + generateBtn.addEventListener("click", render); + downloadBtn.addEventListener("click", downloadPng); + + render(); +})(); diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..2191801 --- /dev/null +++ b/app/index.html @@ -0,0 +1,81 @@ + + + + + + QR Code Generator + + + +
+

QR Code Generator

+

Générez des QR codes pour du texte ou des liens, avec logo optionnel.

+ 🔒 100% local — rien n'est envoyé à un serveur +
+ +
+
+

Contenu

+
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+
+ +
+

Logo (optionnel)

+
+ + +
+

Un logo au centre force automatiquement la correction d'erreur au niveau H pour rester scannable.

+
+ +
+ + +
+ +
+
+ +
+

Aperçu généré automatiquement à chaque modification.

+
+
+ +
+ Générateur de QR code — tout le traitement (encodage, rendu, superposition du logo) se fait dans votre navigateur. +
+ + + + + diff --git a/app/style.css b/app/style.css new file mode 100644 index 0000000..bf6d50d --- /dev/null +++ b/app/style.css @@ -0,0 +1,237 @@ +:root { + --bg: #0c0a09; + --bg-elevated: #171310; + --surface: #1f1a15; + --surface-hover: #2a221a; + --border: #33291f; + --border-strong: #4a3a29; + + --text: #f5efe8; + --text-muted: #b8ab9a; + --text-faint: #877a6a; + + --accent: #f97316; + --accent-strong: #fb923c; + --accent-contrast: #1a0f04; + --accent-soft: rgba(249, 115, 22, 0.14); + + --ok: #34d399; + --ok-soft: rgba(52, 211, 153, 0.12); + --warn: #facc15; + --warn-soft: rgba(250, 204, 21, 0.12); + --danger: #f87171; + --danger-soft: rgba(248, 113, 113, 0.12); + + --radius-sm: 6px; + --radius-md: 10px; + --radius-lg: 16px; +} + +* { box-sizing: border-box; } + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.5; +} + +header { + padding: 2.5rem 1.5rem 1rem; + text-align: center; +} + +header h1 { + margin: 0 0 0.4rem; + font-size: 1.7rem; +} + +header h1 span { + color: var(--accent-strong); +} + +header p { + margin: 0; + color: var(--text-muted); + font-size: 0.95rem; +} + +.badge { + display: inline-block; + margin-top: 0.7rem; + padding: 0.25rem 0.7rem; + border-radius: 999px; + background: var(--ok-soft); + color: var(--ok); + border: 1px solid rgba(52, 211, 153, 0.3); + font-size: 0.78rem; +} + +main { + max-width: 980px; + margin: 0 auto; + padding: 1rem 1.5rem 4rem; + display: grid; + gap: 1.5rem; +} + +.panel { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 1.25rem 1.5rem; +} + +.panel h2 { + margin-top: 0; + font-size: 1.1rem; + color: var(--text); +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 1rem; +} + +label { + display: block; + font-size: 0.85rem; + color: var(--text-muted); + margin-bottom: 0.3rem; +} + +textarea, input[type="text"], input[type="number"], input[type="range"], input[type="color"], select { + width: 100%; + padding: 0.55rem 0.7rem; + background: var(--bg-elevated); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + color: var(--text); + font-size: 0.9rem; + font-family: inherit; +} + +textarea { + min-height: 90px; + resize: vertical; +} + +input[type="color"] { + padding: 0.25rem; + height: 42px; +} + +textarea:focus, input:focus, select:focus { + outline: 2px solid var(--accent-strong); + outline-offset: 1px; + border-color: var(--accent); +} + +.field { + margin-bottom: 0.9rem; +} + +.checkbox-row { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.checkbox-row input { width: auto; } + +.file-row { + display: flex; + align-items: center; + gap: 0.6rem; + flex-wrap: wrap; +} + +button { + cursor: pointer; + border: none; + border-radius: var(--radius-sm); + padding: 0.55rem 1rem; + font-size: 0.9rem; + font-family: inherit; + font-weight: 600; +} + +.btn-primary { + background: var(--accent); + color: var(--accent-contrast); +} + +.btn-primary:hover { background: var(--accent-strong); } + +.btn-secondary { + background: var(--surface-hover); + color: var(--text); + border: 1px solid var(--border-strong); +} + +.btn-secondary:hover { border-color: var(--accent); color: var(--accent-strong); background: var(--accent-soft); } + +.btn-danger { + background: transparent; + color: var(--danger); + border: 1px solid rgba(248, 113, 113, 0.4); + padding: 0.35rem 0.6rem; + font-size: 0.8rem; +} + +.btn-danger:hover { background: var(--danger-soft); } + +.actions { + display: flex; + gap: 0.7rem; + flex-wrap: wrap; + margin-top: 1rem; +} + +.preview-panel { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +.qr-canvas-wrap { + background: white; + padding: 16px; + border-radius: var(--radius-md); + display: inline-flex; +} + +#qr-canvas { + display: block; + max-width: 100%; + height: auto; +} + +.hint { + color: var(--text-faint); + font-size: 0.8rem; + margin-top: 0.3rem; +} + +footer { + text-align: center; + color: var(--text-faint); + font-size: 0.78rem; + padding: 1rem 1.5rem 2.5rem; +} + +.section-title-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.6rem; +} + +.color-fields { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 1rem; +} diff --git a/app/vendor/qrcode.min.js b/app/vendor/qrcode.min.js new file mode 100644 index 0000000..76889b5 --- /dev/null +++ b/app/vendor/qrcode.min.js @@ -0,0 +1,2297 @@ +//--------------------------------------------------------------------- +// +// QR Code Generator for JavaScript +// +// Copyright (c) 2009 Kazuhiko Arase +// +// URL: http://www.d-project.com/ +// +// Licensed under the MIT license: +// http://www.opensource.org/licenses/mit-license.php +// +// The word 'QR Code' is registered trademark of +// DENSO WAVE INCORPORATED +// http://www.denso-wave.com/qrcode/faqpatent-e.html +// +//--------------------------------------------------------------------- + +var qrcode = function() { + + //--------------------------------------------------------------------- + // qrcode + //--------------------------------------------------------------------- + + /** + * qrcode + * @param typeNumber 1 to 40 + * @param errorCorrectionLevel 'L','M','Q','H' + */ + var qrcode = function(typeNumber, errorCorrectionLevel) { + + var PAD0 = 0xEC; + var PAD1 = 0x11; + + var _typeNumber = typeNumber; + var _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel]; + var _modules = null; + var _moduleCount = 0; + var _dataCache = null; + var _dataList = []; + + var _this = {}; + + var makeImpl = function(test, maskPattern) { + + _moduleCount = _typeNumber * 4 + 17; + _modules = function(moduleCount) { + var modules = new Array(moduleCount); + for (var row = 0; row < moduleCount; row += 1) { + modules[row] = new Array(moduleCount); + for (var col = 0; col < moduleCount; col += 1) { + modules[row][col] = null; + } + } + return modules; + }(_moduleCount); + + setupPositionProbePattern(0, 0); + setupPositionProbePattern(_moduleCount - 7, 0); + setupPositionProbePattern(0, _moduleCount - 7); + setupPositionAdjustPattern(); + setupTimingPattern(); + setupTypeInfo(test, maskPattern); + + if (_typeNumber >= 7) { + setupTypeNumber(test); + } + + if (_dataCache == null) { + _dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList); + } + + mapData(_dataCache, maskPattern); + }; + + var setupPositionProbePattern = function(row, col) { + + for (var r = -1; r <= 7; r += 1) { + + if (row + r <= -1 || _moduleCount <= row + r) continue; + + for (var c = -1; c <= 7; c += 1) { + + if (col + c <= -1 || _moduleCount <= col + c) continue; + + if ( (0 <= r && r <= 6 && (c == 0 || c == 6) ) + || (0 <= c && c <= 6 && (r == 0 || r == 6) ) + || (2 <= r && r <= 4 && 2 <= c && c <= 4) ) { + _modules[row + r][col + c] = true; + } else { + _modules[row + r][col + c] = false; + } + } + } + }; + + var getBestMaskPattern = function() { + + var minLostPoint = 0; + var pattern = 0; + + for (var i = 0; i < 8; i += 1) { + + makeImpl(true, i); + + var lostPoint = QRUtil.getLostPoint(_this); + + if (i == 0 || minLostPoint > lostPoint) { + minLostPoint = lostPoint; + pattern = i; + } + } + + return pattern; + }; + + var setupTimingPattern = function() { + + for (var r = 8; r < _moduleCount - 8; r += 1) { + if (_modules[r][6] != null) { + continue; + } + _modules[r][6] = (r % 2 == 0); + } + + for (var c = 8; c < _moduleCount - 8; c += 1) { + if (_modules[6][c] != null) { + continue; + } + _modules[6][c] = (c % 2 == 0); + } + }; + + var setupPositionAdjustPattern = function() { + + var pos = QRUtil.getPatternPosition(_typeNumber); + + for (var i = 0; i < pos.length; i += 1) { + + for (var j = 0; j < pos.length; j += 1) { + + var row = pos[i]; + var col = pos[j]; + + if (_modules[row][col] != null) { + continue; + } + + for (var r = -2; r <= 2; r += 1) { + + for (var c = -2; c <= 2; c += 1) { + + if (r == -2 || r == 2 || c == -2 || c == 2 + || (r == 0 && c == 0) ) { + _modules[row + r][col + c] = true; + } else { + _modules[row + r][col + c] = false; + } + } + } + } + } + }; + + var setupTypeNumber = function(test) { + + var bits = QRUtil.getBCHTypeNumber(_typeNumber); + + for (var i = 0; i < 18; i += 1) { + var mod = (!test && ( (bits >> i) & 1) == 1); + _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod; + } + + for (var i = 0; i < 18; i += 1) { + var mod = (!test && ( (bits >> i) & 1) == 1); + _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod; + } + }; + + var setupTypeInfo = function(test, maskPattern) { + + var data = (_errorCorrectionLevel << 3) | maskPattern; + var bits = QRUtil.getBCHTypeInfo(data); + + // vertical + for (var i = 0; i < 15; i += 1) { + + var mod = (!test && ( (bits >> i) & 1) == 1); + + if (i < 6) { + _modules[i][8] = mod; + } else if (i < 8) { + _modules[i + 1][8] = mod; + } else { + _modules[_moduleCount - 15 + i][8] = mod; + } + } + + // horizontal + for (var i = 0; i < 15; i += 1) { + + var mod = (!test && ( (bits >> i) & 1) == 1); + + if (i < 8) { + _modules[8][_moduleCount - i - 1] = mod; + } else if (i < 9) { + _modules[8][15 - i - 1 + 1] = mod; + } else { + _modules[8][15 - i - 1] = mod; + } + } + + // fixed module + _modules[_moduleCount - 8][8] = (!test); + }; + + var mapData = function(data, maskPattern) { + + var inc = -1; + var row = _moduleCount - 1; + var bitIndex = 7; + var byteIndex = 0; + var maskFunc = QRUtil.getMaskFunction(maskPattern); + + for (var col = _moduleCount - 1; col > 0; col -= 2) { + + if (col == 6) col -= 1; + + while (true) { + + for (var c = 0; c < 2; c += 1) { + + if (_modules[row][col - c] == null) { + + var dark = false; + + if (byteIndex < data.length) { + dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1); + } + + var mask = maskFunc(row, col - c); + + if (mask) { + dark = !dark; + } + + _modules[row][col - c] = dark; + bitIndex -= 1; + + if (bitIndex == -1) { + byteIndex += 1; + bitIndex = 7; + } + } + } + + row += inc; + + if (row < 0 || _moduleCount <= row) { + row -= inc; + inc = -inc; + break; + } + } + } + }; + + var createBytes = function(buffer, rsBlocks) { + + var offset = 0; + + var maxDcCount = 0; + var maxEcCount = 0; + + var dcdata = new Array(rsBlocks.length); + var ecdata = new Array(rsBlocks.length); + + for (var r = 0; r < rsBlocks.length; r += 1) { + + var dcCount = rsBlocks[r].dataCount; + var ecCount = rsBlocks[r].totalCount - dcCount; + + maxDcCount = Math.max(maxDcCount, dcCount); + maxEcCount = Math.max(maxEcCount, ecCount); + + dcdata[r] = new Array(dcCount); + + for (var i = 0; i < dcdata[r].length; i += 1) { + dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]; + } + offset += dcCount; + + var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); + var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1); + + var modPoly = rawPoly.mod(rsPoly); + ecdata[r] = new Array(rsPoly.getLength() - 1); + for (var i = 0; i < ecdata[r].length; i += 1) { + var modIndex = i + modPoly.getLength() - ecdata[r].length; + ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0; + } + } + + var totalCodeCount = 0; + for (var i = 0; i < rsBlocks.length; i += 1) { + totalCodeCount += rsBlocks[i].totalCount; + } + + var data = new Array(totalCodeCount); + var index = 0; + + for (var i = 0; i < maxDcCount; i += 1) { + for (var r = 0; r < rsBlocks.length; r += 1) { + if (i < dcdata[r].length) { + data[index] = dcdata[r][i]; + index += 1; + } + } + } + + for (var i = 0; i < maxEcCount; i += 1) { + for (var r = 0; r < rsBlocks.length; r += 1) { + if (i < ecdata[r].length) { + data[index] = ecdata[r][i]; + index += 1; + } + } + } + + return data; + }; + + var createData = function(typeNumber, errorCorrectionLevel, dataList) { + + var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel); + + var buffer = qrBitBuffer(); + + for (var i = 0; i < dataList.length; i += 1) { + var data = dataList[i]; + buffer.put(data.getMode(), 4); + buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) ); + data.write(buffer); + } + + // calc num max data. + var totalDataCount = 0; + for (var i = 0; i < rsBlocks.length; i += 1) { + totalDataCount += rsBlocks[i].dataCount; + } + + if (buffer.getLengthInBits() > totalDataCount * 8) { + throw 'code length overflow. (' + + buffer.getLengthInBits() + + '>' + + totalDataCount * 8 + + ')'; + } + + // end code + if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { + buffer.put(0, 4); + } + + // padding + while (buffer.getLengthInBits() % 8 != 0) { + buffer.putBit(false); + } + + // padding + while (true) { + + if (buffer.getLengthInBits() >= totalDataCount * 8) { + break; + } + buffer.put(PAD0, 8); + + if (buffer.getLengthInBits() >= totalDataCount * 8) { + break; + } + buffer.put(PAD1, 8); + } + + return createBytes(buffer, rsBlocks); + }; + + _this.addData = function(data, mode) { + + mode = mode || 'Byte'; + + var newData = null; + + switch(mode) { + case 'Numeric' : + newData = qrNumber(data); + break; + case 'Alphanumeric' : + newData = qrAlphaNum(data); + break; + case 'Byte' : + newData = qr8BitByte(data); + break; + case 'Kanji' : + newData = qrKanji(data); + break; + default : + throw 'mode:' + mode; + } + + _dataList.push(newData); + _dataCache = null; + }; + + _this.isDark = function(row, col) { + if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) { + throw row + ',' + col; + } + return _modules[row][col]; + }; + + _this.getModuleCount = function() { + return _moduleCount; + }; + + _this.make = function() { + if (_typeNumber < 1) { + var typeNumber = 1; + + for (; typeNumber < 40; typeNumber++) { + var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, _errorCorrectionLevel); + var buffer = qrBitBuffer(); + + for (var i = 0; i < _dataList.length; i++) { + var data = _dataList[i]; + buffer.put(data.getMode(), 4); + buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) ); + data.write(buffer); + } + + var totalDataCount = 0; + for (var i = 0; i < rsBlocks.length; i++) { + totalDataCount += rsBlocks[i].dataCount; + } + + if (buffer.getLengthInBits() <= totalDataCount * 8) { + break; + } + } + + _typeNumber = typeNumber; + } + + makeImpl(false, getBestMaskPattern() ); + }; + + _this.createTableTag = function(cellSize, margin) { + + cellSize = cellSize || 2; + margin = (typeof margin == 'undefined')? cellSize * 4 : margin; + + var qrHtml = ''; + + qrHtml += ''; + qrHtml += ''; + + for (var r = 0; r < _this.getModuleCount(); r += 1) { + + qrHtml += ''; + + for (var c = 0; c < _this.getModuleCount(); c += 1) { + qrHtml += ''; + } + + qrHtml += ''; + qrHtml += '
'; + } + + qrHtml += '
'; + + return qrHtml; + }; + + _this.createSvgTag = function(cellSize, margin, alt, title) { + + var opts = {}; + if (typeof arguments[0] == 'object') { + // Called by options. + opts = arguments[0]; + // overwrite cellSize and margin. + cellSize = opts.cellSize; + margin = opts.margin; + alt = opts.alt; + title = opts.title; + } + + cellSize = cellSize || 2; + margin = (typeof margin == 'undefined')? cellSize * 4 : margin; + + // Compose alt property surrogate + alt = (typeof alt === 'string') ? {text: alt} : alt || {}; + alt.text = alt.text || null; + alt.id = (alt.text) ? alt.id || 'qrcode-description' : null; + + // Compose title property surrogate + title = (typeof title === 'string') ? {text: title} : title || {}; + title.text = title.text || null; + title.id = (title.text) ? title.id || 'qrcode-title' : null; + + var size = _this.getModuleCount() * cellSize + margin * 2; + var c, mc, r, mr, qrSvg='', rect; + + rect = 'l' + cellSize + ',0 0,' + cellSize + + ' -' + cellSize + ',0 0,-' + cellSize + 'z '; + + qrSvg += '' + + escapeXml(title.text) + '' : ''; + qrSvg += (alt.text) ? '' + + escapeXml(alt.text) + '' : ''; + qrSvg += ''; + qrSvg += ''; + qrSvg += ''; + + return qrSvg; + }; + + _this.createDataURL = function(cellSize, margin) { + + cellSize = cellSize || 2; + margin = (typeof margin == 'undefined')? cellSize * 4 : margin; + + var size = _this.getModuleCount() * cellSize + margin * 2; + var min = margin; + var max = size - margin; + + return createDataURL(size, size, function(x, y) { + if (min <= x && x < max && min <= y && y < max) { + var c = Math.floor( (x - min) / cellSize); + var r = Math.floor( (y - min) / cellSize); + return _this.isDark(r, c)? 0 : 1; + } else { + return 1; + } + } ); + }; + + _this.createImgTag = function(cellSize, margin, alt) { + + cellSize = cellSize || 2; + margin = (typeof margin == 'undefined')? cellSize * 4 : margin; + + var size = _this.getModuleCount() * cellSize + margin * 2; + + var img = ''; + img += '': escaped += '>'; break; + case '&': escaped += '&'; break; + case '"': escaped += '"'; break; + default : escaped += c; break; + } + } + return escaped; + }; + + var _createHalfASCII = function(margin) { + var cellSize = 1; + margin = (typeof margin == 'undefined')? cellSize * 2 : margin; + + var size = _this.getModuleCount() * cellSize + margin * 2; + var min = margin; + var max = size - margin; + + var y, x, r1, r2, p; + + var blocks = { + '██': '█', + '█ ': '▀', + ' █': '▄', + ' ': ' ' + }; + + var blocksLastLineNoMargin = { + '██': '▀', + '█ ': '▀', + ' █': ' ', + ' ': ' ' + }; + + var ascii = ''; + for (y = 0; y < size; y += 2) { + r1 = Math.floor((y - min) / cellSize); + r2 = Math.floor((y + 1 - min) / cellSize); + for (x = 0; x < size; x += 1) { + p = '█'; + + if (min <= x && x < max && min <= y && y < max && _this.isDark(r1, Math.floor((x - min) / cellSize))) { + p = ' '; + } + + if (min <= x && x < max && min <= y+1 && y+1 < max && _this.isDark(r2, Math.floor((x - min) / cellSize))) { + p += ' '; + } + else { + p += '█'; + } + + // Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square. + ascii += (margin < 1 && y+1 >= max) ? blocksLastLineNoMargin[p] : blocks[p]; + } + + ascii += '\n'; + } + + if (size % 2 && margin > 0) { + return ascii.substring(0, ascii.length - size - 1) + Array(size+1).join('▀'); + } + + return ascii.substring(0, ascii.length-1); + }; + + _this.createASCII = function(cellSize, margin) { + cellSize = cellSize || 1; + + if (cellSize < 2) { + return _createHalfASCII(margin); + } + + cellSize -= 1; + margin = (typeof margin == 'undefined')? cellSize * 2 : margin; + + var size = _this.getModuleCount() * cellSize + margin * 2; + var min = margin; + var max = size - margin; + + var y, x, r, p; + + var white = Array(cellSize+1).join('██'); + var black = Array(cellSize+1).join(' '); + + var ascii = ''; + var line = ''; + for (y = 0; y < size; y += 1) { + r = Math.floor( (y - min) / cellSize); + line = ''; + for (x = 0; x < size; x += 1) { + p = 1; + + if (min <= x && x < max && min <= y && y < max && _this.isDark(r, Math.floor((x - min) / cellSize))) { + p = 0; + } + + // Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square. + line += p ? white : black; + } + + for (r = 0; r < cellSize; r += 1) { + ascii += line + '\n'; + } + } + + return ascii.substring(0, ascii.length-1); + }; + + _this.renderTo2dContext = function(context, cellSize) { + cellSize = cellSize || 2; + var length = _this.getModuleCount(); + for (var row = 0; row < length; row++) { + for (var col = 0; col < length; col++) { + context.fillStyle = _this.isDark(row, col) ? 'black' : 'white'; + context.fillRect(row * cellSize, col * cellSize, cellSize, cellSize); + } + } + } + + return _this; + }; + + //--------------------------------------------------------------------- + // qrcode.stringToBytes + //--------------------------------------------------------------------- + + qrcode.stringToBytesFuncs = { + 'default' : function(s) { + var bytes = []; + for (var i = 0; i < s.length; i += 1) { + var c = s.charCodeAt(i); + bytes.push(c & 0xff); + } + return bytes; + } + }; + + qrcode.stringToBytes = qrcode.stringToBytesFuncs['default']; + + //--------------------------------------------------------------------- + // qrcode.createStringToBytes + //--------------------------------------------------------------------- + + /** + * @param unicodeData base64 string of byte array. + * [16bit Unicode],[16bit Bytes], ... + * @param numChars + */ + qrcode.createStringToBytes = function(unicodeData, numChars) { + + // create conversion map. + + var unicodeMap = function() { + + var bin = base64DecodeInputStream(unicodeData); + var read = function() { + var b = bin.read(); + if (b == -1) throw 'eof'; + return b; + }; + + var count = 0; + var unicodeMap = {}; + while (true) { + var b0 = bin.read(); + if (b0 == -1) break; + var b1 = read(); + var b2 = read(); + var b3 = read(); + var k = String.fromCharCode( (b0 << 8) | b1); + var v = (b2 << 8) | b3; + unicodeMap[k] = v; + count += 1; + } + if (count != numChars) { + throw count + ' != ' + numChars; + } + + return unicodeMap; + }(); + + var unknownChar = '?'.charCodeAt(0); + + return function(s) { + var bytes = []; + for (var i = 0; i < s.length; i += 1) { + var c = s.charCodeAt(i); + if (c < 128) { + bytes.push(c); + } else { + var b = unicodeMap[s.charAt(i)]; + if (typeof b == 'number') { + if ( (b & 0xff) == b) { + // 1byte + bytes.push(b); + } else { + // 2bytes + bytes.push(b >>> 8); + bytes.push(b & 0xff); + } + } else { + bytes.push(unknownChar); + } + } + } + return bytes; + }; + }; + + //--------------------------------------------------------------------- + // QRMode + //--------------------------------------------------------------------- + + var QRMode = { + MODE_NUMBER : 1 << 0, + MODE_ALPHA_NUM : 1 << 1, + MODE_8BIT_BYTE : 1 << 2, + MODE_KANJI : 1 << 3 + }; + + //--------------------------------------------------------------------- + // QRErrorCorrectionLevel + //--------------------------------------------------------------------- + + var QRErrorCorrectionLevel = { + L : 1, + M : 0, + Q : 3, + H : 2 + }; + + //--------------------------------------------------------------------- + // QRMaskPattern + //--------------------------------------------------------------------- + + var QRMaskPattern = { + PATTERN000 : 0, + PATTERN001 : 1, + PATTERN010 : 2, + PATTERN011 : 3, + PATTERN100 : 4, + PATTERN101 : 5, + PATTERN110 : 6, + PATTERN111 : 7 + }; + + //--------------------------------------------------------------------- + // QRUtil + //--------------------------------------------------------------------- + + var QRUtil = function() { + + var PATTERN_POSITION_TABLE = [ + [], + [6, 18], + [6, 22], + [6, 26], + [6, 30], + [6, 34], + [6, 22, 38], + [6, 24, 42], + [6, 26, 46], + [6, 28, 50], + [6, 30, 54], + [6, 32, 58], + [6, 34, 62], + [6, 26, 46, 66], + [6, 26, 48, 70], + [6, 26, 50, 74], + [6, 30, 54, 78], + [6, 30, 56, 82], + [6, 30, 58, 86], + [6, 34, 62, 90], + [6, 28, 50, 72, 94], + [6, 26, 50, 74, 98], + [6, 30, 54, 78, 102], + [6, 28, 54, 80, 106], + [6, 32, 58, 84, 110], + [6, 30, 58, 86, 114], + [6, 34, 62, 90, 118], + [6, 26, 50, 74, 98, 122], + [6, 30, 54, 78, 102, 126], + [6, 26, 52, 78, 104, 130], + [6, 30, 56, 82, 108, 134], + [6, 34, 60, 86, 112, 138], + [6, 30, 58, 86, 114, 142], + [6, 34, 62, 90, 118, 146], + [6, 30, 54, 78, 102, 126, 150], + [6, 24, 50, 76, 102, 128, 154], + [6, 28, 54, 80, 106, 132, 158], + [6, 32, 58, 84, 110, 136, 162], + [6, 26, 54, 82, 110, 138, 166], + [6, 30, 58, 86, 114, 142, 170] + ]; + var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); + var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); + var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); + + var _this = {}; + + var getBCHDigit = function(data) { + var digit = 0; + while (data != 0) { + digit += 1; + data >>>= 1; + } + return digit; + }; + + _this.getBCHTypeInfo = function(data) { + var d = data << 10; + while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { + d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) ); + } + return ( (data << 10) | d) ^ G15_MASK; + }; + + _this.getBCHTypeNumber = function(data) { + var d = data << 12; + while (getBCHDigit(d) - getBCHDigit(G18) >= 0) { + d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) ); + } + return (data << 12) | d; + }; + + _this.getPatternPosition = function(typeNumber) { + return PATTERN_POSITION_TABLE[typeNumber - 1]; + }; + + _this.getMaskFunction = function(maskPattern) { + + switch (maskPattern) { + + case QRMaskPattern.PATTERN000 : + return function(i, j) { return (i + j) % 2 == 0; }; + case QRMaskPattern.PATTERN001 : + return function(i, j) { return i % 2 == 0; }; + case QRMaskPattern.PATTERN010 : + return function(i, j) { return j % 3 == 0; }; + case QRMaskPattern.PATTERN011 : + return function(i, j) { return (i + j) % 3 == 0; }; + case QRMaskPattern.PATTERN100 : + return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; }; + case QRMaskPattern.PATTERN101 : + return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; }; + case QRMaskPattern.PATTERN110 : + return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; }; + case QRMaskPattern.PATTERN111 : + return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; }; + + default : + throw 'bad maskPattern:' + maskPattern; + } + }; + + _this.getErrorCorrectPolynomial = function(errorCorrectLength) { + var a = qrPolynomial([1], 0); + for (var i = 0; i < errorCorrectLength; i += 1) { + a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) ); + } + return a; + }; + + _this.getLengthInBits = function(mode, type) { + + if (1 <= type && type < 10) { + + // 1 - 9 + + switch(mode) { + case QRMode.MODE_NUMBER : return 10; + case QRMode.MODE_ALPHA_NUM : return 9; + case QRMode.MODE_8BIT_BYTE : return 8; + case QRMode.MODE_KANJI : return 8; + default : + throw 'mode:' + mode; + } + + } else if (type < 27) { + + // 10 - 26 + + switch(mode) { + case QRMode.MODE_NUMBER : return 12; + case QRMode.MODE_ALPHA_NUM : return 11; + case QRMode.MODE_8BIT_BYTE : return 16; + case QRMode.MODE_KANJI : return 10; + default : + throw 'mode:' + mode; + } + + } else if (type < 41) { + + // 27 - 40 + + switch(mode) { + case QRMode.MODE_NUMBER : return 14; + case QRMode.MODE_ALPHA_NUM : return 13; + case QRMode.MODE_8BIT_BYTE : return 16; + case QRMode.MODE_KANJI : return 12; + default : + throw 'mode:' + mode; + } + + } else { + throw 'type:' + type; + } + }; + + _this.getLostPoint = function(qrcode) { + + var moduleCount = qrcode.getModuleCount(); + + var lostPoint = 0; + + // LEVEL1 + + for (var row = 0; row < moduleCount; row += 1) { + for (var col = 0; col < moduleCount; col += 1) { + + var sameCount = 0; + var dark = qrcode.isDark(row, col); + + for (var r = -1; r <= 1; r += 1) { + + if (row + r < 0 || moduleCount <= row + r) { + continue; + } + + for (var c = -1; c <= 1; c += 1) { + + if (col + c < 0 || moduleCount <= col + c) { + continue; + } + + if (r == 0 && c == 0) { + continue; + } + + if (dark == qrcode.isDark(row + r, col + c) ) { + sameCount += 1; + } + } + } + + if (sameCount > 5) { + lostPoint += (3 + sameCount - 5); + } + } + }; + + // LEVEL2 + + for (var row = 0; row < moduleCount - 1; row += 1) { + for (var col = 0; col < moduleCount - 1; col += 1) { + var count = 0; + if (qrcode.isDark(row, col) ) count += 1; + if (qrcode.isDark(row + 1, col) ) count += 1; + if (qrcode.isDark(row, col + 1) ) count += 1; + if (qrcode.isDark(row + 1, col + 1) ) count += 1; + if (count == 0 || count == 4) { + lostPoint += 3; + } + } + } + + // LEVEL3 + + for (var row = 0; row < moduleCount; row += 1) { + for (var col = 0; col < moduleCount - 6; col += 1) { + if (qrcode.isDark(row, col) + && !qrcode.isDark(row, col + 1) + && qrcode.isDark(row, col + 2) + && qrcode.isDark(row, col + 3) + && qrcode.isDark(row, col + 4) + && !qrcode.isDark(row, col + 5) + && qrcode.isDark(row, col + 6) ) { + lostPoint += 40; + } + } + } + + for (var col = 0; col < moduleCount; col += 1) { + for (var row = 0; row < moduleCount - 6; row += 1) { + if (qrcode.isDark(row, col) + && !qrcode.isDark(row + 1, col) + && qrcode.isDark(row + 2, col) + && qrcode.isDark(row + 3, col) + && qrcode.isDark(row + 4, col) + && !qrcode.isDark(row + 5, col) + && qrcode.isDark(row + 6, col) ) { + lostPoint += 40; + } + } + } + + // LEVEL4 + + var darkCount = 0; + + for (var col = 0; col < moduleCount; col += 1) { + for (var row = 0; row < moduleCount; row += 1) { + if (qrcode.isDark(row, col) ) { + darkCount += 1; + } + } + } + + var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; + lostPoint += ratio * 10; + + return lostPoint; + }; + + return _this; + }(); + + //--------------------------------------------------------------------- + // QRMath + //--------------------------------------------------------------------- + + var QRMath = function() { + + var EXP_TABLE = new Array(256); + var LOG_TABLE = new Array(256); + + // initialize tables + for (var i = 0; i < 8; i += 1) { + EXP_TABLE[i] = 1 << i; + } + for (var i = 8; i < 256; i += 1) { + EXP_TABLE[i] = EXP_TABLE[i - 4] + ^ EXP_TABLE[i - 5] + ^ EXP_TABLE[i - 6] + ^ EXP_TABLE[i - 8]; + } + for (var i = 0; i < 255; i += 1) { + LOG_TABLE[EXP_TABLE[i] ] = i; + } + + var _this = {}; + + _this.glog = function(n) { + + if (n < 1) { + throw 'glog(' + n + ')'; + } + + return LOG_TABLE[n]; + }; + + _this.gexp = function(n) { + + while (n < 0) { + n += 255; + } + + while (n >= 256) { + n -= 255; + } + + return EXP_TABLE[n]; + }; + + return _this; + }(); + + //--------------------------------------------------------------------- + // qrPolynomial + //--------------------------------------------------------------------- + + function qrPolynomial(num, shift) { + + if (typeof num.length == 'undefined') { + throw num.length + '/' + shift; + } + + var _num = function() { + var offset = 0; + while (offset < num.length && num[offset] == 0) { + offset += 1; + } + var _num = new Array(num.length - offset + shift); + for (var i = 0; i < num.length - offset; i += 1) { + _num[i] = num[i + offset]; + } + return _num; + }(); + + var _this = {}; + + _this.getAt = function(index) { + return _num[index]; + }; + + _this.getLength = function() { + return _num.length; + }; + + _this.multiply = function(e) { + + var num = new Array(_this.getLength() + e.getLength() - 1); + + for (var i = 0; i < _this.getLength(); i += 1) { + for (var j = 0; j < e.getLength(); j += 1) { + num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) ); + } + } + + return qrPolynomial(num, 0); + }; + + _this.mod = function(e) { + + if (_this.getLength() - e.getLength() < 0) { + return _this; + } + + var ratio = QRMath.glog(_this.getAt(0) ) - QRMath.glog(e.getAt(0) ); + + var num = new Array(_this.getLength() ); + for (var i = 0; i < _this.getLength(); i += 1) { + num[i] = _this.getAt(i); + } + + for (var i = 0; i < e.getLength(); i += 1) { + num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio); + } + + // recursive call + return qrPolynomial(num, 0).mod(e); + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // QRRSBlock + //--------------------------------------------------------------------- + + var QRRSBlock = function() { + + var RS_BLOCK_TABLE = [ + + // L + // M + // Q + // H + + // 1 + [1, 26, 19], + [1, 26, 16], + [1, 26, 13], + [1, 26, 9], + + // 2 + [1, 44, 34], + [1, 44, 28], + [1, 44, 22], + [1, 44, 16], + + // 3 + [1, 70, 55], + [1, 70, 44], + [2, 35, 17], + [2, 35, 13], + + // 4 + [1, 100, 80], + [2, 50, 32], + [2, 50, 24], + [4, 25, 9], + + // 5 + [1, 134, 108], + [2, 67, 43], + [2, 33, 15, 2, 34, 16], + [2, 33, 11, 2, 34, 12], + + // 6 + [2, 86, 68], + [4, 43, 27], + [4, 43, 19], + [4, 43, 15], + + // 7 + [2, 98, 78], + [4, 49, 31], + [2, 32, 14, 4, 33, 15], + [4, 39, 13, 1, 40, 14], + + // 8 + [2, 121, 97], + [2, 60, 38, 2, 61, 39], + [4, 40, 18, 2, 41, 19], + [4, 40, 14, 2, 41, 15], + + // 9 + [2, 146, 116], + [3, 58, 36, 2, 59, 37], + [4, 36, 16, 4, 37, 17], + [4, 36, 12, 4, 37, 13], + + // 10 + [2, 86, 68, 2, 87, 69], + [4, 69, 43, 1, 70, 44], + [6, 43, 19, 2, 44, 20], + [6, 43, 15, 2, 44, 16], + + // 11 + [4, 101, 81], + [1, 80, 50, 4, 81, 51], + [4, 50, 22, 4, 51, 23], + [3, 36, 12, 8, 37, 13], + + // 12 + [2, 116, 92, 2, 117, 93], + [6, 58, 36, 2, 59, 37], + [4, 46, 20, 6, 47, 21], + [7, 42, 14, 4, 43, 15], + + // 13 + [4, 133, 107], + [8, 59, 37, 1, 60, 38], + [8, 44, 20, 4, 45, 21], + [12, 33, 11, 4, 34, 12], + + // 14 + [3, 145, 115, 1, 146, 116], + [4, 64, 40, 5, 65, 41], + [11, 36, 16, 5, 37, 17], + [11, 36, 12, 5, 37, 13], + + // 15 + [5, 109, 87, 1, 110, 88], + [5, 65, 41, 5, 66, 42], + [5, 54, 24, 7, 55, 25], + [11, 36, 12, 7, 37, 13], + + // 16 + [5, 122, 98, 1, 123, 99], + [7, 73, 45, 3, 74, 46], + [15, 43, 19, 2, 44, 20], + [3, 45, 15, 13, 46, 16], + + // 17 + [1, 135, 107, 5, 136, 108], + [10, 74, 46, 1, 75, 47], + [1, 50, 22, 15, 51, 23], + [2, 42, 14, 17, 43, 15], + + // 18 + [5, 150, 120, 1, 151, 121], + [9, 69, 43, 4, 70, 44], + [17, 50, 22, 1, 51, 23], + [2, 42, 14, 19, 43, 15], + + // 19 + [3, 141, 113, 4, 142, 114], + [3, 70, 44, 11, 71, 45], + [17, 47, 21, 4, 48, 22], + [9, 39, 13, 16, 40, 14], + + // 20 + [3, 135, 107, 5, 136, 108], + [3, 67, 41, 13, 68, 42], + [15, 54, 24, 5, 55, 25], + [15, 43, 15, 10, 44, 16], + + // 21 + [4, 144, 116, 4, 145, 117], + [17, 68, 42], + [17, 50, 22, 6, 51, 23], + [19, 46, 16, 6, 47, 17], + + // 22 + [2, 139, 111, 7, 140, 112], + [17, 74, 46], + [7, 54, 24, 16, 55, 25], + [34, 37, 13], + + // 23 + [4, 151, 121, 5, 152, 122], + [4, 75, 47, 14, 76, 48], + [11, 54, 24, 14, 55, 25], + [16, 45, 15, 14, 46, 16], + + // 24 + [6, 147, 117, 4, 148, 118], + [6, 73, 45, 14, 74, 46], + [11, 54, 24, 16, 55, 25], + [30, 46, 16, 2, 47, 17], + + // 25 + [8, 132, 106, 4, 133, 107], + [8, 75, 47, 13, 76, 48], + [7, 54, 24, 22, 55, 25], + [22, 45, 15, 13, 46, 16], + + // 26 + [10, 142, 114, 2, 143, 115], + [19, 74, 46, 4, 75, 47], + [28, 50, 22, 6, 51, 23], + [33, 46, 16, 4, 47, 17], + + // 27 + [8, 152, 122, 4, 153, 123], + [22, 73, 45, 3, 74, 46], + [8, 53, 23, 26, 54, 24], + [12, 45, 15, 28, 46, 16], + + // 28 + [3, 147, 117, 10, 148, 118], + [3, 73, 45, 23, 74, 46], + [4, 54, 24, 31, 55, 25], + [11, 45, 15, 31, 46, 16], + + // 29 + [7, 146, 116, 7, 147, 117], + [21, 73, 45, 7, 74, 46], + [1, 53, 23, 37, 54, 24], + [19, 45, 15, 26, 46, 16], + + // 30 + [5, 145, 115, 10, 146, 116], + [19, 75, 47, 10, 76, 48], + [15, 54, 24, 25, 55, 25], + [23, 45, 15, 25, 46, 16], + + // 31 + [13, 145, 115, 3, 146, 116], + [2, 74, 46, 29, 75, 47], + [42, 54, 24, 1, 55, 25], + [23, 45, 15, 28, 46, 16], + + // 32 + [17, 145, 115], + [10, 74, 46, 23, 75, 47], + [10, 54, 24, 35, 55, 25], + [19, 45, 15, 35, 46, 16], + + // 33 + [17, 145, 115, 1, 146, 116], + [14, 74, 46, 21, 75, 47], + [29, 54, 24, 19, 55, 25], + [11, 45, 15, 46, 46, 16], + + // 34 + [13, 145, 115, 6, 146, 116], + [14, 74, 46, 23, 75, 47], + [44, 54, 24, 7, 55, 25], + [59, 46, 16, 1, 47, 17], + + // 35 + [12, 151, 121, 7, 152, 122], + [12, 75, 47, 26, 76, 48], + [39, 54, 24, 14, 55, 25], + [22, 45, 15, 41, 46, 16], + + // 36 + [6, 151, 121, 14, 152, 122], + [6, 75, 47, 34, 76, 48], + [46, 54, 24, 10, 55, 25], + [2, 45, 15, 64, 46, 16], + + // 37 + [17, 152, 122, 4, 153, 123], + [29, 74, 46, 14, 75, 47], + [49, 54, 24, 10, 55, 25], + [24, 45, 15, 46, 46, 16], + + // 38 + [4, 152, 122, 18, 153, 123], + [13, 74, 46, 32, 75, 47], + [48, 54, 24, 14, 55, 25], + [42, 45, 15, 32, 46, 16], + + // 39 + [20, 147, 117, 4, 148, 118], + [40, 75, 47, 7, 76, 48], + [43, 54, 24, 22, 55, 25], + [10, 45, 15, 67, 46, 16], + + // 40 + [19, 148, 118, 6, 149, 119], + [18, 75, 47, 31, 76, 48], + [34, 54, 24, 34, 55, 25], + [20, 45, 15, 61, 46, 16] + ]; + + var qrRSBlock = function(totalCount, dataCount) { + var _this = {}; + _this.totalCount = totalCount; + _this.dataCount = dataCount; + return _this; + }; + + var _this = {}; + + var getRsBlockTable = function(typeNumber, errorCorrectionLevel) { + + switch(errorCorrectionLevel) { + case QRErrorCorrectionLevel.L : + return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; + case QRErrorCorrectionLevel.M : + return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; + case QRErrorCorrectionLevel.Q : + return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; + case QRErrorCorrectionLevel.H : + return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; + default : + return undefined; + } + }; + + _this.getRSBlocks = function(typeNumber, errorCorrectionLevel) { + + var rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel); + + if (typeof rsBlock == 'undefined') { + throw 'bad rs block @ typeNumber:' + typeNumber + + '/errorCorrectionLevel:' + errorCorrectionLevel; + } + + var length = rsBlock.length / 3; + + var list = []; + + for (var i = 0; i < length; i += 1) { + + var count = rsBlock[i * 3 + 0]; + var totalCount = rsBlock[i * 3 + 1]; + var dataCount = rsBlock[i * 3 + 2]; + + for (var j = 0; j < count; j += 1) { + list.push(qrRSBlock(totalCount, dataCount) ); + } + } + + return list; + }; + + return _this; + }(); + + //--------------------------------------------------------------------- + // qrBitBuffer + //--------------------------------------------------------------------- + + var qrBitBuffer = function() { + + var _buffer = []; + var _length = 0; + + var _this = {}; + + _this.getBuffer = function() { + return _buffer; + }; + + _this.getAt = function(index) { + var bufIndex = Math.floor(index / 8); + return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1; + }; + + _this.put = function(num, length) { + for (var i = 0; i < length; i += 1) { + _this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1); + } + }; + + _this.getLengthInBits = function() { + return _length; + }; + + _this.putBit = function(bit) { + + var bufIndex = Math.floor(_length / 8); + if (_buffer.length <= bufIndex) { + _buffer.push(0); + } + + if (bit) { + _buffer[bufIndex] |= (0x80 >>> (_length % 8) ); + } + + _length += 1; + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // qrNumber + //--------------------------------------------------------------------- + + var qrNumber = function(data) { + + var _mode = QRMode.MODE_NUMBER; + var _data = data; + + var _this = {}; + + _this.getMode = function() { + return _mode; + }; + + _this.getLength = function(buffer) { + return _data.length; + }; + + _this.write = function(buffer) { + + var data = _data; + + var i = 0; + + while (i + 2 < data.length) { + buffer.put(strToNum(data.substring(i, i + 3) ), 10); + i += 3; + } + + if (i < data.length) { + if (data.length - i == 1) { + buffer.put(strToNum(data.substring(i, i + 1) ), 4); + } else if (data.length - i == 2) { + buffer.put(strToNum(data.substring(i, i + 2) ), 7); + } + } + }; + + var strToNum = function(s) { + var num = 0; + for (var i = 0; i < s.length; i += 1) { + num = num * 10 + chatToNum(s.charAt(i) ); + } + return num; + }; + + var chatToNum = function(c) { + if ('0' <= c && c <= '9') { + return c.charCodeAt(0) - '0'.charCodeAt(0); + } + throw 'illegal char :' + c; + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // qrAlphaNum + //--------------------------------------------------------------------- + + var qrAlphaNum = function(data) { + + var _mode = QRMode.MODE_ALPHA_NUM; + var _data = data; + + var _this = {}; + + _this.getMode = function() { + return _mode; + }; + + _this.getLength = function(buffer) { + return _data.length; + }; + + _this.write = function(buffer) { + + var s = _data; + + var i = 0; + + while (i + 1 < s.length) { + buffer.put( + getCode(s.charAt(i) ) * 45 + + getCode(s.charAt(i + 1) ), 11); + i += 2; + } + + if (i < s.length) { + buffer.put(getCode(s.charAt(i) ), 6); + } + }; + + var getCode = function(c) { + + if ('0' <= c && c <= '9') { + return c.charCodeAt(0) - '0'.charCodeAt(0); + } else if ('A' <= c && c <= 'Z') { + return c.charCodeAt(0) - 'A'.charCodeAt(0) + 10; + } else { + switch (c) { + case ' ' : return 36; + case '$' : return 37; + case '%' : return 38; + case '*' : return 39; + case '+' : return 40; + case '-' : return 41; + case '.' : return 42; + case '/' : return 43; + case ':' : return 44; + default : + throw 'illegal char :' + c; + } + } + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // qr8BitByte + //--------------------------------------------------------------------- + + var qr8BitByte = function(data) { + + var _mode = QRMode.MODE_8BIT_BYTE; + var _data = data; + var _bytes = qrcode.stringToBytes(data); + + var _this = {}; + + _this.getMode = function() { + return _mode; + }; + + _this.getLength = function(buffer) { + return _bytes.length; + }; + + _this.write = function(buffer) { + for (var i = 0; i < _bytes.length; i += 1) { + buffer.put(_bytes[i], 8); + } + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // qrKanji + //--------------------------------------------------------------------- + + var qrKanji = function(data) { + + var _mode = QRMode.MODE_KANJI; + var _data = data; + + var stringToBytes = qrcode.stringToBytesFuncs['SJIS']; + if (!stringToBytes) { + throw 'sjis not supported.'; + } + !function(c, code) { + // self test for sjis support. + var test = stringToBytes(c); + if (test.length != 2 || ( (test[0] << 8) | test[1]) != code) { + throw 'sjis not supported.'; + } + }('\u53cb', 0x9746); + + var _bytes = stringToBytes(data); + + var _this = {}; + + _this.getMode = function() { + return _mode; + }; + + _this.getLength = function(buffer) { + return ~~(_bytes.length / 2); + }; + + _this.write = function(buffer) { + + var data = _bytes; + + var i = 0; + + while (i + 1 < data.length) { + + var c = ( (0xff & data[i]) << 8) | (0xff & data[i + 1]); + + if (0x8140 <= c && c <= 0x9FFC) { + c -= 0x8140; + } else if (0xE040 <= c && c <= 0xEBBF) { + c -= 0xC140; + } else { + throw 'illegal char at ' + (i + 1) + '/' + c; + } + + c = ( (c >>> 8) & 0xff) * 0xC0 + (c & 0xff); + + buffer.put(c, 13); + + i += 2; + } + + if (i < data.length) { + throw 'illegal char at ' + (i + 1); + } + }; + + return _this; + }; + + //===================================================================== + // GIF Support etc. + // + + //--------------------------------------------------------------------- + // byteArrayOutputStream + //--------------------------------------------------------------------- + + var byteArrayOutputStream = function() { + + var _bytes = []; + + var _this = {}; + + _this.writeByte = function(b) { + _bytes.push(b & 0xff); + }; + + _this.writeShort = function(i) { + _this.writeByte(i); + _this.writeByte(i >>> 8); + }; + + _this.writeBytes = function(b, off, len) { + off = off || 0; + len = len || b.length; + for (var i = 0; i < len; i += 1) { + _this.writeByte(b[i + off]); + } + }; + + _this.writeString = function(s) { + for (var i = 0; i < s.length; i += 1) { + _this.writeByte(s.charCodeAt(i) ); + } + }; + + _this.toByteArray = function() { + return _bytes; + }; + + _this.toString = function() { + var s = ''; + s += '['; + for (var i = 0; i < _bytes.length; i += 1) { + if (i > 0) { + s += ','; + } + s += _bytes[i]; + } + s += ']'; + return s; + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // base64EncodeOutputStream + //--------------------------------------------------------------------- + + var base64EncodeOutputStream = function() { + + var _buffer = 0; + var _buflen = 0; + var _length = 0; + var _base64 = ''; + + var _this = {}; + + var writeEncoded = function(b) { + _base64 += String.fromCharCode(encode(b & 0x3f) ); + }; + + var encode = function(n) { + if (n < 0) { + // error. + } else if (n < 26) { + return 0x41 + n; + } else if (n < 52) { + return 0x61 + (n - 26); + } else if (n < 62) { + return 0x30 + (n - 52); + } else if (n == 62) { + return 0x2b; + } else if (n == 63) { + return 0x2f; + } + throw 'n:' + n; + }; + + _this.writeByte = function(n) { + + _buffer = (_buffer << 8) | (n & 0xff); + _buflen += 8; + _length += 1; + + while (_buflen >= 6) { + writeEncoded(_buffer >>> (_buflen - 6) ); + _buflen -= 6; + } + }; + + _this.flush = function() { + + if (_buflen > 0) { + writeEncoded(_buffer << (6 - _buflen) ); + _buffer = 0; + _buflen = 0; + } + + if (_length % 3 != 0) { + // padding + var padlen = 3 - _length % 3; + for (var i = 0; i < padlen; i += 1) { + _base64 += '='; + } + } + }; + + _this.toString = function() { + return _base64; + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // base64DecodeInputStream + //--------------------------------------------------------------------- + + var base64DecodeInputStream = function(str) { + + var _str = str; + var _pos = 0; + var _buffer = 0; + var _buflen = 0; + + var _this = {}; + + _this.read = function() { + + while (_buflen < 8) { + + if (_pos >= _str.length) { + if (_buflen == 0) { + return -1; + } + throw 'unexpected end of file./' + _buflen; + } + + var c = _str.charAt(_pos); + _pos += 1; + + if (c == '=') { + _buflen = 0; + return -1; + } else if (c.match(/^\s$/) ) { + // ignore if whitespace. + continue; + } + + _buffer = (_buffer << 6) | decode(c.charCodeAt(0) ); + _buflen += 6; + } + + var n = (_buffer >>> (_buflen - 8) ) & 0xff; + _buflen -= 8; + return n; + }; + + var decode = function(c) { + if (0x41 <= c && c <= 0x5a) { + return c - 0x41; + } else if (0x61 <= c && c <= 0x7a) { + return c - 0x61 + 26; + } else if (0x30 <= c && c <= 0x39) { + return c - 0x30 + 52; + } else if (c == 0x2b) { + return 62; + } else if (c == 0x2f) { + return 63; + } else { + throw 'c:' + c; + } + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // gifImage (B/W) + //--------------------------------------------------------------------- + + var gifImage = function(width, height) { + + var _width = width; + var _height = height; + var _data = new Array(width * height); + + var _this = {}; + + _this.setPixel = function(x, y, pixel) { + _data[y * _width + x] = pixel; + }; + + _this.write = function(out) { + + //--------------------------------- + // GIF Signature + + out.writeString('GIF87a'); + + //--------------------------------- + // Screen Descriptor + + out.writeShort(_width); + out.writeShort(_height); + + out.writeByte(0x80); // 2bit + out.writeByte(0); + out.writeByte(0); + + //--------------------------------- + // Global Color Map + + // black + out.writeByte(0x00); + out.writeByte(0x00); + out.writeByte(0x00); + + // white + out.writeByte(0xff); + out.writeByte(0xff); + out.writeByte(0xff); + + //--------------------------------- + // Image Descriptor + + out.writeString(','); + out.writeShort(0); + out.writeShort(0); + out.writeShort(_width); + out.writeShort(_height); + out.writeByte(0); + + //--------------------------------- + // Local Color Map + + //--------------------------------- + // Raster Data + + var lzwMinCodeSize = 2; + var raster = getLZWRaster(lzwMinCodeSize); + + out.writeByte(lzwMinCodeSize); + + var offset = 0; + + while (raster.length - offset > 255) { + out.writeByte(255); + out.writeBytes(raster, offset, 255); + offset += 255; + } + + out.writeByte(raster.length - offset); + out.writeBytes(raster, offset, raster.length - offset); + out.writeByte(0x00); + + //--------------------------------- + // GIF Terminator + out.writeString(';'); + }; + + var bitOutputStream = function(out) { + + var _out = out; + var _bitLength = 0; + var _bitBuffer = 0; + + var _this = {}; + + _this.write = function(data, length) { + + if ( (data >>> length) != 0) { + throw 'length over'; + } + + while (_bitLength + length >= 8) { + _out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) ); + length -= (8 - _bitLength); + data >>>= (8 - _bitLength); + _bitBuffer = 0; + _bitLength = 0; + } + + _bitBuffer = (data << _bitLength) | _bitBuffer; + _bitLength = _bitLength + length; + }; + + _this.flush = function() { + if (_bitLength > 0) { + _out.writeByte(_bitBuffer); + } + }; + + return _this; + }; + + var getLZWRaster = function(lzwMinCodeSize) { + + var clearCode = 1 << lzwMinCodeSize; + var endCode = (1 << lzwMinCodeSize) + 1; + var bitLength = lzwMinCodeSize + 1; + + // Setup LZWTable + var table = lzwTable(); + + for (var i = 0; i < clearCode; i += 1) { + table.add(String.fromCharCode(i) ); + } + table.add(String.fromCharCode(clearCode) ); + table.add(String.fromCharCode(endCode) ); + + var byteOut = byteArrayOutputStream(); + var bitOut = bitOutputStream(byteOut); + + // clear code + bitOut.write(clearCode, bitLength); + + var dataIndex = 0; + + var s = String.fromCharCode(_data[dataIndex]); + dataIndex += 1; + + while (dataIndex < _data.length) { + + var c = String.fromCharCode(_data[dataIndex]); + dataIndex += 1; + + if (table.contains(s + c) ) { + + s = s + c; + + } else { + + bitOut.write(table.indexOf(s), bitLength); + + if (table.size() < 0xfff) { + + if (table.size() == (1 << bitLength) ) { + bitLength += 1; + } + + table.add(s + c); + } + + s = c; + } + } + + bitOut.write(table.indexOf(s), bitLength); + + // end code + bitOut.write(endCode, bitLength); + + bitOut.flush(); + + return byteOut.toByteArray(); + }; + + var lzwTable = function() { + + var _map = {}; + var _size = 0; + + var _this = {}; + + _this.add = function(key) { + if (_this.contains(key) ) { + throw 'dup key:' + key; + } + _map[key] = _size; + _size += 1; + }; + + _this.size = function() { + return _size; + }; + + _this.indexOf = function(key) { + return _map[key]; + }; + + _this.contains = function(key) { + return typeof _map[key] != 'undefined'; + }; + + return _this; + }; + + return _this; + }; + + var createDataURL = function(width, height, getPixel) { + var gif = gifImage(width, height); + for (var y = 0; y < height; y += 1) { + for (var x = 0; x < width; x += 1) { + gif.setPixel(x, y, getPixel(x, y) ); + } + } + + var b = byteArrayOutputStream(); + gif.write(b); + + var base64 = base64EncodeOutputStream(); + var bytes = b.toByteArray(); + for (var i = 0; i < bytes.length; i += 1) { + base64.writeByte(bytes[i]); + } + base64.flush(); + + return 'data:image/gif;base64,' + base64; + }; + + //--------------------------------------------------------------------- + // returns qrcode function. + + return qrcode; +}(); + +// multibyte support +!function() { + + qrcode.stringToBytesFuncs['UTF-8'] = function(s) { + // http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array + function toUTF8Array(str) { + var utf8 = []; + for (var i=0; i < str.length; i++) { + var charcode = str.charCodeAt(i); + if (charcode < 0x80) utf8.push(charcode); + else if (charcode < 0x800) { + utf8.push(0xc0 | (charcode >> 6), + 0x80 | (charcode & 0x3f)); + } + else if (charcode < 0xd800 || charcode >= 0xe000) { + utf8.push(0xe0 | (charcode >> 12), + 0x80 | ((charcode>>6) & 0x3f), + 0x80 | (charcode & 0x3f)); + } + // surrogate pair + else { + i++; + // UTF-16 encodes 0x10000-0x10FFFF by + // subtracting 0x10000 and splitting the + // 20 bits of 0x0-0xFFFFF into two halves + charcode = 0x10000 + (((charcode & 0x3ff)<<10) + | (str.charCodeAt(i) & 0x3ff)); + utf8.push(0xf0 | (charcode >>18), + 0x80 | ((charcode>>12) & 0x3f), + 0x80 | ((charcode>>6) & 0x3f), + 0x80 | (charcode & 0x3f)); + } + } + return utf8; + } + return toUTF8Array(s); + }; + +}(); + +(function (factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } +}(function () { + return qrcode; +})); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b95f801 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +services: + qrcode-generator: + build: . + container_name: qrcode-generator + restart: unless-stopped + ports: + - "8091:8080" + read_only: true + tmpfs: + - /tmp + - /var/cache/nginx + - /var/run + security_opt: + - no-new-privileges:true + cap_drop: + - ALL diff --git a/docs/screenshot.png b/docs/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c86e7784a298cc714982eefec7e97c8c17e0a37c GIT binary patch literal 73204 zcmeFZRa6`8+b>K@Tb@#+XiKr;4lVA`0>#}ew73TkPL1MHw0J2REJZ?q04Yw<;+jxg zli*I+dEWQ`uJ7bK-TPp#Jtx;%nar%2xv%^B$t^K@I;y0^&xi>K2uRh{lnn?72)76b z?g##J7ynBu=jt4_8tuPSjW&mUPR sz+#n)!BGZhvm0 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 F{x{midVl}` literal 0 HcmV?d00001 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..02a3681 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,23 @@ +server { + listen 8080; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Rien ne doit jamais sortir du navigateur : CSP verrouillée, pas de connect-src externe. + add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'none'" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "no-referrer" always; + add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always; + + location / { + try_files $uri $uri/ =404; + } + + location = /healthz { + access_log off; + return 200 "ok\n"; + add_header Content-Type text/plain; + } +}