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.
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.
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.
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.
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.
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
.
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.
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.
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
.
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.
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:
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 derechaEste 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.
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.
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:
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:
0x1d,0x28,0x4c
(GS ( L
) es el comando ESC POS para iniciar la descarga de la imagen NV Graphics0x0b,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 00x30,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.0x01,0x01
representan bx
y by
para redimensionar (cambiar la densidad de) la imagen. No queremos redimensionarla así que ambos quedan en 10x31
es el color, que es 49
en decimal0x08,0x00
es x
(ancho de la imagen) separado en dos bytes, como xL
y xH
0x01,0x00
es y
(alto de la imagen) separado en dos bytes como yL
y yH
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 byteCon 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:
0x1d,0x28,0x4c
: es el comando GS ( L
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 20x30,0x32
: el número literal (m) y la función 50 (la 50 es para imprimir, la 112 y 113 para descargar la imagen)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:
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.
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.
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…
Esta web usa cookies.