- Agregar imports de Tailwind CSS v4 y Nuxt UI en main.css - Renombrar QueueActions.vue -> Actions.vue y QueueItem.vue -> Item.vue para evitar conflictos de nombres de componentes - Crear composable useMediaQuery para manejo de responsive - Corregir referencias a componentes en index.vue y PrintQueue.vue - Actualizar imports de servidor a rutas relativas - Instalar @iconify-json/heroicons y @iconify-json/lucide - Actualizar Jimp a sintaxis v1.x
119 lines
2.7 KiB
TypeScript
119 lines
2.7 KiB
TypeScript
// Endpoint para imprimir imágenes usando Jimp
|
|
import { Jimp } from 'jimp'
|
|
import { EposMessageBuilder } from '../../utils/eposBuilder'
|
|
import { buildSoapEnvelope, sendToPrinter, parsePrinterResponse } from '../../utils/printer'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
try {
|
|
const config = useRuntimeConfig()
|
|
const body = await readBody(event)
|
|
|
|
const {
|
|
path,
|
|
width,
|
|
threshold = 128,
|
|
mode = 'mono'
|
|
} = body as {
|
|
path?: string
|
|
width?: number
|
|
threshold?: number
|
|
mode?: string
|
|
}
|
|
|
|
if (!path) {
|
|
return {
|
|
ok: false,
|
|
error: 'path required'
|
|
}
|
|
}
|
|
|
|
// Leer y procesar imagen
|
|
const img = await Jimp.read(path)
|
|
let targetWidth = width || img.width
|
|
if (targetWidth <= 0) targetWidth = img.width
|
|
|
|
const scale = targetWidth / img.width
|
|
const targetHeight = Math.max(1, Math.round(img.height * scale))
|
|
|
|
// Resize y convertir a escala de grises
|
|
img.resize({ w: targetWidth, h: targetHeight })
|
|
img.greyscale()
|
|
|
|
// Empaquetar bits MSB first por byte
|
|
const bytesPerRow = Math.ceil(targetWidth / 8)
|
|
const out = Buffer.alloc(bytesPerRow * targetHeight)
|
|
let outIdx = 0
|
|
|
|
for (let y = 0; y < targetHeight; y++) {
|
|
let byte = 0
|
|
let bit = 7
|
|
let rowBytes = 0
|
|
|
|
for (let x = 0; x < targetWidth; x++) {
|
|
const color = img.getPixelColor(x, y)
|
|
// Extraer componente rojo (ya es greyscale, todos los canales son iguales)
|
|
const r = (color >> 24) & 0xFF
|
|
const isBlack = r < threshold
|
|
|
|
if (isBlack) byte |= (1 << bit)
|
|
bit--
|
|
|
|
if (bit < 0) {
|
|
out[outIdx++] = byte
|
|
rowBytes++
|
|
byte = 0
|
|
bit = 7
|
|
}
|
|
}
|
|
|
|
// Rellenar bits restantes
|
|
if (bit !== 7) {
|
|
out[outIdx++] = byte
|
|
rowBytes++
|
|
}
|
|
|
|
// Rellenar a bytes completos si es necesario
|
|
while (rowBytes < bytesPerRow) {
|
|
out[outIdx++] = 0
|
|
rowBytes++
|
|
}
|
|
}
|
|
|
|
const base64 = out.toString('base64')
|
|
|
|
// Construir mensaje ePOS
|
|
const builder = new EposMessageBuilder()
|
|
builder.imageRaw({
|
|
width: targetWidth,
|
|
height: targetHeight,
|
|
mode,
|
|
base64
|
|
})
|
|
|
|
const soap = buildSoapEnvelope(builder.build())
|
|
|
|
const result = await sendToPrinter(
|
|
soap,
|
|
config.printerHost,
|
|
config.printerDeviceId,
|
|
parseInt(config.printerTimeoutMs)
|
|
)
|
|
|
|
const { success, code } = parsePrinterResponse(result.data)
|
|
|
|
return {
|
|
ok: success,
|
|
httpStatus: result.status,
|
|
code,
|
|
raw: result.data,
|
|
width: targetWidth,
|
|
height: targetHeight
|
|
}
|
|
} catch (err: any) {
|
|
return {
|
|
ok: false,
|
|
error: err.message
|
|
}
|
|
}
|
|
})
|