import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.StringTemplateResolver;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class InvoiceImageGenerator {
// 解析XML并提取数据
private Map<String, Object> parseXmlData(byte[] xmlBytes) throws Exception {
Map<String, Object> data = new HashMap<>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new ByteArrayInputStream(xmlBytes)));
XPath xPath = XPathFactory.newInstance().newXPath();
// 提取购买方信息
data.put("buyerName", xPath.evaluate("//BuyerName", document));
data.put("buyerTaxId", xPath.evaluate("//BuyerIdNum", document));
// 提取销售方信息
data.put("sellerName", xPath.evaluate("//SellerName", document));
data.put("sellerTaxId", xPath.evaluate("//SellerIdNum", document));
// 提取发票信息
data.put("invoiceNumber", xPath.evaluate("//InvoiceNumber", document));
String issueDate = xPath.evaluate("//IssueTime", document);
data.put("issueDate", formatDate(issueDate));
// 提取项目信息
data.put("itemName", xPath.evaluate("//ItemName", document));
data.put("specMod", xPath.evaluate("//SpecMod", document));
data.put("meaUnits", xPath.evaluate("//MeaUnits", document));
data.put("quantity", xPath.evaluate("//Quantity", document));
data.put("unPrice", xPath.evaluate("//UnPrice", document));
data.put("amount", xPath.evaluate("//Amount", document));
data.put("taxRate", formatTaxRate(xPath.evaluate("//TaxRate", document)));
data.put("taxAmount", xPath.evaluate("//ComTaxAm", document));
data.put("totalTaxIncludedAmount", xPath.evaluate("//TotaltaxIncludedAmount", document));
data.put("taxClassificationCode", xPath.evaluate("//TaxClassificationCode", document));
// 提取合计信息
data.put("totalAmount", xPath.evaluate("//TotalAmWithoutTax", document));
data.put("totalTax", xPath.evaluate("//TotalTaxAm", document));
data.put("totalInWords", xPath.evaluate("//TotalTax-includedAmountInChinese", document));
data.put("totalInFigures", xPath.evaluate("//TotalTax-includedAmount", document));
// 提取开票人信息
data.put("drawer", xPath.evaluate("//Drawer", document));
return data;
}
// 格式化日期
private String formatDate(String dateStr) {
if (dateStr == null || dateStr.length() < 10) return dateStr;
return dateStr.substring(0, 4) + "年" +
dateStr.substring(5, 7) + "月" +
dateStr.substring(8, 10) + "日";
}
// 格式化税率
private String formatTaxRate(String taxRateStr) {
try {
double rate = Double.parseDouble(taxRateStr) * 100;
return (int) rate + "%";
} catch (NumberFormatException e) {
return taxRateStr;
}
}
// 使用Thymeleaf填充HTML模板
private String generateHtmlContent(Map<String, Object> data, String htmlTemplate) {
// 配置Thymeleaf模板解析器
StringTemplateResolver templateResolver = new StringTemplateResolver();
templateResolver.setTemplateMode(TemplateMode.HTML);
// 创建模板引擎
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
// 创建上下文并添加数据
Context context = new Context();
context.setVariables(data);
// 处理模板
return templateEngine.process(htmlTemplate, context);
}
// 使用Selenium将HTML渲染为图片
private byte[] renderHtmlToImage(String htmlContent) throws IOException {
// 创建临时HTML文件
File htmlFile = File.createTempFile("invoice", ".html");
try (FileWriter writer = new FileWriter(htmlFile, StandardCharsets.UTF_8)) {
writer.write(htmlContent);
}
// 配置Chrome选项
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");
options.addArguments("--disable-gpu");
options.addArguments("--window-size=794,1123"); // 设置窗口大小与发票尺寸匹配
options.addArguments("--no-sandbox");
options.addArguments("--disable-dev-shm-usage");
// 初始化WebDriver
WebDriver driver = new ChromeDriver(options);
try {
// 加载HTML文件
driver.get("file:///" + htmlFile.getAbsolutePath());
// 等待页面加载完成
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// 截取屏幕截图
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
// 读取截图文件为字节数组
return FileUtils.readFileToByteArray(screenshot);
} finally {
driver.quit();
htmlFile.delete();
}
}
// 将图片字节数组转换为Data URI
private String convertToDataUri(byte[] imageBytes, String format) {
String base64 = Base64.getEncoder().encodeToString(imageBytes);
return "data:image/" + format + ";base64," + base64;
}
// 主方法 - 生成发票图片并返回Data URI
public String generateInvoiceImage(byte[] xmlBytes, String htmlTemplate) {
try {
// 1. 解析XML数据
Map<String, Object> invoiceData = parseXmlData(xmlBytes);
// 2. 生成HTML内容
String htmlContent = generateHtmlContent(invoiceData, htmlTemplate);
// 3. 将HTML渲染为图片
byte[] imageBytes = renderHtmlToImage(htmlContent);
// 4. 转换为Data URI并返回
return convertToDataUri(imageBytes, "png");
} catch (Exception e) {
throw new RuntimeException("生成发票图片失败", e);
}
}
}
如何获取src/main/resources/templates/下的invoice-template.html并适配上面的程序
最新发布