// ========== 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 = '
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 = '