android局域网连接TSC桌面打印机打印

最近收到了需求,安卓设备连接打印机打印,因为使用场景选择了局域网连接打印机,并最终选择了TSC的PE210打印机,下面陈述实现方式

一:将所有的图文排版成一张图片,然后通过sendPicture方法进行打印

二:将生成的图片转为BMP格式图片,下载到打印机后,通过指令打印这张图片

第一个方法,将图片文件下载到打印机内存,存储位置可以通过diagtool工具看到,查看源码看到,传送的file必须存储在/Download目录下,而传送指令PUTBMP时指定的文件名 必须与打印机存储好的文件名一样(.bmp存储到打印机后 会变成.BMP)

总结:以上两种方式,能够打印出效果,但是有两个缺点:

          1,打印速度很慢,大多数时间花在了图片数据传输与开始打印的过程,尽管图片被我压缩到了只有40K ;

          2,这种打印方式,虽然排版异常的简单,但是会出现打印不定数张后,机器死机,绿灯常亮,但是机器不再接受任何指令,咨询多个售后技术后,他们提供的解决方案包括每次打印后删除这张图片 或者延时打印,并无效果

因此,建议大家直接使用图文混排的方式,避免踩坑,排雷

三:正式实现

     1:打开打印机工具 Diagnostic

    

将通讯端口切换为ETHERENT,可以自动搜索到局域网内的打印机,更改IP地址可以设定这个打印机的ip

打印机选择感应器矫正,可以矫正为当前选择的纸张类型

矫正完成后,设定打印一张的宽,高以及一些其他参数即可

可以在档案管理里面,将制作好的BMP格式图片下载到打印机里

如果想要打印中文或者其他字体,可以通过工具制作字体文件,关键点都标了出来,制作完成后,传送字型文件 会将刚刚选择的字体文件制作,并传送到打印机存储空间中

 

准备工作完成,接下来开始代码实现

将打印行为放在服务中,并且绑定这个服务

<service android:name="com.jmj.lib_comm.server.PrinterService"/>
val serviceConnection: ServiceConnection = object : ServiceConnection 
    override fun onServiceDisconnected(name: ComponentName?) {
    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        Constant.printerService = (service as PrinterService.MyBinder).getService()
    }

}

private fun bindPrinterService() {
    bindService(
        Intent(this, PrinterService::class.java),
        serviceConnection,
        Context.BIND_AUTO_CREATE
    )
}

override fun onDestroy() {
    unbindService(serviceConnection)
    super.onDestroy()
}

每次打印启动的时候,连接端口,打印完成后,关闭端口,指的是一次动作,无论这次动作打印多少张,最好不要在没有关闭端口的时候,再次去申请连接端口,否则很大概率会死机

fun connectPrint(): Boolean {
    val openResult = tscEthernetDll.openport(Constant.printerIp, Constant.printerPort)
    return openResult == "1"
}
fun closePort() {
    tscEthernetDll.closeport()
}

连接完成后,开始打印

fun printSewnInLabel(waterBarCode: WaterBarCode) {
    try {
        tscEthernetDll.clearbuffer()
        //打印标线
        printText(
            0,
            0,
            0,
            "------------------------------------------------------------------------------------------------------------"
        )
        //订单号
        tscEthernetDll.printerfont(520, 220, "3", 90, 1, 1, waterBarCode.orderSn)
        //店铺名
        printText(420, 220, 90, "店铺:${waterBarCode.shopName}")
        //客户
        printText(320, 220, 90, "客户名:${waterBarCode.clientName}")
        //详情
        printText(
            220,
            220,
            90,
            "${waterBarCode.roomType} ${waterBarCode.orderProductType}:${if (waterBarCode.ignoreFolio) waterBarCode.getPartNumber() else waterBarCode.getDealFolio()}"
        )
        //宽高或规格
        tscEthernetDll.printerfont(
            120,
            220,
            if (waterBarCode.getSizeOrSpecification().length > 16) "2" else "3",
            90,
            1,
            1,
            waterBarCode.getSizeOrSpecification()
        )

        //打印logo
        tscEthernetDll.sendcommand("PUTBMP 440,640,\"${Constant.sewninLabelLogo}\"\n");
        //打印二维码
        printQrCode("${waterBarCode.barCode}")
        //打印barcode
        tscEthernetDll.printerfont(140, 720, "3", 90, 1, 1, "${waterBarCode.barCode}")
        tscEthernetDll.printlabel(1, 1)
    } catch (e: java.lang.Exception) {
        KLog.e(e.message)
    }
}

private fun printText(x: Int, y: Int, rotation: Int, content: String) {
    val byte = content.toByteArray(Charset.forName("GB2312"))
    tscEthernetDll.sendcommand("TEXT ${x},${y},\"FONT001\",${rotation},1,1,\"")
    tscEthernetDll.sendcommand(byte)
    tscEthernetDll.sendcommand("\"\n")
}

private fun printQrCode(barCode: String) {
    tscEthernetDll.sendcommand("QRCODE 180,720,H,6,A,0,M2,\"${Host.BARCODE_URL}$barCode\"\n")
}

以上 演示了打印 图片,中文,英文(打印中文的方法也可打印英文,但是偶尔出现打印问题,如M 打印不全),二维码

如此就完成了一张打印,多张打印循环即可,不用加延时,可以一次性把指令都传送给打印机 它会以队列的形式打印出来,但是注意Socket端口的连接与断开

PS:跑一边他的源码会发现,原理还是Socket连接,但是不能通过发送心跳包的形式去检测socket是否连接与断开,emm  一言难尽

这边附上android Bmp单色图制作方法

  fun createPrintPicture(onCreateFileFinish: () -> Unit) {
        Thread {
            val bitmap =
                Bitmap.createBitmap(
                    ivLogo!!.width,
                    ivLogo!!.height,
                    Bitmap.Config.RGB_565
                )
            val canvas = Canvas(bitmap)
            ivLogo!!.draw(canvas)

            //bitmap旋转缩放
            val matrix = Matrix()
            matrix.setRotate(90f)
            val rotateBitmap =
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)

//            val baos = ByteArrayOutputStream()
//            //质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
//            rotateBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
//            //把压缩后的数据baos存放到ByteArrayInputStream中
//            val isBm = ByteArrayInputStream(baos.toByteArray())
//            val compressBitmap =
//                BitmapFactory.decodeStream(isBm, null, null) //把ByteArrayInputStream数据生成图片

            val changeToMonochromeBitmap = BmpUtil.changeToMonochromeBitmap(rotateBitmap)

            //bitmap生成为本地临时文件
            try {
                val printFile = File("${Constant.printPictureFilePath}${Constant.printFileName}")
                if (!printFile.exists()) {
                    printFile.createNewFile()
                }

                val fos =
                    FileOutputStream(printFile)
                fos.write(changeToMonochromeBitmap)
                fos.close()

                bitmap.recycle()
                rotateBitmap.recycle()
//                compressBitmap.recycle()
                onCreateFileFinish()
            } catch (e: Exception) {
                KLog.e("print", e.message)
            }
        }.start()
    }

 

/**
 * 保存为单色位图,即使黑白位图
 */
public class BmpUtil {


    /**
     * 保存为单色bmp格式的完整字节数
     **/
    public static byte[] changeToMonochromeBitmap(Bitmap bmp) {
        int[] binarys = gray2Binary(bmp);
        byte[] data = compressMonoBitmap(bmp, binarys);
        byte[] header = addBMPImageHeader(data.length + 62);
        byte[] infos = addBMPImageInfosHeader(bmp.getWidth(), bmp.getHeight());
        byte[] buffer = new byte[62 + data.length];
        System.arraycopy(header, 0, buffer, 0, header.length);
        System.arraycopy(infos, 0, buffer, 14, infos.length);
        System.arraycopy(data, 0, buffer, 62, data.length);
        return buffer;
    }

    /**
     * 保存为单色bmp数据,不包含头,正向头
     **/
    public static byte[] changeSingleBytes(Bitmap bmp) {
        int[] binarys = gray2Binary(bmp);
        byte[] data = compressMonoBitmap_ps(bmp, binarys);
        return data;
    }

    /**
     * 将彩色图转换为灰阶图,并二值化处理
     *
     * @param bmp 位图
     * @return 返回灰阶图二值化后的颜色int[]
     */
    private static int[] gray2Binary(Bitmap bmp) {
        int width = bmp.getWidth();   // 获取位图的宽
        int height = bmp.getHeight(); // 获取位图的高
        int[] pixels = new int[width * height];  // 通过位图的大小创建像素点数组,
        bmp.getPixels(pixels, 0, width, 0, 0, width, height);   // int 0 代表0XFFFFFFFF,即是1.0完全不透明,0.0f完全透明。黑色完全透明。
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int grey = pixels[width * i + j];  // 第几行,第几个
                // 分离三原色
                int alpha = ((grey & 0xFF000000) >> 24); // 透明度
                int red = ((grey & 0x00FF0000) >> 16);   // 红色
                int green = ((grey & 0x0000FF00) >> 8);  // 绿色
                int blue = (grey & 0x000000FF);          // 蓝色
                if (alpha == 0) {  // 透明度为0,则说明没有颜色,那变更为白色
                    pixels[width * i + j] = 0;           // 白色是0
                    continue;
                }
                grey = (int) (red * 0.3 + green * 0.59 + blue * 0.11);  // 转化为灰度图  灰度值:255为白色,0为黑色
                // TODO: 2016/12/27 灰度值为200,可调整该参数
                grey = grey <= 127 ? 0 : 255;  // 灰度小于200就转化为黑色,不然就为白色。200为可调整参数。// 二值化
                pixels[width * i + j] = grey;
            }
        }
        return pixels;
    }


    /**
     * 压缩为完整单色bmp数组,并反向
     *
     * @param bmp     压缩需要用到位图的宽度,高度。
     * @param binarys 二值化数据
     * @return
     */
    private static byte[] compressMonoBitmap(Bitmap bmp, int[] binarys) {
        int width = bmp.getWidth();   // 获取位图的宽
        int height = bmp.getHeight(); // 获取位图的高
        // 行补位的公式为 widthBytes = (width*biBitCount+31)/32*4
        // 需要转化为单色,所以biBitCount=1;
        // 确定一行几个字节
        int widthBytes = (width + 31) / 32 * 4;

        byte[] newss = new byte[widthBytes * height];
        for (int i = height; i > 0; i--) {
            for (int j = 0; j < width; j++) {
                if (binarys[width * (i - 1) + j] > 0)
                    newss[(height - i) * widthBytes + j / 8] |= (byte) (1 << (7 - j % 8));
            }
        }
        return newss;
        // 方法二:
        /*  // 确定需要一行要补足的位数
        int wei = widthBytes * 8 - width;
        int[] newbs = new int[widthBytes * 8 * height];  // 总字节数
        // 白色是要插入0,并完成翻转;  bmp保存格式是从下到上,从左到右
        for (int i = 0; i < height; i++) {
            System.arraycopy(binarys, i * width, newbs, (height - i - 1) * (width + wei), width);
        }
        // 压缩
        byte[] data = new byte[newbs.length / 8];
        for (int i = 0; i < newbs.length; i++) {
            if (newbs[i] > 0) {
                data[i / 8] |= (1 << (7 - i % 8));
            }
        }
        return data;*/
    }

    /**
     * 压缩为完整单色bmp数组,并正向
     *
     * @param bmp     压缩需要用到位图的宽度,高度。
     * @param binarys 二值化数据
     * @return
     */
    private static byte[] compressMonoBitmap_ps(Bitmap bmp, int[] binarys) {
        int width = bmp.getWidth();   // 获取位图的宽
        int height = bmp.getHeight(); // 获取位图的高
        // 行补位的公式为 widthBytes = (width*biBitCount+31)/32*4
        // 需要转化为单色,所以biBitCount=1;
        // 确定一行几个字节
        int widthBytes = (width + 31) / 32 * 4;

        byte[] newss = new byte[widthBytes * height];
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                if (binarys[width * i + j] > 0)  // 第几行第几个字节
                    newss[i * widthBytes + j / 8] |= (byte) (1 << (7 - j % 8));  // 新压缩的第几行第几个
            }
        }
        return newss;
        // 方法二:
        /*  // 确定需要一行要补足的位数
        int wei = widthBytes * 8 - width;
        int[] newbs = new int[widthBytes * 8 * height];  // 总字节数
        // 白色是要插入0,并完成翻转;  bmp保存格式是从下到上,从左到右
        for (int i = 0; i < height; i++) {
            System.arraycopy(binarys, i * width, newbs, (height - i - 1) * (width + wei), width);
        }
        // 压缩
        byte[] data = new byte[newbs.length / 8];
        for (int i = 0; i < newbs.length; i++) {
            if (newbs[i] > 0) {
                data[i / 8] |= (1 << (7 - i % 8));
            }
        }
        return data;*/
    }

    /**
     * BMP文件头
     *
     * @param size 整个文件的大小,包括文件头,信息头,和位图内容
     * @return
     */
    private static byte[] addBMPImageHeader(int size) {
        byte[] buffer = new byte[14];
        //magic number 'BM'
        buffer[0] = 0x42;
        buffer[1] = 0x4D;
        //记录大小
        buffer[2] = (byte) (size);
        buffer[3] = (byte) (size >> 8);
        buffer[4] = (byte) (size >> 16);
        buffer[5] = (byte) (size >> 24);
        buffer[6] = 0x00;
        buffer[7] = 0x00;
        buffer[8] = 0x00;
        buffer[9] = 0x00;
        buffer[10] = 0x3E;
        buffer[11] = 0x00;
        buffer[12] = 0x00;
        buffer[13] = 0x00;
        return buffer;
    }


    /**
     * BMP文件信息头
     *
     * @param w 宽,单位像素
     * @param h 高,单位像素
     * @return
     */
    private static byte[] addBMPImageInfosHeader(int w, int h) {
        byte[] buffer = new byte[48];
        //这个是固定的 BMP 信息头要40个字节
        buffer[0] = 0x28;
        buffer[1] = 0x00;
        buffer[2] = 0x00;
        buffer[3] = 0x00;
        //宽度 地位放在序号前的位置 高位放在序号后的位置
        buffer[4] = (byte) (w);
        buffer[5] = (byte) (w >> 8);
        buffer[6] = (byte) (w >> 16);
        buffer[7] = (byte) (w >> 24);
        //长度 同上
        buffer[8] = (byte) (h);
        buffer[9] = (byte) (h >> 8);
        buffer[10] = (byte) (h >> 16);
        buffer[11] = (byte) (h >> 24);
        //总是被设置为1
        buffer[12] = 0x01;
        buffer[13] = 0x00;
        //比特数 像素 32位保存一个比特 这个不同的方式(ARGB 32位 RGB24位不同的!!!!)
        //黑白图置1
        buffer[14] = 0x01;
        buffer[15] = 0x00;
        //0-不压缩 1-8bit位图
        //2-4bit位图 3-16/32位图
        //4 jpeg 5 png
        //设置为不压缩
        buffer[16] = 0x00;
        buffer[17] = 0x00;
        buffer[18] = 0x00;
        buffer[19] = 0x00;
        //说明图像大小
        buffer[20] = 0x00;
        buffer[21] = 0x00;
        buffer[22] = 0x00;
        buffer[23] = 0x00;
        //水平分辨率
        buffer[24] = 0x00;
        buffer[25] = 0x00;
        buffer[26] = 0x00;
        buffer[27] = 0x00;
        //垂直分辨率
        buffer[28] = 0x00;
        buffer[29] = 0x00;
        buffer[30] = 0x00;
        buffer[31] = 0x00;
        //0 使用所有的调色板项
        buffer[32] = 0x00;
        buffer[33] = 0x00;
        buffer[34] = 0x00;
        buffer[35] = 0x00;
        //开颜色索引
        buffer[36] = 0x00;
        buffer[37] = 0x00;
        buffer[38] = 0x00;
        buffer[39] = 0x00;
        // 加上颜色表
        // 00 00 00 00 ff ff ff 00 那么 0代表黑,1代表白
        // 若为
        // ff ff ff 00 00 00 00 00 那么 1代表黑,0代表白  --- 选择这个
        buffer[40] = (byte) 0xFF;
        buffer[41] = (byte) 0xFF;
        buffer[42] = (byte) 0xFF;
        buffer[43] = (byte) 0x00;

        buffer[44] = (byte) 0x00;
        buffer[45] = (byte) 0x00;
        buffer[46] = (byte) 0x00;
        buffer[47] = (byte) 0xFF;
        return buffer;
    }
}

这是TSC官网的资源链接:https://www.chinatsc.cn/SC/support/support_download/TTP-244M_Pro_Series

其中比较重要的 Diagtool 工具,sdk ,示例文档,指令集

懒得去下的 ,我这边上传了个整合版资源:https://download.csdn.net/download/LZ511321/15466176

踩了很多坑,卡在图片方式打印这块很久最终决定更换方式,希望有所帮助

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
微信小程序的蓝牙连接打印机功能可以实现与TSC规范的打印机进行连接打印操作。下面是一个简单的示例代码。 首先,在小程序的配置文件中声明蓝牙权限,确保可以使用蓝牙功能: "permissions": { "bluetooth": true, "openBluetoothAdapter": true, "writeCharacteristicValue": true } 接下来,我们需要编写相关的代码来连接和操作打印机。首先需要初始化蓝牙适配器: wx.openBluetoothAdapter({ success: function(res) { console.log("蓝牙适配器初始化成功"); // 在成功回调中,开始搜索附近的蓝牙设备 wx.startBluetoothDevicesDiscovery({ allowDuplicatesKey: false, success: function(res) { console.log("开始搜索蓝牙设备"); // 在成功回调中,获取附近的蓝牙设备列表 wx.getBluetoothDevices({ success: function(res) { console.log("获取蓝牙设备列表成功"); // 在成功回调中,筛选并连接TSC规范的打印机 var devices = res.devices; for (var i = 0; i < devices.length; i++) { if (devices[i].name == "TSC") { var deviceId = devices[i].deviceId; // 连接选定的打印机 wx.createBLEConnection({ deviceId: deviceId, success: function(res) { console.log("连接打印机成功"); // 在成功回调中,进行打印操作 wx.writeBLECharacteristicValue({ deviceId: deviceId, serviceId: "0000FFF0-0000-1000-8000-00805F9B34FB", characteristicId: "0000FFF1-0000-1000-8000-00805F9B34FB", value: "打印内容" }); } }); } } } }); } }); } }); 以上是一个简单的微信小程序连接打印TSC规范打印机的示例代码。需要注意的是,具体的打印内容和打印机的服务和特征值等需要根据实际情况进行修改和调整。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值