客户嫌弃调用浏览器打印太low,需要在页面上自己设置打印的样式,考虑写个服务做到客户机上,前端直接调用本地服务接口来打印。可以采用java或者go语言。
实现
java 是提供打印机调用相关接口PrintService的,所在的包是javax.print.PrintService,但是只能打印PNG文件apache 的pdfbox 封装了Java Service Print框架的打印功能, 则实现了pdf的打印。参考 java 调用打印机打印pdf
PDFbox 需要引入如下依赖:
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.8</version> </dependency>
我们目前的思路是可以通过前端页面选择打印机,所以需要编写一个获取本地所有打印机的接口
/** * 获取本地打印机列表 * @return */ public static List<String> getServicesName(){ PrintService[] services=PrinterJob.lookupPrintServices(); if(ObjectUtils.isEmpty(services)){ return new ArrayList<>(); }else{ System.out.println(services); return Arrays.stream(services).map(PrintService::getName).collect(Collectors.toList()); } }
然后给前端写个本地接口
/** * 查询本机打印机列表 * @return */ @PostMapping("/getPrintList") public ApiResult<List<String>> getPrintList(){ return new ApiResult<>(PrintUtil.getServicesName()); }
这样就实现了获取打印机列表的功能。下面就是如何根据打印机名称调用本地的打印机打印前端传来的文件了
/** * 打印PDF * @param stream * @param printerName * @param paper * @return * @throws Exception */ public static ApiResult<Boolean> PDFprint(InputStream stream, String printerName, Paper paper) throws Exception { PDDocument document = null; try { document = PDDocument.load(stream); PrinterJob printJob = PrinterJob.getPrinterJob(); printJob.setJobName("2010"); if (printerName != null) { // 查找并设置打印机 //获得本台电脑连接的所有打印机 PrintService[] printServices = PrinterJob.lookupPrintServices(); if(printServices == null || printServices.length == 0) { System.out.print("打印失败,未找到可用打印机,请检查。"); return new ApiResult<>(false,-1,"打印失败,未找到可用打印机,请检查。"); } PrintService printService = null; //匹配指定打印机 for (int i = 0;i < printServices.length; i++) { System.out.println(printServices[i].getName()); if (printServices[i].getName().contains(printerName)) { printService = printServices[i]; break; } } if(printService!=null){ printJob.setPrintService(printService); }else{ System.out.print("打印失败,未找到名称为" + printerName + "的打印机,请检查。"); return new ApiResult<>(false,-1,"\"打印失败,未找到名称为\" + printerName + \"的打印机,请检查。\""); } } //设置纸张及缩放 PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE); //设置多页打印 Book book = new Book(); PageFormat pageFormat = new PageFormat(); //设置打印方向 pageFormat.setOrientation(PageFormat.PORTRAIT);//纵向 pageFormat.setPaper(paper);//设置纸张 book.append(pdfPrintable, pageFormat, document.getNumberOfPages()); printJob.setPageable(book); printJob.setCopies(1);//设置打印份数 //添加打印属性 HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet(); pars.add(Sides.DUPLEX); //设置单双页 printJob.print(pars); return new ApiResult<>(true); }finally { stream.close(); if (document != null) { try { document.close(); } catch (IOException e) { e.printStackTrace(); } } } }
接口思路如下,前端选择打印机后,把打印机名字、需要打印的文件流,打印的数量,纸张大小传给本地java服务,java服务调用打印机,直接实现打印:
/** * 打印文件流 pdf * @param file * @param printerName * @param number * @return * @throws Exception */ @PostMapping("/printReport/{printerName}/{number}/{paperSize}") public ApiResult<Boolean> printReport(@RequestBody MultipartFile file,@PathVariable("printerName") String printerName,@PathVariable("number") Integer number,@PathVariable("paperSize") String paperSize) throws Exception { Paper paper; PrintVo printVo=new PrintVo(); printVo.setFile(file); printVo.setPrinterName(printerName); printVo.setNumber(number); printVo.setPaperName(paperSize); if(printVo.getPaperName()==null){ printVo.setPaperName("A4"); } switch (printVo.getPaperName()){ case "A4": paper=PrintUtil.getPaper(); break; default: paper=PrintUtil.getPaper(); break; } return PrintUtil.PDFprint(printVo.getFile().getInputStream(),printVo.getPrinterName(),paper); }
PrintUtil 代码如下:
/** * 打印机工具类 */ public class PrintUtil { /** * 打印PDF * @param stream * @param printerName * @param paper * @return * @throws Exception */ public static ApiResult<Boolean> PDFprint(InputStream stream, String printerName, Paper paper) throws Exception { PDDocument document = null; try { document = PDDocument.load(stream); PrinterJob printJob = PrinterJob.getPrinterJob(); printJob.setJobName("2010"); if (printerName != null) { // 查找并设置打印机 //获得本台电脑连接的所有打印机 PrintService[] printServices = PrinterJob.lookupPrintServices(); if(printServices == null || printServices.length == 0) { System.out.print("打印失败,未找到可用打印机,请检查。"); return new ApiResult<>(false,-1,"打印失败,未找到可用打印机,请检查。"); } PrintService printService = null; //匹配指定打印机 for (int i = 0;i < printServices.length; i++) { System.out.println(printServices[i].getName()); if (printServices[i].getName().contains(printerName)) { printService = printServices[i]; break; } } if(printService!=null){ printJob.setPrintService(printService); }else{ System.out.print("打印失败,未找到名称为" + printerName + "的打印机,请检查。"); return new ApiResult<>(false,-1,"\"打印失败,未找到名称为\" + printerName + \"的打印机,请检查。\""); } } //设置纸张及缩放 PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE); //设置多页打印 Book book = new Book(); PageFormat pageFormat = new PageFormat(); //设置打印方向 pageFormat.setOrientation(PageFormat.PORTRAIT);//纵向 pageFormat.setPaper(paper);//设置纸张 book.append(pdfPrintable, pageFormat, document.getNumberOfPages()); printJob.setPageable(book); printJob.setCopies(1);//设置打印份数 //添加打印属性 HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet(); pars.add(Sides.DUPLEX); //设置单双页 printJob.print(pars); return new ApiResult<>(true); }finally { stream.close(); if (document != null) { try { document.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 获取纸张 A4 纸 * @return */ public static Paper getPaper() { Paper paper = new Paper(); // 默认为A4纸张,对应像素宽和高分别为 595, 842 int width = 595; int height = 842; // 设置边距,单位是像素,10mm边距,对应 28px int marginLeft = 10; int marginRight = 0; int marginTop = 10; int marginBottom = 0; paper.setSize(width, height); // 下面一行代码,解决了打印内容为空的问题 paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom)); return paper; } /** * 设置纸张大小 * @param width * @param height * @return */ public static Paper getPaperA4(Integer width,Integer height) { Paper paper = new Paper(); // 设置边距,单位是像素,10mm边距,对应 28px int marginLeft = 10; int marginRight = 0; int marginTop = 10; int marginBottom = 0; paper.setSize(width, height); // 下面一行代码,解决了打印内容为空的问题 paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom)); return paper; } /** * 获取本地打印机列表 * @return */ public static List<String> getServicesName(){ PrintService[] services=PrinterJob.lookupPrintServices(); if(ObjectUtils.isEmpty(services)){ return new ArrayList<>(); }else{ System.out.println(services); return Arrays.stream(services).map(PrintService::getName).collect(Collectors.toList()); } } /** * 测试方法 * @param args * @throws Exception */ public static void main(String[] args) throws Exception{ String pdfFile = "E:\\1.pdf";//文件路径 File file = new File(pdfFile); String printerName = "Kyocera TASKalfa 300i KX";//打印机名包含字串 PDFprint(new FileInputStream(file),printerName,getPaper()); }
然后把springboot 的jar 包打包成windows服务就行了(用winsw让任何Windows程序都能运行为服务),前端直接调用本地服务,则实现本地打印服务的调用。