小哥看了Java调用TSC标签打印机的代码,直说666

最新项目需要调用标签打印机打印标签,使用的是T-4502E系列的打印机,使用的是从官网下载的Java版本的demo代码,记录下整合过程。

TSC资料下载:www.chinatsc.cn/zh-CN/downl...

项目是运行在windows系统下的,linux系统暂不考虑。

首先了解下什么是TSPL标签:

TSPL是一套通用的标签打印,常用指令 SIZE 设置标签的大小

SIZE 60mm,40mm

标签宽度60毫米,高度40毫米

GAP 2mm

标签间距2毫米

这里不做过多的介绍,感兴趣可以去百度搜索。

1. 第一步,安装驱动

驱动可以从官网下载自行安装,这里就不细说了。

2. 确定标签的高度和宽度、间距

这一步尤为重要,作者在这一步踩了不少坑,最直接的问题就是标签内容上下偏移,打的越多偏移的越多,所以一定要确认好高度、宽度和间距,作者的标签宽度是70mm,高度为50mm,间距为2mm。

3. 设置电脑打印机的高度和宽度、间距

电脑打开打印机和扫描仪设置,选择安装的打印机驱动,选择管理,打印首选项

这里设置宽度和高度,还有方向。

设置标签间距为2mm

这个是打印的标签,中间是有间距的

4. 电脑连接打印机

打印机使用的是usb连接,打印机插上电源后会自动校准标签位置,并且打印机指示灯为绿色。

5. 将demo整合到项目中

这是demo lib目录下需要的依赖,jna是调用dll程序的jar包,这个包可以在maven中引入:

java 复制代码
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>4.5.1</version>
</dependency>

TSCLIB.dll则是调用打印机的动态链接库。

jna可以通过maven引入,那么TSCLIB.dll文件如果引入那?这个文件在生产环境下是放到和jar包同目录下的位置,一般是在配置文件中配置该文件的全路径,然后在加载动态链接库时加载该文件,这里有一个问题就是在部署项目时需要将该文件复制到同级目录下,并且还需要配置动态链接库文件路径,如果这个路径改变了就需要修改配置文件,使用起来比较麻烦。经过作者对百度一阵搜索,使用了一个比较简单些的方案,就是将TSCLIB.dll文件放入到resources目录下,然后项目启动时将该文件复制到jar包同级目录下。

放到这里。

java 复制代码
    private TscLibDll tscLibDll;

    @PostConstruct
    public void loadTscLibDll() {
        try {
            String targetFolderInJar = "BOOT-INF/classes/dll";
            String destDir = JarUtils.getJarPath(Demo.class);
            String dllPath = destDir + File.separator + "TSCLIB.dll";
            File file = new File(dllPath);
            if (!file.exists()) {
                JarUtils.extractFilesFromJar(targetFolderInJar, destDir);
            }
            // 这里开发时使用,需要将上面代码注释掉
//           tscLibDll = Native.loadLibrary("dll文件路径", TscLibDll.class);
            tscLibDll = Native.loadLibrary(dllPath, TscLibDll.class);
            log.info("打印机TscLibDll加载完成");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

这段就是在项目启动时将TSCLIB.dll文件复制到jar包同级目录下的代码。

6. 整合业务逻辑,打印标签

我的需求是标签不需要打印二维码,只需要打印文字即可,所以这里只演示打印文字。

先说下作者遇到的问题,就是tscLibDll.openport('')当打印机名称配置错误时,在win10系统下会出现错误提示弹窗,然后你不点击就阻塞线程运行,经过debug后发现是调用dll文件产生的,暂时没有解决方法,有没有大佬知道怎么解决这个问题?

这里咱们可以通过提前检查打印机名称来避免这个问题。

arduino 复制代码
// 检查打印机驱动是否存在,避免tscLibDll.openport()出现错误提示弹窗
String labelPrinterName = "Bar Code Printer T-4503E";
PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
boolean exists = false;
for (PrintService printService : printServices) {
    String name = printService.getName();
    if (StringUtils.equals(name, labelPrinterName)) {
        exists = true;
        break;
    }
}
if (!exists) {
    // 获取打印机驱动失败,请检查打印机驱动名称或者是否安装驱动
}

// 判断是否在线
String rawOutput = WMI4Java
        .get()
        .properties(Arrays.asList("Name", "WorkOffline"))
        .filters(Arrays.asList("$_.WorkOffline -eq 0"))
        .getRawWMIObjectOutput(WMIClass.WIN32_PRINTER);
List<String> printers = Arrays.stream(rawOutput.split("(\r?\n)"))
        .filter(line -> line.startsWith("Name"))
        .map(line -> line.replaceFirst(".* : ", ""))
        .sorted()
        .collect(Collectors.toList());
if (!printers.contains(labelPrinterName)) {
    // 打印失败,打印机脱机!
}
    

这里判断了系统中是否存在打印机以及打印机是否在线,代码中打印机名称是写死的,实际开发中打印机名称应该是动态配置的,包括宽度和高度、间隔这些参数也一样。这样就避免了打印机名称配置错误导致线程卡死的问题。

ini 复制代码
try {
    String labelPrinterName = "Bar Code Printer T-4503E";
    // 打印的数据
    List<PrintData> datas = new ArrayList<>();
    
    // 解决中文乱码问题
    System.setProperty("jna.encoding", "GBK");

    int openport = tscLibDll.openport(labelPrinterName);
    if (openport == 1) {
        tscLibDll.sendcommand("SIZE 70 mm, 50 mm");
        tscLibDll.sendcommand("GAP 2 mm");
        // 方向
        tscLibDll.sendcommand("DIRECTION 1");
        tscLibDll.clearbuffer();

        // X轴初始位置
        int initX = 30;
        // 换行数据的X轴初始位置
        int initX2 = 220;

        datas.add(new PrintData("姓名: 王非非", initX));
        datas.add(new PrintData("年龄: 23", initX));
        
        // 这是需要换行的数据
        String addr = "山东省**市百花小区百花小区2111号一单元一楼";
        // 26是一行占用的字节,注意,中文和字母、数字的字节大小是不一样的,为了让每一行尽量对齐
        // 使用字节大小切割字符串
        List<String> addrSubList = StringUtils.strSpacSub(addr, 26);
        for (int i = 0; i < addrSubList.size(); i++) {
            if (i == 0) {
                datas.add(new PrintData("家庭地址: " + addrSubList.get(i), initX));
            } else {
                // 换行的数据
                datas.add(new PrintData(addrSubList.get(i), initX2));
            }
        }
        
        // Y轴初始位置
        int initY = 50;
        // Y轴行间距
        int space = 60;
        // 字体高度
        int fontheight = 48;

        for (int i = 0; i < datas.size(); i++) {
            PrintData data = datas.get(i);
            tscLibDll.windowsfont(data.getX(), initY + i * space, fontheight, 0, 0, 0, "Arial", data.getData());
        }
        tscLibDll.printlabel("1", "1");

    } else {
        // 打印机连接失败
    }
} catch (Exception e) {
    e.printStackTrace();
    // 打印失败
} finally {
    // 关闭打印机
    tscLibDll.closeport();
}

StringUtils.strSpacSub方法是将字符串按照字节大小切割的方法,该方法不会使每一行都绝对整齐,会误差1到2个字节位置。

7. 打印

调用打印代码:

成功打印标签。感觉观看

附加代码

TSCLIB.dll文件复制到jar包同级目录下,代码是百度搜索的,膜拜大佬。

java 复制代码
public static void extractFilesFromJar(String targetFolderInJar, String destDir) {
    String jarFile = System.getProperty("java.class.path");
    System.out.println("当前jarFile包路径:" + jarFile);
    if (StringUtils.isBlank(jarFile) || !jarFile.endsWith(".jar")) {
        log.error("定位jar失败");
        return;
    }
    try (JarFile jar = new JarFile(jarFile)) {
        Enumeration enumeration = jar.entries();
        while (enumeration.hasMoreElements()) {
            JarEntry jarEntry = (JarEntry) enumeration.nextElement();
            String name = jarEntry.getName();
            // 根据路径定位目标文件
            if (!name.startsWith( targetFolderInJar )) {
                continue;
            }
            // 优化路径名称,这里也可以直接用jarEntry.getName()来看下效果
            // "/HCNetSDKCom/AudioRender.dll"
            String fixedName = jarEntry.getName().replace(targetFolderInJar, "");
            // "E:/git/xxx/dll/windows_test1/HCNetSDKCom/AudioRender.dll"
            // String destPath = destDir + File.separator + fixedName;
            String destPath = destDir + fixedName;
            System.out.println( "destPath = " + destPath );
            File file = new File( destPath );
            if( jarEntry.isDirectory() ) {
                // 是目录
                file.mkdirs();
            }else {
                // 是文件
                // try (InputStream is = jar.getInputStream(jarEntry);
                try (InputStream is = jar.getInputStream( jar.getEntry(name) );
                     FileOutputStream fos = new FileOutputStream(file)) {
                    FileCopyUtils.copy( is,fos );
                }
            }
        }
    } catch (IOException e) {
        log.error("提取文件异常", e);
    }
}

检测打印机是否在线的依赖

xml 复制代码
<dependency>
    <groupId>com.profesorfalken</groupId>
    <artifactId>WMI4Java</artifactId>
    <version>1.4.2</version>
</dependency>

打印数据封装

arduino 复制代码
@Data
public class PrintData {

    /**
     * 打印数据
     */
    private String data;

    /**
     * x轴位置
     */
    private int x;

    public PrintData() {
    }

    public PrintData(String data, int x) {
        this.data = data;
        this.x = x;
    }
}

字符串根据间距循环切割

ini 复制代码
/**
 * 字符串根据间距循环切割
 *
 * @param str    切割的字符串
 * @param length 间距
 * @return
 * @throws UnsupportedEncodingException
 */
public static List<String> strSpacSub(String str, int length) throws UnsupportedEncodingException {
    List<String> subStrList = new ArrayList<>();
    List<Integer> indexs = new ArrayList<>();
    indexs.add(0);
    int strBytesLength = str.getBytes("GBK").length;
    if (strBytesLength <= length) {
        subStrList.add(str);
        return subStrList;
    }

    int currLength = 0;
    int lastLength = length;
    int strIndex = 0;
    char[] chars = str.toCharArray();
    for (char char_ : chars) {
        String str_ = String.valueOf(char_);
        int charLength = str_.getBytes("GBK").length;
        currLength += charLength;
        strIndex++;
        if (currLength > lastLength) {
            indexs.add(strIndex);
            lastLength += length;
        }
    }

    for (int i = 0; i < indexs.size(); i++) {
        if (i < indexs.size() - 1) {
            int start = indexs.get(i);
            int end = indexs.get(i + 1);

            String substring = str.substring(start, end);
            subStrList.add(substring);
        } else {
            int start = indexs.get(i);
            int end = str.length();

            String substring = str.substring(start, end);
            subStrList.add(substring);
        }
    }
    return subStrList;
}
相关推荐
计算机学姐3 分钟前
基于python+django+vue的影视推荐系统
开发语言·vue.js·后端·python·mysql·django·intellij-idea
小筱在线8 分钟前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
luoluoal12 分钟前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea18 分钟前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web
小游鱼KF21 分钟前
Spring学习前置知识
java·学习·spring
扎克begod25 分钟前
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
java·开发语言·python
青灯文案126 分钟前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
我就是程序猿36 分钟前
tomcat的配置
java·tomcat
阳光阿盖尔42 分钟前
EasyExcel的基本使用——Java导入Excel数据
java·开发语言·excel
二十雨辰43 分钟前
[苍穹外卖]-12Apache POI入门与实战
java·spring boot·mybatis