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;
}
}