diff --git a/src/main/java/com/gaotao/modules/base/controller/FontController.java b/src/main/java/com/gaotao/modules/base/controller/FontController.java new file mode 100644 index 00000000..f74815d9 --- /dev/null +++ b/src/main/java/com/gaotao/modules/base/controller/FontController.java @@ -0,0 +1,108 @@ +package com.gaotao.modules.base.controller; + +import com.gaotao.modules.base.service.FontService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 字体管理控制器 + */ +@Slf4j +@RestController +@RequestMapping("/font") +public class FontController { + + @Autowired + private FontService fontService; + + /** + * 获取系统可用字体列表 + */ + @RequestMapping("/available") + public Map getAvailableFonts() { + Map result = new HashMap<>(); + + try { + List> fonts = fontService.getAvailableFonts(); + + result.put("success", true); + result.put("data", fonts); + result.put("total", fonts.size()); + result.put("message", "获取字体列表成功"); + + log.info("返回字体列表,共 {} 个字体", fonts.size()); + + } catch (Exception e) { + log.error("获取字体列表失败", e); + + result.put("success", false); + result.put("data", null); + result.put("message", "获取字体列表失败: " + e.getMessage()); + } + + return result; + } + + /** + * 检查字体是否可用 + */ + @GetMapping("/check/{fontName}") + public Map checkFont(@PathVariable String fontName) { + Map result = new HashMap<>(); + + try { + boolean available = fontService.isFontAvailable(fontName); + String fontFile = fontService.getFontFilePath(fontName); + + result.put("success", true); + result.put("fontName", fontName); + result.put("available", available); + result.put("fontFile", fontFile); + result.put("supported", fontFile != null); + + } catch (Exception e) { + log.error("检查字体失败: {}", fontName, e); + + result.put("success", false); + result.put("message", "检查字体失败: " + e.getMessage()); + } + + return result; + } + + /** + * 获取字体分类 + */ + @GetMapping("/categories") + public Map getFontCategories() { + Map result = new HashMap<>(); + + try { + List> fonts = fontService.getAvailableFonts(); + + // 按类别分组 + Map categories = new HashMap<>(); + for (Map font : fonts) { + String category = font.get("category"); + categories.put(category, categories.getOrDefault(category, 0) + 1); + } + + result.put("success", true); + result.put("categories", categories); + result.put("message", "获取字体分类成功"); + + } catch (Exception e) { + log.error("获取字体分类失败", e); + + result.put("success", false); + result.put("message", "获取字体分类失败: " + e.getMessage()); + } + + return result; + } +} diff --git a/src/main/java/com/gaotao/modules/base/entity/ReportLabelList.java b/src/main/java/com/gaotao/modules/base/entity/ReportLabelList.java index 720df5a4..39fec656 100644 --- a/src/main/java/com/gaotao/modules/base/entity/ReportLabelList.java +++ b/src/main/java/com/gaotao/modules/base/entity/ReportLabelList.java @@ -56,4 +56,14 @@ public class ReportLabelList { private String replaceFrom; private String replaceTo; + // 字体相关字段 + private String fontFamily; // 字体族名称 + private String fontStyle; // 字体样式:normal, italic + private String fontWeight; // 字体粗细:normal, bold + private String textAlign; // 文本对齐:left, center, right + private Integer letterSpacing; // 字符间距 + private Integer lineHeight; // 行高 + private Boolean fontItalic; // 斜体 + private Boolean fontUnderline; // 下划线 + } diff --git a/src/main/java/com/gaotao/modules/base/service/FontService.java b/src/main/java/com/gaotao/modules/base/service/FontService.java new file mode 100644 index 00000000..098c4049 --- /dev/null +++ b/src/main/java/com/gaotao/modules/base/service/FontService.java @@ -0,0 +1,30 @@ +package com.gaotao.modules.base.service; + +import java.util.List; +import java.util.Map; + +/** + * 字体服务接口 + */ +public interface FontService { + + /** + * 获取系统可用字体列表 + * @return 字体列表 + */ + List> getAvailableFonts(); + + /** + * 获取字体文件路径 + * @param fontName 字体名称 + * @return 字体文件路径,如果不存在返回null + */ + String getFontFilePath(String fontName); + + /** + * 检查字体是否可用 + * @param fontName 字体名称 + * @return 是否可用 + */ + boolean isFontAvailable(String fontName); +} \ No newline at end of file diff --git a/src/main/java/com/gaotao/modules/base/service/Impl/FontServiceImpl.java b/src/main/java/com/gaotao/modules/base/service/Impl/FontServiceImpl.java new file mode 100644 index 00000000..36232c29 --- /dev/null +++ b/src/main/java/com/gaotao/modules/base/service/Impl/FontServiceImpl.java @@ -0,0 +1,267 @@ +package com.gaotao.modules.base.service.Impl; + +import com.gaotao.modules.base.service.FontService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.awt.*; +import java.util.*; +import java.util.List; + +/** + * 字体服务实现类 + */ +@Slf4j +@Service +public class FontServiceImpl implements FontService { + + // 字体名称到ZPL字体文件的映射 + private static final Map FONT_FILE_MAPPING = new HashMap<>(); + + static { + // 中文字体映射 + FONT_FILE_MAPPING.put("Microsoft YaHei", "MSYH.TTF"); + FONT_FILE_MAPPING.put("微软雅黑", "MSYH.TTF"); + FONT_FILE_MAPPING.put("SimSun", "SIMSUN.TTC"); + FONT_FILE_MAPPING.put("宋体", "SIMSUN.TTC"); + FONT_FILE_MAPPING.put("SimHei", "SIMHEI.TTF"); + FONT_FILE_MAPPING.put("黑体", "SIMHEI.TTF"); + FONT_FILE_MAPPING.put("KaiTi", "KAITI.TTF"); + FONT_FILE_MAPPING.put("楷体", "KAITI.TTF"); + FONT_FILE_MAPPING.put("FangSong", "SIMFANG.TTF"); + FONT_FILE_MAPPING.put("仿宋", "SIMFANG.TTF"); + + // 英文字体映射 + FONT_FILE_MAPPING.put("Arial", "ARIAL.TTF"); + FONT_FILE_MAPPING.put("Times New Roman", "TIMES.TTF"); + FONT_FILE_MAPPING.put("Courier New", "COUR.TTF"); + FONT_FILE_MAPPING.put("Helvetica", "HELV.TTF"); + FONT_FILE_MAPPING.put("Verdana", "VERDANA.TTF"); + FONT_FILE_MAPPING.put("Georgia", "GEORGIA.TTF"); + FONT_FILE_MAPPING.put("Tahoma", "TAHOMA.TTF"); + FONT_FILE_MAPPING.put("Trebuchet MS", "TREBUC.TTF"); + FONT_FILE_MAPPING.put("Lucida Console", "LUCON.TTF"); + FONT_FILE_MAPPING.put("Impact", "IMPACT.TTF"); + FONT_FILE_MAPPING.put("Comic Sans MS", "COMIC.TTF"); + FONT_FILE_MAPPING.put("Palatino", "PALA.TTF"); + FONT_FILE_MAPPING.put("Garamond", "GARA.TTF"); + FONT_FILE_MAPPING.put("Bookman", "BOOKOS.TTF"); + } + + @Override + public List> getAvailableFonts() { + List> fontList = new ArrayList<>(); + + try { + // 获取系统图形环境 + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + + // 获取所有可用字体的名字 + String[] fontNames = ge.getAvailableFontFamilyNames(); + + log.info("系统检测到 {} 个可用字体", fontNames.length); + + // 处理系统字体 + Set processedFonts = new HashSet<>(); + + for (String fontName : fontNames) { + // 避免重复添加 + if (processedFonts.contains(fontName)) { + continue; + } + processedFonts.add(fontName); + + Map fontInfo = new HashMap<>(); + fontInfo.put("name", fontName); + fontInfo.put("value", fontName); + fontInfo.put("category", getFontCategory(fontName)); + fontInfo.put("description", getFontDescription(fontName)); + + // 检查是否有对应的ZPL字体文件 + String fontFile = getFontFilePath(fontName); + if (fontFile != null) { + fontInfo.put("zplFile", fontFile); + fontInfo.put("supported", "true"); + } else { + fontInfo.put("supported", "false"); + } + + fontList.add(fontInfo); + } + + // 按类别和名称排序 + fontList.sort((f1, f2) -> { + // 默认字体排在最前面 + if ("default".equals(f1.get("value"))) return -1; + if ("default".equals(f2.get("value"))) return 1; + + // 按类别排序 + int categoryCompare = f1.get("category").compareTo(f2.get("category")); + if (categoryCompare != 0) { + return categoryCompare; + } + + // 同类别按名称排序 + return f1.get("name").compareTo(f2.get("name")); + }); + + log.info("处理后可用字体数量: {}", fontList.size()); + + } catch (Exception e) { + log.error("获取系统字体失败", e); + + // 如果获取系统字体失败,返回基本字体列表 + fontList = getBasicFontList(); + } + + return fontList; + } + + @Override + public String getFontFilePath(String fontName) { + if (fontName == null || fontName.isEmpty()) { + return null; + } + + // 直接查找映射 + String fontFile = FONT_FILE_MAPPING.get(fontName); + if (fontFile != null) { + return fontFile; + } + + // 尝试模糊匹配 + for (Map.Entry entry : FONT_FILE_MAPPING.entrySet()) { + if (fontName.toLowerCase().contains(entry.getKey().toLowerCase()) || + entry.getKey().toLowerCase().contains(fontName.toLowerCase())) { + return entry.getValue(); + } + } + + return null; + } + + @Override + public boolean isFontAvailable(String fontName) { + if (fontName == null || fontName.isEmpty() || "default".equals(fontName)) { + return true; + } + + try { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + String[] fontNames = ge.getAvailableFontFamilyNames(); + + for (String availableFont : fontNames) { + if (availableFont.equals(fontName)) { + return true; + } + } + } catch (Exception e) { + log.warn("检查字体可用性失败: {}", fontName, e); + } + + return false; + } + + /** + * 获取字体类别 + */ + private String getFontCategory(String fontName) { + if (fontName == null) { + return "other"; + } + + String lowerName = fontName.toLowerCase(); + + // 中文字体 + if (lowerName.contains("微软雅黑") || lowerName.contains("microsoft yahei") || + lowerName.contains("宋体") || lowerName.contains("simsun") || + lowerName.contains("黑体") || lowerName.contains("simhei") || + lowerName.contains("楷体") || lowerName.contains("kaiti") || + lowerName.contains("仿宋") || lowerName.contains("fangsong")) { + return "chinese"; + } + + // 常用英文字体 + if (lowerName.contains("arial") || lowerName.contains("times") || + lowerName.contains("courier") || lowerName.contains("helvetica") || + lowerName.contains("verdana") || lowerName.contains("georgia") || + lowerName.contains("tahoma") || lowerName.contains("calibri")) { + return "english"; + } + + // 等宽字体 + if (lowerName.contains("mono") || lowerName.contains("courier") || + lowerName.contains("consolas") || lowerName.contains("lucida console")) { + return "monospace"; + } + + // 装饰字体 + if (lowerName.contains("comic") || lowerName.contains("impact") || + lowerName.contains("brush") || lowerName.contains("script")) { + return "decorative"; + } + + return "other"; + } + + /** + * 获取字体描述 + */ + private String getFontDescription(String fontName) { + if (fontName == null) { + return ""; + } + + String category = getFontCategory(fontName); + switch (category) { + case "chinese": + return "中文字体"; + case "english": + return "英文字体"; + case "monospace": + return "等宽字体"; + case "decorative": + return "装饰字体"; + default: + return "系统字体"; + } + } + + /** + * 获取基本字体列表(备用方案) + */ + private List> getBasicFontList() { + List> basicFonts = new ArrayList<>(); + + // 基本字体配置 + String[][] basicFontConfig = { + {"默认字体", "default", "system", "系统默认字体"}, + {"微软雅黑", "Microsoft YaHei", "chinese", "现代中文字体"}, + {"宋体", "SimSun", "chinese", "传统中文字体"}, + {"黑体", "SimHei", "chinese", "粗体中文字体"}, + {"Arial", "Arial", "english", "无衬线英文字体"}, + {"Times New Roman", "Times New Roman", "english", "衬线英文字体"}, + {"Courier New", "Courier New", "monospace", "等宽英文字体"} + }; + + for (String[] config : basicFontConfig) { + Map fontInfo = new HashMap<>(); + fontInfo.put("name", config[0]); + fontInfo.put("value", config[1]); + fontInfo.put("category", config[2]); + fontInfo.put("description", config[3]); + + String fontFile = getFontFilePath(config[1]); + if (fontFile != null) { + fontInfo.put("zplFile", fontFile); + fontInfo.put("supported", "true"); + } else { + fontInfo.put("supported", "false"); + } + + basicFonts.add(fontInfo); + } + + return basicFonts; + } +} diff --git a/src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java b/src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java index 2916c3e9..f430413b 100644 --- a/src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java +++ b/src/main/java/com/gaotao/modules/base/service/Impl/LabelDataProcessorServiceImpl.java @@ -40,6 +40,9 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService @Autowired private com.gaotao.modules.base.service.BaseService baseService; + @Autowired + private com.gaotao.modules.base.service.FontService fontService; + @Override public List processLabelData(List elements, Map dataMap) { if (elements == null || elements.isEmpty()) { @@ -47,18 +50,18 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService } List processedElements = new ArrayList<>(); - + // 第一轮处理:处理基本数据替换(不包含元素组合) List firstPassElements = new ArrayList<>(); for (ReportLabelList element : elements) { - ReportLabelList processedElement = processElementData(element, dataMap); + ReportLabelList processedElement = processElementData(element, dataMap,true); firstPassElements.add(processedElement); } - + // 第二轮处理:处理元素组合引用 for (ReportLabelList element : firstPassElements) { ReportLabelList finalElement = processElementCombinations(element, dataMap, firstPassElements); - + // 如果元素被设置为不显示,则跳过该元素 if (("onecode".equals(element.getType()) || "qrcode".equals(element.getType()) || "text".equals(element.getType()) || "pic".equals(element.getType())) @@ -76,14 +79,14 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService /** * 处理元素组合引用 */ - private ReportLabelList processElementCombinations(ReportLabelList element, Map dataMap, + private ReportLabelList processElementCombinations(ReportLabelList element, Map dataMap, List allElements) { if (element == null || element.getData() == null || element.getData().isEmpty()) { return element; } String originalData = element.getData(); - + // 检查是否包含元素引用 if (containsElementReferences(originalData)) { ReportLabelList processedElement = copyElement(element); @@ -103,14 +106,14 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService if (data == null || data.isEmpty()) { return false; } - + // 检查是否包含 {元素类型} 格式的引用(排除 #{字段名} 格式) Pattern pattern = Pattern.compile("\\{(?!#)[^}]+\\}"); return pattern.matcher(data).find() || data.startsWith("CUSTOM:"); } @Override - public ReportLabelList processElementData(ReportLabelList element, Map dataMap) { + public ReportLabelList processElementData(ReportLabelList element, Map dataMap,Boolean nextSeqNotExist) { if (element == null || element.getData() == null || element.getData().isEmpty()) { return element; } @@ -152,7 +155,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService break; case "serialNumber": // 处理流水号元素,生成真实的流水号 - processedElement.setData(processSerialNumberElement(element, dataMap)); + processedElement.setData(processSerialNumberElement(element, dataMap,nextSeqNotExist)); break; case "pic": break; @@ -178,7 +181,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService String orientation = labelSetting.getPaperOrientation() != null ? labelSetting.getPaperOrientation() : "portrait"; Integer dpi = labelSetting.getDpi() != null ? labelSetting.getDpi() : 203; - ZplGenerator generator = new ZplGenerator(orientation, dpi, canvasSize); + ZplGenerator generator = new ZplGenerator(orientation, dpi, canvasSize, fontService); // 4. 生成ZPL代码 String zplCode = generator.generate(processedElements); @@ -211,7 +214,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService * 处理元素组合数据 * 支持将多个元素的内容按特定格式组合 */ - private String processElementCombination(String originalData, Map dataMap, + private String processElementCombination(String originalData, Map dataMap, List allElements, ReportLabelList currentElement) { if (originalData == null || originalData.isEmpty()) { return originalData; @@ -224,7 +227,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService // 处理模板模式的元素引用 {元素类型} 或 {元素类型(内容)} String processedData = originalData; - + // 创建元素映射,便于查找 Map elementMap = new HashMap<>(); for (ReportLabelList element : allElements) { @@ -253,7 +256,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService ReportLabelList referencedElement = findElementByReference(reference, elementMap); if (referencedElement != null) { // 递归处理引用的元素数据 - replacement = processElementData(referencedElement, dataMap).getData(); + replacement = processElementData(referencedElement, dataMap,false).getData(); if (replacement == null) { replacement = ""; } @@ -273,19 +276,19 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService /** * 处理自定义组合表达式 */ - private String processCustomCombination(String expression, Map dataMap, + private String processCustomCombination(String expression, Map dataMap, List allElements) { try { // 这里可以实现简单的表达式处理 // 为了安全起见,只支持有限的操作 - + // 示例:支持简单的字符串拼接和条件判断 if (expression.contains("elements.filter")) { return processElementsFilter(expression, allElements, dataMap); } else if (expression.contains("dataMap.")) { return processDataMapAccess(expression, dataMap); } - + // 默认返回表达式本身 return expression; } catch (Exception e) { @@ -297,7 +300,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService /** * 处理元素过滤表达式 */ - private String processElementsFilter(String expression, List allElements, + private String processElementsFilter(String expression, List allElements, Map dataMap) { // 简单实现:elements.filter(e => e.type === 'text').map(e => e.data).join('-') if (expression.contains("e.type === 'text'")) { @@ -305,7 +308,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService .filter(e -> "text".equals(e.getType())) .map(e -> e.getData() != null ? e.getData() : "") .collect(Collectors.toList()); - + String separator = "-"; if (expression.contains(".join('")) { int start = expression.indexOf(".join('") + 7; @@ -314,10 +317,10 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService separator = expression.substring(start, end); } } - + return String.join(separator, textData); } - + return expression; } @@ -329,7 +332,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService Pattern pattern = Pattern.compile("dataMap\\.([a-zA-Z_][a-zA-Z0-9_]*)"); Matcher matcher = pattern.matcher(expression); StringBuffer result = new StringBuffer(); - + while (matcher.find()) { String fieldName = matcher.group(1); Object value = dataMap.get(fieldName); @@ -337,7 +340,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); } matcher.appendTail(result); - + return result.toString(); } @@ -353,29 +356,29 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService // 检查是否是带内容的引用格式:文本(内容) 或 类型(内容) Pattern contentPattern = Pattern.compile("^(文本|一维码|二维码|图片|流水号)\\((.+)\\)$"); Matcher contentMatcher = contentPattern.matcher(reference); - + if (contentMatcher.matches()) { String typeName = contentMatcher.group(1); String expectedContent = contentMatcher.group(2); - + // 根据类型名称获取对应的类型键 String targetType = getTypeKeyByName(typeName); if (targetType != null) { // 精确匹配:同时匹配类型和内容 for (ReportLabelList element : elementMap.values()) { - if (targetType.equals(element.getType()) && + if (targetType.equals(element.getType()) && expectedContent.equals(element.getData())) { log.debug("精确匹配到元素: type={}, data={}", element.getType(), element.getData()); return element; } } - + // 如果精确匹配失败,尝试部分内容匹配 for (ReportLabelList element : elementMap.values()) { - if (targetType.equals(element.getType()) && - element.getData() != null && + if (targetType.equals(element.getType()) && + element.getData() != null && element.getData().contains(expectedContent)) { - log.debug("部分匹配到元素: type={}, data={}, expected={}", + log.debug("部分匹配到元素: type={}, data={}, expected={}", element.getType(), element.getData(), expectedContent); return element; } @@ -386,7 +389,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService // 模糊匹配(按类型)- 只有在没有内容指定时才使用 String[] typeNames = {"文本", "一维码", "二维码", "图片", "流水号"}; String[] typeKeys = {"text", "onecode", "qrcode", "pic", "serialNumber"}; - + for (int i = 0; i < typeNames.length; i++) { if (reference.equals(typeNames[i])) { // 改为精确匹配类型名称 // 返回该类型的第一个元素(保持向后兼容) @@ -402,7 +405,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService log.warn("未找到匹配的元素引用: {}", reference); return null; } - + /** * 根据类型名称获取类型键 */ @@ -429,12 +432,12 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService typeNames.put("serialNumber", "流水号"); String typeName = typeNames.getOrDefault(element.getType(), element.getType()); - String content = element.getData() != null && !element.getData().isEmpty() - ? "(" + (element.getData().length() > 10 - ? element.getData().substring(0, 10) + "..." + String content = element.getData() != null && !element.getData().isEmpty() + ? "(" + (element.getData().length() > 10 + ? element.getData().substring(0, 10) + "..." : element.getData()) + ")" : ""; - + return typeName + content; } @@ -948,6 +951,15 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService copy.setReplaceFrom(original.getReplaceFrom()); copy.setReplaceTo(original.getReplaceTo()); + copy.setFontFamily(original.getFontFamily()); + copy.setFontStyle(original.getFontStyle()); + copy.setFontWeight(original.getFontWeight()); + copy.setTextAlign(original.getTextAlign()); + copy.setLetterSpacing(original.getLetterSpacing()); + copy.setLineHeight(original.getLineHeight()); + copy.setFontItalic(original.getFontItalic()); + copy.setFontUnderline(original.getFontUnderline()); + return copy; } @@ -958,7 +970,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService * @param dataMap 数据源映射 * @return 生成的流水号字符串 */ - private String processSerialNumberElement(ReportLabelList element, Map dataMap) { + private String processSerialNumberElement(ReportLabelList element, Map dataMap,Boolean nextSeqNotExist) { try { log.debug("开始处理流水号元素: itemNo={}, data={}", element.getItemNo(), element.getData()); @@ -986,7 +998,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService log.debug("生成的KeyInfo: {}", keyInfo); // 3. 获取或生成下一个流水号 - int nextSeqNo = getNextSerialNumber(element.getReportId(), element.getItemNo(), keyInfo, intervalValue); + int nextSeqNo = getNextSerialNumber(element.getReportId(), element.getItemNo(), keyInfo, intervalValue,nextSeqNotExist); log.debug("获取的下一个流水号: {}", nextSeqNo); // 4. 格式化流水号 @@ -1067,7 +1079,7 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService * @param intervalValue 步长 * @return 下一个流水号 */ - private int getNextSerialNumber(String reportId, Integer itemNo, String keyInfo, int intervalValue) { + private int getNextSerialNumber(String reportId, Integer itemNo, String keyInfo, int intervalValue,Boolean nextSeqNotExist) { try { // 查询现有的流水号信息 com.gaotao.modules.base.entity.LabelContentData query = new com.gaotao.modules.base.entity.LabelContentData(); @@ -1086,36 +1098,39 @@ public class LabelDataProcessorServiceImpl implements LabelDataProcessorService } int nextSeqNo; - if (matchingInfo != null) { - // 存在记录,递增流水号 - nextSeqNo = matchingInfo.getLastSeqNo() + intervalValue; - - // 更新数据库记录 - com.gaotao.modules.base.entity.LabelContentSerialInfoData updateInfo = new com.gaotao.modules.base.entity.LabelContentSerialInfoData(); - updateInfo.setLabelNo(reportId); - updateInfo.setItemNo(itemNo); - updateInfo.setKeyInfo(keyInfo); - updateInfo.setLastSeqNo(nextSeqNo); - updateInfo.setOriKeyInfo(keyInfo); // 用于WHERE条件 - - baseService.updateLabelSerialInfo(updateInfo); - log.debug("更新流水号记录: reportId={}, itemNo={}, keyInfo={}, lastSeqNo={}", - reportId, itemNo, keyInfo, nextSeqNo); + if (nextSeqNotExist) { + if (matchingInfo != null) { + // 存在记录,递增流水号 + nextSeqNo = matchingInfo.getLastSeqNo() + intervalValue; + + // 更新数据库记录 + com.gaotao.modules.base.entity.LabelContentSerialInfoData updateInfo = new com.gaotao.modules.base.entity.LabelContentSerialInfoData(); + updateInfo.setLabelNo(reportId); + updateInfo.setItemNo(itemNo); + updateInfo.setKeyInfo(keyInfo); + updateInfo.setLastSeqNo(nextSeqNo); + updateInfo.setOriKeyInfo(keyInfo); // 用于WHERE条件 + + baseService.updateLabelSerialInfo(updateInfo); + log.debug("更新流水号记录: reportId={}, itemNo={}, keyInfo={}, lastSeqNo={}", + reportId, itemNo, keyInfo, nextSeqNo); + } else { + // 不存在记录,创建新记录 + nextSeqNo = intervalValue; // 从步长开始 + + com.gaotao.modules.base.entity.LabelContentSerialInfoData newInfo = new com.gaotao.modules.base.entity.LabelContentSerialInfoData(); + newInfo.setLabelNo(reportId); + newInfo.setItemNo(itemNo); + newInfo.setKeyInfo(keyInfo); + newInfo.setLastSeqNo(nextSeqNo); + + baseService.insertLabelSerialInfo(newInfo); + log.debug("创建流水号记录: reportId={}, itemNo={}, keyInfo={}, lastSeqNo={}", + reportId, itemNo, keyInfo, nextSeqNo); + } } else { - // 不存在记录,创建新记录 - nextSeqNo = intervalValue; // 从步长开始 - - com.gaotao.modules.base.entity.LabelContentSerialInfoData newInfo = new com.gaotao.modules.base.entity.LabelContentSerialInfoData(); - newInfo.setLabelNo(reportId); - newInfo.setItemNo(itemNo); - newInfo.setKeyInfo(keyInfo); - newInfo.setLastSeqNo(nextSeqNo); - - baseService.insertLabelSerialInfo(newInfo); - log.debug("创建流水号记录: reportId={}, itemNo={}, keyInfo={}, lastSeqNo={}", - reportId, itemNo, keyInfo, nextSeqNo); + nextSeqNo = matchingInfo!=null?matchingInfo.getLastSeqNo(): intervalValue; // 如果不存在记录,则使用步长作为初始值 } - return nextSeqNo; } catch (Exception e) { diff --git a/src/main/java/com/gaotao/modules/base/service/LabelDataProcessorService.java b/src/main/java/com/gaotao/modules/base/service/LabelDataProcessorService.java index 6fbea1f5..a32ec0f6 100644 --- a/src/main/java/com/gaotao/modules/base/service/LabelDataProcessorService.java +++ b/src/main/java/com/gaotao/modules/base/service/LabelDataProcessorService.java @@ -10,7 +10,7 @@ import java.util.Map; * 负责将标签模板中的数据源字段替换成真实数据,并进行格式化处理,生成ZPL代码 */ public interface LabelDataProcessorService { - + /** * 处理标签元素数据,将数据源字段替换为真实数据 * @param elements 标签元素列表 @@ -18,15 +18,16 @@ public interface LabelDataProcessorService { * @return 处理后的标签元素列表 */ List processLabelData(List elements, Map dataMap); - + /** * 处理单个标签元素的数据 * @param element 标签元素 * @param dataMap 数据源映射 + * @param nextSeqNotExist 是否存在下一个序列号 * @return 处理后的标签元素 */ - ReportLabelList processElementData(ReportLabelList element, Map dataMap); - + ReportLabelList processElementData(ReportLabelList element, Map dataMap,Boolean nextSeqNotExist); + /** * 生成带真实数据的ZPL代码 * @param elements 标签元素列表 @@ -35,7 +36,7 @@ public interface LabelDataProcessorService { * @return ZPL代码 */ String generateZplWithData(List elements, Map dataMap, LabelSettingData labelSetting); - + /** * 格式化数字数据 * @param value 原始数值 @@ -45,7 +46,7 @@ public interface LabelDataProcessorService { * @return 格式化后的字符串 */ String formatNumber(Object value, Integer decimalPlaces, Boolean showDecimalPlaces, Boolean thousandsSeparator); - + /** * 替换数据源字段 * @param text 包含数据源字段的文本 @@ -62,4 +63,4 @@ public interface LabelDataProcessorService { * @return 处理后的ZPL代码 */ String generateZplWithRealData(String reportId, List elements, LabelSettingData labelSetting); -} \ No newline at end of file +} diff --git a/src/main/java/com/gaotao/modules/base/utils/ZplGenerator.java b/src/main/java/com/gaotao/modules/base/utils/ZplGenerator.java index cdafbcf8..849b195d 100644 --- a/src/main/java/com/gaotao/modules/base/utils/ZplGenerator.java +++ b/src/main/java/com/gaotao/modules/base/utils/ZplGenerator.java @@ -19,6 +19,7 @@ public class ZplGenerator { private Integer dpi; private CoordinateTransformer transformer; private ZplConfig config; + private com.gaotao.modules.base.service.FontService fontService; public ZplGenerator(String orientation, Integer dpi, CoordinateTransformer.CanvasSize canvasSize) { this.orientation = orientation != null ? orientation : "portrait"; @@ -27,6 +28,12 @@ public class ZplGenerator { this.config = getConfig(); } + public ZplGenerator(String orientation, Integer dpi, CoordinateTransformer.CanvasSize canvasSize, + com.gaotao.modules.base.service.FontService fontService) { + this(orientation, dpi, canvasSize); + this.fontService = fontService; + } + /** * 获取ZPL配置参数 */ @@ -110,9 +117,10 @@ public class ZplGenerator { private String generateTextZPL(ReportLabelList element, int x, int y) { StringBuilder zpl = new StringBuilder(); - // 设置中文字体 + // 设置字体 Integer fontSize = element.getFontSize() != null ? element.getFontSize() : 30; - zpl.append("^CI28 ^CWJ,E:MSYH.TTF ^CFJ,").append(fontSize).append("\n"); + String fontCommand = generateFontCommand(element, fontSize); + zpl.append(fontCommand).append("\n"); // 如果有复选框 if (Boolean.TRUE.equals(element.getIsChecked())) { @@ -123,6 +131,9 @@ public class ZplGenerator { String data = element.getData() != null ? element.getData() : ""; + // 处理文本对齐 + String alignmentParam = getTextAlignmentParam(element.getTextAlign()); + // 基础文本 if (Boolean.TRUE.equals(element.getNewline())) { // 多行文本 @@ -130,7 +141,7 @@ public class ZplGenerator { int lineRows = element.getLineRows() != null ? element.getLineRows() : 2; zpl.append("^FO").append(x).append(",").append(y) .append("^FB").append(lineWidth).append(",").append(lineRows) - .append(",0^CFJ,").append(fontSize) + .append(",0,").append(alignmentParam).append("^CFJ,").append(fontSize) .append("^FD").append(data).append("^FS"); } else { // 单行文本 @@ -145,15 +156,15 @@ public class ZplGenerator { int lineRows = element.getLineRows() != null ? element.getLineRows() : 2; zpl.append("\n^FO").append(x + 1).append(",").append(y) .append("^FB").append(lineWidth).append(",").append(lineRows) - .append(",0^CFJ,").append(fontSize) + .append(",0,").append(alignmentParam).append("^CFJ,").append(fontSize) .append("^FD").append(data).append("^FS"); zpl.append("\n^FO").append(x).append(",").append(y + 1) .append("^FB").append(lineWidth).append(",").append(lineRows) - .append(",0^CFJ,").append(fontSize) + .append(",0,").append(alignmentParam).append("^CFJ,").append(fontSize) .append("^FD").append(data).append("^FS"); zpl.append("\n^FO").append(x + 1).append(",").append(y + 1) .append("^FB").append(lineWidth).append(",").append(lineRows) - .append(",0^CFJ,").append(fontSize) + .append(",0,").append(alignmentParam).append("^CFJ,").append(fontSize) .append("^FD").append(data).append("^FS"); } else { zpl.append("\n^FO").append(x + 1).append(",").append(y) @@ -165,9 +176,158 @@ public class ZplGenerator { } } + // 下划线效果(通过线条实现) + if (Boolean.TRUE.equals(element.getFontUnderline())) { + int textWidth = estimateTextWidth(data, fontSize); + int underlineY = y + fontSize + 2; + zpl.append("\n^FO").append(x).append(",").append(underlineY) + .append("^GB").append(textWidth).append(",2,2,B^FS"); + } + return zpl.toString(); } + /** + * 生成字体命令 + */ + private String generateFontCommand(ReportLabelList element, Integer fontSize) { + StringBuilder fontCmd = new StringBuilder(); + + // 设置字符编码 + fontCmd.append("^CI28"); + + // 根据字体族选择字体 + String fontFamily = element.getFontFamily(); + String fontFile = null; + + if (fontFamily != null && !"default".equals(fontFamily)) { + // 优先使用FontService获取字体文件 + if (fontService != null) { + fontFile = fontService.getFontFilePath(fontFamily); + log.debug("通过FontService获取字体文件: {} -> {}", fontFamily, fontFile); + } + + // 如果FontService没有找到,使用静态映射 + if (fontFile == null) { + fontFile = mapFontFamilyToFile(fontFamily); + log.debug("通过静态映射获取字体文件: {} -> {}", fontFamily, fontFile); + } + + if (fontFile != null) { + fontCmd.append(" ^CWJ,E:").append(fontFile); + } else { + // 使用系统默认字体 + fontCmd.append(" ^CWJ,E:MSYH.TTF"); + log.warn("未找到字体文件映射,使用默认字体: {}", fontFamily); + } + } else { + // 默认使用微软雅黑 + fontCmd.append(" ^CWJ,E:MSYH.TTF"); + } + + // 设置字体大小 + fontCmd.append(" ^CFJ,").append(fontSize); + + return fontCmd.toString(); + } + + /** + * 映射字体族到字体文件 + * 注意:这是一个静态方法,在实际使用中应该通过FontService来获取 + */ + private String mapFontFamilyToFile(String fontFamily) { + // 基本字体映射(静态备用方案) + switch (fontFamily) { + case "Microsoft YaHei": + case "微软雅黑": + return "MSYH.TTF"; + case "SimSun": + case "宋体": + return "SIMSUN.TTC"; + case "SimHei": + case "黑体": + return "SIMHEI.TTF"; + case "KaiTi": + case "楷体": + return "KAITI.TTF"; + case "FangSong": + case "仿宋": + return "SIMFANG.TTF"; + case "Arial": + return "ARIAL.TTF"; + case "Times New Roman": + return "TIMES.TTF"; + case "Courier New": + return "COUR.TTF"; + case "Helvetica": + return "HELV.TTF"; + case "Verdana": + return "VERDANA.TTF"; + case "Georgia": + return "GEORGIA.TTF"; + case "Tahoma": + return "TAHOMA.TTF"; + case "Trebuchet MS": + return "TREBUC.TTF"; + case "Lucida Console": + return "LUCON.TTF"; + case "Impact": + return "IMPACT.TTF"; + case "Comic Sans MS": + return "COMIC.TTF"; + case "Palatino": + return "PALA.TTF"; + case "Garamond": + return "GARA.TTF"; + case "Bookman": + return "BOOKOS.TTF"; + default: + return null; // 使用默认字体 + } + } + + /** + * 获取文本对齐参数 + */ + private String getTextAlignmentParam(String textAlign) { + if (textAlign == null) { + return "L"; // 默认左对齐 + } + + switch (textAlign) { + case "center": + return "C"; + case "right": + return "R"; + case "left": + default: + return "L"; + } + } + + /** + * 估算文本宽度(用于下划线) + */ + private int estimateTextWidth(String text, Integer fontSize) { + if (text == null || text.isEmpty()) { + return 0; + } + + // 简单估算:中文字符按字体大小计算,英文字符按字体大小的0.6倍计算 + int chineseCount = 0; + int englishCount = 0; + + for (char c : text.toCharArray()) { + if (c >= 0x4e00 && c <= 0x9fff) { + chineseCount++; + } else { + englishCount++; + } + } + + return (int) (chineseCount * fontSize + englishCount * fontSize * 0.6); + } + /** * 生成一维码ZPL代码 */