在开发二维码打印的过程中走过几次弯路,所以在这里特意将其记录下来留作备忘。一开始参考其他博主写的文章,有介绍通过编写JAVA后台代码来获取本地默认打印机的驱动实现打印。BUT!这样就导致在本地开发测试时看似一切正常,一旦项目部署到linux环境下,就会完全失效了(JAVA后台代码去获取linux本地的打印机驱动)。还有介绍并提供编写的插件的(不甚了解这块),鉴于时间要求比较苛刻,那就简单的来吧。
需求:生成带水印效果的二维码图片,可以批量预览,并连接打印机批量打印。
开发思路:
1.编写二维码生成工具类,实现二维码图片的生成
2.提供二维码打印前的预览
3.通过隐藏的iframe实现打印(简单粗暴)
以下是自己编写的一个小案例,可以直接运行测试,并提供了code下载。如果有其它更好的实现方式,也希望大家多提出宝贵的意见。
一、项目结构
-
二、主要CODE
1.MyQRUtils.java 二维码工具类
1 package com.webprint.qr.tools;
2
3 import java.awt.Color;
4 import java.awt.Font;
5 import java.awt.Graphics2D;
6 import java.awt.image.BufferedImage;
7 import java.io.File;
8 import java.io.IOException;
9 import java.io.OutputStream;
10 import java.util.Hashtable;
11
12 import javax.imageio.ImageIO;
13
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16
17 import com.google.zxing.BarcodeFormat;
18 import com.google.zxing.EncodeHintType;
19 import com.google.zxing.MultiFormatWriter;
20 import com.google.zxing.WriterException;
21 import com.google.zxing.common.BitMatrix;
22 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
23
24 public class MyQRUtils{
25
26 private static final Log logger = LogFactory.getLog(MyQRUtils.class);
27
28 private static final int BLACK = 0xFF000000;
29 private static final int WHITE = 0xFFFFFFFF;
30 private static final int LogoPart = 4;
31
32 /**
33 * 生成二维码前的配置信息
34 * @param content 生成二维码图片内容
35 * @param width 二维码图片的宽度
36 * @param height 二维码图片的高度
37 * @return
38 */
39 public static BitMatrix setBitMatrix(String content,int width,int height){
40 Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
41 hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
42 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); //指定纠错等级
43 BitMatrix bitMatrix=null;
44 try {
45 bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
46 } catch (WriterException e) {
47 logger.error("生成二维码错误",e);
48 }
49 return bitMatrix;
50 }
51
52 /**
53 * 将LOGO图片放在二维码中间(水印效果)
54 * 将生成的图片以流的形式输出到页面展示
55 * @param matrix BitMatrix
56 * @param format 图片格式
57 * @param outStream 输出流
58 * @param logoPath LOGO地址
59 * @param showBottomText 二维码图片底部需要显示的问题
60 * @throws IOException
61 */
62 public static void megerToFile(BitMatrix matrix,String format,OutputStream outStream,String logoPath,String showBottomText) throws IOException {
63 BufferedImage image = toBufferedImage(matrix);
64 Graphics2D gs = image.createGraphics();
65
66 //1.加入LOGO水印效果
67 if(null != logoPath && !"".equals(logoPath)){
68 //1.1 载入LOGO图片
69 BufferedImage logoImg = ImageIO.read(new File(logoPath));
70 //1.2 考虑到LOGO图片贴到二维码中,建议大小不要超过二维码的1/5;
71 int width = image.getWidth() / LogoPart;
72 int height = image.getHeight() / LogoPart;
73 //1.3 LOGO居中显示
74 int x = (image.getWidth() - width) / 2;
75 int y = (image.getHeight() - height) / 2;
76 gs.drawImage(logoImg, x, y, logoImg.getWidth(), logoImg.getHeight(), null);
77 logoImg.flush();
78 }
79 //2.二维码图片底部插入文字
80 if(null != showBottomText && !"".equals(showBottomText)){
81 //2.1 设置字体样式
82 Font font = new Font("微软雅黑", Font.PLAIN, 14);
83 gs.setColor(Color.BLACK);
84 gs.setFont(font);
85 //2.2 字体显示位置
86 int x = (image.getWidth() - getWatermarkLength(showBottomText, gs))/2;
87 int y = image.getHeight()-2;
88 gs.drawString(showBottomText, x, y);
89 }
90 gs.dispose();
91 ImageIO.write(image, format, outStream);
92 }
93
94 /**
95 * 将LOGO图片放在二维码中间(水印效果)
96 * 将生成的图片生成到本地硬盘路径下
97 * @param matrix BitMatrix
98 * @param format 图片格式
99 * @param imagePath 图片存放路径
100 * @param logoPath LOGO地址
101 * @param showBottomText 二维码图片底部需要显示的问题
102 * @throws IOException
103 */
104 public static void megerToFile2(BitMatrix matrix,String format,String imagePath,String logoPath,String showBottomText) throws IOException {
105 BufferedImage image = toBufferedImage(matrix);
106 Graphics2D gs = image.createGraphics();
107
108 //1.加入LOGO水印效果
109 if(null != logoPath && !"".equals(logoPath)){
110 BufferedImage logoImg = ImageIO.read(new File(logoPath));
111 int width = image.getWidth() / LogoPart;
112 int height = image.getHeight() / LogoPart;
113 int x = (image.getWidth() - width) / 2;
114 int y = (image.getHeight() - height) / 2;
115 gs.drawImage(logoImg, x, y, logoImg.getWidth(), logoImg.getHeight(), null);
116 logoImg.flush();
117 }
118
119 //2.二维码图片底部插入文字
120 if(null != showBottomText && !"".equals(showBottomText)){
121 //2.1 设置字体样式
122 Font font = new Font("微软雅黑", Font.PLAIN, 14);
123 gs.setColor(Color.BLACK);
124 gs.setFont(font);
125 //2.2 字体显示位置
126 int x = (image.getWidth() - getWatermarkLength(showBottomText, gs))/2;
127 int y = image.getHeight()-2;
128 gs.drawString(showBottomText, x, y);
129 }
130 gs.dispose();
131 ImageIO.write(image, format, new File(imagePath));
132 }
133
134 /**
135 * 获取水印字体的长度
136 * @param fontString
137 * @param gs
138 * @return
139 */
140 public static int getWatermarkLength(String fontString,Graphics2D gs){
141 return gs.getFontMetrics(gs.getFont()).charsWidth(fontString.toCharArray(),0,fontString.length());
142 }
143
144 public static BufferedImage toBufferedImage(BitMatrix matrix){
145 int width = matrix.getWidth();
146 int height = matrix.getHeight();
147 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
148
149 for(int x=0;x<width;x++){
150 for(int y=0;y<height;y++){
151 image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
152 }
153 }
154 return image;
155 }
156 }
说明:二维码实现方式有多种,此处代码可根据具体需求具体开发。
2.WebPrintController.java SpringMVC的controller层代码
1 package com.webprint.qr.controller;
2
3 import java.io.IOException;
4 import java.io.OutputStream;
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15 import org.springframework.stereotype.Controller;
16 import org.springframework.ui.Model;
17 import org.springframework.web.bind.annotation.RequestMapping;
18
19 import com.google.zxing.common.BitMatrix;
20 import com.webprint.qr.tools.MyQRUtils;
21
22 @Controller("WebPrintController")
23 @RequestMapping("/qrPrint")
24 public class WebPrintController {
25
26 private static final Log logger = LogFactory.getLog(MyQRUtils.class);
27
28 /**
29 * 二维码预览页面
30 * @param model
31 * @return
32 */
33 @RequestMapping("/showList.do")
34 public String showQRList(Model model){
35 //模拟获取数据库数据
36 List listData = new ArrayList();
37 StringBuffer ids = new StringBuffer();
38 String code = "print00";
39 for (int i = 0; i < 3; i++) {
40 Map<String,String> map = new HashMap<String,String>(); //模拟VO
41 map.put("id",code+""+i);
42 ids.append(code+""+i).append(",");
43 listData.add(map);
44 }
45 model.addAttribute("showListData", listData);
46 model.addAttribute("ids", ids);
47 return "showQR";
48 }
49
50 /**
51 * 二维码打印页面
52 * 隐藏在iframe中
53 * @param model
54 * @return
55 */
56 @RequestMapping("/printEWM.do")
57 public String printQRFrame(Model model,HttpServletRequest request){
58 String ids = request.getParameter("ids");
59 model.addAttribute("ids", ids);
60 return "printFrameQR";
61 }
62
63 /**
64 * 显示二维码图片
65 * @param request
66 * @param response
67 * @throws Exception
68 */
69 @RequestMapping("/showEWMImage.do")
70 public void showImageByType(HttpServletRequest request,HttpServletResponse response){
71 String id = request.getParameter("showID"); //ID
72
73 //此处可从数据库中获取内容
74 String content ="打印二维码\n打印测试\nID:"+id;
75
76 OutputStream outStream = null;
77 try {
78 outStream = response.getOutputStream();
79 String format = "jpg";
80 String bottomText = "一路不停"; //水印文字
81
82 BitMatrix bitMatrix = MyQRUtils.setBitMatrix(content, 180 , 180);
83 //暂时不使用LOGO图片
84 MyQRUtils.megerToFile(bitMatrix, format, outStream, null,bottomText);
85
86 } catch (Exception e) {
87 logger.error("二维码生成失败", e);
88 }finally{
89 try {
90 if(outStream!=null){
91 outStream.flush();
92 outStream.close();
93 }
94 } catch (IOException e) {
95 logger.error("关闭数据流失败", e);
96 }
97
98 }
99 }
100
101
102 }
说明:二维码图片的展示及其打印以流的方式操作,这样就无需将图片保存在服务器上了。
3.index.jsp 首页
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
3 <html>
4 <head>
5 <meta http-equiv="pragma" content="no-cache">
6 <meta http-equiv="cache-control" content="no-cache">
7 <meta http-equiv="expires" content="0">
8 <%@include file="/include.jsp" %>
9 <title></title>
10 </head>
11 <iframe id="iframePrintEWM" frameborder="0" width="0" height="0"></iframe>
12 <body>
13 <div> 1.二维码预览</br>
14 2.二维码打印
15 </div></br>
16 </br></br>
17 <hr>
18 </br>
19 <input type="button" id ="showQRList" name="showQRList" value="二维码预览" οnclick="showQRList()"/>
20 <div id='divDialog'></div>
21 <div id='divPrintDialog'></div>
22 </body>
23 <script type="text/javascript">
24 var height = $("body").height();
25 var width = $("body").width();
26
27 //二维码预览
28 function showQRList(){
29 var url ="${rootPath}/qrPrint/showList.do";
30 $("#divDialog").dialog({
31 title:"预览",
32 width:260,
33 height:500,
34 top:(height-500)/2,
35 left:(width-260)/2,
36 href:url,
37 cache:false,
38 closed:false,
39 modal:true
40 });
41 }
42
43 //二维码打印
44 function printEWM(ids){
45 $('#divDialog').window('close');
46 var url = '${rootPath}/qrPrint/printEWM.do?ids='+ids;
47 $('#iframePrintEWM').attr("src", url);
48 }
49 </script>
50 </html>
说明:将iframe放在body标签外,并将其宽高和border都设置为0,达到隐藏的效果。
4.showQR.jsp 二维码预览页
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 <html>
3 <head>
4 <%@include file="/include.jsp" %>
5 <title>showQRList</title>
6 </head>
7 <body>
8 <div >
9 <div align="left" >
10 <a href="javascript:printEWM('${ids }');" class="easyui-linkbutton" iconCls="icon-printer" style="width: 95px">二维码打印</a>
11 </div>
12 </div>
13 <div>
14 <c:forEach var="printID" items="${showListData}" varStatus="lis">
15 <table align="center" class="codetable" cellpadding="0" style="float: left;border:1px solid #333333;">
16 <tr>
17 <td align="center">
18 <div id="bcTarget${lis.index }">
19 <img src="${rootPath}/qrPrint/showEWMImage.do?showID=${printID.id}">
20 </div>
21 </td>
22 </tr>
23 </table>
24 </c:forEach>
25 </div>
26 </body>
27 </html>
说明:如果项目中不需要自定义预览,此处代码可以去掉。
5.printFrameQR.jsp 隐藏的iframe页,用于打印(非常简单,非常强大)
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 <html>
3 <head>
4 <%@include file="/include.jsp" %>
5 <title>printFrameQR</title>
6 </head>
7 <body οnlοad="this.focus();this.print()">
8 <div>
9 <c:forEach var="printID" items="${ids}" varStatus="lis">
10 <div id="bcTarget${lis.index }">
11 <img src="${rootPath}/qrPrint/showEWMImage.do?showID=${printID}">
12 </div></br>
13 </c:forEach>
14 </div>
15 </body>
16 </html>
说明:简单粗暴的方式,打印的关键就在于body标签中的 οnlοad="this.focus();this.print()"
三、预览效果
注意对IE打印页面的设置;
四、code下载部署说明
以上是周末自己编写的小案例,供大家参考。
环境:myeclipse8.5 + tomcat6 + jdk7 +jQuery EasyUI + core-3.3.0.jar(google zxing)