likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 20

最新项目需要调用标签打印机打印标签,使用的是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. 设置电脑打印机的高度和宽度、间距

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

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

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

小哥看了Java调用TSC标签打印机的代码,直说666 设置标签间距为2mm

小哥看了Java调用TSC标签打印机的代码,直说666 这个是打印的标签,中间是有间距的

4. 电脑连接打印机

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

5. 将demo整合到项目中

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

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

<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调用TSC标签打印机的代码,直说666

放到这里。


    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文件产生的,暂时没有解决方法,有没有大佬知道怎么解决这个问题?

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

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

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

// 检查打印机驱动是否存在,避免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)) {
    // 打印失败,打印机脱机!
}
    

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

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. 打印

调用打印代码:

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

成功打印标签。感觉观看

附加代码

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

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);
    }
}

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

<dependency>
    <groupId>com.profesorfalken</groupId>
    <artifactId>WMI4Java</artifactId>
    <version>1.4.2</version>
</dependency>

打印数据封装


@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;
    }
}

字符串根据间距循环切割

/**
 * 字符串根据间距循环切割
 *
 * @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;
}