Java获取配置打印机,并(佳博打印机)打印小票

因项目需求,需要打印类似超市的收银小票。

贴上代码,以供记录和参考。

一:获取电脑本地所有的打印服务。

@RequestMapping("/getPrint")
    @ResponseBody
    public Object getPrint(){
        PrintRequestAttributeSet printRequestAttributeSet = new HashPrintRequestAttributeSet();
        DocFlavor flavor = DocFlavor.BYTE_ARRAY.PNG;
        PrintService printService[] = PrintServiceLookup.lookupPrintServices(flavor,printRequestAttributeSet );
        //获取打印机列表
        List<String> printList = new ArrayList<>();
        for (int i = 0;i<printService.length;i++){
            printList.add(printService[i].getName());
        }
        return new ReportResponse(printList);
    }

返回结果:

{
    "header": {
        "code": "200",
        "memo": "success"
    },
    "result": [
        "Microsoft XPS Document Writer",
        "GP-U80250 Series",
        "Fax"
    ]
}

二:打印小票。

1:配置打印机

public class Printer {
    private SalesTicket salesTicket;

    public Printer(SalesTicket salesTicket) {
        this.salesTicket = salesTicket;
    }

    public void printer(String printerName) {
        //Book 类提供文档的表示形式,该文档的页面可以使用不同的页面格式和页面 painter
        //要打印的文档
        Book book = new Book();
        //PageFormat类描述要打印的页面大小和方向
        //初始化一个页面打印对象
        PageFormat pf = new PageFormat();
        //设置页面打印方向,从上往下,从左往右
        pf.setOrientation(PageFormat.PORTRAIT);
        //设置打印纸页面信息。通过Paper设置页面的空白边距和可打印区域。必须与实际打印纸张大小相符。
        Paper paper = new Paper();
        MediaSize mediaSize = new MediaSize(80, 3276, MediaPrintableArea.MM);
        //paper.setSize(200,30000);// 纸张大小
        //paper.setImageableArea(0,0,200,30000);// A4(595 X 842)设置打印区域,其实0,0应该是72,72,因为A4纸的默认X,Y边距是72
        paper.setSize((double) mediaSize.getX(25400) * 72.0D, (double) mediaSize.getY(25400) * 72.0D);
        paper.setImageableArea(0.0D, 0.0D, paper.getWidth(), paper.getHeight());
        pf.setPaper(paper);
        book.append(salesTicket, pf);
        HashAttributeSet hs = new HashAttributeSet();
        hs.add(new PrinterName(printerName, null));
        PrintService[] pss = PrintServiceLookup.lookupPrintServices(null, hs);
        if (pss.length == 0) {
            throw new ServiceException(ErrorCodes.PRINTER_NOTHAVE);
        }
        //获取打印服务对象
        PrinterJob job = PrinterJob.getPrinterJob();
        //设置打印类
        job.setPageable(book);
        try {
            // 添加指定的打印机
            job.setPrintService(pss[0]);
            // 打印执行
            job.print();
        } catch (PrinterException e) {
            throw new ServiceException(ErrorCodes.PRINTER_ERROR);
        }
    }
}

2:配置小票格式以及数据

public class SalesTicket implements Printable {
    private List<GoodsInfoPo> goods;
    private List<PayTypePo> payTypePos;
    private String deptNum;
    private String orderNum;
    private Integer peopleCount;
    private String orgName;
    private String turntableInfo;
    private Long orderPeopleNum;
    private String orderPeopleName;
    private String openTime;
    private String payTime;
    private Integer payCount;
    private Double shouldPayAmount;
    private Double truePayAmount;
    private Double discountAmount;

    private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE");

    public SalesTicket(List<GoodsInfoPo> goods, List<PayTypePo> payTypePos, String deptNum, String orderNum, Integer peopleCount, String orgName, String turntableInfo, Long orderPeopleNum, String orderPeopleName, String openTime, String payTime, Integer payCount, Double shouldPayAmount, Double truePayAmount, Double discountAmount) {
        super();
        this.goods = goods;
        this.payTypePos = payTypePos;
        this.deptNum = deptNum;
        this.orderNum = orderNum;
        this.peopleCount = peopleCount;
        this.orgName = orgName;
        this.turntableInfo = turntableInfo;
        this.orderPeopleNum = orderPeopleNum;
        this.orderPeopleName = orderPeopleName;
        this.openTime = openTime;
        this.payTime = payTime;
        this.payCount = payCount;
        this.shouldPayAmount = shouldPayAmount;
        this.truePayAmount = truePayAmount;
        this.discountAmount = discountAmount;
    }

    @Override
    public String toString() {
        return "SalesTicket{" +
                "goods=" + goods +
                ", payTypePos=" + payTypePos +
                ", deptNum='" + deptNum + '\'' +
                ", orderNum='" + orderNum + '\'' +
                ", peopleCount=" + peopleCount +
                ", orgName='" + orgName + '\'' +
                ", turntableInfo='" + turntableInfo + '\'' +
                ", orderPeopleNum=" + orderPeopleNum +
                ", orderPeopleName='" + orderPeopleName + '\'' +
                ", openTime='" + openTime + '\'' +
                ", payTime='" + payTime + '\'' +
                ", payCount=" + payCount +
                ", shouldPayAmount=" + shouldPayAmount +
                ", truePayAmount=" + truePayAmount +
                ", discountAmount=" + discountAmount +
                '}';
    }

    /**
     * 打印方法
     * 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);
        //虚线设置
        g2.setStroke(new BasicStroke(0.5f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER,1.0f,new float[]{1.0f},0.0f));

        //打印起点坐标
        //返回与此 PageFormat相关的 Paper对象的可成像区域左上方点的 x坐标。
        double x= pageFormat.getImageableX();
        //返回与此 PageFormat相关的 Paper对象的可成像区域左上方点的 y坐标。
        double y= pageFormat.getImageableY();
        //Font.PLAIN: 普通样式常量  	Font.ITALIC 斜体样式常量	Font.BOLD 粗体样式常量。
        //根据指定名称、样式和磅值大小,创建一个新 Font。
        Font font = new Font("宋体",Font.BOLD,12);
        //设置标题打印字体
        g2.setFont(font);
        //获取字体的高度
        float heigth = font.getSize2D();
        //设置小票的标题标题
        FontMetrics fm = g2.getFontMetrics(font);
        int textWidth = fm.stringWidth(orgName);
        //获取标题居中宽度
        int widthX = (210 - textWidth) / 2;
        g2.drawString(orgName,(float)x+widthX,(float)y+heigth);
        heigth+=heigth;
        textWidth = fm.stringWidth("结算单");
        widthX = (210 - textWidth) / 2;
        g2.drawString("结算单",(float)x+widthX,(float)y+heigth);
        heigth+=heigth;
        g2.drawString("桌号:"+deptNum,(float)x+5 , (float)y+heigth);
        textWidth = fm.stringWidth("人数:"+peopleCount);
        g2.drawString("人数:"+peopleCount, 200 - textWidth, (float)y+heigth);
        //0.25不能改
        heigth+=heigth * 0.25;
        //下一行开始打印的高度
        float line = heigth;
        g2.drawLine((int)x,(int)(y+line),(int)x+227,(int)(y+line));
        line+=15;
        //设置正文字体
        g2.setFont(new Font("宋体", Font.PLAIN,8));
        // 字体高度
        heigth = font.getSize2D();
        //设置单号
        g2.drawString("单号:"+orderNum,(float)x+5,(float)y+line);
        //设置收银员
        textWidth = fm.stringWidth("收银员:"+orderPeopleName);
        g2.drawString("收银员:"+orderPeopleName,210 - textWidth,(float)y+line);
        line+=heigth;
        //设置订台人编号
        g2.drawString("订台人编号:"+orderPeopleNum,(float)x+5,(float)y+line);
        //设置订台人
        textWidth = fm.stringWidth("订台人:"+orderPeopleName);
        g2.drawString("订台人:"+orderPeopleName,210 - textWidth,(float)y+line);
        line+=heigth;

        //设置开台时间
        g2.drawString("开台时间:"+openTime,(float)x+5,(float)y+line);
        line+=heigth;
        //设置结算时间
        g2.drawString("结算时间:"+payTime,(float)x+5,(float)y+line);
        line+=heigth;

        g2.drawLine((int)x,(int)(y+line),(int)x+227,(int)(y+line));
        line+=heigth;

        //设置标题
        g2.drawString("商品名称",(float)x+5, (float)y+line);
        g2.drawString("数量",(float)x+75, (float)y+line);
        g2.drawString("规格",(float)x+100, (float)y+line);
        g2.drawString("单价",(float)x+140, (float)y+line);
        g2.drawString("小计",(float)x+170, (float)y+line);
        line+=heigth;

        g2.drawLine((int)x,(int)(y+line),(int)x+227,(int)(y+line));
        line+=heigth;
        //设置商品清单
        if(goods!=null && goods.size()>0){
            for(GoodsInfoPo gdf:goods){
                g2.drawString(gdf.getGname(),(float)x+5, (float)y+line);
                g2.drawString(gdf.getCount().toString(),(float)x+80, (float)y+line);
                g2.drawString(gdf.getSpec(),(float)x+105, (float)y+line);
                g2.drawString(gdf.getPrice(),(float)x+140,(float)y+line);
                g2.drawString(gdf.getAmount(),(float)x+170,(float)y+line);
                line += heigth + 3;
                if (gdf.getPrintGoodsComboPoList()!=null && gdf.getPrintGoodsComboPoList().size()>0){
                    for (PrintGoodsComboPo printGoodsComboPo:gdf.getPrintGoodsComboPoList()){
                        g2.drawString(printGoodsComboPo.getName(), (float)x+15, (float)y+line);
                        g2.drawString(printGoodsComboPo.getCount().toString(),(float)x+80 ,(float)y+line );
                        g2.drawString(printGoodsComboPo.getSpec(), (float)x+105, (float)y+line);
                        line += heigth + 3;
                    }
                }
            }
        }
        g2.drawLine((int) x, (int)(y+line), (int) x + 227, (int)(y+line));
        line += heigth;
        g2.drawString("结算明细",(float)x+5,(float)y+line);
        line += heigth;
        g2.drawLine((int) x, (int)(y+line), (int) x + 227, (int)(y+line));
        line += heigth;
        if(payTypePos!=null && payTypePos.size()>0){
            for(PayTypePo payTypePo:payTypePos){
                if (payTypePo.getAmount() != 0){
                    g2.drawString(payTypePo.getCnlName(),(float)x+5,(float)y+line);
                    textWidth = fm.stringWidth(payTypePo.getAmount().toString());
                    g2.drawString("¥ "+payTypePo.getAmount().toString(),200 - textWidth,(float)y+line);
                }
            }
        }
        line += heigth;
        g2.drawLine((int) x, (int)(y+line), (int) x + 227, (int)(y+line));
        line += heigth;
        g2.drawString("账单合计:¥ "+shouldPayAmount, (float)x+110, (float)y+line);
        line += heigth;
        g2.drawString("实收金额:¥ "+truePayAmount, (float)x+110, (float)y+line);
        line += heigth;
        g2.drawString("赠送合计:¥ 0", (float)x+110, (float)y+line);
        line += heigth;
        g2.drawString("折扣合计:¥ "+discountAmount, (float)x+110, (float)y+line);
        line += heigth;
        g2.drawString("券优惠合计:¥ 0", (float)x+110, (float)y+line);
        line += heigth;
        g2.drawString("积分合计:¥ 0", (float)x+110, (float)y+line);
        line += heigth;
        g2.drawString("赠送比例:0%", (float)x+110, (float)y+line);
        line += heigth;
        g2.drawString("抵消比例:999%", (float)x+110, (float)y+line);
        line += heigth;
        g2.drawString("换桌:"+turntableInfo,(float)x+5,(float)y+line);
        line += heigth;
        g2.drawString("打印时间:"+payTime,(float)x+5,(float)y+line);
        line += heigth;
        g2.drawString("结账次数:"+payCount,(float)x+5,(float)y+line);
        switch (pageIndex) {
            case 0:
                return PAGE_EXISTS;
            default:
                return NO_SUCH_PAGE;
        }
    }
}

3:实现接口:

@RequestMapping("/printReport")
    @ResponseBody
    public Object testPrint(@Validated @RequestBody PrintFrom printFrom){
        Map<String,Object> map = new HashMap<>(1);
        List<PrintContentPo> printContentPoList = printMapper.getPrintContent(printFrom);
        for (PrintContentPo printContentPo:printContentPoList){
            List<GoodsInfoPo> goods = printGoodsMapper.getPrintGoods(printContentPo.getPayId());
            List<PayTypePo> payTypePos = printPayTypeMapper.getPayTypeList(printContentPo.getPayId());
            for (GoodsInfoPo goodsInfoPo:goods){
                if (goodsInfoPo.getIsCombo().equals(ComboType.COMBO.getCode())){
                    List<PrintGoodsComboPo> comboPoList = printGoodsComboMapper.getGoodsCombo(printContentPo.getPayId(),goodsInfoPo.getBarConfigId());
                    goodsInfoPo.setPrintGoodsComboPoList(comboPoList);
                }
            }
            SalesTicket stk=new SalesTicket(goods,payTypePos,printContentPo.getDeptNum(),printContentPo.getOrderNum(),printContentPo.getPeopleCount(),printContentPo.getOrgName(),printContentPo.getTurntableInfo(),printContentPo.getOrderPeopleNum(),printContentPo.getOrderPeopleName(),printContentPo.getOpenTime(),printContentPo.getPayTime(),printContentPo.getPayCount(),printContentPo.getShouldPayAmount(),printContentPo.getTruePayAmount(),printContentPo.getDiscountAmount());
            Printer p=new Printer(stk);
            p.printer(printFrom.getPrinterName());
            map.put("result","success" );
        }
        return new ReportResponse(map);
    }

4:注意事项:

主要实现方法就这些。

实体类,mapper和sql就不展示了。

另外,设置小票格式根据自己的需求配置。

谢谢!