Algoritmo ESC POS: NV Graphics – Impresora térmica

Hoy toca revisar otro algoritmo para imprimir imágenes en una impresora térmica usando el protocolo ESC POS.

Existen varias maneras de convertir una imagen a bytes ESC POS para imprimirla, y en este post te voy a explicar el algoritmo NV Graphics cuyo comando es GS ( L.

Se supone que la impresión de NV Graphics es la recomendada, ya que es soportada en las impresoras más recientes e incluso permite especificar distintos tonos.

Al usar este método de impresión de imágenes podemos enviar los bytes en formato rasterizado o como columnas; yo prefiero enviarlos rasterizados pues es más sencillo.

Nota: algunas impresoras antiguas no soportan este comando; en su lugar recomiendo usar bit image column format.

Las funciones GS (

Para imprimir una imagen usando este método necesitamos 2 cosas. Primero es descargar la imagen en la impresora térmica, y después es imprimir la imagen previamente descargada.

Entonces para enviar la imagen a la impresora, usamos:

GS ( L pL pH m fn a bx by c xL xH yL yH d1...dk

Y para imprimir lo previamente “descargado”, usamos:

GS ( L pL pH m fn

No te preocupes, al principio yo tampoco entendía qué son todas esas letras, pero después de investigar tanto tiempo lo he entendido y por ello es que lo vengo a compartir.

Enviar imagen a impresora térmica

Comencemos con el comando más importante para enviar una imagen a una impresora térmica usando el método NV Graphics con GS ( L. Primero veamos cada valor.

El GS ( L solo es el comando, así que no hay problema; esos son valores literales. Veamos las siguientes variables.

Cantidad de bytes: p, pL y pH

Lo interesante viene con pL y pH. Vamos a centrarnos en p, que se refiere a la cantidad de bytes que van después de pH.

La L y la H se refieren a Low y High respectivamente, y básicamente es separar el número p en 2 bytes. Para separarlo, se sigue lo siguiente

pL = p % 256, es decir, p módulo 256

pH = p / 256, y el resultado redondearlo al entero anterior más cercano (sería usar la función floor de algunos lenguajes de programación)

Toma en cuenta esto del low y high ya que vamos a seguir usándolo para otros valores.

Nota: el valor de p será dado por la cantidad de bytes que van después de pH. Esto sería contar los bytes de la imagen en sí, y sumarle los 10 bytes: m fn a bx by c xL xH yL yH.

El valor de m

Este es un valor literal, debe ser 48 para esta función. Recuerda que estamos trabajando con bytes, así que es 48 en decimal pero 0x30 en hexadecimal.

La función: fn

Después de m tenemos a fn, que es la función. En este caso para la imagen con rasterización es 112, si fuera por columnas sería 113. De nuevo, es 112 en decimal, pero en hexadecimal es 0x70. Si fuera para imprimir la imagen descargada sería la función 50.

El tono: a

Ahora podemos definir el tono, que puede ser monocromático o de múltiples tonos. Si es monocromático el valor decimal es 48, si fuera múltiple sería 52. Yo voy a elegir 48.

Densidad: bx y by

Podemos hacer cada pixel de la imagen más grande ya sea en X o en Y, para ello existe el valor bx y by. En este caso pueden tener el valor decimal 1 o 2.

Aquí podemos modificar la impresión de imagen para que salga normal, doble ancho, doble largo o doble ancho y largo. Yo lo dejaré en 1 en ambos casos, ya que la redimensión de la imagen no es nítida.

Color: c

El siguiente parámetro es el color. Se supone que hay 4 colores. El color 1 es el decimal 49, el 2 es el decimal 50, hasta el color 4 que sería el 52. En mi caso lo he dejado en el color 49, que expresado como byte es 0x31.

Ancho y alto: xL, xH, yL, yH

Este, y los valores de pL y pH fueron los que me dieron complicaciones para entenderlos. Básicamente es enviar en x el ancho de la imagen, y enviar el alto de la imagen en y.

Como en los casos anteriores, debemos separar cada valor en 2 bytes en su parte baja y alta, siguiendo la misma fórmula.

La imagen rasterizada

Una vez que tenemos todo el encabezado de la imagen es momento de rasterizar la imagen y, ahora sí, convertir ese montón de pixeles en bytes entendibles por la impresora térmica gracias al comando ESC POS NV Graphics.

El algoritmo es más sencillo que el del envío por columna. Básicamente debes:

  1. Recorrer la imagen fila por fila. Es decir, primero iniciamos en 0,0 hasta ancho,0. Una vez que llegamos al ancho, aumentamos el valor de Y. Dicho con otras palabras es recorrer la imagen en líneas de arriba hacia abajo, y en cada línea, recorrer de izquierda a derecha
  2. En cada paso, decide si el pixel actual va a ser negro o blanco usando un algoritmo adecuado para convertir el valor RGB (o RGBA) en un blanco o negro
  3. En cada paso, agrega el bit del pixel (que será un 1 o 0) a un byte, comenzando en el MSB (el bit que vale 128) y terminando en el LSB (el bit que vale 1). Dicho con otras palabras, el primer pixel va en el bit de la izquierda, y el último pixel (de ese byte) va en el bit de la derecha. Cuando tengas el byte completo, agrega ese byte a un arreglo de bytes y sigue el mismo proceso.
  4. Cuando llegues al final de cada línea de pixeles, baja a la siguiente línea y sigue el mismo procedimiento.
  5. Al final vas a tener un arreglo unidimensional de bytes, y cada byte va a representar 8 pixeles de la imagen en blanco o negro.

Este ejemplo supone que el ancho de la imagen es múltiplo de 8. Si no quieres obligar a que el ancho sea divisible entre 8 siempre puedes rellenar los bits faltantes.

Imprimir imagen previamente descargada

Ya vimos el modo de enviar una imagen a una impresora térmica usando comandos ESC POS con NV Graphics, ahora veamos cómo imprimirla. Para imprimirla, usamos GS ( L pL pH m fn

En este caso GS ( L son valores literales. Ya te expliqué pL y pH anteriormente, así como el valor de p. En este caso el valor de p (cantidad de bytes después de pH) es 2, ya que solo enviamos el byte de m y el byte de fn.

Por cierto, en este caso el valor de m en decimal es 48 y el de fn es 50 (ya que la 112 y 113 son para almacenar, pero la 50 es para imprimir).

Se podría decir que este comando siempre será el mismo, ya que no va a cambiar sin importar el tamaño de la imagen, casi siempre será GS ( L 2 0 48 50 y no se me ocurre un escenario donde no lo sea, pero siempre existe alguna posibilidad.

Ejemplo real

Anteriormente te expliqué el algoritmo para que tú lo implementes en cualquier lenguaje de programación. Ahora veamos un ejemplo hecho a mano para convertir una imagen en bytes entendibles por una impresora térmica usando el método de impresión NV Graphics.

La imagen más pequeña que podemos imprimir (sin rellenar bytes) es una de 8 de ancho por 1 de alto. La he abierto en GIMP para mostrar sus pixeles ampliados:

Imagen para NV Graphics ESC POS - Conversión a bytes
Imagen para NV Graphics ESC POS – Conversión a bytes

Te repito que la imagen es extremadamente pequeña, pues solo tiene 8 pixeles, pero estos nos bastan. Fíjate en que 3 de sus pixeles son negros y el resto son blancos.

Voy a comenzar transformando la imagen en bytes. Comenzamos a recorrer. El primer pixel es 1, el segundo es 0, tercero 0, cuarto 0, quinto 0, sexto 0, séptimo 1, octavo 1.

En conjunto, son 10000011 y si convertimos ese valor en decimal, se convierte en 131. Esos 8 pixeles se convirtieron en el byte 131, representados como 0x83 en hexadecimal.

Comenzamos la transformación. El comando es GS ( L pL pH m fn a bx by c xL xH yL yH d1...dk.

El ancho es 8, el alto es 1, así que:

x =8, así que xL = 8 % 256 = 8 y xH = redondearHaciaAbajo(8/256) = 0

y = 1, así que yL = 1 % 256 = 1 y yH = redondearHaciaAbajo(1/256) = 0

Recordemos que m es 48, fn (función) es 112, a (tono) es 48, bx y by son 1 en ambos casos ya que no queremos redimensionar la imagen. El color (c) es 49. Así que por el momento el comando es:

GS ( L pL pH 48 112 48 1 1 49 8 0 1 0 131

Como puedes ver, falta calcular el valor de pL y pH. El valor de p es la cantidad de bytes que van después de pH, y si vemos los bytes del encabezado son los soguientes: 48 112 48 1 1 49 8 0 1 0, que en total son 10.

A ese valor todavía hay que sumarle la cantidad de bytes de la imagen, que en este caso viene dado por (ancho de la imagen / cantidad de bits en un byte) * alto de la imagen.

Sustituyendo, es (8/8) * 1 = 1 byte.

Si los bytes del encabezado eran 10 y sumamos el de la imagen, tenemos un total de 11 bytes que van a ir después de pH. Ahora ya tenemos el valor de p, que es 11.

Calculamos pL y pH. pL = 11 % 256 = 11, pH = redondearHaciaAbajo(11 / 256) = 0.

Tenemos el comando ESC POS completo. En decimal mezclado con ASCII queda así:

GS ( L 11 0 48 112 48 1 1 49 8 0 1 0 131

Y en hexadecimal queda así:

0x1d,0x28,0x4c,0x0b,0x00,0x30,0x70,0x30,0x01,0x01,0x31,0x08,0x00,0x01,0x00,0x83

Te lo desgloso de nuevo:

  1. 0x1d,0x28,0x4c (GS ( L )  es el comando ESC POS para iniciar la descarga de la imagen NV Graphics
  2. 0x0b,0x00 indica pL y pH (p separado en dos bytes; parte alta y baja). p es 11 (cantidad de bytes que van después de pH), pL es 11 y pH es 0
  3. 0x30,0x70,0x30 representan m, función y tono. La función es 112 para almacenar en forma de raster image y la 50 para imprimir.
  4. 0x01,0x01 representan bx y by para redimensionar (cambiar la densidad de) la imagen. No queremos redimensionarla así que ambos quedan en 1
  5. 0x31 es el color, que es 49 en decimal
  6. 0x08,0x00 es x (ancho de la imagen) separado en dos bytes, como xL y xH
  7. 0x01,0x00 es y (alto de la imagen) separado en dos bytes como yL y yH
  8. 0x83 es el byte de la imagen. Si la imagen midiera más, tal vez enviaríamos más bytes. Como la imagen cabe perfectamente en un byte solo enviamos ese byte

Con ese comando habremos descargado una imagen de 8×1 en la impresora, falta imprimirla. Ya expliqué el comando anteriormente en donde básicamente solo cambia la función y el conteo de bytes y queda, en hexadecimal, así:

0x1d,0x28,0x4c,0x02,0x00,0x30,0x32

Lo desgloso de nuevo:

  1. 0x1d,0x28,0x4c: es el comando GS ( L
  2. 0x02,0x00: el valor de p separado en 2 bytes, componiendo así pL y pH. Recuerda que p es la cantidad de bytes que van después de pH, que en este caso solo son 2
  3. 0x30,0x32: el número literal (m) y la función 50 (la 50 es para imprimir, la 112 y 113 para descargar la imagen)

Otro ejemplo

Te he mostrado el ejemplo más sencillo usando la imagen más pequeña, pero si quieres un ejemplo un poco más complejo aquí lo tienes en una imagen de 24×3. La imagen en cuestión es:

Imagen de 24×3 para NV Graphics – Algoritmo Impresora térmica

Fíjate que en este caso los bytes en hexadecimal serán 0xff, 0x00, 0xff para la primera fila, luego 0x00, 0xff, 0x00 para la segunda y 0xff, 0x00, 0xfe para la última.

Si te preguntas por qué el 0xfe al final, es porque el último pixel se representa como 11111110 en binario y FE en hexadecimal. El comando completo incluyendo la imagen queda así:

0x1D, 0x28, 0x4C, 0x13, 0x00, 0x30, 0x70, 0x30, 0x01, 0x01, 0x31, 0x18, 0x00, 0x03, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFE

Después, para imprimir la imagen descargada con el comando anterior, queda así:

0x1D, 0x28, 0x4C, 0x02, 0x00, 0x30, 0x32

Toma en cuenta que en este caso la imagen es para ejemplificar de manera sencilla la conversión de pixeles a bytes para NV Graphics, obviamente en una imagen real los pixeles no siempre serán 0xFF o 0x00, van a tener una variación dependiendo de la composición de la imagen.

Plugin

Si quieres un plugin que ya incorpora todos estos métodos de manera sencilla para el programador, usando cualquier lenguaje de programación, mira su presentación en este enlace, ya que también puedes usar el diseñador web para impresoras térmicas.

Encantado de ayudarte


Estoy disponible para trabajar en tu proyecto, modificar el programa del post o realizar tu tarea pendiente, no dudes en ponerte en contacto conmigo.

No te pierdas ninguno de mis posts

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario