最新项目需要调用标签打印机打印标签,使用的是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;
}