Fix: CAD-Skizzen Baustellenschild zurueck, Key-Gen wieder in TOOL_DEFS, QR-Code browser-kompatibel
This commit is contained in:
@@ -68,8 +68,8 @@ export const TOOLS = [
|
||||
{
|
||||
id: 'skizze',
|
||||
Icon: SkizzeIcon,
|
||||
label: 'CAD-Skizzen',
|
||||
navLabel: 'CAD-Skizzen',
|
||||
label: 'CAD-Skizzen 🚧',
|
||||
navLabel: 'CAD-Skizzen 🚧',
|
||||
group: 'Werkzeuge',
|
||||
component: Skizze,
|
||||
},
|
||||
|
||||
@@ -2701,69 +2701,96 @@ const CAP_POS = [
|
||||
|
||||
function drawQrToCanvas(canvas, data, opts) {
|
||||
const { size, fgColor, bgColor, margin, dotStyle, cornerStyle, caption, captionPos, captionColor, captionSize } = opts;
|
||||
const capH = caption && captionPos !== 'none' ? (captionSize + 16) : 0;
|
||||
const totalH = size + capH;
|
||||
const capH = caption && captionPos !== 'none' ? (Number(captionSize) + 16) : 0;
|
||||
const totalH = size + capH;
|
||||
canvas.width = size;
|
||||
canvas.height = totalH;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = bgColor;
|
||||
ctx.fillRect(0, 0, size, totalH);
|
||||
|
||||
return QRCode.create(data, { errorCorrectionLevel: 'M' }).then(qr => {
|
||||
const modules = qr.modules;
|
||||
const n = modules.size;
|
||||
const cellSize = (size - margin * 2) / n;
|
||||
const qrTop = captionPos === 'top' ? capH : 0;
|
||||
// Step 1: generate QR to temporary canvas (black on white, no margin)
|
||||
const tmp = document.createElement('canvas');
|
||||
return QRCode.toCanvas(tmp, data, {
|
||||
width: size,
|
||||
margin: 0,
|
||||
color: { dark: '#000000ff', light: '#ffffffff' },
|
||||
errorCorrectionLevel: 'M',
|
||||
}).then(() => {
|
||||
// Step 2: read pixel data to detect module grid
|
||||
const tctx = tmp.getContext('2d');
|
||||
const pixels = tctx.getImageData(0, 0, size, size).data;
|
||||
|
||||
// Find module size by scanning first dark run
|
||||
let firstDark = -1, runEnd = -1;
|
||||
for (let x = 0; x < size; x++) {
|
||||
const idx = x * 4;
|
||||
const dark = pixels[idx] < 128;
|
||||
if (dark && firstDark < 0) firstDark = x;
|
||||
if (!dark && firstDark >= 0 && runEnd < 0) { runEnd = x; break; }
|
||||
}
|
||||
const moduleSize = (runEnd > 0 && firstDark >= 0) ? (runEnd - firstDark) : Math.round(size / 25);
|
||||
const n = Math.round(size / moduleSize);
|
||||
|
||||
// Step 3: draw background
|
||||
ctx.fillStyle = bgColor;
|
||||
ctx.fillRect(0, 0, size, totalH);
|
||||
|
||||
const qrTop = captionPos === 'top' ? capH : 0;
|
||||
const pad = Math.round(margin * moduleSize);
|
||||
const cellSize = (size - pad * 2) / n;
|
||||
|
||||
ctx.fillStyle = fgColor;
|
||||
|
||||
for (let row = 0; row < n; row++) {
|
||||
for (let col = 0; col < n; col++) {
|
||||
if (!modules.get(row, col)) continue;
|
||||
const x = margin + col * cellSize;
|
||||
const y = qrTop + margin + row * cellSize;
|
||||
// Sample the center pixel of this module from the temp canvas
|
||||
const px = Math.round((col + 0.5) * moduleSize);
|
||||
const py = Math.round((row + 0.5) * moduleSize);
|
||||
const idx = (py * size + px) * 4;
|
||||
if (pixels[idx] > 128) continue; // light module
|
||||
|
||||
const x = pad + col * cellSize;
|
||||
const y = qrTop + pad + row * cellSize;
|
||||
const w = cellSize;
|
||||
|
||||
// Corner squares (finder patterns): rows/cols 0-7 and last 7
|
||||
const isCorner = (row < 7 && col < 7) || (row < 7 && col >= n-7) || (row >= n-7 && col < 7);
|
||||
// Finder pattern corners: rows/cols 0-6 and last 6
|
||||
const isCorner = (row < 7 && col < 7) || (row < 7 && col >= n - 7) || (row >= n - 7 && col < 7);
|
||||
|
||||
if (isCorner) {
|
||||
if (cornerStyle === 'dot') {
|
||||
const r = w * 0.5;
|
||||
ctx.beginPath(); ctx.arc(x+r, y+r, r, 0, Math.PI*2); ctx.fill();
|
||||
} else if (cornerStyle === 'rounded') {
|
||||
const r = w * 0.35;
|
||||
ctx.beginPath(); ctx.roundRect(x, y, w, w, r); ctx.fill();
|
||||
} else {
|
||||
ctx.fillRect(x, y, w, w);
|
||||
}
|
||||
const style = isCorner ? cornerStyle : dotStyle;
|
||||
|
||||
if (style === 'dots') {
|
||||
const r = w * 0.45;
|
||||
ctx.beginPath(); ctx.arc(x + w/2, y + w/2, r, 0, Math.PI * 2); ctx.fill();
|
||||
} else if (style === 'rounded' || style === 'classy') {
|
||||
const r = style === 'classy' ? w * 0.42 : w * 0.32;
|
||||
ctx.beginPath(); ctx.roundRect(x + 0.5, y + 0.5, w - 1, w - 1, r); ctx.fill();
|
||||
} else if (style === 'dot') {
|
||||
const r = w * 0.45;
|
||||
ctx.beginPath(); ctx.arc(x + w/2, y + w/2, r, 0, Math.PI * 2); ctx.fill();
|
||||
} else {
|
||||
if (dotStyle === 'dots') {
|
||||
const r = w * 0.45;
|
||||
ctx.beginPath(); ctx.arc(x+r, y+r, r, 0, Math.PI*2); ctx.fill();
|
||||
} else if (dotStyle === 'rounded') {
|
||||
const r = w * 0.3;
|
||||
ctx.beginPath(); ctx.roundRect(x, y, w, w, r); ctx.fill();
|
||||
} else if (dotStyle === 'classy') {
|
||||
const r = w * 0.4;
|
||||
ctx.beginPath(); ctx.roundRect(x+1, y+1, w-2, w-2, r); ctx.fill();
|
||||
} else {
|
||||
ctx.fillRect(x, y, w, w);
|
||||
}
|
||||
ctx.fillRect(x, y, w, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Caption
|
||||
// Step 4: caption
|
||||
if (caption && captionPos !== 'none') {
|
||||
ctx.fillStyle = captionColor;
|
||||
ctx.font = `${captionSize}px sans-serif`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillStyle = captionColor;
|
||||
ctx.font = `${captionSize}px sans-serif`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
const cy = captionPos === 'top' ? capH / 2 : size + capH / 2;
|
||||
ctx.fillText(caption, size / 2, cy, size - 16);
|
||||
}
|
||||
}).catch(() => {});
|
||||
}).catch(err => {
|
||||
// Show error message on canvas
|
||||
ctx.fillStyle = bgColor;
|
||||
ctx.fillRect(0, 0, size, totalH);
|
||||
ctx.fillStyle = '#ff6b9d';
|
||||
ctx.font = '13px monospace';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('Ungültige URL', size/2, size/2);
|
||||
});
|
||||
}
|
||||
|
||||
function QrGenerator({ toast, mobile }) {
|
||||
@@ -3086,6 +3113,7 @@ const TOOL_DEFS = [
|
||||
{id:'netzwerk', label:'🌐 Netzwerk', desc:'IP, Subnetz, Geschwindigkeit, CIDR', ready:true, component:NetzwerkRechner},
|
||||
{id:'datetime', label:'📅 Datetime', desc:'Zeiten umrechnen · Geburtstag · Differenz', ready:true, component:DatetimeRechner},
|
||||
{id:'farben', label:'🎨 Farben', desc:'HEX, RGB, HSL, OKLCH umrechnen', ready:true, component:FarbRechner},
|
||||
{id:'keygen', label:'🔐 Key-Gen', desc:'Sichere Keys & Secrets generieren', ready:true, component:KeyGen},
|
||||
{id:'qrcode', label:'◻ QR-Code', desc:'QR-Codes erstellen & verwalten', ready:true, component:QrGenerator},
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user