Android 蓝牙连接 ESC/POS 热敏打印机打印实例(ESC/POS指令篇)

上一篇 主要介绍了如何通过蓝牙连接到打印机。这一篇,我们就介绍如何向打印机发送打印指令,来打印字符和图片。

1. 构造输出流

首先要明确一点,就是蓝牙连接打印机这种场景下,手机是 Client 端,打印机是 Server 端。

在上一篇的最后,我们从 BluetoothSocket 得到了一个OutputStream。这里我们做一层包装,得到一个OutputStreamWriter 对象:

OutputStreamWriter writer = new OutputStreamWriter(outputStream, "GBK");

这样做主要是为了后面可以直接输出字符串,不然只能输出 int 或 byte 数据;

2. 常用打印指令

手机通过蓝牙向打印机发送的都是纯字节流,那么打印机如何知道该打印的是一个文本,还是条形码,还是图片数据呢?

初始化打印机 :

在每次打印开始之前要调用该指令对打印机进行初始化。向打印机发送这条指令对应的代码就是:

protected void initPrinter() throws IOException { writer.write(0x1B); writer.write(0x40); writer.flush(); }

打印文本:

没有对应指令,直接输出

protected void printText(String text) throws IOException { writer.write(text); writer.flush(); }

设置文本对齐方式:

对应的发送指令的代码:

/* 设置文本对齐方式 * @param align 打印位置 0:居左(默认) 1:居中 2:居右 * @throws IOException */ protected void setAlignPosition(int align) throws IOException { writer.write(0x1B); writer.write(0x61); writer.write(align); writer.flush(); }

与初始化指令不同的是,这条指令带有一个参数n。

换行和制表符:

直接输出对应的字符:

protected void nextLine() throws IOException { writer.write("\n"); writer.flush(); } protected void printTab(int length) throws IOException { for (int i = 0; i < length; i++) { writer.write("\t"); } writer.flush(); }

这两个指令在打印订单详情的时候使用最多。尤其是制表符,可以让每一列的文字对齐。

设置行间距:

n表示行间距为n个像素点,最大值256

protected void setLineGap(int gap) throws IOException { writer.write(0x1B); writer.write(0x33); writer.write(gap); writer.flush(); }

这个指令在后面打印图片的时候会用到。

3. 打印图片

很多小票上面都会附上一个二维码,用户扫描之后,可以获得更多的信息。因为热敏打印机只能打印黑白两色,所以首先把图片转成纯黑白的,再调用图片打印指令进行打印。

3.1 打印图片指令

这个指令的参数很多,一个一个来说:

m:取值十进制 0、1、32、33。设置打印精度,0、1对应每行8个点,32、33对应每行24个点,对应最高的打印精度(其实这里也没太搞清楚取值0、1或者取值32、33的区别,只要记住取值33,对应每行24个点,后面还有用) n1, n2 : 表示图片的宽度,为什么有两个?其实只是分成了高位和低位两部分,因为每部分只有8bit,最大表示256。所以 n1 = 图片宽度 % 256,n2 = 图片宽度 / 256。假设图片宽300,那么n1=1,n2=44 d1 d2 ... dk 这部分就是转换成字节流的图像数据了

3.2 图片分辨率调整

如果分辨率过大,超过了打印机可打印的最大宽度,那么超出的部分将无法打印。我试验的这台最大宽度是 384 个像素点,超过这个宽度的数据无法被打印出来。所以在开始打印之前,我们需要调整图片的分辨率。代码如下:

/** * 对图片进行压缩(去除透明度) * * @param bitmapOrg */ public static Bitmap compressPic(Bitmap bitmap) { // 获取这个图片的宽和高 int width = bitmap.getWidth(); int height = bitmap.getHeight(); // 指定调整后的宽度和高度 int newWidth = 240; int newHeight = 240; Bitmap targetBmp = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); Canvas targetCanvas = new Canvas(targetBmp); targetCanvas.drawColor(0xffffffff); targetCanvas.drawBitmap(bitmap, new Rect(0, 0, width, height), new Rect(0, 0, newWidth, newHeight), null); return targetBmp; }

3.2 图片黑白化处理

因为能够打印的图像只有黑白两色,所以需要先做黑白化的处理。这一部分其实又细分为彩色图片->灰度图片,灰度图片->黑白图片两步。直接上代码:

/** * 灰度图片黑白化,黑色是1,白色是0 * * @param x 横坐标 * @param y 纵坐标 * @param bit 位图 * @return */ public static byte px2Byte(int x, int y, Bitmap bit) { if (x < bit.getWidth() && y < bit.getHeight()) { byte b; int pixel = bit.getPixel(x, y); int red = (pixel & 0x00ff0000) >> 16; // 取高两位 int green = (pixel & 0x0000ff00) >> 8; // 取中两位 int blue = pixel & 0x000000ff; // 取低两位 int gray = RGB2Gray(red, green, blue); if (gray < 128) { b = 1; } else { b = 0; } return b; } return 0; } /** * 图片灰度的转化 */ private static int RGB2Gray(int r, int g, int b) { int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b); //灰度转化公式 return gray; }

其中的灰度化转换公式是一个广为流传的公式,具体原理不明。我们直接看灰度转化为黑白的函数 px2Byte(int x, int y, Bitmap bit)。对于一个 Bitmap 中的任意一个坐标点,取出其 RGB 三色信息后做灰度化处理,然后对于灰度小于128的,用黑色表示,灰度大于128的,用白色表示。

3.3 逐行打印图片

其实打印图片和打印文本是一样的,也是一行一行的打印。直接上代码吧,注释已经尽量详细了。

/************************************************************************* * 假设一个240*240的图片,分辨率设为24, 共分10行打印 * 每一行,是一个 240*24 的点阵, 每一列有24个点,存储在3个byte里面。 * 每个byte存储8个像素点信息。因为只有黑白两色,所以对应为1的位是黑色,对应为0的位是白色 **************************************************************************/ /** * 把一张Bitmap图片转化为打印机可以打印的字节流 * * @param bmp * @return */ public static byte[] draw2PxPoint(Bitmap bmp) { //用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法 //整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte, //但是实际上要存储11行数据,每一行需要 24 * 240 / 8 =720byte 的空间。再加上一些指令存储的开销, //所以多申请 1000byte 的空间是稳妥的,不然运行时会抛出数组访问越界的异常。 int size = bmp.getWidth() * bmp.getHeight() / 8 + 1000; byte[] data = new byte[size]; int k = 0; //设置行距为0的指令 data[k++] = 0x1B; data[k++] = 0x33; data[k++] = 0x00; // 逐行打印 for (int j = 0; j < bmp.getHeight() / 24f; j++) { //打印图片的指令 data[k++] = 0x1B; data[k++] = 0x2A; data[k++] = 33; data[k++] = (byte) (bmp.getWidth() % 256); //nL data[k++] = (byte) (bmp.getWidth() / 256); //nH //对于每一行,逐列打印 for (int i = 0; i < bmp.getWidth(); i++) { //每一列24个像素点,分为3个字节存储 for (int m = 0; m < 3; m++) { //每个字节表示8个像素点,0表示白色,1表示黑色 for (int n = 0; n < 8; n++) { byte b = px2Byte(i, j * 24 + m * 8 + n, bmp); data[k] += data[k] + b; } k++; } } data[k++] = 10;//换行 } return data; }

4. 总结

用两篇介绍了一个比较冷门的应用,纯粹是因为自己花了很多时间去搞懂原理,所以希望记录下来。尤其是图片打印部分,废了好多纸啊哈哈哈,一个字节操作错误,打印出来就是一堆乱码。感觉和 java 的 .class 文件很像,每一个指令占用多少位,每一位表示什么都是严格规定好的,不能超出也不能缺少。

最后希望能帮到需要的人吧,感觉网上这部分资料还是比较少的。也希望大家多多支持脚本之家。

时间: 2024-10-30 04:19:56

Android 蓝牙连接 ESC/POS 热敏打印机打印实例(ESC/POS指令篇)的相关文章

Android 蓝牙连接 ESC/POS 热敏打印机打印实例(蓝牙连接篇)

公司的一个手机端的 CRM 项目最近要增加小票打印的功能,就是我们点外卖的时候经常会见到的那种小票.这里主要涉及到两大块的知识: 蓝牙连接及数据传输 ESC/POS 打印指令 蓝牙连接不用说了,太常见了,这篇主要介绍这部分的内容.但ESC/POS 打印指令是个什么鬼?简单说,我们常见的热敏小票打印机都支持这样一种指令,只要按照指令的格式向打印机发送指令,哪怕是不同型号品牌的打印机也会执行相同的动作.比如打印一行文本,换行,加粗等都有对应的指令,这部分内容放在下一篇介绍. 本篇主要基于官方文档,相

Android手机通过蓝牙连接佳博打印机的实例代码_Android

所使用的打印机为佳博打印机,支持蓝牙.wifi.usb我所使用的是通过蓝牙来连接. 在网上找到一个佳博官方针对安卓开发的App源码,但是各种的跳转,没有看太懂,所以又去问度娘,找到了一个不错的文章 Android对于蓝牙开发从2.0版本的sdk才开始支持,而且模拟器不支持,测试至少需要两部手机,所以制约了很多技术人员的开发. 1. 首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 // 管理蓝牙设备的权限 <uses-permissionandroid:name="

Android手机通过蓝牙连接佳博打印机的实例代码

所使用的打印机为佳博打印机,支持蓝牙.wifi.usb我所使用的是通过蓝牙来连接. 在网上找到一个佳博官方针对安卓开发的App源码,但是各种的跳转,没有看太懂,所以又去问度娘,找到了一个不错的文章 Android对于蓝牙开发从2.0版本的sdk才开始支持,而且模拟器不支持,测试至少需要两部手机,所以制约了很多技术人员的开发. 1. 首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 // 管理蓝牙设备的权限 <uses-permissionandroid:name="

android-下面这段代码Android蓝牙连接的时候的弹出框,怎么没起到作用!

问题描述 下面这段代码Android蓝牙连接的时候的弹出框,怎么没起到作用! Dialog dlg = new AlertDialog.Builder(MainActivity.this).setTitle("蓝牙连接......").create(); dlg.show(); new Thread(){ public void run() { try { MainActivity.this.mySock.connect(); Toast.makeText( MainActivity.

框架-android蓝牙连接已经实现了,设备之间互相发送信息怎么做?

问题描述 android蓝牙连接已经实现了,设备之间互相发送信息怎么做? 我想知道两个设备间数据的传输需要做哪些事情,能说明下大致的框架吗? 解决方案 可以参考google的sample:https://github.com/googlesamples/android-BluetoothChat,已经实现了通过蓝牙进行聊天的功能了. 解决方案二: http://download.csdn.net/detail/hyb745250618/8922395可以语音传输,也可以文字聊天

Android 蓝牙连接 部分手机不成功问题(小米)

问题描述 Android 蓝牙连接 部分手机不成功问题(小米) BluetoothAdapter.getDefaultAdapter().cancelDiscovery(); BluetoothDevice mDevice = mBluetoothAdapter .getRemoteDevice(strAddress); mSocket = mDevice .createRfcommSocketToServiceRecord(MY_UUID); mSocket.connect(); mInput

Android进阶——安卓调用ESC/POS打印机打印实例

前言 前一段时间由于工作需要,要研究一下安卓程序调用打印机打印小票,并且要求不能使用蓝牙调用,研究了一下,可以利用socket连接,来实现打印功能.写了个Demo,分享一下. 工具:一台打印机(芯烨XP-80XX),一台安卓测试机 开发环境:Android Studio 1.5 需求:点击按钮,实现打印小票功能,小票上除必要文字外,还要有二维码. 封装了一个Pos打印工具类: package com.example.haoguibao.myapplication; import java.io.

Android——蓝牙连接

    今天要做一个蓝牙4.0的通信,先做个小test,之后再看看具体的api: @Override public void onClick(View v) { switch (v.getId()) { case R.id.scan: // bluetoothService.startBluetooth(); //1,初始化蓝牙适配器 final BluetoothManager bluetoothManager=(BluetoothManager) getSystemService(Conte

lw 600p-android蓝牙连接LW-600P蓝牙打印机可以连接成功但是打印没反应

问题描述 android蓝牙连接LW-600P蓝牙打印机可以连接成功但是打印没反应 我在网上找了好多源码进行蓝牙打印的开发,发现好多都是写着可以用,但是我在使用的时候就发现,连接我手上的蓝牙打印机(LW-600P),连接部分可以成功,打印部分不管是只打印字,还是图片,打印机都没反应,也不报错,做了一星期了,一点进展都没,有人说不同打印机发送的指令不一样,我给客服打过,客服竟然什么都不提供,郁闷了,希望有做过跟我一样需求的大神指教一下,不胜感激. 解决方案 您好,我们是生产开发蓝牙打印机的,如有需