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

Posted LZ涸泽而渔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

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

 

 

以上是关于android局域网连接TSC桌面打印机打印的主要内容,如果未能解决你的问题,请参考以下文章

远程桌面连接不上怎么办有啥办法

Java调用TSC打印机进行打印

Java如何调用TSC打印机进行打印详解

在android中通过wifi访问连接在本地网络上的打印机

tsc打印机模板怎么编辑环形文字

TSC如何设置打印标签的距离?急???