Android列印二維碼對熱敏印表機的適配
阿新 • • 發佈:2019-02-13
經過一段時間的研究,目前得出了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);
}
採用該點陣圖方式基本上都能在熱敏印表機上打印出二維碼,但缺點就是慢,要是能接受,可以採用這種方式。
就先寫到這,如果以後有新的進展再來補充。