一 前言
前段時間在開發項目時,需要實現打印的功能,有着封裝好的工具類,不過使用的時候有些地方還是遇到了一些小坑,在此整理出來希望對大家有所幫助。
二 準備工作
1.實體類和工具類
實體類
package com.yc.entity;
import lombok.Data;
import java.io.Serializable;
/**
* @author Wzh
* Create by Wzh on 2018/4/21
*/
@Data
public class Orders implements Serializable{
private static final long serialVersionUID = -560708444936642252L;
/**
* id
*/
private Integer id;
/**
* 寄件人姓名
*/
private String sendname;
/**
* 寄件人電話
*/
private String sendtel;
/**
* 寄件人地址
*/
private String sendaddress;
/**
* 收件人姓名
*/
private String recname;
/**
* 收件人電話
*/
private String rectel;
/**
* 收件人地址
*/
private String recaddress;
/**
* 收件人郵編
*/
private String reccode;
/**
* 訂單日期
*/
private Long orderstime;
/**
* 商品類型
*/
private String goodstype;
/**
* 重量 (kg)
*/
private Double weight;
/**
* 體積 (m^3)
*/
private Double volume;
/**
* 配送費
*/
private Double price;
/**
* 報價金額
*/
private Double insureprice;
/**
* 訂單類型(0快件, 1慢件, 2大宗)
*/
private Integer orderstype;
/**
* 訂單狀態 (0未發件, 1已發件, 2已送達,3 已取消,4 正在排件(備用屬性,不一定使用), 100前臺下單)
*/
private Integer status;
/**
* 條形碼編碼
*/
private Long barcode;
/**
* 條形碼圖片
*/
private String barpic;
/**
* 預計到達時間
*/
private Long endtime;
/**
* 配送點姓名
*/
private String recspname;
/**
* 備註
*/
private String remark;
/**
* 客戶信息(Id)
*/
private Integer usid;
/**
* 代理點(PID)
*/
private Integer pid;
/**
* 系統管理員id
*/
private Integer sysid;
/**
* 路線(rid)
*/
private Integer rid;
/**
* 憑證,圖片
*/
private String url;
/**
* 4個備用字段
*/
/**
* 付款方式
*/
private String res1;
private String res2;
private String res3;
private String res4;
/**
* 路線名稱
*/
private String rname;
/**
* 路線地址
*/
private String raddr;
private Client client;
private Route route;
private Proxy proxy;
public Orders(){
super();
}
public Orders(String sendname, String sendtel, String sendaddress, String recname, String rectel, String recaddress, String goodstype, Double price) {
super();
this.sendname = sendname;
this.sendtel = sendtel;
this.sendaddress = sendaddress;
this.recname = recname;
this.rectel = rectel;
this.recaddress = recaddress;
this.goodstype = goodstype;
this.price = price;
}
}
這是關於物流訂單的實體類,實體類可以自行創建根據你需要打印的信息。我這裏使用的是lombok的工具,如果沒有請自行添加實體屬性的get 和 set 方法。末尾的有參的構造方法是你所需要打印的信息
工具類實現打印機的接口
package com.yc.util;
import com.yc.entity.Orders;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* 源辰信息
* 銷售清單對象
* @author navy
* 2017年5月23日
*/
public class SalesTicket implements Printable{
private List<Orders> goods; //商品列表
private String operatorName="源辰信息"; //操作員
private String orderId; //訂單編號
private String totalGoodsNum; //商品總數
private String totalPrice; //總金額
private String actualCollection; //實收款
private String giveChange; //找零
private String cardNumber; //會員編號
private String integral; //積分
private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE");
@Override
public String toString() {
return "SalesTicket [goods=" + goods + ", operatorName=" + operatorName + ", orderId=" + orderId
+ ", totalGoodsNum=" + totalGoodsNum + ", totalPrice=" + totalPrice + ", actualCollection="
+ actualCollection + ", giveChange=" + giveChange + ", cardNumber=" + cardNumber + ", integral="
+ integral + "]";
}
public SalesTicket(List<Orders> goods, String operatorName, String orderId, String totalGoodsNum,
String totalPrice, String actualCollection, String giveChange, String cardNumber, String integral) {
super();
this.goods = goods;
this.operatorName = operatorName;
this.orderId = orderId;
this.totalGoodsNum = totalGoodsNum;
this.totalPrice = totalPrice;
this.actualCollection = actualCollection;
this.giveChange = giveChange;
this.cardNumber = cardNumber;
this.integral = integral;
}
public SalesTicket(List<Orders> goods, String operatorName, String orderId, String totalGoodsNum,
String totalPrice, String actualCollection, String giveChange) {
super();
this.goods = goods;
this.operatorName = operatorName;
this.orderId = orderId;
this.totalGoodsNum = totalGoodsNum;
this.totalPrice = totalPrice;
this.actualCollection = actualCollection;
this.giveChange = giveChange;
}
/**
* 打印方法
* graphics - 用來繪製頁面的上下文,即打印的圖形
* pageFormat - 將繪製頁面的大小和方向,即設置打印格式,如頁面大小一點爲計量單位(以1/72 英寸爲單位,1英寸爲25.4毫米。A4紙大致爲595 × 842點)
* 小票紙寬度一般爲58mm,大概爲165點
* pageIndex - 要繪製的頁面從 0 開始的索引 ,即頁號
*/
@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
//此 Graphics2D 類擴展 Graphics 類,以提供對幾何形狀、座標轉換、顏色管理和文本佈局更爲複雜的控制。
//它是用於在 Java(tm) 平臺上呈現二維形狀、文本和圖像的基礎類。
Graphics2D g2 = (Graphics2D) graphics;
g2.setColor(Color.black);//設置打印顏色爲黑色
//打印起點座標
double x= pageFormat.getImageableX(); //返回與此 PageFormat相關的 Paper對象的可成像區域左上方點的 x座標。
double y= pageFormat.getImageableY(); //返回與此 PageFormat相關的 Paper對象的可成像區域左上方點的 y座標。
//Font.PLAIN: 普通樣式常量 Font.ITALIC 斜體樣式常量 Font.BOLD 粗體樣式常量。
Font font = new Font("宋體",Font.BOLD,10); //根據指定名稱、樣式和磅值大小,創建一個新 Font。
g2.setFont(font);//設置標題打印字體
float heigth = font.getSize2D();//獲取字體的高度
//設置小票的標題標題
g2.drawString("源辰信息科技有限公司",(float)x+25,(float)y+heigth);
float line = 2*heigth; //下一行開始打印的高度
g2.setFont(new Font("宋體", Font.PLAIN,8));//設置正文字體
heigth = font.getSize2D();// 字體高度
line+=2;
//設置操作員
g2.drawString("操作員:"+operatorName,(float)x+20,(float)y+line);
line+=heigth;
//設置訂單號
g2.drawString("訂單號:"+orderId, (float)x+20,(float)y+line);
line+=heigth+2;
//***********************************打印圖片*********************************************//
//Component c = null;
//Image src =Toolkit.getDefaultToolkit().getImage(Common.GITPATH+"\\"+orderId+".gif");
//使圖片旋轉90度
//BufferedImage simage = RotateImage.Rotate(src,90);
//Image image = simage;
//獲取圖片的寬度
//int width=image.getWidth(c);
//獲取圖片的高度
//int height2=image.getHeight(c);
// g2.drawImage(src,(int)x,40,c); 打印原圖 ,因爲圖片的原寬度超出了紙張,所以打印機會報錯誤
//**************************************************************************************//
//設置標題
//g2.drawString("名稱",(float)x+20, (float)y+line);
//g2.drawString("單價",(float)x+60, (float)y+line);
//g2.drawString("數量",(float)x+90, (float)y+line);
//g2.drawString("小計",(float)x+120, (float)y+line);
line+=heigth;
/*
* 虛線繪製設置 setStroke(Stroke):爲 Graphics2D 上下文設置 Stroke
* 由 BasicStroke定義的呈現屬性描述了用畫筆沿 Shape 的輪廓繪製的某個標記的形狀,以及應用在 Shape 路徑線段的末端和連接處的裝飾。
* 這些呈現屬性包括:
* width:畫筆的寬度,是垂直於畫筆軌跡的測量值。 此寬度必須大於或等於 0.0f,0.0f爲最細線條。
* end caps:在未封閉子路徑和虛線線段的末端應用的一些裝飾。如果子路徑沒有 CLOSE 段,則在同一點上開始和結束的子路徑仍被認爲是未封閉的。
* 關於 CLOSE 段的更多信息,請參閱 SEG_CLOSE。三個不同的裝飾是:
* CAP_BUTT:無裝飾地結束未封閉的子路徑和虛線線段。
* CAP_ROUND:使用半徑等於畫筆寬度一半的圓形裝飾結束未封閉的子路徑和虛線線段。
* CAP_SQUARE:使用正方形結束未封閉的子路徑和虛線線段,正方形越過線段端點,並延長等於線條寬度一半的距離。
* line joins:在兩個路徑線段的交匯處,以及使用 SEG_CLOSE 封閉的子路徑端點的交匯處應用的裝飾。
* 三個不同的裝飾是:
* JOIN_BEVEL:通過直線連接寬體輪廓的外角,將路徑線段連接在一起。
* JOIN_MITER:擴展路徑線段的外邊緣,直到它們連接在一起。
* JOIN_ROUND:通過捨去半徑爲線長的一半的圓角,將路徑線段連接在一起。
* miter limit:對剪裁具有 JOIN_MITER 裝飾的線接合點的限制。當斜接長度與筆劃寬度的比大於 miterlimit 值時,需要剪裁線接合點。
* 斜接長度是斜接的對角線長度,即交匯處的內棱角和外棱角之間的距離。兩條線段形成的角度越小,斜接長度就越長,交匯處的角度就越尖銳。
* 默認 miterlimit 值爲 10.0f,它會使所有小於 11 度的角都被剪裁。剪裁斜接會使線接合點的裝飾變成斜角。 必須大於或等於 1.0f。
* dash attributes:關於如何通過在不透明和透明部分之間交替形成一個虛線模式的定義。 表示虛線模式的數組
* dash phase - 開始虛線模式的偏移量
*/
//虛線設置
g2.setStroke(new BasicStroke(1f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER,4.0f,new float[]{4.0f},0.0f));
//在此圖形上下文的座標系中使用當前顏色在點 (x1, y1) 和 (x2, y2) 之間畫一條線。 即繪製虛線
g2.drawLine((int)x,(int)(y+line),(int)x+158,(int)(y+line));
line+=heigth;
//設置商品清單
if(goods!=null && goods.size()>0){
for(Orders gdf:goods){
g2.drawString("寄件人姓名:",(float)x+15,(float)y+line);
g2.drawString(gdf.getSendname(),(float)x+60, (float)y+line);
line+=heigth;
g2.drawString("寄件電話:",(float)x+15,(float)y+line);
g2.drawString(gdf.getSendtel(),(float)x+60, (float)y+line);
line+=heigth;
g2.drawString("寄件地址:",(float)x+15,(float)y+line);
line+=heigth;
g2.drawString(gdf.getSendaddress(),(float)x+15,(float)y+line);
line+=heigth;
g2.drawString("收件人姓名:",(float)x+15,(float)y+line);
g2.drawString(gdf.getRecname(),(float)x+60,(float)y+line);
line+=heigth;
g2.drawString("收件人電話:",(float)x+15,(float)y+line);
g2.drawString(gdf.getRectel(),(float)x+60,(float)y+line);
line+=heigth;
g2.drawString("收件人地址:",(float)x+15,(float)y+line);
line+=heigth;
g2.drawString(gdf.getRecaddress(),(float)x+15,(float)y+line);
line+=heigth;
g2.drawString("商品類型:",(float)x+15,(float)y+line);
g2.drawString(gdf.getGoodstype(),(float)x+60,(float)y+line);
line+=heigth;
g2.drawString("配送費:",(float)x+15,(float)y+line);
g2.drawString(String.valueOf(gdf.getPrice()),(float)x+60,(float)y+line);
line += heigth;
}
}
g2.drawLine((int) x, (int)(y+line), (int) x + 158, (int)(y+line));
line += heigth+2;
g2.drawString("商品總數:"+"一"+ "件",(float)x+15,(float)y+line);
line += heigth;
//從新設置打印圖片的大小
//g2.drawImage(image, (int)x+5,200, width,height2, c);
if(cardNumber!=null && !"".equals(cardNumber)){
g2.drawString("當前會員:"+cardNumber,(float)x+15,(float)y+line);
line += heigth;
g2.drawString("積分:"+integral,(float)x+15,(float)y+line);
}
g2.drawString("時間:"+sdf.format(new Date()),(float)x+15,(float)y+line);
line += heigth;
g2.drawString("錢票請當面點清,離開櫃檯恕不負責",(float)x+15,(float)y+line);
switch (pageIndex) {
case 0:
return PAGE_EXISTS; //0
default:
return NO_SUCH_PAGE; //1
}
}
}
goods的集合爲你所要打印的實體類的泛型
package com.yc.util;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
/**
* 源辰信息
* 打印對象 -> 實現Printable接口即可調用打印機打印
* @author navy
* 2017年5月23日
*/
public class YcPrinter{
private SalesTicket salesTicket;
public YcPrinter(SalesTicket salesTicket){
this.salesTicket=salesTicket;
}
public void printer() {
try {
//Book 類提供文檔的表示形式,該文檔的頁面可以使用不同的頁面格式和頁面 painter
Book book = new Book(); //要打印的文檔
//PageFormat類描述要打印的頁面大小和方向
PageFormat pf = new PageFormat(); //初始化一個頁面打印對象
pf.setOrientation(PageFormat.PORTRAIT); //設置頁面打印方向,從上往下,從左往右
//設置打印紙頁面信息。通過Paper設置頁面的空白邊距和可打印區域。必須與實際打印紙張大小相符。
Paper paper = new Paper();
paper.setSize(158,30000);// 紙張大小
paper.setImageableArea(0,0,158,30000);// A4(595 X 842)設置打印區域,其實0,0應該是72,72,因爲A4紙的默認X,Y邊距是72
pf.setPaper(paper);
book.append(salesTicket,pf);
PrinterJob job = PrinterJob.getPrinterJob(); //獲取打印服務對象
job.setPageable(book); //設置打印類
job.print(); //開始打印
} catch (PrinterException e) {
e.printStackTrace();
}
}
}
然後就是調用這個功能,因爲是web項目我在控制層來調用打印功能
@RequestMapping(value = "printOrders.action",method =RequestMethod.POST)
@ResponseBody
public JsonMode printOrders(String sendname,String sendtel,String sendaddress,String recname,String rectel
,String recaddress,String goodstype,Double price,Long barcode){
JsonMode jsonMode = new JsonMode();
List<Orders> goods=new ArrayList<Orders>();
goods.add(new Orders(sendname,sendtel,sendaddress,recname,rectel,recaddress,goodstype,price));
SalesTicket stk=new SalesTicket(goods,"源辰信息",""+barcode,"3","38400","38400","0");
YcPrinter p=new YcPrinter(stk);
p.printer();
jsonMode.setCode(1);
return jsonMode;
}
至此我們可以調用windows默認的打印機把樣式文件打印到桌面上,如果要調用外部打印機需要自己去下載打印機對應的驅動來實現打印.
3細節描述(小坑)
1.打印的範圍一定要按自己打印機紙張的規模來,如果超出範圍(如字段的長度)打印機會報錯,但後臺是不會報異常的,於是打印機就會一直重複打印同一份文檔遇到超出範圍的地方便會報錯然後重新打印變成死循環,可以選擇斷開電源或者在右下角點開打印機的圖標終止打印的文檔.(在看後臺的時候,打印機打印了好幾份。。。所以浪費了很多紙)
2.調用drawImage可以實現打印圖片,顏色樣式是看你的打印機所決定。
獲取到圖片
Image src =Toolkit.getDefaultToolkit().getImage(Common.GITPATH+"\\"+orderId+".gif");
獲取圖片的長和寬
//獲取圖片的寬度
//int width=image.getWidth(c);
//獲取圖片的高度
//int height2=image.getHeight(c);
g2.drawImage(src,(int)x,40,c); 打印原圖 ,因爲圖片的原寬度超出了紙張,所以打印機會報錯誤
g2.drawImage(image, (int)x+5,200, width,height2, c);
第一個方法,只設置了圖片所打印的位置,圖片的尺寸是原圖如果範圍超過了紙張,那麼打印機就會報錯
第二個方法新加的兩個參數決定了打印圖片的寬度和高度,實現的圖片打印的縮放,但是對於條形碼會破壞它的結構,導致光槍可能掃描不出來。