You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Node.js中将图片转为base64Binary用于EPSON TM-M30打印机XML打印

在Node.js中转换图片为EPSON TM-M30打印机所需的base64Binary格式

要给EPSON TM-M30打印机发送XML打印请求时,<image>标签要求的base64Binary不是直接对原图做base64编码的结果,而是先将图片转换为打印机支持的光栅位图格式(单色或16级灰度的压缩数据),再对这个光栅数据做base64编码得到的。

下面是具体的Node.js实现方案:

实现步骤

  1. 使用canvas库加载图片并获取像素数据(替代浏览器中的Canvas API)
  2. 移植EPSON SDK中的光栅转换函数(生成打印机可识别的位图数据)
  3. 移植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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 02:07:17