57

我必须在热敏蓝牙打印机上打印一些数据,我正在这样做:

String message="abcdef any message 12345";
byte[] send;
send = message.getBytes();
mService.write(send);

它适用于文本,但不适用于图像。我想我需要获取byte[]图像数据的。我尝试以这种方式获取图像的数据:

Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.qrcode);
ByteArrayOutputStream stream=new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
byte[] image=stream.toByteArray();

不幸的是,打印机打印了很多奇怪的字符(大约 50 厘米的纸)。我不知道如何打印图像。

我想尝试获取位图的像素,然后将其转换为 abyte[]并发送,但我不知道该怎么做。

谢谢

更新:

经过这么长时间,我正在这样做:我有一个名为 print_image(String file) 的方法,它获取我要打印的图像的路径:

private void print_image(String file) {
    File fl = new File(file);
    if (fl.exists()) {
        Bitmap bmp = BitmapFactory.decodeFile(file);
        convertBitmap(bmp);
        mService.write(PrinterCommands.SET_LINE_SPACING_24);

        int offset = 0;
        while (offset < bmp.getHeight()) {
            mService.write(PrinterCommands.SELECT_BIT_IMAGE_MODE);
            for (int x = 0; x < bmp.getWidth(); ++x) {

                for (int k = 0; k < 3; ++k) {

                    byte slice = 0;
                    for (int b = 0; b < 8; ++b) {
                        int y = (((offset / 8) + k) * 8) + b;
                        int i = (y * bmp.getWidth()) + x;
                        boolean v = false;
                        if (i < dots.length()) {
                            v = dots.get(i);
                        }
                        slice |= (byte) ((v ? 1 : 0) << (7 - b));
                    }
                    mService.write(slice);
                }
            }
            offset += 24;
            mService.write(PrinterCommands.FEED_LINE);
            mService.write(PrinterCommands.FEED_LINE);          
            mService.write(PrinterCommands.FEED_LINE);
            mService.write(PrinterCommands.FEED_LINE);
            mService.write(PrinterCommands.FEED_LINE);
            mService.write(PrinterCommands.FEED_LINE);
        }
        mService.write(PrinterCommands.SET_LINE_SPACING_30);


    } else {
        Toast.makeText(this, "file doesn't exists", Toast.LENGTH_SHORT)
                .show();
    }
}

我是根据这篇文章做的

这是类 PrinterCommands:

public class PrinterCommands {
public static final byte[] INIT = {27, 64};
public static byte[] FEED_LINE = {10};

public static byte[] SELECT_FONT_A = {27, 33, 0};

public static byte[] SET_BAR_CODE_HEIGHT = {29, 104, 100};
public static byte[] PRINT_BAR_CODE_1 = {29, 107, 2};
public static byte[] SEND_NULL_BYTE = {0x00};

public static byte[] SELECT_PRINT_SHEET = {0x1B, 0x63, 0x30, 0x02};
public static byte[] FEED_PAPER_AND_CUT = {0x1D, 0x56, 66, 0x00};

public static byte[] SELECT_CYRILLIC_CHARACTER_CODE_TABLE = {0x1B, 0x74, 0x11};

public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};
public static byte[] SET_LINE_SPACING_24 = {0x1B, 0x33, 24};
public static byte[] SET_LINE_SPACING_30 = {0x1B, 0x33, 30};

public static byte[] TRANSMIT_DLE_PRINTER_STATUS = {0x10, 0x04, 0x01};
public static byte[] TRANSMIT_DLE_OFFLINE_PRINTER_STATUS = {0x10, 0x04, 0x02};
public static byte[] TRANSMIT_DLE_ERROR_STATUS = {0x10, 0x04, 0x03};
public static byte[] TRANSMIT_DLE_ROLL_PAPER_SENSOR_STATUS = {0x10, 0x04, 0x04};
}

正如在 print_image 方法中看到的那样,我正在调用一个名为 convertBitmap 的方法,并且我正在发送一个位图,这是代码:

   public String convertBitmap(Bitmap inputBitmap) {

    mWidth = inputBitmap.getWidth();
    mHeight = inputBitmap.getHeight();

    convertArgbToGrayscale(inputBitmap, mWidth, mHeight);
    mStatus = "ok";
    return mStatus;

}

private void convertArgbToGrayscale(Bitmap bmpOriginal, int width,
        int height) {
    int pixel;
    int k = 0;
    int B = 0, G = 0, R = 0;
    dots = new BitSet();
    try {

        for (int x = 0; x < height; x++) {
            for (int y = 0; y < width; y++) {
                // get one pixel color
                pixel = bmpOriginal.getPixel(y, x);

                // retrieve color of all channels
                R = Color.red(pixel);
                G = Color.green(pixel);
                B = Color.blue(pixel);
                // take conversion up to one single value by calculating
                // pixel intensity.
                R = G = B = (int) (0.299 * R + 0.587 * G + 0.114 * B);
                // set bit into bitset, by calculating the pixel's luma
                if (R < 55) {                       
                    dots.set(k);//this is the bitset that i'm printing
                }
                k++;

            }


        }


    } catch (Exception e) {
        // TODO: handle exception
        Log.e(TAG, e.toString());
    }
}

这是我正在使用的打印机,分辨率:8 点/毫米,576 点/行

这就是我喜欢做的事情(我使用同一台打印机,但使用从 Play 商店下载的应用程序) 我要打印的图像

这就是我现在得到的 我的印刷尝试

更接近: 更近的部分

关闭器2: 在此处输入图像描述

可以看到一小部分图像,所以我认为我更接近可以打印图像......

我正在使用的图像是这个(576x95):在此处输入图像描述

这是转换后的图像(我用上面的代码转换它): 转换后的图像

倒

所以,答案是:我做错了什么?,我认为错误出在这个命令中:

  public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};

但是,我怎样才能为我的图像计算正确的值?,谢谢

4

8 回答 8

24

我解决了将位图转换为字节数组的问题。请记住,您的图像必须是黑白格式。

完整源代码: https ://github.com/imrankst1221/Thermal-Printer-in-Android

在此处输入图像描述

 public void printPhoto() {
        try {
            Bitmap bmp = BitmapFactory.decodeResource(getResources(),
                    R.drawable.img);
            if(bmp!=null){
                byte[] command = Utils.decodeBitmap(bmp);
                printText(command);
            }else{
                Log.e("Print Photo error", "the file isn't exists");
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("PrintTools", "the file isn't exists");
        }
    }
于 2017-02-07T09:22:05.147 回答
14

我也试过这个,我得到了我自己的解决方案,我想我弄清楚了这个SELECT_BIT_IMAGE_MODE命令是如何工作的。

public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, 255, 3};类中的命令PrinterCommands是图像打印的 POS 命令。

前两个非常标准,接下来的三个确定要打印的图像的模式和尺寸。为了这个解决方案,我们假设第二个元素(33,我们的索引为零)总是 33。

该 byte[] 的最后两个元素是指您要打印的图像的Width(以像素为单位)nL属性,元素 3 有时称为,元素 4 有时称为nH。它们实际上都是指 Width,nLLow BytewhilenHHigh Byte. 这意味着我们最多可以拥有一个宽度为 1111 1111 1111 1111b(二进制)的图像,即 65535d(十进制),尽管我还没有尝试过。如果 nL 或 nH 未设置为正确的值,则将与图像一起打印垃圾字符。

不知何故,Android 文档告诉我们字节数组中一个字节的值的限制是 -128 和 +127,当我尝试输入 255 时,Eclipse 要求我将其转换为字节。

无论如何,回到 nL 和 nW,对于您的情况,您有一个宽度为 576 的图像,如果我们将 576 转换为二进制,我们会得到两个字节,如下所示:

0000 0010 0100 0000

在这种情况下,低字节是0100 0000,而高字节是0000 0010。将其转换回十进制,我们得到nL = 64nH = 2

就我而言,我打印了一个宽度为 330 像素的图像,将 330 转换为二进制我们得到:

0000 0001 0100 1010

现在在这种情况下,低字节是0100 1010,高字节是0000 0001。转换为十进制,我们得到nL = 74nH = 1

有关更多信息,请查看以下文档/教程:

星亚移动打印机文档

ECS-POS 编程指南 - 非常广泛

另一个文档

上面代码的扩展版本,有更多解释

上面代码的解释

希望这些有所帮助。

于 2013-07-22T08:41:37.557 回答
12

解决了!,我正在做一个错误的打印机初始化......正确的方法是:

 public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, 255, 3};

所以,通过这种方式,图像打印得非常好

于 2013-06-04T16:35:49.713 回答
10

编辑:根据阅读您的问题进行更新:https ://stackoverflow.com/questions/16597789/print-bitmap-on-esc-pos-printer-java

我假设您要打印的打印机与上面的打印机相同,即 Rego 热敏打印机。正如您所注意到的,这支持ESC/POS 页面描述语言


打印机将流向它们的数据解释为标记的文档(以类似于浏览器解释 HTML 的方式)。在某些情况下,打印机实际上将文档作为程序(例如 PostScript)运行。链接:页面描述语言

常用语言有:

您需要阅读打印机的规格以确定要使用的语言 - 如果您需要支持任何打印机,那么您面临的工作量非常大:(

在 ESC/POS 中,您将需要使用GS v 0命令(记录在 p33 中)。为此,您可以通过0x1D7630串行链路发送字符,后跟一组参数:

ASCII:       Gs   v  0 
Decimal:     29 118 48 m xL xH yL yH [d]k 
Hexadecimal: 1D  76 30 m xL xH yL yH [d]k 

参数定义:

  • 米:
    • 0,48:正常模式(1:1 比例)
    • 1,49:双宽
    • 2,50:双高
    • 3,51:双宽+双高
  • xL, xH 指定位图水平方向的 (xL + xH × 256) 字节。
  • yL, yH 指定位图垂直方向的 (yL + yH × 256) 个点。
  • [d]k 指定位图数据(光栅格式)。
  • k表示位图像数据的数量。k是解释参数;因此,它不需要传输。

笔记:

  • 当数据 [d]k 为 1 时,指定打印为 1 且不打印为 0 的位。
  • 如果光栅位图超过一行打印区域,则不打印超出的数据。
  • 无论 ESC 2 或 ESC 3 的设置如何,此命令都会执行打印位图所需的进纸量。
  • 打印位图后,该命令将打印位置设置为行首,并清空缓冲区。
  • 执行该命令时,数据同步传输和打印。因此不需要其他打印命令。

还有几个更广泛的阐述:


不幸的是,Android 中没有打印机 API。如果您对此有强烈的感觉,请遵循以下问题:

于 2013-05-23T06:55:55.303 回答
3

我是 ESC/POS 的新手,并且正在为此苦苦挣扎。我遇到了这个页面,它似乎有一些有用的功能:http ://code.taobao.org/p/printer/src/trunk/prtest/src/com/enjar/plugins/PrintTools_58mm.java 虽然它是中文的,但是可能值得经历。如果有人想通了,我也想开悟……

于 2013-11-14T11:53:33.230 回答
2

我知道 evolute 和 AMDL 蓝牙打印机。首先阅读打印机的协议定义文档,告诉您设备需要哪些特定字节-

public void connect() throws Exception 
{

    BluetoothDevice printer = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(connParams);

    Method m = printer.getClass().getMethod("createInsecureRfcommSocket",new Class[] { int.class });
    sock = (BluetoothSocket)m.invoke(printer, Integer.valueOf(1));
    sock.connect();
    os=sock.getOutputStream();
    in=sock.getInputStream();

}

通过上述代码连接后,您将获得套接字的输出流。然后通过打印机提供的工具将您的图像转换为相应的字节,您会得到类似的东西

public byte[] Packet1={
        (byte)0X8A,(byte)0XC6,(byte)0X94,(byte)0XF4,(byte)0X0B,(byte)0X5E,(byte)0X30,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X04,(byte)0X24,(byte)0X01,(byte)0X0C,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X01,(byte)0X08,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X04,(byte)0X24,(byte)0X05,(byte)0X0C,(byte)0X00,(byte)0X60,(byte)0X00,(byte)0X18,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X30,(byte)0X1E,(byte)0X10,(byte)0X60,(byte)0X00,(byte)0X18,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X70,(byte)0X3F,(byte)0X18,(byte)0XF0,(byte)0X00,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X70,(byte)0X3C,(byte)0X39,(byte)0XF1,(byte)0X80,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF8,(byte)0X7C,(byte)0X9F,(byte)0XF1,(byte)0X80,(byte)0X7F,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF9,(byte)0X9E,(byte)0X1C,(byte)0XFF,(byte)0XC2,(byte)0X7E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XF9,(byte)0X9E,(byte)0X1C,(byte)0XE7,(byte)0XE2,(byte)0X7E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XFB,(byte)0X1E,(byte)0X1C,(byte)0XFF,(byte)0XE7,(byte)0XBE,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X7B,(byte)0X16,(byte)0X1C,(byte)0XFF,(byte)0XDF,(byte)0X3E,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X71,(byte)0X12,(byte)0X1C,(byte)0XE7,(byte)0XF7,(byte)0X34,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X51,(byte)0X12,(byte)0X1C,(byte)0XF7,(byte)0XF7,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X12,(byte)0X1C,(byte)0XFF,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X12,(byte)0X3F,(byte)0XFD,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0X49,(byte)0X96,(byte)0X3F,(byte)0XFC,(byte)0XF3,(byte)0X24,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X05,(byte)0X49,(byte)0X80,(byte)0X00,(byte)0X08,(byte)0X10,(byte)0X5E,(byte)0X28,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X30,(byte)0X25,(byte)
        0X01,(byte)0X5E,(byte)0X03,(byte)0X24,(byte)0X06,(byte)0XE0,(byte)0X74,(byte)0XA9,(byte)0X33,(byte)0X23,(byte)0X26,(byte)0X5E,(byte)0X27,(byte)0X25,(byte)0X04
        };

其中 8A 是起始字节 C6 是模式字节(智能卡、刷卡和指纹不同),94 是字体字节,最后一个字节 04 是结束字节,告诉硬件这是数据包的结尾。取决于图像的大小获取其中几个长度为 256 字节的数据包(大多数打印机)。将它们写入 outputStream。

os.write(Packet1)
于 2013-05-26T19:53:57.883 回答
0

这对我有用:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTargetDensity = 200;
options.inDensity = 200;
Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), img, options);

在使用 BitmapFactory.Options 之前,我只能打印 60X60 尺寸的图像,现在我也可以打印更大尺寸的图像。

于 2020-06-19T19:20:57.193 回答
-6

使用此代码:

public static void print(Context context) {

    String examplePath = "file:///sdcard/dcim/Camera/20111210_181524.jpg";

    Intent sendIntent = new Intent(Intent.ACTION_SEND);
    sendIntent.setType("image/jpeg");
    sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Photo");
    sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(examplePath));
    sendIntent.putExtra(Intent.EXTRA_TEXT, "Enjoy the photo");
    context.startActivity(Intent.createChooser(sendIntent, "Email:"));
}
于 2013-05-29T12:36:03.890 回答