feat: 增加打印机接口

This commit is contained in:
2024-12-15 15:20:49 +08:00
parent b55931a438
commit 38865909af
9 changed files with 316 additions and 3 deletions

View File

@@ -0,0 +1,171 @@
package cn.lihongjie.coal.common;
import cn.hutool.core.util.HexUtil;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;
import javax.imageio.ImageIO;
@Slf4j
@UtilityClass
public class TscPrinterUtil {
public static String toHex(String text) {
return HexUtil.encodeHexStr(toBytes(text));
}
private static @NotNull String processLineSep(String text) {
String placeholder = "PLACEHOLDER_FOR_CRLF";
text = text.replace("\r\n", placeholder);
// Step 2: Replace all occurrences of \n with \r\n
text = text.replace("\n", "\r\n");
// Step 3: Replace the placeholder back to \r\n
text = text.replace(placeholder, "\r\n");
return text;
}
public static byte[] toBytes(String text) {
text = processLineSep(text);
// convert ascii to byte[]
return text.getBytes();
}
@SneakyThrows
public static byte[] base64ImageToTscBmpImage(String base64Image) {
var image =
ImageIO.read(new ByteArrayInputStream((Base64.getDecoder().decode(base64Image))));
int width = image.getWidth();
int height = image.getHeight();
// width必须是8的倍数
int newWidth = width % 8 == 0 ? width : width + 8 - width % 8;
BitSet bs = new BitSet();
// 循环每一个像素
for (int i = 0; i < height; i++) {
for (int j = 0; j < newWidth; j++) {
int idx = i * newWidth + j;
if (j < width) {
bs.set(idx, image.getRGB(j, i) != Color.black.getRGB());
} else {
bs.set(idx, true);
}
}
}
TscPrinterUtil.printBS(bs, newWidth);
return bs.toByteArray();
}
public static void printBS(BitSet bs, int width) {
StringBuilder sb = new StringBuilder();
sb.append("\n");
for (int i = 0; i < bs.length(); i++) {
if (i % width == 0) {
sb.append("\n");
}
sb.append(bs.get(i) ? "1" : "0").append(" ");
}
log.debug(sb.toString());
}
private static byte[] processBitMap(String s) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int lasted = s.lastIndexOf(",");
String prefix = s.substring(0, lasted + 1);
buffer.put(prefix.getBytes(StandardCharsets.UTF_8));
// 剩下的内容为base64
buffer.put(base64ImageToTscBmpImage(s.substring(lasted + 1)));
buffer.put("\r\n".getBytes(StandardCharsets.UTF_8));
return bytebufferToBytes(buffer);
}
public static byte[] processCommand(String command) {
String[] lines = StringUtils.split(command, "\n");
ByteBuffer buffer = ByteBuffer.allocate(1024);
for (String line : lines) {
int i = line.indexOf(" ");
if (i > 0) {
String cmd = line.substring(0, i);
buffer.put(Command.of(cmd).process(line));
} else {
buffer.put(line.getBytes(StandardCharsets.UTF_8));
}
}
return bytebufferToBytes(buffer);
}
public static byte[] bytebufferToBytes(ByteBuffer buffer) {
byte[] bytes = new byte[buffer.position()];
buffer.flip();
buffer.get(bytes);
return bytes;
}
public enum Command {
BITMAP(TscPrinterUtil::processBitMap),
DEFAULT(t -> (t + "\r\n").getBytes(StandardCharsets.UTF_8)),
;
private final Function<String, byte[]> processor;
Command(Function<String, byte[]> processor) {
this.processor = processor;
}
public static Command of(String cmd) {
return Arrays.stream(values())
.filter(c -> c.name().equalsIgnoreCase(cmd))
.findFirst()
.orElse(DEFAULT);
}
public byte[] process(String text) {
return processor.apply(text);
}
}
}

View File

@@ -35,11 +35,12 @@ public class FileController {
@RequestParam("name") String name,
@RequestParam("dir") String dir,
@RequestParam(value = "url", required = false) String url,
@RequestParam(value = "base64", required = false) String base64,
@RequestParam(value = "sortKey", required = false) String sortKey,
@RequestParam(value = "code", required = false) String code,
@RequestParam(value = "remarks", required = false) String remarks
) {
return this.service.upload(file, name, dir, sortKey, code, remarks, url);
return this.service.upload(file, name, dir, sortKey, code, remarks, url, base64);
}
@PostMapping("/update")

View File

@@ -38,6 +38,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Base64;
import java.util.UUID;
@Service
@@ -110,7 +111,7 @@ public class FileService extends BaseService<FileEntity, FileRepository> {
String sortKey,
String code,
String remarks,
String url) {
String url, String base64) {
long size = 0;
InputStream fis = null;
@@ -123,7 +124,12 @@ public class FileService extends BaseService<FileEntity, FileRepository> {
HttpUtil.download(url, out, true);
fis = new ByteArrayInputStream(out.toByteArray());
size = out.size();
} else {
} else if (StringUtils.isNotBlank(base64)) {
fis = new ByteArrayInputStream(Base64.getDecoder().decode(base64.replace("data:image/png;base64,", "")));
size = fis.available();
}else {
throw new BizException("文件为空");
}

View File

@@ -0,0 +1,22 @@
package cn.lihongjie.coal.tscprinter.controller;
import cn.hutool.core.codec.Base64;
import cn.lihongjie.coal.common.TscPrinterUtil;
import cn.lihongjie.coal.tscprinter.dto.ProcessCommandRequest;
import cn.lihongjie.coal.tscprinter.dto.ProcessCommandResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/tscprinter")
public class TscPrinterController {
@PostMapping("/processCommand")
public ProcessCommandResponse processCommand(ProcessCommandRequest request) {
return new ProcessCommandResponse(
Base64.encode(TscPrinterUtil.processCommand(request.getCommand())));
}
}

View File

@@ -0,0 +1,17 @@
package cn.lihongjie.coal.tscprinter.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProcessCommandRequest {
private String command;
}

View File

@@ -0,0 +1,16 @@
package cn.lihongjie.coal.tscprinter.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProcessCommandResponse {
private String base64;
}

View File

@@ -0,0 +1,80 @@
package cn.lihongjie.coal.common;
import static cn.lihongjie.coal.common.TscPrinterUtil.processCommand;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.resource.ResourceUtil;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.*;
import java.util.stream.Stream;
class TscPrinterUtilTest {
public static Stream<Arguments> hexData() {
return Stream.of(
Arguments.of("S", "53"),
Arguments.of("T", "54"),
Arguments.of("C", "43"),
Arguments.of("A", "41"),
Arguments.of("SET TEAR ON", "53 45 54 20 54 45 41 52 20 4f 4e".replace(" ", "")),
Arguments.of("SET TEAR ON\nSET TEAR ON", "53 45 54 20 54 45 41 52 20 4f 4e 0d 0a 53 45 54 20 54 45 41 52 20 4f 4e".replace(" ", "")
)
);
}
@ParameterizedTest
@MethodSource("hexData")
void name(String text, String expectHex) {
assertThat(TscPrinterUtil.toHex(text)).isEqualToIgnoringCase(expectHex);
}
@Test
void test1() {
byte[] bytes = TscPrinterUtil.base64ImageToTscBmpImage(Base64.encode(ResourceUtil.getStream("1.bmp")));
assertThat(bytes).asHexString().isEqualTo("FD");
}
@Test
void test2() {
byte[] bytes = TscPrinterUtil.base64ImageToTscBmpImage(Base64.encode(ResourceUtil.getStream("2x2_1.bmp")));
assertThat(bytes).asHexString().isEqualTo("FEFD");
}
@Test
void test3() {
assertThat(processCommand("SET TEAR ON")).asHexString().isEqualToIgnoringCase("53 45 54 20 54 45 41 52 20 4f 4e 0D 0A".replace(" ", ""));
assertThat(processCommand("SET TEAR ON\nSET TEAR ON")).asHexString().isEqualToIgnoringCase("5345542054454152204F4E0D0A5345542054454152204F4E0D0A".replace(" ", ""));
}
}

BIN
src/test/resources/1.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B