package com.co.lane.print; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Toolkit; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.util.ArrayList; import java.util.List; import com.co.lane.print.test.OrderBean; import com.co.lane.print.test.OrderItem; import com.co.lane.print.test.OrderOldItem; import com.co.lane.util.ReflectUtil; import com.co.lane.util.StringUtil; /** * * <ul> * <li>打印处理,只要根据设定好的数据List循环打印即可,无须知道数据是什么或者有多少内容。</li> * <li>打印数据对象是:[{type=[0:字符文字, 1:线 , 2:图片], value, x坐标位置,y坐标位置]</li> * <ul> * Ver1.0<br> * 数据转换处理在Printer的printDataConvert中。<br> * Ver1.1<br> * 打印依赖Graphics对象处理,由于涉及到文字换行坐标,即文字的打印宽度,高度等。所以把数据转换处理放到print方法中。<br> * * @author Xuyd * */ public class PrinterExec implements Printable { /** * 指定内容坐标属性,可以直接打印的对象 */ private List<PrintDataItem> printData = new ArrayList<PrintDataItem>(); /** * 原始的打印数据 */ private Object printDataOriginal = new Object(); /** * 配置文件对象 */ private ConfigDataBean configData = new ConfigDataBean(); private double imageableWidth = 0; private double originalX = 0; private double originalY = 0; /** * List中单行字符串超过单元格宽度后最大换行数 */ private int maxRow = 0; /** * constructor */ public PrinterExec(ConfigDataBean configData, Object printDataOriginal) { this.printDataOriginal = printDataOriginal; this.configData = configData; } @Override public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { try { // paper参数处理 imageableWidth = pageFormat.getPaper().getImageableWidth(); originalX = pageFormat.getPaper().getImageableX(); originalY = pageFormat.getPaper().getImageableY(); // 数据处理转换 Graphics2D g = (Graphics2D) graphics; printDataConvert(g); if (printData == null || printData.size() == 0) { return NO_SUCH_PAGE; } // 打印内容开始 for (PrintDataItem bean : printData) { // 设置绘制对象属性 if (bean.isResetPorperty()) { g.setColor(bean.getItemProperty().getColor()); g.setStroke(bean.getItemProperty().getBasicStroke()); g.setFont(bean.getItemProperty().getFont()); } float fontHeight = g.getFont().getSize2D() / 2; switch (bean.getPrintType()) { // 绘制文字,文字是以基线为坐标的,所以这里Y坐标要加上字体高度。 case PrintDataItem.PRINT_STRING: if (bean.getData() != null) { StringUtil.trace(bean.getData() + "(" + (float) bean.getStartX() + "," + (float) bean.getStartY() + ")"); g.drawString(bean.getData(), (float) bean.getStartX(), (float) bean.getStartY() + fontHeight); } break; // 绘制线 case PrintDataItem.PRINT_LINE: StringUtil.trace("(" + (float) bean.getStartX() + "," + (float) bean.getStartY() + ")"); g.drawLine((int) bean.getStartX(), (int) bean.getStartY(), (int) bean.getEndX(), (int) bean.getEndY()); break; // 绘制图片 case PrintDataItem.PRINT_IMAGE: Image image = Toolkit.getDefaultToolkit().getImage(bean.getImagePath()); g.drawImage(image, (int) bean.getStartX(), (int) bean.getStartY(), image.getWidth(null), image.getHeight(null), null); // TODO add print image handler break; } } } catch (Exception e) { e.printStackTrace(); } return PAGE_EXISTS; } /** * 数据转换处理 * * @param g * @return 返回指定内容坐标属性,可以直接打印的对象 */ public void printDataConvert(Graphics2D g) { List<PrintDataItem> listPrintDataItem = new ArrayList<PrintDataItem>(); // com.topsun.posclient.datamodel.Retail retail = (com.topsun.posclient.datamodel.Retail)this.getPrintDataOriginal(); OrderBean orderBean = (OrderBean) this.printDataOriginal; ReflectUtil reflect = null; int indexRow = 0; // 设置Header打印数据 reflect = ReflectUtil.forClass(OrderBean.class); for (ConfigDataItem item : configData.getListHeaderItem()) { // 直接可以打印的数据 PrintDataItem printItem = generalWraper(item, reflect, orderBean, g); listPrintDataItem.add(printItem); } // 设置Detail List打印数据 ========================= reflect = ReflectUtil.forClass(OrderBean.class); // 设置List的标题静态数据 for (ConfigDataItem item : configData.getListDetailItem()) { // 直接可以打印的数据 PrintDataItem printItem = generalWraper(item, reflect, orderBean, g); listPrintDataItem.add(printItem); } // 循环设置List的DB内容数据 // 初始化,用于换行处理 maxRow = 0; indexRow = 0; reflect = ReflectUtil.forClass(OrderItem.class); for(int i = 0; i < orderBean.getListOrderItem().size(); i++){ OrderItem item = orderBean.getListOrderItem().get(i); // 直接可以打印的数据 indexRow += maxRow; List<PrintDataItem> listPrintItem = dataListWraper(configData.getListDetailItem(), item, indexRow, reflect, g); listPrintDataItem.addAll(listPrintItem); } // 设置Total打印数据 reflect = ReflectUtil.forClass(OrderBean.class); for (ConfigDataItem item : configData.getListTotalItem()) { // 直接可以打印的数据 PrintDataItem printItem = generalWraper(item, reflect, orderBean, g); listPrintDataItem.add(printItem); } // 设置Total1打印数据 reflect = ReflectUtil.forClass(OrderBean.class); for (ConfigDataItem item : configData.getListTotal1Item()) { // 直接可以打印的数据 PrintDataItem printItem = generalWraper(item, reflect, orderBean, g); listPrintDataItem.add(printItem); } // 设置Detail1 List 打印数据 ========================= reflect = ReflectUtil.forClass(OrderBean.class); // 设置List的标题静态数据 for (ConfigDataItem item : configData.getListDetail1Item()) { // 直接可以打印的数据 PrintDataItem printItem = generalWraper(item, reflect, orderBean, g); listPrintDataItem.add(printItem); } // 循环设置List的DB内容数据 // 初始化,用于换行处理 maxRow = 0; indexRow = 0; reflect = ReflectUtil.forClass(OrderOldItem.class); for(int i = 0; i < orderBean.getListOrderOldItem().size(); i++){ OrderOldItem item = orderBean.getListOrderOldItem().get(i); // 直接可以打印的数据 indexRow += maxRow; List<PrintDataItem> listPrintItem = dataListWraper(configData.getListDetail1Item(), item, indexRow, reflect, g); listPrintDataItem.addAll(listPrintItem); } // 设置Pay打印数据 reflect = ReflectUtil.forClass(OrderBean.class); for (ConfigDataItem item : configData.getListPayItem()) { // 直接可以打印的数据 PrintDataItem printItem = generalWraper(item, reflect, orderBean, g); listPrintDataItem.add(printItem); } // 设置Bottom打印数据 reflect = ReflectUtil.forClass(OrderBean.class); List<ConfigDataItem> listBottomItem = configData.getListBottomItem(); for (ConfigDataItem item : listBottomItem) { // 直接可以打印的数据 PrintDataItem printItem = generalWraper(item, reflect, orderBean, g); listPrintDataItem.add(printItem); } this.setPrintData(listPrintDataItem); } /** * 数据组装 * * @param item * 配置文件数据 * @param reflect * 要处理的类 * @param orderBean * 要处理的数据 * @param Graphics2D g * * @return 打印目标数据 */ public <T> PrintDataItem generalWraper(ConfigDataItem configItem, ReflectUtil reflect, T t, Graphics2D g) { PrintDataItem printItem = new PrintDataItem(); double startX = 0; double startY = 0; double endX = 0; double endY = 0; double width = 0; FontMetrics fm = g.getFontMetrics(); if (configItem.isText()) { // 设置直接打印标题 printItem.setResetPorperty(true); String data = configItem.getTitle(); printItem.setData(data); if(StringUtil.isEmpty(data)){ return printItem; } startX = configItem.getColumn() + this.originalX; startX = getCoordinationX(startX, fm.stringWidth(data) / Printer.rate, configItem.getWidth(), configItem.getAlignment()); startY = configItem.getRow() + this.originalY; printItem.setStartX(startX * Printer.rate); printItem.setStartY(startY * Printer.rate); printItem.setPrintType(PrintDataItem.PRINT_STRING); } else if (configItem.isLine()) { // 设置直接打印线 if (configItem.isLineH()) { // 如果是横线 startX = configItem.getColumn() + this.originalX; startY = configItem.getRow() + this.originalY; width = configItem.getWidth() == 0 ? this.imageableWidth : configItem.getWidth(); endX = startX + width; endY = startY; } else if (configItem.isLineV()) { // 如果是竖线 startX = configItem.getColumn() + this.originalX; startY = configItem.getRow() + this.originalY; width = configItem.getWidth() == 0 ? this.imageableWidth : configItem.getWidth(); endX = startX; endY = startY + width; } printItem.setStartX(startX * Printer.rate); printItem.setStartY(startY * Printer.rate); printItem.setEndX(endX * Printer.rate); printItem.setEndY(endY * Printer.rate); printItem.setPrintType(PrintDataItem.PRINT_LINE); } else if (configItem.isDataBase()) { // 设置打印内容 printItem.setResetPorperty(true); // 键值对形式数据 String field = configItem.getField().substring(1); String data = (String) reflect.getGetMethodValue(t, field.toLowerCase()); if(StringUtil.isEmpty(data)){ return printItem; } printItem.setData(data); startX = configItem.getColumn() + this.originalX; startX = getCoordinationX(startX, fm.stringWidth(data) / Printer.rate, configItem.getWidth(), configItem.getAlignment()); startY = configItem.getRow() + this.originalY; printItem.setStartX(startX * Printer.rate); printItem.setStartY(startY * Printer.rate); printItem.setPrintType(PrintDataItem.PRINT_STRING); } return printItem; } /** * List数据组装,按照列方式组装,即:商品编号List,商品名称List * * @param configItem 配置文件数据 * @param reflect * @param list * @param g * @return 打印目标数据 */ public <T> List<PrintDataItem> dataListWraper(List<ConfigDataItem> listConfigItem, T t, int index, ReflectUtil reflect, Graphics2D g) { List<PrintDataItem> listPrintDataItem = new ArrayList<PrintDataItem>(); double startX = 0; double startY = 0; // 计算字体高度 double fontHeight = g.getFont().getSize2D() / Printer.rate; FontMetrics fm = g.getFontMetrics(); int maxRowLine = 0; for (ConfigDataItem configItem : listConfigItem) { if (!configItem.isDataBase()) { continue; } // List形式数据 String field = configItem.getField().substring(1); String data = (String) reflect.getGetMethodValue(t, field.toLowerCase()); if(StringUtil.isEmpty(data)){ continue; } // 字符长度超过单元格要换行处理 List<String> listNewLine = getNewLines(fm, data, configItem.getWidth() * Printer.rate); for (int n = 0; n < listNewLine.size(); n++){ PrintDataItem printItem = new PrintDataItem(); String val = listNewLine.get(n); printItem.setData(val); // 这些处理应该通过Graphics才能取到字体高度 // 定义font size = 10的字体height是3.58mm startX = configItem.getColumn() + this.originalX; startX = getCoordinationX(startX, fm.stringWidth(val) / Printer.rate, configItem.getWidth(), configItem.getAlignment()); startY = configItem.getRow() + (index * fontHeight) + (n * fontHeight) + this.originalY; printItem.setStartX(startX * Printer.rate); printItem.setStartY(startY * Printer.rate); printItem.setPrintType(PrintDataItem.PRINT_STRING); listPrintDataItem.add(printItem); } // 取得一行中最大换行行数 if(listNewLine.size() > maxRowLine){ maxRowLine = listNewLine.size(); } } // 最大换行行数返回出来供下一行使用 if(maxRowLine > maxRow){ maxRow = maxRowLine; } return listPrintDataItem; } /** * 根据指定宽度设置字符串换行处理 * * @param fontMetrics Graphics取得字体矩阵 * @param value 要处理的字符串 * @param cellWidth 指定单元格宽度 * @return 分组换行后的字符串 */ public static List<String> getNewLines(FontMetrics fontMetrics, String value, double cellWidth){ List<String> listNewLine = new ArrayList<String>(); if(StringUtil.isEmpty(value)){ return listNewLine; } // 获取字符宽度 int width = fontMetrics.stringWidth(value); // 未超过单元宽度,直接返回 if(width <= cellWidth){ listNewLine.add(value); return listNewLine; } // 超过单元宽度的时候 int len = value.length(); String str = ""; String tmp = ""; for(int i = 0; i < len; i++){ // 一个一个取字符串判断合计长度,超过则放入容器,否则继续拼接 tmp = value.substring(i, i + 1); if(fontMetrics.stringWidth(str + tmp) > cellWidth){ listNewLine.add(str); str = tmp; } else { str += tmp; } // 最后剩余字符放入容器 if(i == len - 1 && str.length() > 0){ listNewLine.add(str); } } return listNewLine; } /** * 根据指定对齐方式,计算出水平坐标 * * @param startX 起始坐标 * @param stringWidth 字符串宽度 * @param cellWidth 单元格宽度 * @param alignment 对其方式 * @return 新的起始坐标 */ public static double getCoordinationX(double startX, double stringWidth, double cellWidth, String alignment){ if(stringWidth >= cellWidth){ return startX; } if(ConfigDataItem.ALIGNMENT_LEFT.equals(alignment)){ return startX; } if(ConfigDataItem.ALIGNMENT_CENTER.equals(alignment)){ startX += (cellWidth - (stringWidth * 0.9)) / 2; return startX; } if(ConfigDataItem.ALIGNMENT_RIGHT.equals(alignment)){ startX += cellWidth - stringWidth; return startX; } return startX; } // =========================SETTER GETTER ============================ /** * @param printData * the printData to set */ public void setPrintData(List<PrintDataItem> printData) { this.printData = printData; } /** * @param printDataOriginal * the printDataOriginal to set */ public void setPrintDataOriginal(Object printDataOriginal) { this.printDataOriginal = printDataOriginal; } }