打印慢的原因
java的RasterPrinterJob会执行很多次printPage方法
他应该是按块填充的, 如果页面元素非常复杂, 那么printPage方法可能会执行十几次.
而如果你用了如下代码中流式打印的方式, 每页pdf单独实现Printable接口, 重写print方法.
1 private static class FinePrintableDemo implements Printable { 2 3 public FinePrintableDemo(PDDocument document, int index) { 4 this.index = index; 5 this.printable = new PDFPrintable(document); 6 } 7 8 private int index; 9 private PDFPrintable printable; 10 11 @Override 12 public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { 13 long start1 = System.currentTimeMillis(); 14 int res = printable.print(graphics, pageFormat, index); 15 long end1 = System.currentTimeMillis(); 16 System.out.println("打印第 " + (index + 1) + "页 耗时 : " + (end1 - start1) +" 毫秒 "); 17 18 return res; 19 } 20 }
会发现print方法同样被执行了十几次, 造成一个带背景的pdf打印耗时十几秒.
慢在哪
慢在每次都重复解析同一页pdf内容. 打了断点后会发现每页都在PDFStreamEngine.processStream
1 public void renderPageToGraphics(int pageIndex, Graphics2D graphics, float scale) throws IOException { 2 PDPage page = this.document.getPage(pageIndex); 3 this.transform(graphics, page, scale); 4 PDRectangle cropBox = page.getCropBox(); 5 graphics.clearRect(0, 0, (int)cropBox.getWidth(), (int)cropBox.getHeight()); 6 PageDrawerParameters parameters = new PageDrawerParameters(this, page); 7 PageDrawer drawer = this.createPageDrawer(parameters); 8 drawer.drawPage(graphics, cropBox); 9 }
这个方法每次都会消耗1s左右.
怎么解决
最好的方式自然是改pdfbox源码, 不用每次都重新生成解析pdf文件. 不过那样稍微有点麻烦.
还有个更简单的方式, 既然pdf会被反复解析, 那么我们在print之前把pdf转成图片, 然后直接打印图片即可.
即使RasterPrinterJob.printPage执行十几次, 也不过在绘制Image, 时间会非常短.
因此我们将代码稍微改造下, 在printable.print方法中直接打印图片.
1 private static class FinePrintableDemo implements Printable { 2 3 private BufferedImage image; 4 5 public FinePrintableDemo(PDDocument document, int index) { 6 // 获取pdf文件, 将其中指定的页面转成图片. 7 PDFRenderer renderer = new PDFRenderer(document); 8 try { 9 this.image = renderer.renderImage(index, 1, ImageType.RGB); 10 } catch (IOException e) { 11 e.printStackTrace(); 12 } 13 14 } 15 16 @Override 17 public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { 18 long start1 = System.currentTimeMillis(); 19 Graphics2D g2d = (Graphics2D) graphics.create(); 20 Paint paint = g2d.getPaint(); 21 22 int width = this.image.getWidth(); 23 int height = this.image.getHeight(); 24 25 Shape shape = new Rectangle2D.Double(0, 0, width, height); 26 g2d.setPaint(createPaint(shape, StableUtils.isNotSupportARGB(g2d), image, width, height)); 27 g2d.fill(shape); 28 g2d.setPaint(paint); 29 g2d.dispose(); 30 long end1 = System.currentTimeMillis(); 31 System.out.println("打印第 " + (pageIndex + 1) + "页 耗时 : " + (end1 - start1) +" 毫秒 "); 32 return Printable.PAGE_EXISTS; 33 } 34 35 private Paint createPaint(Shape shape, boolean isNotSupportARGB, BufferedImage image, int width, int height) { 36 Rectangle2D rec2D = shape.getBounds2D(); 37 if ((int) rec2D.getWidth() <= 0) { 38 rec2D.setRect(rec2D.getX(), rec2D.getY(), rec2D.getWidth() + 40, rec2D.getHeight()); 39 } 40 if ((int) rec2D.getHeight() <= 0) { 41 rec2D.setRect(rec2D.getX(), rec2D.getY(), rec2D.getWidth(), rec2D.getHeight() + 40); 42 } 43 BufferedImage buffered = new BufferedImage((int) rec2D.getWidth(), (int) rec2D.getHeight(), isNotSupportARGB ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); 44 Graphics2D g2 = buffered.createGraphics(); 45 GraphHelper.paintImage(g2, (int) rec2D.getWidth(), (int) rec2D.getHeight(), image, 46 Constants.IMAGE_CENTER , Constants.LEFT, Constants.TOP, width, height, isNotSupportARGB); 47 48 g2.dispose(); 49 50 return new TexturePaint(buffered, rec2D); 51 } 52 }
这一次print方法同样被执行了十几次, 但是每次也就十几毫秒, 文件很快就被打印出来了.
完整代码如下
1 package com.fr.base; 2 3 import com.fr.stable.Constants; 4 import com.fr.stable.StableUtils; 5 import org.apache.pdfbox.pdmodel.PDDocument; 6 import org.apache.pdfbox.rendering.ImageType; 7 import org.apache.pdfbox.rendering.PDFRenderer; 8 9 import java.awt.Graphics; 10 import java.awt.Graphics2D; 11 import java.awt.Paint; 12 import java.awt.Shape; 13 import java.awt.TexturePaint; 14 import java.awt.geom.Rectangle2D; 15 import java.awt.image.BufferedImage; 16 import java.awt.print.Book; 17 import java.awt.print.PageFormat; 18 import java.awt.print.Paper; 19 import java.awt.print.Printable; 20 import java.awt.print.PrinterException; 21 import java.awt.print.PrinterJob; 22 import java.io.FileInputStream; 23 import java.io.IOException; 24 import java.io.InputStream; 25 26 public class doStreamImagePrint { 27 28 public static void main(String[] args) throws IOException, PrinterException { 29 PrinterJob job = PrinterJob.getPrinterJob(); 30 31 int width = 595; 32 int height = 842; 33 int marginLeft = 0; 34 int marginRight = 0; 35 int marginTop = 0; 36 int marginBottom = 0; 37 38 Paper paper = new Paper(); 39 paper.setSize(width, height); 40 // 设置边距 41 paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom)); 42 // 自定义页面设置 43 PageFormat pageFormat = new PageFormat(); 44 // 设置页面横纵向 45 pageFormat.setOrientation(PageFormat.PORTRAIT); 46 pageFormat.setPaper(paper); 47 48 // 构建一个有size的空book, book里都是引用. 实际打印哪一页就从远程获取哪一页 49 Book printBook = new Book(); 50 // 真正打印的时候, 每页的printable都new pdfprintable.print(); 51 printBook.append(convertPDFToPrint("D:\bg.pdf", 0), pageFormat); 52 printBook.append(convertPDFToPrint("D:\bg.pdf", 0), pageFormat); 53 54 55 job.setPageable(printBook); 56 job.print(); 57 } 58 59 private static FinePrintableDemo convertPDFToPrint(String path, int index) throws IOException { 60 InputStream in = new FileInputStream(path); 61 PDDocument document = PDDocument.load(in); 62 return new FinePrintableDemo(document, index); 63 } 64 65 private static class FinePrintableDemo implements Printable { 66 67 private BufferedImage image; 68 69 public FinePrintableDemo(PDDocument document, int index) { 70 // 获取pdf文件, 将其中指定的页面转成图片. 71 PDFRenderer renderer = new PDFRenderer(document); 72 try { 73 this.image = renderer.renderImage(index, 1, ImageType.RGB); 74 } catch (IOException e) { 75 e.printStackTrace(); 76 } 77 78 } 79 80 @Override 81 public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { 82 long start1 = System.currentTimeMillis(); 83 Graphics2D g2d = (Graphics2D) graphics.create(); 84 Paint paint = g2d.getPaint(); 85 86 int width = this.image.getWidth(); 87 int height = this.image.getHeight(); 88 89 Shape shape = new Rectangle2D.Double(0, 0, width, height); 90 g2d.setPaint(createPaint(shape, StableUtils.isNotSupportARGB(g2d), image, width, height)); 91 g2d.fill(shape); 92 g2d.setPaint(paint); 93 g2d.dispose(); 94 long end1 = System.currentTimeMillis(); 95 System.out.println("打印第 " + (pageIndex + 1) + "页 耗时 : " + (end1 - start1) +" 毫秒 "); 96 return Printable.PAGE_EXISTS; 97 } 98 99 private Paint createPaint(Shape shape, boolean isNotSupportARGB, BufferedImage image, int width, int height) { 100 Rectangle2D rec2D = shape.getBounds2D(); 101 if ((int) rec2D.getWidth() <= 0) { 102 rec2D.setRect(rec2D.getX(), rec2D.getY(), rec2D.getWidth() + 40, rec2D.getHeight()); 103 } 104 if ((int) rec2D.getHeight() <= 0) { 105 rec2D.setRect(rec2D.getX(), rec2D.getY(), rec2D.getWidth(), rec2D.getHeight() + 40); 106 } 107 BufferedImage buffered = new BufferedImage((int) rec2D.getWidth(), (int) rec2D.getHeight(), isNotSupportARGB ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); 108 Graphics2D g2 = buffered.createGraphics(); 109 GraphHelper.paintImage(g2, (int) rec2D.getWidth(), (int) rec2D.getHeight(), image, 110 Constants.IMAGE_CENTER , Constants.LEFT, Constants.TOP, width, height, isNotSupportARGB); 111 112 g2.dispose(); 113 114 return new TexturePaint(buffered, rec2D); 115 } 116 } 117 118 }