如何在Node.js中将图片转为base64Binary用于EPSON TM-M30打印机XML打印
在Node.js中转换图片为EPSON TM-M30打印机所需的base64Binary格式
要给EPSON TM-M30打印机发送XML打印请求时,<image>标签要求的base64Binary不是直接对原图做base64编码的结果,而是先将图片转换为打印机支持的光栅位图格式(单色或16级灰度的压缩数据),再对这个光栅数据做base64编码得到的。
下面是具体的Node.js实现方案:
实现步骤
- 使用
canvas库加载图片并获取像素数据(替代浏览器中的Canvas API) - 移植EPSON SDK中的光栅转换函数(生成打印机可识别的位图数据)
- 移植
toBase64Binary函数将光栅数据编码为要求的格式
第一步:安装依赖
npm install canvas
第二步:完整代码实现
const { createCanvas, loadImage } = require('canvas'); // 定义常量(对应SDK中的配置) const HALFTONE_DITHER = 0; const HALFTONE_ERROR_DIFFUSION = 1; const HALFTONE_THRESHOLD = 2; const MODE_MONO = 0; // 单色模式 const MODE_GRAY16 = 1; // 16级灰度模式 // 移植SDK中的toMonoImage函数:生成单色光栅数据 function toMonoImage(imgdata, halftoneMode, brightness) { const x = String.fromCharCode; const m8 = [ [2, 130, 34, 162, 10, 138, 42, 170], [194, 66, 226, 98, 202, 74, 234, 106], [50, 178, 18, 146, 58, 186, 26, 154], [242, 114, 210, 82, 250, 122, 218, 90], [14, 142, 46, 174, 6, 134, 38, 166], [206, 78, 238, 110, 198, 70, 230, 102], [62, 190, 30, 158, 54, 182, 22, 150], [254, 126, 222, 94, 246, 118, 214, 86], ]; const d = imgdata.data; const w = imgdata.width; const h = imgdata.height; const r = new Array(((w + 7) >> 3) * h); let n = 0; let p = 0; let q = 0; let t = 128; const e = []; let e1, e2, b, v, f, i, j; if (halftoneMode === 1) { for (i = w; i--;) e.push(0); } for (j = 0; j < h; j++) { e1 = 0; e2 = 0; i = 0; while (i < w) { b = i & 7; if (halftoneMode === 0) { t = m8[j & 7][b]; } // 计算灰度值并调整亮度 v = (Math.pow( (((d[p++] * 0.29891 + d[p++] * 0.58661 + d[p++] * 0.11448) * d[p]) / 255 + 255 - d[p++]) / 255, 1 / brightness ) * 255) | 0; if (halftoneMode === 1) { v += (e[i] + e1) >> 4; f = v - (v < t ? 0 : 255); if (i > 0) e[i - 1] += f; e[i] = f * 7 + e2; e1 = f * 5; e2 = f * 3; } if (v < t) { n |= 128 >> b; } i++; if (b === 7 || i === w) { r[q++] = x(n === 16 ? 32 : n); n = 0; } } } return r.join(''); } // 移植SDK中的toGrayImage函数:生成16级灰度光栅数据 function toGrayImage(imgdata, brightness) { const x = String.fromCharCode; const m4 = [ [0, 9, 2, 11], [13, 4, 15, 6], [3, 12, 1, 10], [16, 7, 14, 5], ]; const thermal = [ 0, 7, 13, 19, 23, 27, 31, 35, 40, 44, 49, 52, 54, 55, 57, 59, 61, 62, 64, 66, 67, 69, 70, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 96, 96, 97, 98, 98, 99, 99, 100, 101, 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 120, 121, 121, 122, 122, 123, 123, 123, 124, 124, 125, 125, 125, 126, 126, 127, 127, 127, 128, 128, 129, 129, 130, 130, 130, 131, 131, 132, 132, 132, 133, 133, 134, 134, 135, 135, 135, 136, 136, 137, 137, 137, 138, 138, 139, 139, 139, 140, 140, 141, 141, 141, 142, 142, 143, 143, 143, 144, 144, 145, 145, 146, 146, 146, 147, 147, 148, 148, 148, 149, 149, 150, 150, 150, 151, 151, 152, 152, 152, 153, 153, 154, 154, 155, 155, 155, 156, 156, 157, 157, 158, 158, 159, 159, 160, 160, 161, 161, 161, 162, 162, 163, 163, 164, 164, 165, 165, 166, 166, 166, 167, 167, 168, 168, 169, 169, 170, 170, 171, 171, 172, 173, 173, 174, 175, 175, 176, 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 184, 185, 186, 186, 187, 189, 191, 193, 195, 198, 200, 202, 255, ]; const d = imgdata.data; const w = imgdata.width; const h = imgdata.height; const r = new Array(((w + 1) >> 1) * h); let n = 0; let p = 0; let q = 0; let b, v, v1, i, j; for (j = 0; j < h; j++) { i = 0; while (i < w) { b = i & 1; v = thermal[ (Math.pow( (((d[p++] * 0.29891 + d[p++] * 0.58661 + d[p++] * 0.11448) * d[p]) / 255 + 255 - d[p++]) / 255, 1 / brightness ) * 255) | 0 ]; v1 = (v / 17) | 0; if (m4[j & 3][i & 3] < v % 17) { v1++; } n |= v1 << ((1 - b) << 2); i++; if (b === 1 || i === w) { r[q++] = x(n); n = 0; } } } return r.join(''); } // 移植SDK中的toBase64Binary函数:将光栅数据转为要求的base64格式 function toBase64Binary(s) { const l = s.length; const r = new Array(((l + 2) / 3) << 2); const t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let p = (3 - (l % 3)) % 3; let j = 0; let i = 0; let n; s += "\x00\x00"; while (i < l) { n = (s.charCodeAt(i++) << 16) | (s.charCodeAt(i++) << 8) | s.charCodeAt(i++); r[j++] = t.charAt((n >> 18) & 63); r[j++] = t.charAt((n >> 12) & 63); r[j++] = t.charAt((n >> 6) & 63); r[j++] = t.charAt(n & 63); } while (p--) { r[--j] = "="; } return r.join(''); } // 主函数:加载图片并转换为EPSON要求的base64Binary async function convertImageToEpsonBase64(imagePath, options = {}) { const { mode = MODE_MONO, halftone = HALFTONE_DITHER, brightness = 1 } = options; // 加载图片到canvas const img = await loadImage(imagePath); const canvas = createCanvas(img.width, img.height); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const imgdata = ctx.getImageData(0, 0, img.width, img.height); // 生成光栅数据 let rasterData; if (mode === MODE_GRAY16) { rasterData = toGrayImage(imgdata, brightness); } else { rasterData = toMonoImage(imgdata, halftone, brightness); } // 转换为base64Binary return toBase64Binary(rasterData); } // 使用示例 (async () => { try { const base64Binary = await convertImageToEpsonBase64('./test.png', { mode: MODE_MONO, halftone: HALFTONE_ERROR_DIFFUSION, brightness: 1.5 }); // 生成XML打印请求中的image标签 const xmlImageTag = `<image>${base64Binary}</image>`; console.log(xmlImageTag); } catch (err) { console.error('转换失败:', err); } })();
关键说明
- 光栅格式的含义:EPSON热敏打印机需要的是经过压缩的位图数据,单色模式下每8个像素用1字节存储,灰度模式下每2个像素用1字节存储(16级灰度),这样能大幅减少传输数据量。
- 参数调整:
halftone:选择半色调处理方式,误差扩散(HALFTONE_ERROR_DIFFUSION)的打印效果最好brightness:调整图片亮度,范围0.1-10,值越大越亮mode:选择单色或16级灰度模式,单色模式打印速度更快
内容的提问来源于stack exchange,提问作者ETTALHAOUI
相关产品推荐
相关产品推荐

