// ========== TEXT-GENERATOR v5 ========== // Features: Separate Padding pro Kante, Reset-Button, Einzelner Pfeil-Export, Pfeilspitzenlaenge var textGenDefaults = { text: '', fontSize: 16, textColor: '#000000', frameColor: '#000000', shape: 'none', frameScale: 100, paddingTop: 10, paddingRight: 10, paddingBottom: 10, paddingLeft: 10, lineStyle: 'solid', lineWeight: 2, arrow: 'none', arrowLength: 40, arrowAngle: 0, arrowBend: 50, arrowSize: 10, arrowTipLength: 15 }; var textGenState = JSON.parse(JSON.stringify(textGenDefaults)); // Custom Symbols aus localStorage var customSymbols = []; function initTextGenerator() { loadCustomSymbols(); var textInput = document.getElementById('customText'); var fontSizeInput = document.getElementById('fontSize'); var fontSizeValue = document.getElementById('fontSizeValue'); var textColorInput = document.getElementById('textColor'); var textColorValue = document.getElementById('textColorValue'); var frameColorInput = document.getElementById('frameColor'); var frameColorValue = document.getElementById('frameColorValue'); var frameScaleInput = document.getElementById('frameScale'); var frameScaleValue = document.getElementById('frameScaleValue'); // Gesamt-Padding Input var paddingAllInput = document.getElementById("paddingAll"); var paddingAllValueEl = document.getElementById("paddingAllValue"); // Separate Padding Inputs var paddingTopInput = document.getElementById('paddingTop'); var paddingTopValue = document.getElementById('paddingTopValue'); var paddingRightInput = document.getElementById('paddingRight'); var paddingRightValue = document.getElementById('paddingRightValue'); var paddingBottomInput = document.getElementById('paddingBottom'); var paddingBottomValue = document.getElementById('paddingBottomValue'); var paddingLeftInput = document.getElementById('paddingLeft'); var paddingLeftValue = document.getElementById('paddingLeftValue'); var arrowLengthInput = document.getElementById('arrowLength'); var arrowLengthValue = document.getElementById('arrowLengthValue'); var arrowAngleInput = document.getElementById('arrowAngle'); var arrowAngleValue = document.getElementById('arrowAngleValue'); var arrowBendInput = document.getElementById('arrowBend'); var arrowBendValue = document.getElementById('arrowBendValue'); var arrowSizeInput = document.getElementById('arrowSize'); var arrowSizeValue = document.getElementById('arrowSizeValue'); var arrowTipLengthInput = document.getElementById('arrowTipLength'); var arrowTipLengthValue = document.getElementById('arrowTipLengthValue'); // Text input if (textInput) { textInput.addEventListener('input', function(e) { textGenState.text = e.target.value; updateTextPreview(); }); } // Font size if (fontSizeInput) { fontSizeInput.addEventListener('input', function(e) { textGenState.fontSize = parseInt(e.target.value); fontSizeValue.textContent = e.target.value + 'px'; updateTextPreview(); }); } // Text color if (textColorInput) { textColorInput.addEventListener('input', function(e) { textGenState.textColor = e.target.value; textColorValue.textContent = e.target.value; updateTextPreview(); }); } // Frame color if (frameColorInput) { frameColorInput.addEventListener('input', function(e) { textGenState.frameColor = e.target.value; frameColorValue.textContent = e.target.value; updateTextPreview(); }); } // Frame scale if (frameScaleInput) { frameScaleInput.addEventListener('input', function(e) { textGenState.frameScale = parseInt(e.target.value); frameScaleValue.textContent = e.target.value + '%'; updateTextPreview(); }); } // Gesamt-Padding handler (setzt alle 4 Seiten) if (paddingAllInput) { paddingAllInput.addEventListener("input", function(e) { var val = parseInt(e.target.value); textGenState.paddingTop = val; textGenState.paddingRight = val; textGenState.paddingBottom = val; textGenState.paddingLeft = val; paddingAllValueEl.textContent = val + "px"; // Update einzelne Slider if (paddingTopInput) { paddingTopInput.value = val; paddingTopValue.textContent = val + "px"; } if (paddingRightInput) { paddingRightInput.value = val; paddingRightValue.textContent = val + "px"; } if (paddingBottomInput) { paddingBottomInput.value = val; paddingBottomValue.textContent = val + "px"; } if (paddingLeftInput) { paddingLeftInput.value = val; paddingLeftValue.textContent = val + "px"; } updateTextPreview(); }); } // Separate Padding handlers if (paddingTopInput) { paddingTopInput.addEventListener('input', function(e) { textGenState.paddingTop = parseInt(e.target.value); paddingTopValue.textContent = e.target.value + 'px'; updateTextPreview(); }); } if (paddingRightInput) { paddingRightInput.addEventListener('input', function(e) { textGenState.paddingRight = parseInt(e.target.value); paddingRightValue.textContent = e.target.value + 'px'; updateTextPreview(); }); } if (paddingBottomInput) { paddingBottomInput.addEventListener('input', function(e) { textGenState.paddingBottom = parseInt(e.target.value); paddingBottomValue.textContent = e.target.value + 'px'; updateTextPreview(); }); } if (paddingLeftInput) { paddingLeftInput.addEventListener('input', function(e) { textGenState.paddingLeft = parseInt(e.target.value); paddingLeftValue.textContent = e.target.value + 'px'; updateTextPreview(); }); } // Arrow length if (arrowLengthInput) { arrowLengthInput.addEventListener('input', function(e) { textGenState.arrowLength = parseInt(e.target.value); arrowLengthValue.textContent = e.target.value + 'px'; updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } }); } // Arrow angle if (arrowAngleInput) { arrowAngleInput.addEventListener('input', function(e) { textGenState.arrowAngle = parseInt(e.target.value); arrowAngleValue.textContent = e.target.value + '\u00B0'; updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } }); } // Arrow bend position if (arrowBendInput) { arrowBendInput.addEventListener('input', function(e) { textGenState.arrowBend = parseInt(e.target.value); arrowBendValue.textContent = e.target.value + '%'; updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } }); } // Arrow size (width) if (arrowSizeInput) { arrowSizeInput.addEventListener('input', function(e) { textGenState.arrowSize = parseInt(e.target.value); arrowSizeValue.textContent = e.target.value + 'px'; updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } }); } // Arrow tip length (laenger = schmaler) if (arrowTipLengthInput) { arrowTipLengthInput.addEventListener('input', function(e) { textGenState.arrowTipLength = parseInt(e.target.value); arrowTipLengthValue.textContent = e.target.value + 'px'; updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } }); } // Shape buttons document.querySelectorAll('.shape-btn').forEach(function(btn) { btn.addEventListener('click', function() { document.querySelectorAll('.shape-btn').forEach(function(b) { b.classList.remove('active'); }); btn.classList.add('active'); textGenState.shape = btn.dataset.shape; updateFrameScaleVisibility(); updateTextPreview(); }); }); // Line style buttons document.querySelectorAll('.line-btn').forEach(function(btn) { btn.addEventListener('click', function() { document.querySelectorAll('.line-btn').forEach(function(b) { b.classList.remove('active'); }); btn.classList.add('active'); textGenState.lineStyle = btn.dataset.style; updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } }); }); // Line weight buttons document.querySelectorAll('.weight-btn').forEach(function(btn) { btn.addEventListener('click', function() { document.querySelectorAll('.weight-btn').forEach(function(b) { b.classList.remove('active'); }); btn.classList.add('active'); textGenState.lineWeight = parseInt(btn.dataset.weight); updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } }); }); // Arrow buttons document.querySelectorAll('.arrow-btn').forEach(function(btn) { btn.addEventListener('click', function() { document.querySelectorAll('.arrow-btn').forEach(function(b) { b.classList.remove('active'); }); btn.classList.add('active'); textGenState.arrow = btn.dataset.arrow; updateArrowDetailsVisibility(); updateTextPreview(); }); }); updateFrameScaleVisibility(); updateArrowDetailsVisibility(); updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } } // ========== RESET FUNCTION ========== function resetToDefaults() { textGenState = JSON.parse(JSON.stringify(textGenDefaults)); // Update all UI elements var textInput = document.getElementById('customText'); if (textInput) textInput.value = ''; var fontSizeInput = document.getElementById('fontSize'); var fontSizeValue = document.getElementById('fontSizeValue'); if (fontSizeInput) fontSizeInput.value = textGenDefaults.fontSize; if (fontSizeValue) fontSizeValue.textContent = textGenDefaults.fontSize + 'px'; var textColorInput = document.getElementById('textColor'); var textColorValue = document.getElementById('textColorValue'); if (textColorInput) textColorInput.value = textGenDefaults.textColor; if (textColorValue) textColorValue.textContent = textGenDefaults.textColor; var frameColorInput = document.getElementById('frameColor'); var frameColorValue = document.getElementById('frameColorValue'); if (frameColorInput) frameColorInput.value = textGenDefaults.frameColor; if (frameColorValue) frameColorValue.textContent = textGenDefaults.frameColor; var frameScaleInput = document.getElementById('frameScale'); var frameScaleValue = document.getElementById('frameScaleValue'); if (frameScaleInput) frameScaleInput.value = textGenDefaults.frameScale; if (frameScaleValue) frameScaleValue.textContent = textGenDefaults.frameScale + '%'; // Reset Gesamt-Padding var paddingAllInput = document.getElementById('paddingAll'); var paddingAllValue = document.getElementById('paddingAllValue'); if (paddingAllInput) paddingAllInput.value = textGenDefaults.paddingTop; if (paddingAllValue) paddingAllValue.textContent = textGenDefaults.paddingTop + 'px'; // Reset separate padding ['Top', 'Right', 'Bottom', 'Left'].forEach(function(side) { var input = document.getElementById('padding' + side); var value = document.getElementById('padding' + side + 'Value'); var defaultVal = textGenDefaults['padding' + side]; if (input) input.value = defaultVal; if (value) value.textContent = defaultVal + 'px'; }); var arrowLengthInput = document.getElementById('arrowLength'); var arrowLengthValue = document.getElementById('arrowLengthValue'); if (arrowLengthInput) arrowLengthInput.value = textGenDefaults.arrowLength; if (arrowLengthValue) arrowLengthValue.textContent = textGenDefaults.arrowLength + 'px'; var arrowAngleInput = document.getElementById('arrowAngle'); var arrowAngleValue = document.getElementById('arrowAngleValue'); if (arrowAngleInput) arrowAngleInput.value = textGenDefaults.arrowAngle; if (arrowAngleValue) arrowAngleValue.textContent = textGenDefaults.arrowAngle + '\u00B0'; var arrowBendInput = document.getElementById('arrowBend'); var arrowBendValue = document.getElementById('arrowBendValue'); if (arrowBendInput) arrowBendInput.value = textGenDefaults.arrowBend; if (arrowBendValue) arrowBendValue.textContent = textGenDefaults.arrowBend + '%'; var arrowSizeInput = document.getElementById('arrowSize'); var arrowSizeValue = document.getElementById('arrowSizeValue'); if (arrowSizeInput) arrowSizeInput.value = textGenDefaults.arrowSize; if (arrowSizeValue) arrowSizeValue.textContent = textGenDefaults.arrowSize + 'px'; var arrowTipLengthInput = document.getElementById('arrowTipLength'); var arrowTipLengthValue = document.getElementById('arrowTipLengthValue'); if (arrowTipLengthInput) arrowTipLengthInput.value = textGenDefaults.arrowTipLength; if (arrowTipLengthValue) arrowTipLengthValue.textContent = textGenDefaults.arrowTipLength + 'px'; // Reset buttons document.querySelectorAll('.shape-btn').forEach(function(btn) { btn.classList.toggle('active', btn.dataset.shape === 'none'); }); document.querySelectorAll('.line-btn').forEach(function(btn) { btn.classList.toggle('active', btn.dataset.style === 'solid'); }); document.querySelectorAll('.weight-btn').forEach(function(btn) { btn.classList.toggle('active', btn.dataset.weight === '2'); }); document.querySelectorAll('.arrow-btn').forEach(function(btn) { btn.classList.toggle('active', btn.dataset.arrow === 'none'); }); updateFrameScaleVisibility(); updateArrowDetailsVisibility(); updateTextPreview(); try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); } showNotification('Auf Standard zurueckgesetzt'); } function updateFrameScaleVisibility() { console.log("updateFrameScaleVisibility called, shape:", textGenState.shape); var row = document.getElementById('frameScaleRow'); var paddingAllRow = document.getElementById('framePaddingAllRow'); var paddingRow = document.getElementById('framePaddingRow'); if (row) { row.style.display = textGenState.shape === 'none' ? 'none' : 'flex'; } if (paddingAllRow) { paddingAllRow.style.display = textGenState.shape === 'none' ? 'none' : 'flex'; } if (paddingRow) { paddingRow.style.display = textGenState.shape === 'none' ? 'none' : 'flex'; } } function updateArrowDetailsVisibility() { var row = document.getElementById('arrowDetailsRow'); var row2 = document.getElementById('arrowDetailsRow2'); if (row) { row.style.display = textGenState.arrow === 'none' ? 'none' : 'flex'; } if (row2) { row2.style.display = textGenState.arrow === 'none' ? 'none' : 'flex'; } } function toggleTextGenerator() { var generator = document.querySelector('.text-generator'); generator.classList.toggle('collapsed'); } function getStrokeDasharray(style) { switch(style) { case 'dashed': return '8,4'; case 'dotted': return '2,3'; default: return 'none'; } } function generateTextSVG() { var text = textGenState.text || 'Text'; var fontSize = textGenState.fontSize; var textColor = textGenState.textColor; var frameColor = textGenState.frameColor; var shape = textGenState.shape; var frameScale = textGenState.frameScale / 100; var paddingTop = textGenState.paddingTop; var paddingRight = textGenState.paddingRight; var paddingBottom = textGenState.paddingBottom; var paddingLeft = textGenState.paddingLeft; var lineStyle = textGenState.lineStyle; var lineWeight = textGenState.lineWeight; var arrow = textGenState.arrow; var arrowLength = textGenState.arrowLength; var arrowAngle = textGenState.arrowAngle; var arrowBend = textGenState.arrowBend / 100; var arrowSize = textGenState.arrowSize; var arrowTipLength = textGenState.arrowTipLength; var lines = text.split('\n'); if (lines.length === 0) lines = ['Text']; var maxLineLength = 0; for (var i = 0; i < lines.length; i++) { if (lines[i].length > maxLineLength) maxLineLength = lines[i].length; } if (maxLineLength === 0) maxLineLength = 4; var charWidth = fontSize * 0.6; var lineHeight = fontSize * 1.3; var textWidth = maxLineLength * charWidth; var textHeight = lines.length * lineHeight; var frameWidth = (textWidth + paddingLeft + paddingRight) * frameScale; var frameHeight = (textHeight + paddingTop + paddingBottom) * frameScale; if (frameWidth < 40) frameWidth = 40; if (frameHeight < 30) frameHeight = 30; var arrowSpace = (arrow !== 'none') ? arrowLength + arrowTipLength + 5 : 0; var svgWidth = frameWidth; var svgHeight = frameHeight; var offsetX = 0, offsetY = 0; if (arrow === 'left') { svgWidth += arrowSpace; offsetX = arrowSpace; } else if (arrow === 'right') { svgWidth += arrowSpace; } else if (arrow === 'top') { svgHeight += arrowSpace; offsetY = arrowSpace; } else if (arrow === 'bottom') { svgHeight += arrowSpace; } var frameCx = offsetX + frameWidth / 2; var frameCy = offsetY + frameHeight / 2; // Text-Verschiebung durch asymmetrisches Padding var textOffsetX = ((paddingLeft - paddingRight) / 2) * frameScale; var textOffsetY = ((paddingTop - paddingBottom) / 2) * frameScale; var textCx = frameCx + textOffsetX; var textCy = frameCy + textOffsetY; var dashArray = getStrokeDasharray(lineStyle); var dashAttr = dashArray !== 'none' ? ' stroke-dasharray="' + dashArray + '"' : ''; var shapeSvg = ''; var shapeX = offsetX + lineWeight / 2; var shapeY = offsetY + lineWeight / 2; var shapeW = frameWidth - lineWeight; var shapeH = frameHeight - lineWeight; switch(shape) { case 'rect': shapeSvg = ''; break; case 'square': var squareSize = Math.max(shapeW, shapeH); shapeSvg = ''; break; case 'circle': var radius = Math.max(shapeW, shapeH) / 2; shapeSvg = ''; break; case 'oval': shapeSvg = ''; break; case 'diamond': var halfW = shapeW / 2, halfH = shapeH / 2; shapeSvg = ''; break; } var arrowSvg = ''; if (arrow !== 'none') { arrowSvg = generateArrowPath(arrow, frameCx, frameCy, frameWidth, frameHeight, offsetX, offsetY, arrowLength, arrowAngle, arrowBend, frameColor, lineWeight, dashAttr, arrowSize, arrowTipLength); } var textSvg = ''; var totalTextHeight = lines.length * lineHeight; var startY = textCy - (totalTextHeight / 2) + (fontSize * 0.8); for (var j = 0; j < lines.length; j++) { var lineY = startY + (j * lineHeight); var lineText = lines[j] || ' '; lineText = lineText.replace(/&/g, '&').replace(//g, '>'); textSvg += '' + lineText + ''; } textSvg += ''; return '' + shapeSvg + arrowSvg + textSvg + ''; } function generateArrowPath(direction, cx, cy, frameWidth, frameHeight, offsetX, offsetY, length, angle, bendPos, color, weight, dashAttr, arrowSize, tipLength) { var startX, startY, midX, midY, endX, endY; var angleRad = angle * Math.PI / 180; // Startpunkt am Rahmen switch(direction) { case 'top': startX = cx; startY = offsetY; midX = startX; midY = startY - length * bendPos; endX = midX + Math.sin(angleRad) * (length * (1 - bendPos)); endY = midY - Math.cos(angleRad) * (length * (1 - bendPos)); break; case 'bottom': startX = cx; startY = offsetY + frameHeight; midX = startX; midY = startY + length * bendPos; endX = midX + Math.sin(angleRad) * (length * (1 - bendPos)); endY = midY + Math.cos(angleRad) * (length * (1 - bendPos)); break; case 'left': startX = offsetX; startY = cy; midX = startX - length * bendPos; midY = startY; endX = midX - Math.cos(angleRad) * (length * (1 - bendPos)); endY = midY + Math.sin(angleRad) * (length * (1 - bendPos)); break; case 'right': startX = offsetX + frameWidth; startY = cy; midX = startX + length * bendPos; midY = startY; endX = midX + Math.cos(angleRad) * (length * (1 - bendPos)); endY = midY + Math.sin(angleRad) * (length * (1 - bendPos)); break; } // Pfad erstellen var pathD; if (angle !== 0 && bendPos > 0 && bendPos < 1) { pathD = 'M ' + startX + ' ' + startY + ' L ' + midX + ' ' + midY + ' L ' + endX + ' ' + endY; } else { pathD = 'M ' + startX + ' ' + startY + ' L ' + endX + ' ' + endY; } // Pfeilspitze: Richtung basiert auf dem LETZTEN Segment var lastSegmentStartX, lastSegmentStartY; if (angle !== 0 && bendPos > 0 && bendPos < 1) { lastSegmentStartX = midX; lastSegmentStartY = midY; } else { lastSegmentStartX = startX; lastSegmentStartY = startY; } // Winkel des letzten Segments berechnen var arrowHeadAngle = Math.atan2(endY - lastSegmentStartY, endX - lastSegmentStartX); // Pfeilspitze Punkte - tipLength bestimmt wie lang (und damit schmaler) die Spitze ist // arrowSize bestimmt die Breite an der Basis // Je laenger tipLength, desto spitzer/schmaler wird der Pfeil var tipHalfWidth = arrowSize / 2; // Basis der Pfeilspitze (zurueck von der Spitze) var baseX = endX - tipLength * Math.cos(arrowHeadAngle); var baseY = endY - tipLength * Math.sin(arrowHeadAngle); // Seitenpunkte der Pfeilspitze var perpAngle = arrowHeadAngle + Math.PI / 2; var ah1x = baseX + tipHalfWidth * Math.cos(perpAngle); var ah1y = baseY + tipHalfWidth * Math.sin(perpAngle); var ah2x = baseX - tipHalfWidth * Math.cos(perpAngle); var ah2y = baseY - tipHalfWidth * Math.sin(perpAngle); var arrowHead = ''; return '' + arrowHead; } // ========== STANDALONE ARROW (Einzelner loser Pfeil) ========== function generateStandaloneArrowSVG() { var length = textGenState.arrowLength; var angle = textGenState.arrowAngle; var bendPos = textGenState.arrowBend / 100; var color = textGenState.frameColor; var weight = textGenState.lineWeight; var lineStyle = textGenState.lineStyle; var arrowSize = textGenState.arrowSize; var tipLength = textGenState.arrowTipLength; var dashArray = getStrokeDasharray(lineStyle); var dashAttr = dashArray !== 'none' ? ' stroke-dasharray="' + dashArray + '"' : ''; // Padding fuer SVG var padding = Math.max(arrowSize, tipLength) + 5; var angleRad = angle * Math.PI / 180; // Pfeil geht von links nach rechts var startX = padding; var startY = padding + length / 2; // Knickpunkt var midX = startX + length * bendPos; var midY = startY; // Endpunkt mit Winkel var endX, endY; if (angle !== 0 && bendPos > 0 && bendPos < 1) { endX = midX + Math.cos(angleRad) * (length * (1 - bendPos)); endY = midY - Math.sin(angleRad) * (length * (1 - bendPos)); } else { endX = startX + length; endY = startY; } // SVG Dimensionen berechnen var minX = Math.min(startX, midX, endX) - padding; var maxX = Math.max(startX, midX, endX) + padding + tipLength; var minY = Math.min(startY, midY, endY) - padding - arrowSize; var maxY = Math.max(startY, midY, endY) + padding + arrowSize; var svgWidth = maxX - minX; var svgHeight = maxY - minY; // Offset korrigieren var offsetX = -minX; var offsetY = -minY; startX += offsetX; startY += offsetY; midX += offsetX; midY += offsetY; endX += offsetX; endY += offsetY; // Pfad erstellen var pathD; if (angle !== 0 && bendPos > 0 && bendPos < 1) { pathD = 'M ' + startX + ' ' + startY + ' L ' + midX + ' ' + midY + ' L ' + endX + ' ' + endY; } else { pathD = 'M ' + startX + ' ' + startY + ' L ' + endX + ' ' + endY; } // Pfeilspitze var lastSegmentStartX, lastSegmentStartY; if (angle !== 0 && bendPos > 0 && bendPos < 1) { lastSegmentStartX = midX; lastSegmentStartY = midY; } else { lastSegmentStartX = startX; lastSegmentStartY = startY; } var arrowHeadAngle = Math.atan2(endY - lastSegmentStartY, endX - lastSegmentStartX); var tipHalfWidth = arrowSize / 2; var baseX = endX - tipLength * Math.cos(arrowHeadAngle); var baseY = endY - tipLength * Math.sin(arrowHeadAngle); var perpAngle = arrowHeadAngle + Math.PI / 2; var ah1x = baseX + tipHalfWidth * Math.cos(perpAngle); var ah1y = baseY + tipHalfWidth * Math.sin(perpAngle); var ah2x = baseX - tipHalfWidth * Math.cos(perpAngle); var ah2y = baseY - tipHalfWidth * Math.sin(perpAngle); var arrowHead = ''; var pathSvg = ''; return '' + pathSvg + arrowHead + ''; } function updateStandaloneArrowPreview() { // Nur anzeigen wenn Pfeil ausgewaehlt if (textGenState.arrow === "none") { var preview = document.getElementById("standaloneArrowPreview"); if (preview) preview.innerHTML = '

Waehle einen Pfeil aus

'; return; } var preview = document.getElementById('standaloneArrowPreview'); if (preview) { preview.innerHTML = generateStandaloneArrowSVG(); } } async function copyStandaloneArrow() { var svg = generateStandaloneArrowSVG(); try { var canvas = await svgToCanvas(svg, 2); canvas.toBlob(async function(blob) { try { await navigator.clipboard.write([ new ClipboardItem({ 'image/png': blob }) ]); showNotification('Pfeil in Zwischenablage kopiert!'); } catch (err) { downloadStandaloneArrowPNG(); } }, 'image/png'); } catch (err) { showNotification('Fehler beim Kopieren', 'error'); } } function downloadStandaloneArrowSVG() { var svg = generateStandaloneArrowSVG(); var blob = new Blob([svg], { type: 'image/svg+xml' }); var url = URL.createObjectURL(blob); var link = document.createElement('a'); link.href = url; link.download = 'pfeil.svg'; link.click(); URL.revokeObjectURL(url); showNotification('Pfeil SVG heruntergeladen!'); } async function downloadStandaloneArrowPNG() { var svg = generateStandaloneArrowSVG(); try { var canvas = await svgToCanvas(svg, 2); canvas.toBlob(function(blob) { var link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'pfeil.png'; link.click(); URL.revokeObjectURL(link.href); showNotification('Pfeil PNG heruntergeladen!'); }, 'image/png'); } catch (err) { showNotification('Fehler beim PNG-Export', 'error'); } } async function downloadStandaloneArrowJPG() { var svg = generateStandaloneArrowSVG(); try { var canvas = await svgToCanvas(svg, 2); var tempCanvas = document.createElement('canvas'); tempCanvas.width = canvas.width; tempCanvas.height = canvas.height; var tempCtx = tempCanvas.getContext('2d'); tempCtx.fillStyle = '#FFFFFF'; tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height); tempCtx.drawImage(canvas, 0, 0); tempCanvas.toBlob(function(blob) { var link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'pfeil.jpg'; link.click(); URL.revokeObjectURL(link.href); showNotification('Pfeil JPG heruntergeladen!'); }, 'image/jpeg', 0.95); } catch (err) { showNotification('Fehler beim JPG-Export', 'error'); } } function downloadStandaloneArrowDXF() { var svg = generateStandaloneArrowSVG(); var dxf = svgToDxf(svg, 1); var blob = new Blob([dxf], { type: 'application/dxf' }); var url = URL.createObjectURL(blob); var link = document.createElement('a'); link.href = url; link.download = 'pfeil.dxf'; link.click(); URL.revokeObjectURL(url); showNotification('Pfeil DXF heruntergeladen!'); } function addStandaloneArrowToLegend() { var svg = generateStandaloneArrowSVG(); legendItems.push({ id: 'arrow_' + Date.now(), name: 'Pfeil', svg: svg, description: '' }); updateLegendCount(); saveLegendToStorage(); showNotification('Pfeil zur Legende hinzugefuegt!'); } function updateTextPreview() { console.log("updateTextPreview called"); var preview = document.getElementById('textPreview'); if (preview) { var svg = generateTextSVG(); console.log("Generated SVG length:", svg.length); preview.innerHTML = svg; } } // Hilfsfunktion: SVG zu Canvas rendern async function svgToCanvas(svg, scale) { var parser = new DOMParser(); var svgDoc = parser.parseFromString(svg, 'image/svg+xml'); var svgEl = svgDoc.documentElement; var svgWidth = parseFloat(svgEl.getAttribute('width')) || 100; var svgHeight = parseFloat(svgEl.getAttribute('height')) || 100; if (!scale) { scale = Math.max(200 / Math.min(svgWidth, svgHeight), 1); scale = Math.min(scale, 512 / Math.max(svgWidth, svgHeight)); } var canvasWidth = Math.round(svgWidth * scale); var canvasHeight = Math.round(svgHeight * scale); var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); canvas.width = canvasWidth; canvas.height = canvasHeight; var img = new Image(); var svgBlob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' }); var url = URL.createObjectURL(svgBlob); await new Promise(function(resolve, reject) { img.onload = resolve; img.onerror = reject; img.src = url; }); ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight); URL.revokeObjectURL(url); return canvas; } async function copyTextAsImage() { var svg = generateTextSVG(); try { var canvas = await svgToCanvas(svg); canvas.toBlob(async function(blob) { try { await navigator.clipboard.write([ new ClipboardItem({ 'image/png': blob }) ]); showNotification('In Zwischenablage kopiert!'); } catch (err) { var link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'text-symbol.png'; link.click(); showNotification('PNG heruntergeladen'); } }, 'image/png'); } catch (err) { console.error('Fehler:', err); showNotification('Fehler beim Kopieren', 'error'); } } function downloadTextSVG() { var svg = generateTextSVG(); var blob = new Blob([svg], { type: 'image/svg+xml' }); var url = URL.createObjectURL(blob); var link = document.createElement('a'); link.href = url; var filename = textGenState.text ? textGenState.text.replace(/\n/g, '_').replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 20) : 'symbol'; link.download = 'text-' + filename + '.svg'; link.click(); URL.revokeObjectURL(url); showNotification('SVG heruntergeladen!'); } async function downloadTextPNG() { var svg = generateTextSVG(); try { var canvas = await svgToCanvas(svg, 2); canvas.toBlob(function(blob) { var link = document.createElement('a'); link.href = URL.createObjectURL(blob); var filename = textGenState.text ? textGenState.text.replace(/\n/g, '_').replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 20) : 'symbol'; link.download = 'text-' + filename + '.png'; link.click(); URL.revokeObjectURL(link.href); showNotification('PNG heruntergeladen!'); }, 'image/png'); } catch (err) { console.error('Fehler:', err); showNotification('Fehler beim PNG-Export', 'error'); } } async function downloadTextJPG() { var svg = generateTextSVG(); try { var canvas = await svgToCanvas(svg, 2); var ctx = canvas.getContext('2d'); var tempCanvas = document.createElement('canvas'); tempCanvas.width = canvas.width; tempCanvas.height = canvas.height; var tempCtx = tempCanvas.getContext('2d'); tempCtx.fillStyle = '#FFFFFF'; tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height); tempCtx.drawImage(canvas, 0, 0); tempCanvas.toBlob(function(blob) { var link = document.createElement('a'); link.href = URL.createObjectURL(blob); var filename = textGenState.text ? textGenState.text.replace(/\n/g, '_').replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 20) : 'symbol'; link.download = 'text-' + filename + '.jpg'; link.click(); URL.revokeObjectURL(link.href); showNotification('JPG heruntergeladen!'); }, 'image/jpeg', 0.95); } catch (err) { console.error('Fehler:', err); showNotification('Fehler beim JPG-Export', 'error'); } } function downloadTextDXF() { var svg = generateTextSVG(); var dxf = svgToDxf(svg, 1); var blob = new Blob([dxf], { type: 'application/dxf' }); var url = URL.createObjectURL(blob); var link = document.createElement('a'); link.href = url; var filename = textGenState.text ? textGenState.text.replace(/\n/g, '_').replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 20) : 'symbol'; link.download = 'text-' + filename + '.dxf'; link.click(); URL.revokeObjectURL(url); showNotification('DXF heruntergeladen!'); } function addTextToLegend() { var svg = generateTextSVG(); var text = textGenState.text || 'Text'; var displayName = text.replace(/\n/g, ' ').substring(0, 30); legendItems.push({ id: 'custom_text_' + Date.now(), name: displayName, svg: svg, description: '' }); updateLegendCount(); saveLegendToStorage(); showNotification('Zur Legende hinzugefuegt!'); } // ========== CUSTOM SYMBOLS STORAGE ========== function loadCustomSymbols() { try { var stored = localStorage.getItem('customSymbols'); if (stored) { customSymbols = JSON.parse(stored); } } catch (e) { console.error('Fehler beim Laden der eigenen Symbole:', e); customSymbols = []; } } function saveCustomSymbols() { try { localStorage.setItem('customSymbols', JSON.stringify(customSymbols)); } catch (e) { console.error('Fehler beim Speichern der eigenen Symbole:', e); } } function openSaveModal() { var modal = document.getElementById('saveModal'); var nameInput = document.getElementById('symbolName'); var descInput = document.getElementById('symbolDescription'); var suggestedName = textGenState.text ? textGenState.text.replace(/\n/g, ' ').substring(0, 30) : ''; nameInput.value = suggestedName; descInput.value = ''; modal.style.display = 'flex'; nameInput.focus(); } function closeSaveModal() { var modal = document.getElementById('saveModal'); modal.style.display = 'none'; } function saveCustomSymbol() { var nameInput = document.getElementById('symbolName'); var descInput = document.getElementById('symbolDescription'); var name = nameInput.value.trim(); if (!name) { showNotification('Bitte einen Namen eingeben!', 'error'); return; } var svg = generateTextSVG(); var newSymbol = { id: 'custom_' + Date.now(), name: name, description: descInput.value.trim(), svg: svg, category: 'custom', tags: ['eigene', 'custom', name.toLowerCase()], createdAt: new Date().toISOString(), state: JSON.parse(JSON.stringify(textGenState)) }; customSymbols.push(newSymbol); saveCustomSymbols(); closeSaveModal(); showNotification('Symbol "' + name + '" gespeichert!'); var activeFilter = document.querySelector('.filter-pill.active'); if (activeFilter && activeFilter.dataset.filter === 'custom') { renderSymbolGrid(); } } function deleteCustomSymbol(id) { if (!confirm('Symbol wirklich loeschen?')) return; customSymbols = customSymbols.filter(function(s) { return s.id !== id; }); saveCustomSymbols(); renderSymbolGrid(); showNotification('Symbol geloescht'); } var originalRenderSymbolGrid = typeof renderSymbolGrid === 'function' ? renderSymbolGrid : null; function renderSymbolGridWithCustom() { var activeFilter = document.querySelector('.filter-pill.active'); var filter = activeFilter ? activeFilter.dataset.filter : 'all'; if (filter === 'custom') { renderCustomSymbolsOnly(); } else if (originalRenderSymbolGrid) { originalRenderSymbolGrid(); if (filter === 'all') { appendCustomSymbols(); } } } function renderCustomSymbolsOnly() { var grid = document.getElementById('symbolGrid'); if (!grid) return; grid.innerHTML = ''; if (customSymbols.length === 0) { grid.innerHTML = '
Noch keine eigenen Symbole gespeichert.
Erstelle ein Text-Symbol und klicke auf "Speichern".
'; return; } customSymbols.forEach(function(symbol) { var card = createCustomSymbolCard(symbol); grid.appendChild(card); }); } function appendCustomSymbols() { var grid = document.getElementById('symbolGrid'); if (!grid || customSymbols.length === 0) return; customSymbols.forEach(function(symbol) { var card = createCustomSymbolCard(symbol); grid.appendChild(card); }); } function createCustomSymbolCard(symbol) { var card = document.createElement('div'); card.className = 'symbol-card'; card.innerHTML = '' + '
' + symbol.svg + '
' + '
' + escapeHtml(symbol.name) + '
' + '
' + '' + '' + '' + '' + '
'; return card; } function escapeHtml(text) { var div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function getSymbolById(id, isCustom) { if (isCustom) { return customSymbols.find(function(s) { return s.id === id; }); } return allSymbols.find(function(s) { return s.id === id; }); } // ========== IMPRESSUM & DATENSCHUTZ ========== function openImpressum() { var modal = document.getElementById('impressumModal'); if (modal) modal.style.display = 'flex'; } function closeImpressum() { var modal = document.getElementById('impressumModal'); if (modal) modal.style.display = 'none'; } function openDatenschutz() { var modal = document.getElementById('datenschutzModal'); if (modal) modal.style.display = 'flex'; } function closeDatenschutz() { var modal = document.getElementById('datenschutzModal'); if (modal) modal.style.display = 'none'; } // Init beim Laden if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initTextGenerator); } else { initTextGenerator(); } // ========== GLOBAL EXPORTS fuer onclick Handler ========== window.resetToDefaults = resetToDefaults; window.copyStandaloneArrow = copyStandaloneArrow; window.downloadStandaloneArrowSVG = downloadStandaloneArrowSVG; window.downloadStandaloneArrowPNG = downloadStandaloneArrowPNG; window.downloadStandaloneArrowJPG = downloadStandaloneArrowJPG; window.downloadStandaloneArrowDXF = downloadStandaloneArrowDXF; window.addStandaloneArrowToLegend = addStandaloneArrowToLegend; window.generateTextSVG = generateTextSVG; window.copyTextAsImage = copyTextAsImage; window.downloadTextSVG = downloadTextSVG; window.downloadTextPNG = downloadTextPNG; window.downloadTextJPG = downloadTextJPG; window.downloadTextDXF = downloadTextDXF; window.addTextToLegend = addTextToLegend; window.toggleTextGenerator = toggleTextGenerator; window.openImpressum = openImpressum; window.closeImpressum = closeImpressum; window.openDatenschutz = openDatenschutz; window.closeDatenschutz = closeDatenschutz;