From 4bf618efd053535ed07f70521b344bffc9b2112d Mon Sep 17 00:00:00 2001 From: "Dr. Jonathan Berrisch" Date: Wed, 7 Oct 2026 12:28:31 +0200 Subject: [PATCH] Add embedding via 404 page --- 404.html | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 14 ++++++++ app.js | 93 +-------------------------------------------------- index.html | 1 + qr-svg.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+), 92 deletions(-) create mode 100644 404.html create mode 100644 qr-svg.js diff --git a/404.html b/404.html new file mode 100644 index 0000000..e3c6c26 --- /dev/null +++ b/404.html @@ -0,0 +1,88 @@ + + + + + + + + + QR Code + + + + +
+ + + + + + + + diff --git a/README.md b/README.md index 963af67..7cc063f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,20 @@ A simplified, SVG-only QR code generator based on QRCode.js. This lightweight li Try it out at [OnlyQR Demo](https://onlyqr.0n8.de). +## Embedding via URL + +Everything after the domain becomes the QR code content, and the page shows only the QR code, scaled to fit: + +```html + + +``` + +- `?color=` takes a hex value without `#` (e.g. `ff0000`) or a CSS colour name. The default is `004c93`. +- Use `encodeURIComponent(text)` for the path, so that `?`, `#`, `%` and `/` in your text come through unchanged. +- Any other query parameters are treated as part of the text. +- This is handled by `404.html` on the static host, so it only works in iframes (not ``), and the HTTP status is 404. + ## Changes from Original QRCode.js - Removed Canvas and Table rendering methods diff --git a/app.js b/app.js index fe2f016..52f5275 100644 --- a/app.js +++ b/app.js @@ -9,97 +9,6 @@ var debounceTimer; - function optimizeSVG(svg) { - // Parse the existing SVG to extract the QR data - const uses = svg.querySelectorAll('use'); - const qrData = []; - let maxX = 0, maxY = 0; - - // Extract positions from use elements - uses.forEach(use => { - const x = parseInt(use.getAttribute('x') || '0'); - const y = parseInt(use.getAttribute('y') || '0'); - if (!qrData[y]) qrData[y] = []; - qrData[y][x] = true; - maxX = Math.max(maxX, x); - maxY = Math.max(maxY, y); - }); - - // Get the color from the original template - const template = svg.querySelector('#template, rect[id]'); - const color = template ? template.getAttribute('fill') || '#000' : '#000'; - - // Create optimized SVG - const optimizedSVG = createOptimizedSVG(qrData, maxX + 1, maxY + 1, color); - - // Replace the old SVG with the optimized one - svg.parentNode.replaceChild(optimizedSVG, svg); - - return optimizedSVG; - } - - function createOptimizedSVG(qrData, width, height, color) { - const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); - svg.setAttribute('viewBox', `0 0 ${width} ${height}`); - svg.setAttribute('width', width * 10); - svg.setAttribute('height', height * 10); - - // Create defs section with reusable cell (no fill - will inherit) - const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); - const cellTemplate = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); - cellTemplate.setAttribute('id', 'cell'); - cellTemplate.setAttribute('width', '1'); - cellTemplate.setAttribute('height', '1'); - defs.appendChild(cellTemplate); - svg.appendChild(defs); - - // Create main group with fill color - const mainGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - mainGroup.setAttribute('fill', color); - svg.appendChild(mainGroup); - - // Create optimized structure - for (let y = 0; y < height; y++) { - if (!qrData[y]) continue; - - // Find consecutive cells and group them - let x = 0; - while (x < width) { - if (qrData[y][x]) { - // Count consecutive cells - let consecutiveCount = 1; - while (x + consecutiveCount < width && qrData[y][x + consecutiveCount]) { - consecutiveCount++; - } - - if (consecutiveCount > 1) { - // Use a single rect for multiple consecutive cells (inherits fill from group) - const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); - rect.setAttribute('x', x); - rect.setAttribute('y', y); - rect.setAttribute('width', consecutiveCount); - rect.setAttribute('height', '1'); - mainGroup.appendChild(rect); - } else { - // Use individual use elements for single cells (inherits fill from group) - const use = document.createElementNS('http://www.w3.org/2000/svg', 'use'); - use.setAttribute('href', '#cell'); - use.setAttribute('x', x); - use.setAttribute('y', y); - mainGroup.appendChild(use); - } - - x += consecutiveCount; - } else { - x++; - } - } - } - - return svg; - } - function makeCode() { var elText = document.getElementById("text"); var elColor = document.getElementById("color"); @@ -112,7 +21,7 @@ setTimeout(() => { const svg = document.querySelector('#qrcode svg'); if (svg) { - optimizeSVG(svg); + optimizeQRSVG(svg); } }, 10); } diff --git a/index.html b/index.html index 12d0a2c..283ef78 100644 --- a/index.html +++ b/index.html @@ -53,6 +53,7 @@ + diff --git a/qr-svg.js b/qr-svg.js new file mode 100644 index 0000000..67408ab --- /dev/null +++ b/qr-svg.js @@ -0,0 +1,97 @@ +/* Shared SVG optimisation used by the generator UI (index.html) and the embed page (404.html). */ +(function(global) { + 'use strict'; + + function optimizeSVG(svg) { + // Parse the existing SVG to extract the QR data + const uses = svg.querySelectorAll('use'); + const qrData = []; + let maxX = 0, maxY = 0; + + // Extract positions from use elements + uses.forEach(use => { + const x = parseInt(use.getAttribute('x') || '0'); + const y = parseInt(use.getAttribute('y') || '0'); + if (!qrData[y]) qrData[y] = []; + qrData[y][x] = true; + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + }); + + // Get the color from the original template + const template = svg.querySelector('#template, rect[id]'); + const color = template ? template.getAttribute('fill') || '#000' : '#000'; + + // Create optimized SVG + const optimizedSVG = createOptimizedSVG(qrData, maxX + 1, maxY + 1, color); + + // Replace the old SVG with the optimized one + svg.parentNode.replaceChild(optimizedSVG, svg); + + return optimizedSVG; + } + + function createOptimizedSVG(qrData, width, height, color) { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); + svg.setAttribute('viewBox', `0 0 ${width} ${height}`); + svg.setAttribute('width', width * 10); + svg.setAttribute('height', height * 10); + + // Create defs section with reusable cell (no fill - will inherit) + const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); + const cellTemplate = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + cellTemplate.setAttribute('id', 'cell'); + cellTemplate.setAttribute('width', '1'); + cellTemplate.setAttribute('height', '1'); + defs.appendChild(cellTemplate); + svg.appendChild(defs); + + // Create main group with fill color + const mainGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + mainGroup.setAttribute('fill', color); + svg.appendChild(mainGroup); + + // Create optimized structure + for (let y = 0; y < height; y++) { + if (!qrData[y]) continue; + + // Find consecutive cells and group them + let x = 0; + while (x < width) { + if (qrData[y][x]) { + // Count consecutive cells + let consecutiveCount = 1; + while (x + consecutiveCount < width && qrData[y][x + consecutiveCount]) { + consecutiveCount++; + } + + if (consecutiveCount > 1) { + // Use a single rect for multiple consecutive cells (inherits fill from group) + const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.setAttribute('x', x); + rect.setAttribute('y', y); + rect.setAttribute('width', consecutiveCount); + rect.setAttribute('height', '1'); + mainGroup.appendChild(rect); + } else { + // Use individual use elements for single cells (inherits fill from group) + const use = document.createElementNS('http://www.w3.org/2000/svg', 'use'); + use.setAttribute('href', '#cell'); + use.setAttribute('x', x); + use.setAttribute('y', y); + mainGroup.appendChild(use); + } + + x += consecutiveCount; + } else { + x++; + } + } + } + + return svg; + } + + global.optimizeQRSVG = optimizeSVG; +})(window);