En este post te voy a explicar cómo funciona el comando ESC POS para imprimir una imagen usando el Bit Image column format.
No importa el lenguaje de programación, te voy a enseñar cómo enviar los datos a partir de los pixeles de una imagen, ya que yo buscaba un ejemplo simple y me parece que no lo encontré.
Voy a explicarte el algoritmo para imprimir una imagen en una impresora térmica y al final te mostraré un ejemplo con una imagen real.
Descripción del comando
El comando ESC POS del que vamos a hablar es:
ESC * Select bit image
Y su descripción dice que en ASCII es ESC * m n1 n2 Data
Entonces tenemos el comando ESC *
, eso es muy fácil. Lo complejo es obtener m
, n1
, n2
y Data
. Justamente de eso hablaremos en todo este artículo.
La densidad: m
La m representa la densidad e indica cómo se van a ampliar los pixeles de la imagen. En este caso la colocaré en 33 pero soporta más valores que mencionaré al final.
Recuerda que los datos también dependen de la densidad, así que si cambias la densidad ajusta los datos como sea necesario, sobre todo los bytes por columna.
n1 y n2: el ancho de la imagen
Hablemos sobre el parámetro n1
y n2
. Estos son simples bytes que enviamos indicando el ancho de la imagen. Básicamente es separar un número en 2 bytes. Aquí hay que recordar un poco algunas cosas
- El número decimal más grande que se puede representar con un byte es 255. Se pueden representar 256 valores (incluyendo al cero)
- En cambio, con dos bytes podemos representar 65536 valores, siendo el 65535 el número más grande.
Con esto podemos representar un número muy grande con 2 simples bytes. Para obtenerlos, se sigue lo siguiente:
- Si queremos obtener
n1
sacamos el residuo de dividir el ancho de la imagen entre 256. Esto se puede hacer con la operación módulo, que en varios lenguajes se obtiene con el operador % - Para obtener
n2
dividimos el ancho de la imagen entre 256 y redondeamos hacia el entero más próximo hacia abajo. Hablando en términos de lenguajes de programación, sería usar la función floor de math.
Datos de la imagen: data
Ahora viene la parte más compleja: transformar una imagen en bytes que la impresora térmica entienda. Por ahora, recuerda lo siguiente:
- Un byte contiene 8 bits. Cada bit puede tener un 1 o un 0
- Un byte se puede representar como binario, decimal o hexadecimal. El decimal 255 sería 11111111 en binario y FF en hexadecimal
- El MSB es el bit más significativo y cambia dependiendo del sistema, pero en la mayoría de casos el MSB es el primer bit de la izquierda. Esto es porque, de izquierda a derecha, las ocho posiciones representan a 128, 64, 32, 16, 8, 4, 2 y 1.
- Se dice que el bit más significativo es el de 128 (el primero de izquierda a derecha) porque si por alguna razón cambia, tenemos una diferencia de 128. En cambio, el bit menos significativo es el último de izquierda a derecha, porque si por alguna razón cambia, tenemos solo una variación de 1.
Sabiendo lo anterior, para convertir la imagen en datos, debes:
- Definir cuántas líneas vas a crear. Si la densidad es 33, entonces debes enviar 3 bytes por columna, lo que se traduce en enviar 24 bits por columna. El alto de la imagen debe ser múltiplo de 24, ya que vamos a enviar la imagen en fragmentos de 24 pixeles de alto (sin importar el ancho).
- Recorrer la imagen pixel por pixel, pero de arriba hacia abajo. Es decir, primero sitúa tu cursor en la posición 0,0. Luego, avanza en Y hasta la cantidad de bits por columna. Cuando llegues a dicha cantidad, avanza en X y así sucesivamente hasta llegar al ancho.
- Al terminar de recorrer toda esa fila de
24 * ancho de la imagen
, si todavía faltan más pixeles, vuelve a recorrer pero ahora el valor de Y será24
, el de X será 0 de nuevo. Vas a recorrer desde0,24
hasta0,47
, y cuando termines esa fila, vas a recorrer desde1,24
hasta1,47
, repitiendo el mismo proceso hasta terminar de recorrer toda la imagen - En cada paso del recorrido, decide si el pixel es un
1
o un0
. Haz las comparaciones necesarias o convierte la imagen a blanco y negro antes de procesarla, pero es importante que decidas si será un uno o un cero - Acomoda cada pixel en un bit de un byte, avanzando el índice del bit. Dicho con otras palabras, los primeros 8 pixeles de la imagen estarán en un byte, comenzando por el MSB. Cuando acabes de usar ese byte vas a crear otro byte y comenzarás colocando el noveno pixel. Cuando tengas un byte completo, agrégalo a un arreglo de bytes que será una fila.
- Cada vez que termines de recorrer una fila (dada por la cantidad de bits por columna), agrega dicha fila a un arreglo de filas.
- En resumen, al final tendrás un arreglo de filas de bytes. Cada fila de bytes tendrá bytes por columna * ancho de la imagen, y el arreglo de filas tendrá alto de la imagen / bits por columna filas.
Si tú no quieres obligar a que el alto de la imagen sea de 24 puedes crear y rellenar las filas que alojarán los bytes al siguiente múltiplo de 24 a partir del ancho de la imagen.
Una vez que tengas las filas, recorre ese arreglo fila por fila. Luego, envía los comandos así:
ESC * densidad n1 n2 bytes de la fila...
Debes enviar esos comandos por cada fila.
Ejemplo real
Ahora que te he explicado todos los detalles, veamos cómo convertir una imagen. Te repito que la densidad es de 33, así que se enviarán 3 bytes por columna y será necesario que el alto de la imagen sea múltiplo de 24.
Para este simple ejemplo he preparado una imagen de 24 pixeles de alto por 2 pixeles de ancho. La imagen más pequeña que podríamos enviar sería una de 24×1 (sin rellenar los bytes). Puedes ver la original a continuación: https://parzibyte.me/blog/wp-content/uploads/2024/01/min.png
La he ampliado y colocado en una rejilla con fondo transparente para que puedas apreciar el color de cada pixel así como la cantidad de pixeles que tiene.
Si te fijas, he coloreado cada byte de distinto color; obviamente esto no es obligatorio y en una imagen real eso cambiará. Ya vimos la imagen, ahora vamos a sacar los datos. Recuerda, el comando es:
ESC * m n1 n2 Data
En este caso la densidad es 33
. Para el caso de n1
y n2
, como el ancho es 2 los cálculos quedan así:
- n1 = 2 % 256 =
2
- n2 = math.floor(2/256) =
0
Luego vamos con los datos. Solo vamos a usar una fila, ya que el alto de la imagen es de 24. Si fuera de 48 serían 2 filas y así sucesivamente (recuerda que el 24 viene de multiplicar los bytes por columna que serán 3 al usar la densidad de 33).
Para el caso de los primeros 8 bits, será 11111111
, es decir, 255
o 0xFF
. Los segundos 8 bits serán 00000000
, 0
o 0x00
y los terceros bits serán 0xFF
.
Ahora vamos con la segunda columna de la imagen, empezamos con 0x00
, luego 0xFF
y finalmente 0x00
. Entonces la imagen que tenemos arriba, se puede representar así:
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00
Mira la misma imagen pero con su correspondencia de bytes:
Nota importante: en este caso, debido a la imagen de ejemplo, todos los bytes serán 255
o 0
, pero en una imagen real pueden ser otros valores. Por ejemplo, si el primer pixel del byte 1 fuera blanco, ahora su representación sería 01111111
, o sea, 127
o 0x7F
.
Y el comando en hexadecimal va quedando así; primero el ESC *
0x1B,0x2A
Luego la densidad que en decimal es 33
pero en hexadecimal es 0x21
Ahora viene n1 y n2 que son 2 y 0 respectivamente:
0x02, 0x00
Nuestro comando está así hasta el momento:
0x1B, 0x2A, 0x21, 0x02, 0x00
Agregamos la data de la imagen y queda así:
0x1B, 0x2A, 0x21, 0x02, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00
Al enviar esos datos, habremos impreso una imagen de 24 por 2 pixeles en una impresora térmica usando comandos ESC POS.
Si, por ejemplo, la imagen midiera 48×2 y tuviera los mismos pixeles, el comando enviado sería 2 veces el mismo:
0x1B, 0x2A, 0x21, 0x02, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00
Que, en otras palabras, sería:
0x1B, 0x2A, 0x21, 0x02, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x000, x1B, 0x2A, 0x21, 0x02, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00
Sobre la densidad
Cuando la densidad es 0, 1, 2, 3, 4 o 6, los bytes por columna son 1. Cuando es 32, 33, 38, 39 o 40 los bytes por columna son 3. Finalmente, cuando es 71, 72 o 73 los bytes por columna son 6.
En la imagen anterior puedes apreciar la diferencia entre densidades. Si no ves un número de densidad, es porque en mi caso solo aparecían caracteres extraños. Desconozco si es debido al modelo de mi impresora o a algún problema en el algoritmo.
Como puedes ver, la mejor densidad es 3 y 33.
Más herramientas
Lo que he expuesto en el post ya está integrado en mi plugin para impresoras térmicas. La impresión de imágenes es totalmente soportada en el plugin y también en el diseñador.