经过一段时间的研究,目前得出了Android连接蓝牙打印机打印二维码的方式有2种:
第一种:采用ESC/POS二维码指令打印的方式打印,代码如下
/**
* 设置二维码大小
*/
public static final byte[] setCodeSize = new byte[8];
/**
* 设置纠错正等级
*/
public static final byte[] setCodeLevel = new byte[] {0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x45, 0x30};
/**
* 加载二维码
*/
public static byte[] setCode = new byte[8];
/**
* 打印二维码
*/
public static final byte[] printCode = new byte[]{0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30};
/**
* 设置加载二维码指令
* @param code
*/
public static void doSetCode(String code) {
setCode[0] = 0x1D;
setCode[1] = 0x28;
setCode[2] = 0x6B;
setCode[3] = (byte) (code.length()+3);
setCode[4] = 0x00;
setCode[5] = 0x31;
setCode[6] = 0x50;
setCode[7] = 0x30;
}
/**
* 设置二维码大小
* @param size
*/
public static void doSetQrSize(int size) {
setCodeSize[0] = 0x1D;
setCodeSize[1] = 0x28;
setCodeSize[2] = 0x6B;
setCodeSize[3] = 0x03;
setCodeSize[4] = 0x00;
setCodeSize[5] = 0x31;
setCodeSize[6] = 0x43;
setCodeSize[7] = (byte)size;
}
/**
* byte转Byte
*
* @param srcArray
* @param cpyArray
*/
public static void CopyArray(byte[] srcArray, byte[] cpyArray) {
for (int index = 0; index < cpyArray.length; index++) {
cpyArray[index] = srcArray[index];
}
}
打印二维码的方法
/**
* 打印二维码
* @param qrCode 二维码url
* @param size 二维码大小
* @throws IOException
*/
public ArrayList<byte[]> printQr(String qrCode,int size) throws IOException{
ArrayList<byte[]> arr = new ArrayList<>();
PrinterUtils.doSetQrSize(size);
arr.add(PrinterUtils.setCodeSize);
arr.add(PrinterUtils.setCodeLevel);
PrinterUtils.doSetCode(qrCode);
arr.add(PrinterUtils.setCode);
byte[] data = (qrCode +"\r\n\r\n").getBytes(CHARSET);
byte[] tempList = new byte[data.length];
PrinterUtils.CopyArray(data, tempList);
arr.add(tempList);
arr.add(PrinterUtils.printCode);
return arr;
}
这种指令方式打印二维码非常快,几乎和打印纯文字的速度差不多,但是在内部测试的时候发现只能适用于佳博GPrinter的打印机,对于其他的机器,打印出来的只是一段URL文字,似乎对该二维码指令不支持,但是经过联系厂家拿到机器指令后,发现指令都是一模一样的,厂家的人也说是支持ESC/POS指令的,但是我实在不清楚问题出在哪里,至今也没有解决,希望知道的大神能够指教一下。
第二种:采用位图的方式打印二维码,代码如下
先将二维码连接生成图片
/**
* 生成二维码Bitmap
*
*/
public static Bitmap createQRImage(String url,int width,int height){
try{
if (url == null || "".equals(url) || url.length() < 1){//判断URL合法性
return null;
}
Hashtable<EncodeHintType, String> hints = new Hashtable<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(url, BarcodeFormat.QR_CODE, width, height, hints);
int[] pixels = new int[width * height];
//下面这里按照二维码的算法,逐个生成二维码的图片,
//两个for循环是图片横列扫描的结果
for (int y = 0; y < height; y++){
for (int x = 0; x < width; x++){
if (bitMatrix.get(x, y)){
pixels[y * width + x] = 0xff000000;
}
else{
pixels[y * width + x] = 0xffffffff;
}
}
}
//生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}catch (WriterException e){
e.printStackTrace();
return null;
}
}
再将生成的图片转换为字节数组
public byte[] draw2PxPoint(Bitmap bit) {
byte[] data = new byte[16290];
int k = 0;
for (int j = 0; j < 15; j++) {
data[k++] = 0x1B;
data[k++] = 0x2A;
data[k++] = 33; // m=33时,选择24点双密度打印,分辨率达到200DPI。
data[k++] = 0x68;
data[k++] = 0x01;
for (int i = 0; i < 360; i++) {
for (int m = 0; m < 3; m++) {
for (int n = 0; n < 8; n++) {
byte b = px2Byte(i, j * 24 + m * 8 + n, bit);
data[k] += data[k] + b;
}
k++;
}
}
data[k++] = 10;
}
return data;
}
public int changePointPx1(byte[] arry){
int v = 0;
for (int j = 0; j <arry.length; j++) {
if( arry[j] == 1) {
v = v | 1 << j;
}
}
return v;
}
public byte px2Byte(int x, int y, Bitmap bit) {
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;
}
生成字节数组后,将这些字节传给打印机就可以了。上面对应的二维码图片时360*360大小的,如果想改变大小得自己去算,比较麻烦,也可以采用下面的方式
/**
* 获取图片数据流
*
* @param image 图片
* @return 数据流
*/
public ArrayList<byte[]> getImageByte(Bitmap image) {
//TYPE_58 纸宽58mm,width = 380;
//TYPE_80 = 80 纸宽80mm,width = 500;
int maxWidth = 380;
Bitmap scalingImage = scalingBitmap(image, maxWidth);
if (scalingImage == null)
return null;
ArrayList<byte[]> data = PrinterUtils.decodeBitmapToDataList(image, heightParting);
image.recycle();
return data;
}
/**
* 解码图片
*
* @param image 图片
* @param parting 高度分割值
* @return 数据流
*/
@SuppressWarnings("unused")
public static ArrayList<byte[]> decodeBitmapToDataList(Bitmap image, int parting) {
if (parting <= 0 || parting > 255)
parting = 255;
if (image == null)
return null;
final int width = image.getWidth();
final int height = image.getHeight();
if (width <= 0 || height <= 0)
return null;
if (width > 2040) {
// 8位9针,宽度限制2040像素(但一般纸张都没法打印那么宽,但并不影响打印)
final float scale = 2040 / (float) width;
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
Bitmap resizeImage;
try {
resizeImage = Bitmap.createBitmap(image, 0, 0, width, height, matrix, true);
} catch (OutOfMemoryError e) {
return null;
}
ArrayList<byte[]> data = decodeBitmapToDataList(resizeImage, parting);
resizeImage.recycle();
return data;
}
// 宽命令
String widthHexString = Integer.toHexString(width % 8 == 0 ? width / 8 : (width / 8 + 1));
if (widthHexString.length() > 2) {
// 超过2040像素才会到达这里
return null;
} else if (widthHexString.length() == 1) {
widthHexString = "0" + widthHexString;
}
widthHexString += "00";
// 每行字节数(除以8,不足补0)
String zeroStr = "";
int zeroCount = width % 8;
if (zeroCount > 0) {
for (int i = 0; i < (8 - zeroCount); i++) {
zeroStr += "0";
}
}
ArrayList<String> commandList = new ArrayList<>();
// 高度每parting像素进行一次分割
int time = height % parting == 0 ? height / parting : (height / parting + 1);// 循环打印次数
for (int t = 0; t < time; t++) {
int partHeight = t == time - 1 ? height % parting : parting;// 分段高度
// 高命令
String heightHexString = Integer.toHexString(partHeight);
if (heightHexString.length() > 2) {
// 超过255像素才会到达这里
return null;
} else if (heightHexString.length() == 1) {
heightHexString = "0" + heightHexString;
}
heightHexString += "00";
// 宽高指令
String commandHexString = "1D763000";
commandList.add(commandHexString + widthHexString + heightHexString);
ArrayList<String> list = new ArrayList<>(); //binaryString list
StringBuilder sb = new StringBuilder();
// 像素二值化,非黑即白
for (int i = 0; i < partHeight; i++) {
sb.delete(0, sb.length());
for (int j = 0; j < width; j++) {
// 实际在图片中的高度
int startHeight = t * parting + i;
//得到当前像素的值
int color = image.getPixel(j, startHeight);
int red, green, blue;
if (image.hasAlpha()) {
//得到alpha通道的值
int alpha = Color.alpha(color);
//得到图像的像素RGB的值
red = Color.red(color);
green = Color.green(color);
blue = Color.blue(color);
final float offset = alpha / 255.0f;
// 根据透明度将白色与原色叠加
red = 0xFF + (int) Math.ceil((red - 0xFF) * offset);
green = 0xFF + (int) Math.ceil((green - 0xFF) * offset);
blue = 0xFF + (int) Math.ceil((blue - 0xFF) * offset);
} else {
//得到图像的像素RGB的值
red = Color.red(color);
green = Color.green(color);
blue = Color.blue(color);
}
// 接近白色改为白色。其余黑色
if (red > 160 && green > 160 && blue > 160)
sb.append("0");
else
sb.append("1");
}
// 每一行结束时,补充剩余的0
if (zeroCount > 0) {
sb.append(zeroStr);
}
list.add(sb.toString());
}
// binaryStr每8位调用一次转换方法,再拼合
ArrayList<String> bmpHexList = new ArrayList<>();
for (String binaryStr : list) {
sb.delete(0, sb.length());
for (int i = 0; i < binaryStr.length(); i += 8) {
String str = binaryStr.substring(i, i + 8);
// 2进制转成16进制
String hexString = binaryStrToHexString(str);
sb.append(hexString);
}
bmpHexList.add(sb.toString());
}
// 数据指令
commandList.addAll(bmpHexList);
}
ArrayList<byte[]> data = new ArrayList<>();
for (String hexStr : commandList) {
data.add(hexStringToBytes(hexStr));
}
return data;
}
/**
* 2进制转成16进制
*
* @param binaryStr 2进制串
* @return 16进制串
*/
@SuppressWarnings("unused")
public static String binaryStrToHexString(String binaryStr) {
String hex = "";
String f4 = binaryStr.substring(0, 4);
String b4 = binaryStr.substring(4, 8);
for (int i = 0; i < binaryArray.length; i++) {
if (f4.equals(binaryArray[i]))
hex += hexStr.substring(i, i + 1);
}
for (int i = 0; i < binaryArray.length; i++) {
if (b4.equals(binaryArray[i]))
hex += hexStr.substring(i, i + 1);
}
return hex;
}
/**
* 16进制指令list转换为byte[]指令
*
* @param list 指令集
* @return byte[]指令
*/
@SuppressWarnings("unused")
public static byte[] hexListToByte(List<String> list) {
ArrayList<byte[]> commandList = new ArrayList<>();
for (String hexStr : list) {
commandList.add(hexStringToBytes(hexStr));
}
int len = 0;
for (byte[] srcArray : commandList) {
len += srcArray.length;
}
byte[] destArray = new byte[len];
int destLen = 0;
for (byte[] srcArray : commandList) {
System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length);
destLen += srcArray.length;
}
return destArray;
}
/**
* 16进制串转byte数组
*
* @param hexString 16进制串
* @return byte数组
*/
@SuppressWarnings("unused")
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* 16进制char 转 byte
*
* @param c char
* @return byte
*/
private static byte charToByte(char c) {
return (byte) hexStr.indexOf(c);
}
采用该位图方式基本上都能在热敏打印机上打印出二维码,但缺点就是慢,要是能接受,可以采用这种方式。
就先写到这,如果以后有新的进展再来补充。