目录

我的学习分享

记录精彩的程序人生

Java打印

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