身份证读取(完成)、社保卡读取(待调试)
This commit is contained in:
parent
47c9d8dc5a
commit
c0b1e80525
|
@ -0,0 +1,89 @@
|
|||
package com.dpkj.modules.autoReplyPrint.base;
|
||||
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
|
||||
import com.dpkj.modules.autoReplyPrint.utils.ImageUtils;
|
||||
import com.sun.jna.Pointer;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
||||
/**
|
||||
* 图片打印基础实现
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-17 14:37:23
|
||||
*/
|
||||
public abstract class BaseImagePrint {
|
||||
|
||||
/**
|
||||
* 必须要实现这个,获取句柄
|
||||
* @param devName 设备名称/串口名称
|
||||
* @return 窗口句柄
|
||||
*/
|
||||
public abstract Pointer getHandle(String devName);
|
||||
|
||||
|
||||
public void printFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
|
||||
Pointer handle = getHandle(devName);
|
||||
try {
|
||||
// 开始打印图片
|
||||
boolean printTag = AutoReplyPrint.INSTANCE.CP_Pos_PrintRasterImageFromFile(handle, dstw, dsth, pszFile, binaryzation_method, compression_method);
|
||||
if( !printTag ){
|
||||
throw new RRException("打印图片失败");
|
||||
}
|
||||
|
||||
// 切纸
|
||||
AutoReplyPrint.INSTANCE.CP_BlackMark_SetBlackMarkPaperCutPosition(handle, 0);
|
||||
boolean cutPaper = AutoReplyPrint.INSTANCE.CP_Pos_FeedAndHalfCutPaper(handle);
|
||||
if ( !cutPaper){
|
||||
throw new RRException("图片裁剪失败,请自行裁剪");
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RRException(e);
|
||||
}finally {
|
||||
if ( handle != null) AutoReplyPrint.INSTANCE.CP_Port_Close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void printFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
|
||||
// 获取图片
|
||||
byte[] data = ImageUtils.imageToBytes(pszFile);
|
||||
this.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
|
||||
public void printFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method) {
|
||||
Pointer handle = getHandle(devName);
|
||||
try {
|
||||
// 开始打印图片
|
||||
boolean printTag = AutoReplyPrint.INSTANCE.CP_Pos_PrintRasterImageFromData(handle, dstw, dsth, data, data.length, binaryzation_method, compression_method);
|
||||
if( !printTag ){
|
||||
throw new RRException("打印图片失败");
|
||||
}
|
||||
|
||||
// 切纸
|
||||
AutoReplyPrint.INSTANCE.CP_BlackMark_SetBlackMarkPaperCutPosition(handle, 0);
|
||||
boolean cutPaper = AutoReplyPrint.INSTANCE.CP_Pos_FeedAndHalfCutPaper(handle);
|
||||
if ( !cutPaper){
|
||||
throw new RRException("图片裁剪失败,请自行裁剪");
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RRException(e);
|
||||
}finally {
|
||||
if ( handle != null) AutoReplyPrint.INSTANCE.CP_Port_Close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void printFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method){
|
||||
byte[] data = ImageUtils.getByteToMultipartFile(file);
|
||||
this.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.dpkj.modules.autoReplyPrint.controller;
|
||||
|
||||
import com.dpkj.common.vo.Result;
|
||||
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 图片打印控制层
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-17 13:57:11
|
||||
*/
|
||||
@RequestMapping("/autoReplyPrint")
|
||||
public class ImagePrintController {
|
||||
|
||||
@Resource(name = "USBImagePrint")
|
||||
private ImagePrintService usbImagePrintService;
|
||||
|
||||
/**
|
||||
* 图片打印,通过路径
|
||||
*/
|
||||
@GetMapping("/usb/imagePrint/path")
|
||||
Result<Void> usbImagePrintFromPath(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String usbName,
|
||||
@RequestParam(defaultValue = "500") Integer width,
|
||||
@RequestParam(defaultValue = "500") Integer height,
|
||||
@RequestParam String path, Integer binaryMethod, Integer compressionMethod){
|
||||
this.usbImagePrintService.imagePrintFromPath(usbName, width, height, path, binaryMethod, compressionMethod);
|
||||
return Result.ok("图片打印成功");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 图片打印,通过byte[]
|
||||
*/
|
||||
@GetMapping("/usb/imagePrint/pathData")
|
||||
Result<Void> usbImagePrintFromData(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String usbName,
|
||||
@RequestParam(defaultValue = "500") Integer width,
|
||||
@RequestParam(defaultValue = "500") Integer height,
|
||||
@RequestParam String path, Integer binaryMethod, Integer compressionMethod){
|
||||
this.usbImagePrintService.imagePrintFromPathData(usbName, width, height, path, binaryMethod, compressionMethod);
|
||||
return Result.ok("图片打印成功");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 图片打印,通过multipartFile文件
|
||||
*/
|
||||
@GetMapping("/usb/imagePrint/multipartFile")
|
||||
Result<Void> usbImagePrintFromData(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String usbName,
|
||||
@RequestParam(defaultValue = "500") Integer width,
|
||||
@RequestParam(defaultValue = "500") Integer height,
|
||||
@RequestParam MultipartFile file, Integer binaryMethod, Integer compressionMethod){
|
||||
this.usbImagePrintService.imagePrintFromMultipartFile(usbName, width, height, file, binaryMethod, compressionMethod);
|
||||
return Result.ok("图片打印成功");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 图片打印,通过byte[]
|
||||
*/
|
||||
@GetMapping("/usb/imagePrint/data")
|
||||
Result<Void> usbImagePrintFromData(@RequestParam(defaultValue = "VID:0x0FE6,PID:0x811E") String usbName,
|
||||
@RequestParam(defaultValue = "500") Integer width,
|
||||
@RequestParam(defaultValue = "500") Integer height,
|
||||
@RequestParam byte[] data, Integer binaryMethod, Integer compressionMethod){
|
||||
this.usbImagePrintService.imagePrintFromData(usbName, width, height, data, binaryMethod, compressionMethod);
|
||||
return Result.ok("图片打印成功");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.dpkj.modules.autoReplyPrint.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dpkj.common.vo.Result;
|
||||
import com.dpkj.modules.autoReplyPrint.service.impl.TemplateService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 打印模板控制层
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-17 15:25:03
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/autoReplyPrint")
|
||||
public class TemplateController {
|
||||
|
||||
@Resource
|
||||
private TemplateService templateService;
|
||||
|
||||
|
||||
@GetMapping("/template/{templateName}")
|
||||
public Result<String> testTemplate(@RequestParam String jsonData,
|
||||
@RequestParam Integer width,
|
||||
@RequestParam Integer height,
|
||||
@PathVariable String templateName,
|
||||
@RequestParam(defaultValue = "E:\\images") String saveDir) {
|
||||
this.templateService.generateReceiptImage(JSONObject.parseObject(jsonData), templateName, width, height, saveDir);
|
||||
return Result.ok("模板生成成功");
|
||||
}
|
||||
|
||||
@GetMapping("/template")
|
||||
public Result<String> testTemplate(@RequestParam String jsonData,
|
||||
@RequestParam Integer width,
|
||||
@RequestParam Integer height, @RequestParam(defaultValue = "E:\\images") String saveDir) {
|
||||
String html = "<!DOCTYPE html>\n" +
|
||||
"<html>\n" +
|
||||
"<head><title>Test</title></head>\n" +
|
||||
"<body>\n" +
|
||||
"<h1>你好,我是一个好的模板</h1>\n" +
|
||||
"<h2><span th:text='${name}'> </span></h2>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
this.templateService.generateReceiptImage(JSONObject.parseObject(jsonData), html, width, height, saveDir);
|
||||
return Result.ok("模板生成成功");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.dpkj.modules.autoReplyPrint.service;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 图片打印服务接口
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-17 11:38:36
|
||||
*/
|
||||
public interface ImagePrintService {
|
||||
|
||||
/**
|
||||
* 图片打印
|
||||
* @param devName 设备名称/usb/tcp/com
|
||||
* @param dstw 要打印的宽度
|
||||
* @param dsth 要打印的高度
|
||||
* @param pszFile 图片的路径
|
||||
* @param binaryzation_method 图片二值化算法。0 表示抖动算法,1 表示阀值算法,2 表示误差扩散法。具体效果请测试查看。
|
||||
* @param compression_method 最终打印数据的压缩方式,各值定义如下:0 不压缩,1 一级压缩,2 二级压缩。
|
||||
*/
|
||||
void imagePrintFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method);
|
||||
|
||||
/**
|
||||
* 图片打印,内部直接读取转换为byte进行打印
|
||||
* @param devName 设备名称/usb/tcp/com
|
||||
* @param dstw 要打印的宽度
|
||||
* @param dsth 要打印的高度
|
||||
* @param pszFile 图片的路径
|
||||
* @param binaryzation_method 图片二值化算法。0 表示抖动算法,1 表示阀值算法,2 表示误差扩散法。具体效果请测试查看。
|
||||
* @param compression_method 最终打印数据的压缩方式,各值定义如下:0 不压缩,1 一级压缩,2 二级压缩。
|
||||
*/
|
||||
void imagePrintFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method);
|
||||
|
||||
/**
|
||||
* 直接传输数据进行打印
|
||||
* @param devName 设备名称/usb/tcp/com
|
||||
* @param dstw 要打印的宽度
|
||||
* @param dsth 要打印的高度
|
||||
* @param data 图片数据
|
||||
* @param binaryzation_method 图片二值化算法。0 表示抖动算法,1 表示阀值算法,2 表示误差扩散法。具体效果请测试查看。
|
||||
* @param compression_method 最终打印数据的压缩方式,各值定义如下:0 不压缩,1 一级压缩,2 二级压缩。
|
||||
*/
|
||||
void imagePrintFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method);
|
||||
|
||||
/**
|
||||
* 直接传的MultipartFile文件过来
|
||||
* @param devName 设备名称/usb/tcp/com
|
||||
* @param dstw 要打印的宽度
|
||||
* @param dsth 要打印的高度
|
||||
* @param file 图片数据
|
||||
* @param binaryzation_method 图片二值化算法。0 表示抖动算法,1 表示阀值算法,2 表示误差扩散法。具体效果请测试查看。
|
||||
* @param compression_method 最终打印数据的压缩方式,各值定义如下:0 不压缩,1 一级压缩,2 二级压缩。
|
||||
*/
|
||||
void imagePrintFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method);
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.dpkj.modules.autoReplyPrint.service.impl;
|
||||
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import com.dpkj.modules.autoReplyPrint.base.BaseImagePrint;
|
||||
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
|
||||
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
|
||||
import com.sun.jna.Pointer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 串口图片打印
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-20 14:55:37
|
||||
*/
|
||||
@Service("COMImagePrint")
|
||||
@Slf4j
|
||||
public class COMImagePrintServiceImpl extends BaseImagePrint implements ImagePrintService {
|
||||
|
||||
@Override
|
||||
public Pointer getHandle(String devName) {
|
||||
try {
|
||||
Pointer pointer = AutoReplyPrint.INSTANCE.CP_Port_OpenLpt(devName);
|
||||
if ( pointer == null ){
|
||||
throw new RRException();
|
||||
}
|
||||
return pointer;
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RRException("获取COM串口句柄失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
|
||||
super.printFromPath(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
|
||||
super.printFromPathData(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method) {
|
||||
super.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method) {
|
||||
super.printFromMultipartFile(devName, dstw, dsth, file, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.dpkj.modules.autoReplyPrint.service.impl;
|
||||
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import com.dpkj.modules.autoReplyPrint.base.BaseImagePrint;
|
||||
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
|
||||
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
|
||||
import com.sun.jna.Pointer;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* tcp类型句柄的图片打印
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-20 14:46:06
|
||||
*/
|
||||
@Data
|
||||
@Service("TCPImagePrint")
|
||||
@Slf4j
|
||||
public class TCPImagePrintServiceImpl extends BaseImagePrint implements ImagePrintService {
|
||||
|
||||
private String destIP; // 目标ip
|
||||
private Short destPort; // 目标端口
|
||||
|
||||
@Override
|
||||
public Pointer getHandle(String devName) {
|
||||
try {
|
||||
Pointer pointer = AutoReplyPrint.INSTANCE.CP_Port_OpenTcp("0", destIP, destPort, 300000, 1);
|
||||
if ( pointer == null ){
|
||||
throw new RRException();
|
||||
}
|
||||
return pointer;
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RRException("获取TCP句柄失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
|
||||
super.printFromPath(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
|
||||
super.printFromPathData(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method) {
|
||||
super.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method) {
|
||||
super.printFromMultipartFile(devName, dstw, dsth, file, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
package com.dpkj.modules.autoReplyPrint.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
|
||||
import org.thymeleaf.templatemode.TemplateMode;
|
||||
import org.thymeleaf.templateresolver.StringTemplateResolver;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xhtmlrenderer.layout.SharedContext;
|
||||
import org.xhtmlrenderer.simple.Graphics2DRenderer;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.thymeleaf.context.Context;
|
||||
|
||||
/**
|
||||
* 模板服务类
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-18 15:33:51
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TemplateService {
|
||||
|
||||
|
||||
/**
|
||||
* 生成小票图片
|
||||
*
|
||||
* @param data json数据,用来填充模板
|
||||
* @param template 模板(html字符串或者模板名称)
|
||||
* @param width 图片宽度
|
||||
* @param height 图片高度
|
||||
* @param saveDir 图片的保存路径,如果为空,那么不进行图片的保存
|
||||
* @return 图片字节数组
|
||||
*/
|
||||
public byte[] generateReceiptImage(JSONObject data, String template, int width, int height, String saveDir) {
|
||||
try {
|
||||
// 获取模板上下文
|
||||
Context context = this.getContext(data);
|
||||
|
||||
TemplateEngine templateEngine = new TemplateEngine();
|
||||
if (checkIsHtml(template)) {
|
||||
StringTemplateResolver resolver = new StringTemplateResolver();
|
||||
resolver.setTemplateMode("HTML");
|
||||
// 设置模板引擎使用这个自定义的模板解析器
|
||||
templateEngine.setTemplateResolver(resolver);
|
||||
}else {
|
||||
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
|
||||
resolver.setPrefix("classpath:/templates/");
|
||||
resolver.setSuffix(".html");
|
||||
resolver.setTemplateMode(TemplateMode.HTML);
|
||||
resolver.setCacheable(true);
|
||||
resolver.setApplicationContext(new AnnotationConfigReactiveWebApplicationContext());
|
||||
templateEngine.setTemplateResolver(resolver);
|
||||
}
|
||||
|
||||
// 渲染模板
|
||||
String html = templateEngine.process(template, context);
|
||||
|
||||
BufferedImage image = this.generate(html, width, height);
|
||||
|
||||
// 保存图片
|
||||
if (saveDir != null && !"".equals(saveDir)) {
|
||||
String outputPath = saveDir + "\\genera_image_" + System.currentTimeMillis() + ".png";
|
||||
ImageIO.write(image, "PNG", new File(outputPath));
|
||||
}
|
||||
|
||||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "PNG", byteOutputStream);
|
||||
return byteOutputStream.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RRException("图片打印失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成图片
|
||||
* @param html html内容
|
||||
*/
|
||||
private BufferedImage generate(String html, int width, int height){
|
||||
try {
|
||||
// 转换为xhtml
|
||||
String xhtml = this.htmlToXhtml(html);
|
||||
|
||||
// 转换为document
|
||||
Document document = this.xhtmlToDocument(xhtml);
|
||||
|
||||
// 生成图片
|
||||
return createImageToDocument(document, width, height);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RRException("图片打印失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过JsonObject进行context内容填充
|
||||
* @param data jsonObject
|
||||
* @return context
|
||||
*/
|
||||
private Context getContext(JSONObject data) {
|
||||
// 创建Thymeleaf上下文
|
||||
Context context = new Context();
|
||||
Set<String> keys = data.keySet();
|
||||
for (String key : keys) {
|
||||
// 判单是否有图片生成,统一后面采用的是_2base64Type
|
||||
String[] split = key.split("_");
|
||||
if (split.length > 1 && split[1].equals("2base64Type")) {
|
||||
int width = split.length > 2 ? Integer.parseInt(split[2]) : 100;
|
||||
int height = split.length > 3 ? Integer.parseInt(split[3]) : 100;
|
||||
// 如果是图片类型,需要进行base64转换
|
||||
String base64 = this.generateQRCode(String.valueOf(data.get(key)), width, height);
|
||||
context.setVariable(split[0], "data:image/jpeg;base64," + base64);
|
||||
} else {
|
||||
// 普通字段直接设置
|
||||
context.setVariable(key, data.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据内容生成二维码
|
||||
* @param content 转换内容
|
||||
*/
|
||||
private String generateQRCode(String content, int width, int height) {
|
||||
try {
|
||||
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||||
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置字符编码为 UTF-8
|
||||
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 设置纠错级别
|
||||
|
||||
QRCodeWriter qrCodeWriter = new QRCodeWriter();
|
||||
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
|
||||
|
||||
BufferedImage qrImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
qrImage.createGraphics();
|
||||
|
||||
Graphics2D graphics = (Graphics2D) qrImage.getGraphics();
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fillRect(0, 0, width, height);
|
||||
graphics.setColor(Color.BLACK);
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
if (bitMatrix.get(i, j)) {
|
||||
graphics.fillRect(i, j, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(qrImage, "png", baos);
|
||||
return Base64.getEncoder().encodeToString(baos.toByteArray());
|
||||
}catch (Exception e){
|
||||
log.error("二维码生成失败");
|
||||
throw new RRException("二维码生成失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* xhtml 转换为 Document
|
||||
* @param xhtml xhtml
|
||||
* @return document
|
||||
* @throws Exception e
|
||||
*/
|
||||
private Document xhtmlToDocument(String xhtml) throws Exception {
|
||||
// 创建DocumentBuilder
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
factory.setValidating(false);
|
||||
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
|
||||
// 解析XHTML字符串为Document
|
||||
return builder.parse(new ByteArrayInputStream(xhtml.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 转换,将html转换为xhtml
|
||||
* @param html html内容
|
||||
* @return xhtml
|
||||
*/
|
||||
private String htmlToXhtml(String html) {
|
||||
org.jsoup.nodes.Document doc = Jsoup.parse(html);
|
||||
doc.outputSettings()
|
||||
.syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml)
|
||||
.charset(StandardCharsets.UTF_8);
|
||||
|
||||
// 不使用外部DTD
|
||||
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
|
||||
"<head>\n" +
|
||||
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n" +
|
||||
"<style type=\"text/css\">\n" +
|
||||
"body { font-family: SimSun, serif; }\n" +
|
||||
"</style>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
doc.body().html() +
|
||||
"\n</body>\n" +
|
||||
"</html>";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过document转换为图片
|
||||
* @param document doc
|
||||
* @param width 图片的宽度
|
||||
* @param height 图片的高度
|
||||
* @return bufferedImage
|
||||
*/
|
||||
private BufferedImage createImageToDocument(Document document, int width, int height) {
|
||||
try {
|
||||
// 创建Dimension对象
|
||||
Dimension dimension = new Dimension(width, height);
|
||||
|
||||
// 创建图片
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
Graphics2D graphics = (Graphics2D) image.getGraphics();
|
||||
|
||||
// 设置白色背景
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fillRect(0, 0, width, height);
|
||||
|
||||
// 设置渲染提示
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
// 创建渲染器
|
||||
Graphics2DRenderer renderer = new Graphics2DRenderer();
|
||||
renderer.setDocument(document, null);
|
||||
|
||||
// 设置渲染参数
|
||||
SharedContext sharedContext = renderer.getSharedContext();
|
||||
sharedContext.setInteractive(false);
|
||||
sharedContext.setDPI(256f);
|
||||
|
||||
// 使用系统默认字体,假设系统默认字体支持UTF - 8
|
||||
Font font = new Font(Font.DIALOG, Font.PLAIN, 20);
|
||||
graphics.setFont(font);
|
||||
|
||||
// 使用Dimension对象进行布局
|
||||
renderer.layout(graphics, dimension);
|
||||
renderer.render(graphics);
|
||||
|
||||
graphics.dispose();
|
||||
return image;
|
||||
} catch (Exception e) {
|
||||
throw new RRException("渲染图片失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验这个模板内容是不是html字符串,而非模板名称
|
||||
* @param template template
|
||||
* @return 是否是html字符串
|
||||
*/
|
||||
private boolean checkIsHtml(String template){
|
||||
try {
|
||||
String pattern = "<(\"[^\"]*\"|'[^']*'|[^'\">])*>";
|
||||
Pattern r = Pattern.compile(pattern);
|
||||
return r.matcher(template).find();
|
||||
}catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将document对象转换为字符串
|
||||
* @param doc document
|
||||
* @return document转换为的字符串
|
||||
*/
|
||||
private String documentToString(Document doc) {
|
||||
try {
|
||||
TransformerFactory tf = TransformerFactory.newInstance();
|
||||
Transformer transformer = tf.newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
|
||||
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
transformer.transform(new DOMSource(doc), new StreamResult(writer));
|
||||
return writer.getBuffer().toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.dpkj.modules.autoReplyPrint.service.impl;
|
||||
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import com.dpkj.modules.autoReplyPrint.base.BaseImagePrint;
|
||||
import com.dpkj.modules.autoReplyPrint.service.ImagePrintService;
|
||||
import com.dpkj.modules.autoReplyPrint.utils.AutoReplyPrint;
|
||||
import com.sun.jna.Pointer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
||||
/**
|
||||
* 图片打印服务
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-17 11:38:36
|
||||
*/
|
||||
@Slf4j
|
||||
@Service("USBImagePrint")
|
||||
public class USBImagePrintServiceImpl extends BaseImagePrint implements ImagePrintService {
|
||||
|
||||
@Override
|
||||
public Pointer getHandle(String devName) {
|
||||
try {
|
||||
Pointer pointer = AutoReplyPrint.INSTANCE.CP_Port_OpenUsb(devName, 1);
|
||||
if ( pointer == null ){
|
||||
throw new RRException();
|
||||
}
|
||||
return pointer;
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RRException("获取USB句柄失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromPath(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
|
||||
super.printFromPath(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromPathData(String devName, int dstw, int dsth, String pszFile, int binaryzation_method, int compression_method) {
|
||||
super.printFromPathData(devName, dstw, dsth, pszFile, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromData(String devName, int dstw, int dsth, byte[] data, int binaryzation_method, int compression_method) {
|
||||
super.printFromData(devName, dstw, dsth, data, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imagePrintFromMultipartFile(String devName, int dstw, int dsth, MultipartFile file, int binaryzation_method, int compression_method) {
|
||||
super.printFromMultipartFile(devName, dstw, dsth, file, binaryzation_method, compression_method);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package com.dpkj.modules.autoReplyPrint.utils;
|
||||
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 图片转换工具
|
||||
*
|
||||
* @author <a href="https://gitee.com/shi-chongli">石头人</a>
|
||||
* @version 1.0
|
||||
* @since 2025-01-17 14:06:09
|
||||
*/
|
||||
public class ImageUtils {
|
||||
|
||||
/**
|
||||
* 读取图片并转换为byte数组
|
||||
* @param imagePath 图片路径
|
||||
* @return byte数组
|
||||
*/
|
||||
public static byte[] imageToBytes(String imagePath) {
|
||||
try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(Paths.get(imagePath)));
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = bis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RRException("读取图片信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件byte[],通过 MultipartFile
|
||||
* @param file 文件信息
|
||||
* @return byte数组
|
||||
*/
|
||||
public static byte[] getByteToMultipartFile(MultipartFile file){
|
||||
// 验证文件是否为空
|
||||
if (file.isEmpty()) {
|
||||
throw new RRException("文件内容为空");
|
||||
}
|
||||
|
||||
// 验证文件类型
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || !contentType.startsWith("image/")) {
|
||||
throw new RRException("只支持图片文件");
|
||||
}
|
||||
|
||||
// 获取文件扩展名
|
||||
String fileName = file.getOriginalFilename();
|
||||
String fileExtension = fileName != null ?
|
||||
fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase() : "";
|
||||
|
||||
// 验证扩展名
|
||||
List<String> allowedExtensions = Arrays.asList("jpg", "jpeg", "png", "gif");
|
||||
if (!allowedExtensions.contains(fileExtension)) {
|
||||
throw new RRException("不支持的文件格式");
|
||||
}
|
||||
|
||||
try {
|
||||
// 转换为字节数组
|
||||
return file.getBytes();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
throw new RRException("获取文件字节失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue