Las impresoras térmicas permiten la impresión de texto y la alineación del mismo, pero ¿qué pasa cuando queremos imprimir una tabla que sea alineada correctamente en caso de que el texto sobrepase el ancho? para ello debemos implementar un algoritmo propio.
Por ello es que en este post te voy a enseñar a imprimir datos tabulados o básicamente imprimir una tabla bien alineada y con separadores en una impresora térmica, usando únicamente texto.
Verás que podremos imprimir las celdas y columnas de la tabla respetando el máximo ancho del ticket, ya sea dibujando los separadores o solo ordenando los datos.
Demostración
Sugiero leer el post completo para ver lo que está pasando, pero si quieres probarlo justo ahora entonces descarga y ejecuta el servidor local de la API ESC POS e instala y comparte tu impresora. Por favor, sigue los pasos que aparecen en la página previamente enlazada, ya que ahí está la API unificada.
Para finalmente ir al siguiente ejemplo, elegir tu impresora e imprimir una tabla formateada: probar en el área de pruebas.
Datos de ejemplo
Por esta ocasión voy a imprimir una lista de productos con distintos valores y longitud para demostrar cómo se puede imprimir una tabla en una impresora térmica. Lo voy a hacer con JavaScript pero se puede hacer con cualquier lenguaje de programación.
// Simple lista de ejemplo. Obviamente tú puedes traerla de cualquier otro lado,
// definir otras propiedades, etcétera
const listaDeProductos = [
{
nombre: "Impresora térmica 58mm",
cantidad: 10,
precio: 600,
},
{
nombre: "The Legend of Zelda: Tears of the kingdom",
cantidad: 1,
precio: 1600,
},
{
nombre: "Resident Evil 4: remake",
cantidad: 1,
precio: 1200,
},
];
Crear tabla usando solo texto
Ahora necesitamos un algoritmo que pueda tabular datos agregando las líneas en blanco que sean necesarias según la cantidad máxima de filas, coloque un separador de columnas, rellene las celdas cuando sea necesario y separe el texto en una nueva línea cuando sobrepase el ancho máximo.
Todo eso ya lo hice en 3 funciones que puedes ver con detalle en el siguiente enlace, te recomiendo leerlas para que sepas lo que estamos haciendo:
JavaScript: tabular datos con límite de longitud, separador y relleno
Entonces voy a importar esas 3 funciones:
const separarCadenaEnArregloSiSuperaLongitud = (cadena, maximaLongitud) => {
const resultado = [];
let indice = 0;
while (indice < cadena.length) {
const pedazo = cadena.substring(indice, indice + maximaLongitud);
indice += maximaLongitud;
resultado.push(pedazo);
}
return resultado;
}
const dividirCadenasYEncontrarMayorConteoDeBloques = (contenidosConMaximaLongitud) => {
let mayorConteoDeCadenasSeparadas = 0;
const cadenasSeparadas = [];
for (const contenido of contenidosConMaximaLongitud) {
const separadas = separarCadenaEnArregloSiSuperaLongitud(contenido.contenido, contenido.maximaLongitud);
cadenasSeparadas.push({ separadas, maximaLongitud: contenido.maximaLongitud });
if (separadas.length > mayorConteoDeCadenasSeparadas) {
mayorConteoDeCadenasSeparadas = separadas.length;
}
}
return [cadenasSeparadas, mayorConteoDeCadenasSeparadas];
}
const tabularDatos = (cadenas, relleno, separadorColumnas) => {
const [arreglosDeContenidosConMaximaLongitudSeparadas, mayorConteoDeBloques] = dividirCadenasYEncontrarMayorConteoDeBloques(cadenas)
let indice = 0;
const lineas = [];
while (indice < mayorConteoDeBloques) {
let linea = "";
for (const contenidos of arreglosDeContenidosConMaximaLongitudSeparadas) {
let cadena = "";
if (indice < contenidos.separadas.length) {
cadena = contenidos.separadas[indice];
}
if (cadena.length < contenidos.maximaLongitud) {
cadena = cadena + relleno.repeat(contenidos.maximaLongitud - cadena.length);
}
linea += cadena + separadorColumnas;
}
lineas.push(linea);
indice++;
}
return lineas;
}
Diseñar tabla para imprimir
Ahora que tenemos los datos de ejemplo y las funciones para crear una tabla de solo texto en JavaScript vamos a proceder a crear la tabla como una cadena para enviarla a una impresora térmica y así imprimir una tabla.
Vamos a necesitar una línea que separe cada fila. Yo la he diseñado con la siguiente función:
const obtenerLineaSeparadora = () => {
const lineasSeparador = tabularDatos(
[
{ contenido: "-", maximaLongitud: maximaLongitudNombre },
{ contenido: "-", maximaLongitud: maximaLongitudCantidad },
{ contenido: "-", maximaLongitud: maximaLongitudPrecio },
],
"-",
"+",
);
let separadorDeLineas = "";
if (lineasSeparador.length > 0) {
separadorDeLineas = lineasSeparador[0]
}
return separadorDeLineas;
}
Eso me va a devolver algo como: -------------------+-----+-----+
Puede que ahora no tenga mucho sentido pero ya verás cómo se usa a continuación. Lo que sigue es crear el encabezado (vendría siendo el equivalente al thead
en HTML) así:
// Comenzar a diseñar la tabla
let tabla = obtenerLineaSeparadora() + "\n";
const lineasEncabezado = tabularDatos([
{ contenido: "Nombre", maximaLongitud: maximaLongitudNombre },
{ contenido: "Cantidad", maximaLongitud: maximaLongitudCantidad },
{ contenido: "Precio", maximaLongitud: maximaLongitudPrecio },
],
relleno,
separadorColumnas,
);
for (const linea of lineasEncabezado) {
tabla += linea + "\n";
}
Es importante que recuerdes que la función tabularDatos
nos va a devolver los datos tabulados en varias líneas (específicamente hablando, devolverá un arreglo de tipo string).
Es responsabilidad del invocador recorrerlas y agregar el salto de línea. En la línea 2 comenzamos diseñando la tabla con un separador, y siempre agregamos un salto de línea al final.
Luego en la línea 14 recorremos las líneas del encabezado y las concatenamos a la tabla. Por ahora nuestra tabla se ve así:
-------------------+-----+-----+
Nombre |Canti|Preci|
|dad |o |
Finalmente recorremos el arreglo de productos, tabulamos los datos con los valores y concatenamos en la tabla:
tabla += obtenerLineaSeparadora() + "\n";
for (const producto of listaDeProductos) {
const lineas = tabularDatos(
[
{ contenido: producto.nombre, maximaLongitud: maximaLongitudNombre },
{ contenido: producto.cantidad.toString(), maximaLongitud: maximaLongitudCantidad },
{ contenido: producto.precio.toString(), maximaLongitud: maximaLongitudPrecio },
],
relleno,
separadorColumnas
);
for (const linea of lineas) {
tabla += linea + "\n";
}
tabla += obtenerLineaSeparadora() + "\n";
}
Ahora ya tenemos la tabla en una simple string y se ve así:
-------------------+-----+-----+
Nombre |Canti|Preci|
|dad |o |
-------------------+-----+-----+
Impresora térmica 5|10 |600 |
8mm | | |
-------------------+-----+-----+
The Legend of Zelda|1 |1600 |
: Tears of the king| | |
dom | | |
-------------------+-----+-----+
Resident Evil 4: re|1 |1200 |
make | | |
-------------------+-----+-----+
Ya podemos conectarnos con la impresora térmica y enviarle ese texto. El ticket va a respetar los espacios y saltos de línea, así que se va a imprimir tal y como está.
Nota: obviamente puedes cambiar el separador, el relleno y el ancho máximo de cada columna. También puedes tener columnas infinitas.
Imprimiendo tabla con impresora térmica
Lo que sigue es imprimir una tabla en una POS printer o thermal printer. Yo usaré mi plugin gratuito presentado en:
Como ya tenemos el texto formateado como tabla, enviaremos la lista de operaciones indicando que queremos hacer la operación para escribir texto pasándole lo que nos devolvieron las otras funciones:
const cargaUtil = {
"serial": "",
"nombreImpresora": "",
"operaciones": [
{
"nombre": "Iniciar",
"argumentos": []
},
{
"nombre": "EscribirTexto",
"argumentos": [
"-------------------+-----+-----+\nNombre |Canti|Preci|\n |dad |o |\n-------------------+-----+-----+\nImpresora térmica 5|10 |600 |\n8mm | | |\n-------------------+-----+-----+\nThe Legend of Zelda|1 |1600 |\n: Tears of the king| | |\ndom | | |\n-------------------+-----+-----+\nResident Evil 4: re|1 |1200 |\nmake | | |\n-------------------+-----+-----+"
]
}
]
};
const respuestaHttp = await fetch("http://localhost:8000/imprimir", {
method: "POST",
body: JSON.stringify(cargaUtil)
});
const respuesta = await respuestaHttp.json();
if (respuesta.ok) {
console.log("Impreso correctamente")
} else {
console.error("Petición ok pero error en el plugin: " + respuesta.message);
}
Así que básicamente es hacer 2 cosas: crear la tabla en formato de texto y luego enviar ese texto a la impresora térmica, ya que la impresora no cuenta con un comando ESC POS para imprimir una tabla. Otra alternativa sería imprimir una tabla HTML.
Y con eso obtenemos el siguiente resultado:
Obviamente puedes imprimir más cosas además de la tabla.
Poniendo todo junto
Puedes ver el código completo viendo el código fuente generado en el área de pruebas.
También tengo un vídeo de YouTube por si quieres revisarlo:
A continuación te enseño algunos ejemplos de cómo he usado este algoritmo para imprimir tickets en mi sistema para restaurantes. Primero vemos un reporte:
También sirve para un ticket con los platillos solicitados:
Y por aquí tengo una versión preliminar de cuando apenas estaba diseñando ese sistema:
Puedes hacer esto con cualquier lenguaje de programación, solo es cuestión de traducir el código a otro lenguaje, ya que el plugin funciona sin importar cuál de ellos utilices.
Por aquí te dejo más tutoriales con JavaScript y también más entradas sobre mi plugin para impresoras térmicas.