Sistema de Códigos de Barras EAN-13
📦 Productos
📁
Arrastra tu archivo Excel aquí
o haz clic para seleccionar
Formatos: .xlsx, .xls
ℹ️
Formato esperado:
• Una columna con “Referencia interna”
• Una columna con “Código de barras” (EAN-13)
* El sistema busca automáticamente estas columnas por nombre
🔍
🖼️ Vista Previa
📋
Carga el archivo Excel y selecciona un producto
La vista previa aparecerá aquí
🖨️ Imprimir Etiquetas
⚡ Imprimir vía ZPL
📥 Descargar Etiqueta PNG
📦 Descargar TODOS (ZIP)
⚙️
Configuración de Etiqueta:
• Tamaño de papel: 76mm × 30mm
• Orientación: Horizontal
• Márgenes: 0mm
• Impresora: 3nStar 4BARCODE 4B-2054L
📏 Tamaño de Etiqueta
35mm × 26mm(3.5 × 2.6cm)
50mm × 26mm(5 × 2.6cm)
50.8mm × 50.8mm(5.08 × 5.08cm)
60mm × 43mm(6 × 4.3cm)
76mm × 30mm(Actual)
76mm × 76mm(7.6 × 7.6cm)
104mm × 104mm(10.4 × 10.4cm)
📐 Personalizado
`);
doc.close();
// Restaurar botón
setTimeout(() => {
printZPLBtn.disabled = false;
printZPLBtn.innerHTML = '
⚡ Imprimir vía ZPL';
}, 1000);
showSuccessMessage(`✅ ZPL enviado a la impresora (${quantity} etiqueta${quantity > 1 ? 's' : ''})`);
// Nota: En algunos casos, el navegador puede bloquear esto
// Si no funciona, mostrar instrucciones alternativas
setTimeout(() => {
const shouldShowAlternative = confirm(
'¿No se imprimió?\n\n' +
'Los navegadores a veces bloquean la impresión directa de ZPL.\n\n' +
'¿Deseas descargar el archivo ZPL para enviarlo manualmente?'
);
if (shouldShowAlternative) {
// Crear archivo .zpl para descargar
const blob = new Blob([zplCommands], { type: 'application/zpl' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `etiqueta_${selectedProduct.reference}_${quantity}.zpl`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
// Mostrar instrucciones simples
alert(
'📄 Archivo ZPL descargado\n\n' +
'MÉTODO RÁPIDO:\n' +
'1. Abre Command Prompt (CMD)\n' +
'2. Arrastra el archivo .zpl a la ventana\n' +
'3. Agrega al final: > \\\\localhost\\nombre_impresora\n' +
'4. Presiona Enter\n\n' +
'O simplemente arrastra el archivo .zpl sobre el ícono de tu impresora.'
);
}
}, 3000);
} catch (error) {
console.error('Error al generar ZPL:', error);
alert('❌ Error al generar código ZPL. Por favor intenta de nuevo.');
// Restaurar botón
printZPLBtn.disabled = false;
printZPLBtn.innerHTML = '
⚡ Imprimir vía ZPL';
}
});
// Descargar como imágenes
downloadImgBtn.addEventListener('click', async () => {
if (!selectedProduct) return;
const quantity = parseInt(quantityInput.value) || 1;
// Deshabilitar botón y mostrar progreso
downloadImgBtn.disabled = true;
downloadImgBtn.innerHTML = '
Generando imágenes en alta calidad...';
try {
// Crear un canvas temporal para cada etiqueta
for (let i = 0; i < quantity; i++) {
// Crear canvas con ALTA RESOLUCIÓN para impresión
// 300 DPI para calidad profesional: 1mm = 11.81 pixels
const DPI_SCALE = 11.81; // 300 DPI
const pixelWidth = Math.round(labelConfig.width * DPI_SCALE);
const pixelHeight = Math.round(labelConfig.height * DPI_SCALE);
const canvas = document.createElement('canvas');
canvas.width = pixelWidth;
canvas.height = pixelHeight;
const ctx = canvas.getContext('2d');
// Habilitar suavizado de alta calidad
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
// Fondo blanco
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Calcular tamaños proporcionales
const titleFontSize = Math.round(labelConfig.titleSize * 3.15); // Escalar título
const marginTop = Math.round(labelConfig.marginTop * DPI_SCALE);
const marginSide = Math.round(labelConfig.marginSide * DPI_SCALE);
// Dibujar título con mayor calidad
ctx.fillStyle = '#000000';
ctx.font = `bold ${titleFontSize}px Arial, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
// Dibujar el título
const titleY = marginTop + 20;
ctx.fillText(selectedProduct.reference, canvas.width / 2, titleY);
// Crear canvas temporal para el código de barras a ALTA RESOLUCIÓN
const barcodeCanvas = document.createElement('canvas');
// Escalar parámetros del código de barras para alta resolución
const barcodeWidth = labelConfig.barcodeWidth * 3.15; // Mayor grosor
const barcodeHeight = Math.round(labelConfig.barcodeHeight * DPI_SCALE * 0.8); // Altura proporcional
const barcodeFontSize = Math.round(labelConfig.fontSize * 3.15);
JsBarcode(barcodeCanvas, selectedProduct.barcode, {
format: "EAN13",
width: barcodeWidth,
height: barcodeHeight,
displayValue: true,
fontSize: barcodeFontSize,
margin: Math.round(5 * DPI_SCALE / 11.81),
marginTop: Math.round(5 * DPI_SCALE / 11.81),
marginBottom: Math.round(5 * DPI_SCALE / 11.81),
background: "#ffffff",
lineColor: "#000000",
fontOptions: "bold"
});
// Centrar y posicionar el código de barras
const barcodeX = (canvas.width - barcodeCanvas.width) / 2;
const barcodeY = titleY + titleFontSize + Math.round(15 * DPI_SCALE / 11.81);
// Dibujar código de barras con alta calidad
ctx.drawImage(barcodeCanvas, barcodeX, barcodeY);
// Convertir a PNG de ALTA CALIDAD y descargar
canvas.toBlob((blob) => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `etiqueta_${selectedProduct.reference}_${selectedProduct.barcode}_${i + 1}.png`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 'image/png', 1.0); // Calidad máxima
// Pequeña pausa entre descargas
await new Promise(resolve => setTimeout(resolve, 250));
}
// Restaurar botón
downloadImgBtn.disabled = false;
downloadImgBtn.innerHTML = '
📥 Descargar como Imágenes';
showSuccessMessage(`✅ ${quantity} imagen${quantity > 1 ? 'es' : ''} de alta calidad descargada${quantity > 1 ? 's' : ''} (300 DPI)`);
} catch (error) {
console.error('Error al generar imágenes:', error);
alert('❌ Error al generar las imágenes. Por favor intenta de nuevo.');
// Restaurar botón
downloadImgBtn.disabled = false;
downloadImgBtn.innerHTML = '
📥 Descargar como Imágenes';
}
});
// Descargar TODOS los productos
downloadAllBtn.addEventListener('click', async () => {
if (products.length === 0) {
alert('⚠️ Primero carga el archivo Excel con los productos');
return;
}
const confirmDownload = confirm(
`¿Deseas generar etiquetas para TODOS los ${products.length} productos?\n\n` +
`Esto puede tomar varios minutos.\n\n` +
`Se crearán ${products.length} imágenes PNG de alta calidad (300 DPI).`
);
if (!confirmDownload) return;
// Deshabilitar botón y mostrar progreso
downloadAllBtn.disabled = true;
downloadAllBtn.innerHTML = '
Generando... 0/' + products.length;
try {
const zip = new JSZip();
const folder = zip.folder("etiquetas_codigos_barras");
// Generar una imagen para cada producto
for (let i = 0; i < products.length; i++) {
const product = products[i];
try {
// Actualizar progreso
downloadAllBtn.innerHTML = `
Generando... ${i + 1}/${products.length}`;
// Crear canvas con ALTA RESOLUCIÓN
const DPI_SCALE = 11.81; // 300 DPI
const pixelWidth = Math.round(labelConfig.width * DPI_SCALE);
const pixelHeight = Math.round(labelConfig.height * DPI_SCALE);
const canvas = document.createElement('canvas');
canvas.width = pixelWidth;
canvas.height = pixelHeight;
const ctx = canvas.getContext('2d');
// Habilitar suavizado de alta calidad
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
// Fondo blanco
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Calcular tamaños proporcionales
const titleFontSize = Math.round(labelConfig.titleSize * 3.15);
const marginTop = Math.round(labelConfig.marginTop * DPI_SCALE);
// Dibujar título
ctx.fillStyle = '#000000';
ctx.font = `bold ${titleFontSize}px Arial, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
const titleY = marginTop + 20;
ctx.fillText(product.reference, canvas.width / 2, titleY);
// Crear canvas temporal para el código de barras
const barcodeCanvas = document.createElement('canvas');
const barcodeWidth = labelConfig.barcodeWidth * 3.15;
const barcodeHeight = Math.round(labelConfig.barcodeHeight * DPI_SCALE * 0.8);
const barcodeFontSize = Math.round(labelConfig.fontSize * 3.15);
JsBarcode(barcodeCanvas, product.barcode, {
format: "EAN13",
width: barcodeWidth,
height: barcodeHeight,
displayValue: true,
fontSize: barcodeFontSize,
margin: Math.round(5 * DPI_SCALE / 11.81),
marginTop: Math.round(5 * DPI_SCALE / 11.81),
marginBottom: Math.round(5 * DPI_SCALE / 11.81),
background: "#ffffff",
lineColor: "#000000",
fontOptions: "bold"
});
// Centrar y posicionar el código de barras
const barcodeX = (canvas.width - barcodeCanvas.width) / 2;
const barcodeY = titleY + titleFontSize + Math.round(15 * DPI_SCALE / 11.81);
// Dibujar código de barras
ctx.drawImage(barcodeCanvas, barcodeX, barcodeY);
// Convertir canvas a blob PNG
const dataURL = canvas.toDataURL('image/png', 1.0);
const base64Data = dataURL.split(',')[1];
// Agregar al ZIP con nombre limpio (sanitizar nombre)
const safeName = product.reference.replace(/[^a-zA-Z0-9\-\_]/g, '_');
const filename = `${safeName}_${product.barcode}.png`;
folder.file(filename, base64Data, {base64: true});
} catch (error) {
console.error(`Error en producto ${i + 1} (${product.reference}):`, error);
// Continuar con el siguiente producto
}
// Pequeña pausa para no bloquear el navegador
if (i % 10 === 0) {
await new Promise(resolve => setTimeout(resolve, 10));
}
}
// Generar el archivo ZIP
downloadAllBtn.innerHTML = '
Creando archivo ZIP...';
const zipBlob = await zip.generateAsync({
type: "blob",
compression: "DEFLATE",
compressionOptions: { level: 6 }
});
// Descargar el ZIP
const fecha = new Date().toISOString().split('T')[0];
saveAs(zipBlob, `etiquetas_codigos_barras_${fecha}.zip`);
// Restaurar botón
downloadAllBtn.disabled = false;
downloadAllBtn.innerHTML = '
📦 Descargar TODOS los Productos';
showSuccessMessage(`✅ ${products.length} etiquetas descargadas en formato ZIP`);
} catch (error) {
console.error('Error al generar ZIP:', error);
alert('❌ Error al generar el archivo ZIP. Por favor intenta de nuevo.');
// Restaurar botón
downloadAllBtn.disabled = false;
downloadAllBtn.innerHTML = '
📦 Descargar TODOS los Productos';
}
});
// Mostrar estadísticas
function showStats() {
stats.style.display = 'grid';
updateStats();
}
function updateStats() {
document.getElementById('totalProducts').textContent = products.length;
document.getElementById('selectedCount').textContent = selectedProduct ? 1 : 0;
document.getElementById('totalToPrint').textContent = selectedProduct ? (parseInt(quantityInput.value) || 1) : 0;
}