Hoy te voy a enseñar una aplicación web (programada con JavaScript en el lado del cliente) para imprimir fotos en una impresora térmica. Las fotos que se van a imprimir serán tomadas al instante con la cámara web o cámara integrada.
Gracias a esta webapp vas a poder tomar una foto de la cámara y enviarla a una impresora térmica aplicando dithering para mejorar su calidad.
Puedes acceder a la demostración ya mismo: cámara a impresora térmica.
El proceso será automático, solo debes conectar tu impresora térmica por USB, abrir la página web, seleccionar tu cámara y presionar el botón para tomar una fotografía e imprimirla en una impresora térmica.
Si no quieres ver los detalles técnicos entonces puedes visitar la siguiente página para tomar fotos con tu cámara e imprimirlas conservando varios detalles: cámara a impresora térmica.
Ya tengo varios tutoriales en mi blog sobre cómo tomar una foto con la cámara desde el navegador web usando JS. Tengo una guía para tomar la foto y descargarla, otra para enviarla a PHP y otra para enviarla a Telegram.
El código importante es el siguiente que accede al stream de la cámara y lo coloca en un elemento HTML video para mostrárselo al usuario.
async function iniciarStream() {
// Comenzamos viendo si tiene soporte, si no, nos detenemos
if (!tieneSoporteUserMedia()) {
alert("Lo siento. Tu navegador no soporta esta característica");
$estado.innerHTML = "Parece que tu navegador no soporta esta característica. Intenta actualizarlo.";
return;
}
const mostrarStream = idDeDispositivo => {
_getUserMedia({
video: {
// Justo aquí indicamos cuál dispositivo usar
deviceId: idDeDispositivo,
}
},
async (streamObtenido) => {
// Aquí ya tenemos permisos, ahora sí llenamos el select,
// pues si no, no nos daría el nombre de los dispositivos
if ($listaDeDispositivos.length <= 0) {
llenarSelectConDispositivosDisponibles($listaDeDispositivos);
}
// Escuchar cuando seleccionen otra opción y entonces llamar a esta función
$listaDeDispositivos.onchange = () => {
// Detener el stream
if (stream) {
stream.getTracks().forEach(function (track) {
track.stop();
});
}
// Mostrar el nuevo stream con el dispositivo seleccionado
mostrarStream($listaDeDispositivos.value);
}
// Simple asignación
stream = streamObtenido;
// Mandamos el stream de la cámara al elemento de vídeo
$video.srcObject = stream;
// Refrescar el canvas
await $video.play();
canvasFueraDePantalla = new OffscreenCanvas($video.videoWidth, $video.videoHeight);
contextoCanvas = canvasFueraDePantalla.getContext("2d");
}, (error) => {
console.log("Permiso denegado o error: ", error);
$estado.innerHTML = "No se puede acceder a la cámara, o no diste permiso.";
});
}
// Comenzamos pidiendo los dispositivos
const dispositivos = await navigator.mediaDevices.enumerateDevices();
// Vamos a filtrarlos y guardar aquí los de vídeo
const dispositivosDeVideo = [];
// Recorrer y filtrar
dispositivos.forEach(function (dispositivo) {
const tipo = dispositivo.kind;
if (tipo === "videoinput") {
dispositivosDeVideo.push(dispositivo);
}
});
// Vemos si encontramos algún dispositivo, y en caso de que si, entonces llamamos a la función
// y le pasamos el id de dispositivo
if (dispositivosDeVideo.length > 0) {
// Mostrar stream con el ID del primer dispositivo, luego el usuario puede cambiar
mostrarStream(dispositivosDeVideo[0].deviceId);
}
}
Para tomar la foto y enviarla a la impresora térmica es necesario pausar el vídeo, pintar el fotograma en un canvas (que será una instancia de OffscreenCanvas
) y convertir ese canvas a imagen en base64:
const tomarFoto = async () => {
//Pausar reproducción
$video.pause();
canvasFueraDePantalla.width = $video.videoWidth;
canvasFueraDePantalla.height = $video.videoHeight;
//Obtener contexto del canvas y dibujar sobre él
contextoCanvas.drawImage($video, 0, 0, canvasFueraDePantalla.width, canvasFueraDePantalla.height);
//Reanudar reproducción
await $video.play();
const blob = await canvasFueraDePantalla.convertToBlob();
const reader = new FileReader();
reader.onloadend = async () => {
await imprimirFoto($impresoras.value, reader.result, parseInt($algoritmo.value), $licencia.value);
}
reader.onerror = () => { }
reader.readAsDataURL(blob);
}
Ahora que ya tenemos la foto procederemos a imprimirla en una impresora térmica. Para ello veamos la siguiente función llamada imprimirFoto
que recibe el nombre de la impresora térmica, la imagen en base64, el algoritmo de conversión de imagen y un serial opcional.
Lo importante está en el arreglo operaciones que contiene la operación ESC POS que se va a enviar a la impresora térmica. Como tenemos la imagen codificada en base64 invocamos a ImprimirImagenEnBase64.
Los argumentos de la operación son la cadena en base64, el ancho máximo, el algoritmo y una bandera indicando si se debería aplicar dithering antes de imprimir la imagen.
const imprimirFoto = async (nombreImpresora, fotoEnBase64, algoritmo, licencia) => {
const payload = {
"serial": licencia,
"nombreImpresora": nombreImpresora,
"operaciones": [
{
"nombre": "ImprimirImagenEnBase64",
"argumentos": [
fotoEnBase64,
380,
algoritmo,
true
]
},
]
};
const httpResponse = await fetch("http://localhost:8000/imprimir",
{
method: "POST",
body: JSON.stringify(payload),
});
const jsonResponse = await httpResponse.json();
if (jsonResponse.ok) {
// Everything is ok
console.log("Printed successfully");
} else {
// Error message is on message property
console.error(jsonResponse.message)
}
}
Y al hacer esta petición, la foto se habrá impreso en la impresora térmica con la calidad máxima posible. En resumen es:
OffscreenCanvas
De este modo puedes imprimir fotos instantáneas desde tu navegador web.
Te he mostrado el código más importante, pero puedes acceder al repositorio completo en GitHub. También puedes acceder a la aplicación web para tomar foto con la cámara e imprimirla en una impresora térmica.
Para que el código funcione necesitas el servidor local de la API ESC POS ejecutándose y que tu impresora esté compartida. Por favor, sigue los pasos que aparecen en la página previamente enlazada, ya que ahí está la API unificada.
La documentación completa está en: https://parzibyte.me/http-esc-pos-desktop-docs/es/
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
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…
Esta web usa cookies.