android开发中接触热敏打印机的开发比较少,然而我换了2份工作老是碰到,乘着有点时间我在这边做下归类和总结,希望对大家开发有所帮助。
一.热敏打印机分类介绍
本人接触过的热敏打印机大体分成2个类别,驱动型打印机和无驱动型热敏打印机。
驱动型打印机
其实就是打印机产商提供打印的接口,这种开发就比较容易,快速,基本上产商都把你所需要做的事情都做了差不多了,不需要进行多于的学习和编程。
无驱动型打印机
这个就是本编文章所要处理的事情,没有产商的协助,从零开搞,为了实现多类型(wifi,蓝牙,串口),多产商(a,b,c型打印机)打印需求所以我在这边进行设计和封装了打印接口,以便适应市场上的大多数热敏打印机进行开发工作。
二.热敏打印机接口介绍
这边的设计采用了工厂设计模型,设计通用型wifi连接打印机的这种打印类型的接口(目前本人就接触了蓝牙,wifi,驱动打印机),由于蓝牙因为手头没有机器无法进行验证接口是否可行,所以不提供这方面的相关代码。
本人初步设计的接口如下:
void InitPrint();//初始化打印
boolean preparePrint();//准备打印操作,
void endPrint();//打印结束释放内存等.
void releasePrint();//释放打印资源
void printText(String content, PrintFormat format);//打印文本内容
void printBitmap(Bitmap bitmap, PrintFormat format, int widthPix, int heightPix);//打印图片
void pinrtBrandCode(String content, PrintFormat format, int widthPix, int heightPix);//打印条形码
void printQrCode(String content, PrintFormat format, Bitmap logo, int widthPix, int heightPix);//打印二维码
void feedPaper(int rownum, int num);//走纸,第一个是走纸n行,第二个是走纸的向前走纸的 数目,
void cutPaper();//裁剪纸张,部分打印机没有这个功能
void printEnter();// 打印换行
boolean startPrint(boolean cutpaper);//开始打印
列举了一些常用的方法。
以下是我的项目地址,有需要的可以去看看,或者加入我的邪恶组织,一起完成统一接口的这个长远目标(组织的目的,开发个通用型打印接口)。
工程内注释很详细,如果有不明的可以通过站内信息联系到我。
https://github.com/GrassQing/CommonPrintProvider
二.打印原理介绍和实现
如果感觉我上面那个项目提供的接口或者写的东西实在太难看了又正在开发热敏打印机打印功能的高手们,不妨看看热敏打印机打印命令和实现方式。
打印指令传输
通过socket进行连接
private boolean SendMsgCommand(final String ip, final int port)
throws UnknownHostException, IOException {
try {
socket = new Socket(ip, port);
outputStream = socket.getOutputStream();
//这边是输入指令根据自己的需求进行输入
for (int i = 0; i < eCmd.getbList().size(); i++) {
outputStream.write(eCmd.getbList().get(i));
}
outputStream.flush();
outputStream.close();
socket.close();
return true;
} catch (UnknownHostException e) {
e.printStackTrace();
if (socket != null)
socket.close();
return false;
} catch (IOException e) {
e.printStackTrace();
if (socket != null)
socket.close();
return false;
}
}
初始化打印机
部分打印机必须初始化
初始化指令初始化new byte[] {ESC, '@'};,byte ESC = 0x1B;
/**
* 初始化打印机
*/
public void esc_init() {
byteList.add(Command.ESC_INIT);
}
文本打印
文本打印指令:new byte[] {FS, '&'};进入汉字模式,byte FS = 0x1C;
接着输入打印内容的byte[] 类型数据,最后结束汉字模式
new byte[] {FS, '.'};
/**
* 中文模式 打印GBK模式的文本
*
* @param text
*/
public void esc_text(String text, String encoding) {
byteList.add(Command.ESC_CN_MODE);
try {
byteList.add(text.getBytes(encoding));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
throw new RuntimeException("port " + text + " is invalid, ", e);
}
byteList.add(Command.ESC_CN_MODE_OFF);
}
字体加粗
粗 :new byte[]{ESC, 'G', 0x01},byte ESC = 0x1B;
不加粗:new byte[]{ESC, 'G', 0x00};
对于打印的字体加粗时必须设置在文本打印之前,打印完必须复原。
例如:
settext(粗),esc_text("打印"),settext(不加粗),继续打印。其他格式设置类似。
字体大小
普通字体:
new byte[]{GS, '!', 0x00},byte GS = 0x1D;
双倍高:
new byte[]{GS, '!', 0x01},byte GS = 0x1D;
双倍宽:
new byte[]{GS, '!', 0x10},byte GS = 0x1D;
双倍高宽:
new byte[]{GS, '!', 0x11},byte GS = 0x1D;
图片打印
这一部分当初还是通过简书方面查找到资料,自己稍微做了下改动,使其更加的稳定了些,可能对部分打印机存在兼容问题。
public static byte[] draw2PxPoint(Bitmap bmp) {
//用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法
//整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte,5:5455,3,5447,4,5427
//但是实际上要存储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;//换行
}
// long a=System.currentTimeMillis();
byte[] data1 = new byte[k];
System.arraycopy(data, 0, data1, 0, k);
// long b=System.currentTimeMillis();
// System.out.println("结束字节:"+k+"---"+data.length+"耗时:"+(b-a));
return data1;
}
二维码,条形码打印
二维码打印部分其实本身也有相应的指令进行打印,本菜鸟感觉有点难,有兴趣的人可以进行摸索下,这边是通过zxing生成相应的二维码,条形码转化成图片进行打印的。上面的接口工厂中,提供了中间无logo,有logo的二维码打印接口,以及各种类型的条形码打印。
其他辅助指令
换行
new byte[]{Command.CR, Command.LF} , byte LF = 0x0A; byte CR = 0x0D;
或者直接调用打印文本的接口打印个"/n"即可。
打印走纸
new byte[]{Command.LF},
new byte[]{GS, 'V', 0x00};
结束
希望对大家有所帮助,详细代码在我的github上面
https://github.com/GrassQing/CommonPrintProvider
里面编写的指令基本上都是通用的。
Tip
1.有人问能不能打印可变的图片,据我所知是不行的。
2.我问了公司的打印机的开发的人,他们是这样打印图片的,图片大小什么的都是没有多大的限制,基本宽度合理,长度没限制。基本每次只能打印图片的一部分,例如图片300k,打印机每次打印3k的大小,就是打印的时候提前把图片横向切割成100份依次打印,大概意思是这样的。我想其他厂家的打印机也应该差不多。
3.另外指令也是重打印机部门那边扣出来的,基本不会有多大的问题,还是通用的。
网友评论
InitPrint(ESC_SYTLE.MODE_PRINT.WIFI_PRINT, "192.168.0.1","12345");
初始化这个IP地址是哪里来的呢? 默认的给手机设置的吗?蓝牙热敏打印机只看到物理地址;谢谢