package com.xxx;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintException;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.SimpleDoc;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.JobName;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.OrientationRequested;
import javax.swing.JOptionPane;
import net.iharder.Base64;
import org.apache.commons.lang3.StringUtils;
public class QRCodePrinter implements Printable {
static final String VERSION = "v1.2.2";
static final Map<String, String> PARAMS = new HashMap<>();
static int infoCount = 0;
static Set<String> brotherPrinterNames = new HashSet<>();
@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
System.out.println("print pageIndex=" + pageIndex + ", infoCount=" + infoCount);
if (pageIndex < infoCount) {
try {
Map map = new HashMap();
map.put(EncodeHintType.MARGIN, 0); // 去掉边距
// warning: 此处必须用小写的utf-8,大写的UTF-8用扫码枪扫码前面会出现\00026等字符
map.put(EncodeHintType.CHARACTER_SET, "utf-8"); //使用小写的编码,大写会出现]Q2\000026开头内容
map.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 容错率
// 20181106, cuizhf, 实际二维码后补逗号分割
BitMatrix matrix = new MultiFormatWriter().encode(PARAMS.get("qrCode" + pageIndex) + ",", BarcodeFormat.QR_CODE, 42, 42, map);
matrix = updateBit(matrix, 0);
BufferedImage bi = MatrixToImageWriter.toBufferedImage(matrix);
BufferedImage code = zoomInImage(bi, 42, 42);//根据size放大、缩小生成的二维码
// @see http://blog.163.com/laowu_000/blog/static/47198890200962144032793/
// 360*360dpi,约相当于1毫米14像素,宽24*14=336,高50*14=700
Graphics2D g2d = (Graphics2D) graphics;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
g2d.drawImage(code, 0, 0, 42, 42, null);
g2d.setColor(Color.black);
g2d.setFont(new Font("宋体", Font.PLAIN, 7));
int y = 6;
// 送检单位(占两行)
String[] clientNames = strSplit(g2d.getFontMetrics(), PARAMS.get("clientName" + pageIndex), 88);
if (clientNames.length >= 2) {
g2d.drawString(clientNames[0], 44, y);
y += 7;
if (StringUtils.isNotBlank(clientNames[1])) {
g2d.drawString(clientNames[1], 44, y);
}
} else {
g2d.drawString(clientNames[0], 44, y);
y += 7;
}
// 器具名称
y += 7;
g2d.drawString(PARAMS.get("instName" + pageIndex), 44, y);
// 型号
y += 7;
g2d.drawString(PARAMS.get("model" + pageIndex), 44, y);
// 出厂编号
y += 7;
g2d.drawString(PARAMS.get("factorySerial" + pageIndex), 44, y);
// 有无附件、参数
y += 9;
g2d.drawString(PARAMS.get("attachment" + pageIndex) + " " + PARAMS.get("param" + pageIndex), 44, y);
// 二维码
y += 7;
g2d.drawString(PARAMS.get("qrCode" + pageIndex), 0, y);
g2d.setFont(new Font("宋体", Font.PLAIN, 8));
y += 10;
g2d.drawString(PARAMS.get("companyName"), 20, y);
return Printable.PAGE_EXISTS;
} catch (WriterException ex) {
JOptionPane.showMessageDialog(null, "打印异常:" + ex.getMessage());
}
}
return Printable.NO_SUCH_PAGE;
}
public static void main(String[] args) {
System.out.println("version=" + VERSION);
// 将参数读入map方便后续处理
for (String arg : args) {
String key = StringUtils.substringBefore(arg, "=");
String value = StringUtils.substringAfter(arg, "=");
System.out.println("解码前: key=" + key + ", value=" + value);
try {
value = new String(Base64.decode(value), "UTF-8"); // 对value进行解码
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "解码异常:" + ex.getMessage());
}
System.out.println("解码后: key=" + key + ", value=" + value);
if (StringUtils.equals(key, "info")) {
String[] info = StringUtils.splitPreserveAllTokens(value, "|");
System.out.println("info.length" + info.length);
if (info.length != 7) {
JOptionPane.showMessageDialog(null, "参数异常: key=" + key + ", value=" + value);
return;
}
PARAMS.put("clientName" + infoCount, StringUtils.trimToEmpty(info[0]));
PARAMS.put("instName" + infoCount, StringUtils.trimToEmpty(info[1]));
PARAMS.put("model" + infoCount, StringUtils.trimToEmpty(info[2]));
PARAMS.put("factorySerial" + infoCount, StringUtils.trimToEmpty(info[3]));
PARAMS.put("attachment" + infoCount, StringUtils.trimToEmpty(info[4]));
PARAMS.put("param" + infoCount, StringUtils.trimToEmpty(info[5]));
PARAMS.put("qrCode" + infoCount, StringUtils.trimToEmpty(info[6]));
System.out.println("qrcode:" + info[6]);
infoCount++;
}
if (StringUtils.equals(key, "brotherPrinterName")) {
brotherPrinterNames.add(StringUtils.trimToEmpty(value));
}
PARAMS.put(key, value);
}
if (!PARAMS.containsKey("companyName")) {
PARAMS.put("companyName", "北京东方计量测试研究所");
}
// 将Brother PT-9700PC、Brother PT-P900默认加入打印机列表
brotherPrinterNames.add("Brother PT-9700PC");
brotherPrinterNames.add("Brother PT-P900");
System.out.println("infoCount=" + infoCount);
for (String brotherPrinter : brotherPrinterNames) {
System.out.println("brotherPrinter=" + brotherPrinter);
}
// just for test
if (infoCount == 0) {
PARAMS.put("clientName" + infoCount, "xxxxxxxx");
PARAMS.put("instName" + infoCount, "xxxxx");
PARAMS.put("model" + infoCount, "523698");
PARAMS.put("factorySerial" + infoCount, "ML462");
PARAMS.put("attachment" + infoCount, "xxxxx");
PARAMS.put("param" + infoCount, "xxxxxx");
PARAMS.put("qrCode" + infoCount, "180000590012");
infoCount++;
}
DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
// locate a print service that can handle the request
PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, null);
for (PrintService service : services) {
System.out.println(service.getName());
if (brotherPrinterNames.contains(service.getName())) {
try {
System.out.println("found printer: " + service.getName());
// create a print job for the chosen service
DocPrintJob job = service.createPrintJob();
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(OrientationRequested.LANDSCAPE);
aset.add(new Copies(1));
aset.add(new JobName("qrcode print job", null));
aset.add(new MediaPrintableArea(0, 0, 24, 48, MediaPrintableArea.MM));
job.print(new SimpleDoc(new QRCodePrinter(), flavor, null), aset);
break;
} catch (PrintException ex) {
JOptionPane.showMessageDialog(null, "打印异常:" + ex.getMessage());
}
}
}
}
/**
* 将某个字符串切割成适合显示长度的数组
*
* @param fontMetrics
* @param strSrc 待切割的文字
* @param width 每行的最大宽度
* @return 处理后的结果字符串数组
*/
public static String[] strSplit(FontMetrics fontMetrics, String strSrc, int width) {
int totalWidth = fontMetrics.stringWidth(strSrc);
int totalLines = totalWidth / width;
if (totalWidth % width != 0) {
totalLines++;
}
String[] lines = new String[totalLines];
int lineIndex = 0;
int startIndex = 0;
int length = strSrc.length(); // 源字符串包含多少个字符
int lineWidth = 0;
int charWidth = 0;
for (int i = 0; i < length; i++) {
charWidth = fontMetrics.charWidth(strSrc.charAt(i));
if (lineWidth + charWidth > width && i < length - 1) {
lines[lineIndex++] = StringUtils.substring(strSrc, startIndex, i);
startIndex = i;
lineWidth = 0;
} else if (i == length - 1) {
// 最后一行,且不足一行
lines[lineIndex++] = StringUtils.substring(strSrc, startIndex);
}
lineWidth += charWidth;
}
return lines;
}
/**
* 因为二维码边框设置那里不起作用,不管设置多少,都会生成白边,所以根据网上的例子进行修改,自定义控制白边宽度,该方法生成自定义白边框后的bitMatrix;
* @param matrix
* @param margin
* @return
*/
public static BitMatrix updateBit(BitMatrix matrix, int margin) {
int tempM = margin * 2;
int[] rec = matrix.getEnclosingRectangle(); //获取二维码图案的属性
int resWidth = rec[2] + tempM;
int resHeight = rec[3] + tempM;
BitMatrix resMatrix = new BitMatrix(resWidth, resHeight); // 按照自定义边框生成新的BitMatrix
resMatrix.clear();
for (int i = margin; i < resWidth - margin; i++) { //循环,将二维码图案绘制到新的bitMatrix中
for (int j = margin; j < resHeight - margin; j++) {
if (matrix.get(i - margin + rec[0], j - margin + rec[1])) {
resMatrix.set(i, j);
}
}
}
return resMatrix;
}
/**
* 图片放大缩小
*/
public static BufferedImage zoomInImage(BufferedImage originalImage, int width, int height){
BufferedImage newImage = new BufferedImage(width,height,originalImage.getType());
Graphics g = newImage.getGraphics();
g.drawImage(originalImage, 0,0,width,height,null);
g.dispose();
return newImage;
}
}