如何在Android的蓝牙打印机上打印图像?
我必须在热蓝牙打印机上打印一些数据,我正在做这个:
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厘米的纸张)。 我不知道如何打印图像。
我想尝试获取位图的像素,然后将其转换为一个byte[]
并发送它,但我不知道该怎么做。
谢谢
更新:
经过这么多的时间,我这样做:我有一个名为print_image(string文件)的方法,它获取我想要打印的图像的path:
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商店下载了一个应用程序)
这是我现在得到的
言归正传:
Closer2:
可以看到图像的一小部分,所以我认为我更接近可以打印图像…
我使用的图像是(576×95):
这是转换后的图像(我用上面的代码转换它):
所以,答案是:我做错了什么?我认为错误是在这个命令:
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};
但是,如何计算我的图像的正确值?谢谢
编辑 :基于阅读您的问题更新: https : //stackoverflow.com/questions/16597789/print-bitmap-on-esc-pos-printer-java
我将假定您正在打印的打印机与上述相同,即Rego热敏打印机。 正如你注意到的,这支持ESC / POS页面描述语言 。
打印机将stream式传输的数据解释为标记文档(与浏览器解释HTML的方式类似)。 在某些情况下,打印机将文档作为程序运行(例如PostScript)。 链接: 页面描述语言 。
常用的语言是:
- 您的 : ESC / POS 。
- 后记
- PCL
- ZPL
您需要阅读打印机的规格以确定使用哪种语言 – 如果您需要支持任何打印机,那么在您之前有一个非常大的工作:(
在ESC / POS中,您将需要使用GS v 0
命令(在第33页上logging)。 您可以通过在串行链路上发送字符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
参数定义:
- L:
- 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的设置如何,该命令都会执行用于打印位图所需量的进纸量。
- 打印位图后,该命令将打印位置设置到行首,清除缓冲区。
- 执行此命令时,数据将被同步传输和打印。 所以不需要其他打印命令。
有几个更广泛的论述:
- http://nicholas.piasecki.name/blog/2009/12/sending-a-bit-image-to-an-epson-tm-t88iii-receipt-printer-using-c-and-escpos/
- 在C#中的SO 。 虽然不是Java,但它足够接近成为一个模板。
不幸的是,Android中没有打印机API。 如果您对此感到强烈,请遵循以下问题:
解决!我正在做一个错误的打印机初始化…核心的方式是:
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, 255, 3};
所以,通过这种方式,图像打印完全好
我解决它将位图转换为字节数组。 请记住,您的图片必须是黑白格式。
完整的源代码: 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"); } }
我知道为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(); }
通过上面的代码连接后,你得到了socket的输出stream。然后通过打印机提供的工具将图像转换为相应的字节
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是字节字节,最后一个字节是结束字节,告诉硬件这是分组的结束。取决于图像的大小得到几个长度为256字节的数据包(大多数打印机)。将它们写入outputStream。
os.write(Packet1)
我也试过这个,我得到了我自己的解决scheme,我想我find了SELECT_BIT_IMAGE_MODE
命令是如何工作的。
命令public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, 255, 3};
在类PrinterCommands
是图像打印的POS命令。
前两个是非常标准的,接下来的三个确定要打印的图像的模式和尺寸。 为了这个解决scheme,我们假设第二个元素(33,我们被索引为零)总是33。
该byte []的最后两个元素是指要打印的图像的宽度(以像素为单位)属性,元素3有时称为nL
,元素4有时称为nH
。 它们实际上都是指宽度, nL
是Low Byte
而nH
是High Byte
。 这意味着我们最多可以有一个宽度为1111 1111 1111 1111b(二进制)的图像,它是65535d(十进制),尽pipe我还没有尝试过。 如果nL或nH未设置为适当的值,则会在图像上打印垃圾字符。
不知何故,Android文档告诉我们,字节数组中的字节值的限制是-128和+127,当我试图放入255时,Eclipse要求我把它转换为Byte。
无论如何,回到nL和nW,对于你的情况,你有一个宽度为576的图像,如果我们将576转换为二进制,我们会得到两个字节,如下所示:
0000 0010 0100 0000
在这种情况下,低字节是0100 0000
而高字节是0000 0010
。 把它转换回十进制,我们得到nL = 64
和nH = 2
。
在我的情况下,我打印了一个宽度为330px的图像,将330转换为二进制,我们得到:
0000 0001 0100 1010
在这种情况下,低字节是0100 1010
,高字节是0000 0001
。 转换为十进制,我们得到nL = 74
和nH = 1
。
有关更多信息,请查看这些文档/教程:
Star Asia移动打印机文档
ECS-POS编程指南 – 非常广泛
另一个文档
上面的代码的扩展版本,更多的解释
上面的代码的解释
希望这些帮助。
我是ESC / POS的新手,正在为此而苦苦挣扎。 我碰到这个页面,似乎有一些有用的function: http : //code.taobao.org/p/printer/src/trunk/prtest/src/com/enjar/plugins/PrintTools_58mm.java这是中文,但可能是值得的。 如果有人知道,我也想开悟
使用这个代码:
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:")); }
- 使用ContentProviderClient与ContentResolver来访问内容提供者
- Android线性布局 – 如何保持在底部的元素?
- parseSdkContent失败无法初始化类android.graphics.Typeface
- (不赞成)片段onOptionsItemSelected没有被调用
- 将DEX反编译成Java源代码
- 我们可以使用JDBC在Android中连接远程MySQL数据库吗?
- 获取android.content.res.Resources $ NotFoundException:即使资源存在于android中也是exception
- 在Android中引起帧中的帧animationOutOfMemoryError
- Android资源转换为stringTypedValue警告