使用Java在4 x 6纸张上打印1800 x 1200图像

我需要在4“x 6”纸张(也称为4r)上打印1800 x 1200像素,300 dpi图像

我试过了什么

我创建了一个PrintRequestAttributeSet来处理我的PrintableArea (4 x 6), Printer print DPIOrientation 。 我在底部贴了一个MCVE。

问题

虽然代码有效,但我得到了一个具有以下属性的PageFormat (对于我的打印机):

 x= 12.0 y= 12.32 w= 276.0 h= 419.67 

宽度和高度都少,因为我的打印机不支持Zero Margin 。 ( 这是我考虑过的。如果有人知道除此之外我可以强制零保证金的方式,请告诉我

我提供的margin as 0 ,因为这些图像将通过支持零保证金的打印机打印(Photobooth打印机)。

 aset.add(new MediaPrintableArea(0, 0, 4, 6, MediaPrintableArea.INCH)); 

包括边距的可打印区域大约为4 x 6。 当我缩放图像以在可打印区域内打印时,会出现问题。

由于图像为1800 x 1200,因此它支持3:2的宽高比,这意味着可以创建图像以在4 x 6纸张上打印(旋转和缩放后)。 以供参考。

现在,由于PageFormat的pageWidth和pageHeight不能被ImageWidth和ImageHeight完全整除。 我正在解决扩展问题。

注意 :我旋转图像是因为它必须以4 x 6而不是6 x 4打印。

应该占据4 x 6空间的图像占用接近4 x 5的图像。图像尺寸也大大减小。

我该如何克服这个问题?

请在这里找到MCVE:

 import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.MediaPrintableArea; import javax.print.attribute.standard.OrientationRequested; import javax.print.attribute.standard.PrintQuality; import javax.print.attribute.standard.PrinterResolution; public class ImgPrinter implements Printable { Image img; @Override public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { Graphics2D g2d = (Graphics2D) graphics; g2d.translate((int) (pageFormat.getImageableX()), (int) (pageFormat.getImageableY())); if (pageIndex == 0) { double pageWidth = pageFormat.getImageableWidth(); double pageHeight = pageFormat.getImageableHeight(); /** * Swapping width and height, coz the image is later rotated */ double imageWidth = img.getHeight(null); double imageHeight = img.getWidth(null); double scaleX = pageWidth / imageWidth; double scaleY = pageHeight / imageHeight; g2d.scale(scaleX, scaleY); g2d.rotate(Math.toRadians(90), img.getWidth(null) / 2, img.getHeight(null) / 2); g2d.drawImage(img, 0, 0, null); return Printable.PAGE_EXISTS; } return Printable.NO_SUCH_PAGE; } public void printPage(String file, String size) { try { Image img = ImageIO.read(new File(file)); this.img = img; PrintRequestAttributeSet aset = createAsetForMedia(size); PrinterJob pj = PrinterJob.getPrinterJob(); PageFormat pageFormat = pj.getPageFormat(aset); pj.setPrintable(this, pageFormat); pj.print(); } catch (PrinterException ex) { ex.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private PrintRequestAttributeSet createAsetForMedia(String size) { PrintRequestAttributeSet aset = null; try { aset = new HashPrintRequestAttributeSet(); aset.add(PrintQuality.NORMAL); aset.add(OrientationRequested.PORTRAIT); /** * Suggesting the print DPI as 300 */ aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI)); /** * Setting the printable area and the margin as 0 */ if (size.equals("3r")) { aset.add(new MediaPrintableArea(0, 0, 3, 5, MediaPrintableArea.INCH)); } else if (size.equals("4r")) { aset.add(new MediaPrintableArea(0, 0, 4, 6, MediaPrintableArea.INCH)); } else if (size.equals("5r")) { aset.add(new MediaPrintableArea(0, 0, 5, 7, MediaPrintableArea.INCH)); } else if (size.equals("6r")) { aset.add(new MediaPrintableArea(0, 0, 6, 8, MediaPrintableArea.INCH)); } } catch (Exception e) { e.printStackTrace(); } return aset; } public static void main(String[] args) { new ImgPrinter().printPage("/Some_URL/sam.jpg", "4r"); } } 

要运行该程序,只需为主程序提供1800×1200图像路径,它将打印到默认打印机。

让我担心的事情……

  1. 更改Graphics上下文的缩放/旋转,无需先复制它或在事后重置它。 这实际上可能会影响后续渲染,因为可以多次调用printable
  2. 使用Graphics2D#scale 。 这真的不是最好的质量,通常也不是那么快。 请参阅Image.getScaledInstance()的Perils 。 我也更喜欢使用AffineTransform ,但那只是我……
  3. 不缓冲结果。 好的,这与之前的评论有关,但是您的print方法可能被多次调用以打印单个页面,每次缩放图像都很昂贵,相反,您应该缩放一次并重新使用缩放结果。
  4. 除非您要实际旋转图像,否则您可能希望围绕页面中心旋转,而不是图像本身,这将影响0x0变为的位置。
  5. 现在请记住,当您旋转Graphics上下文时,原点会发生变化,因此它不会位于左上角,在这种情况下,它将成为顶部/右上角。 现在你知道为什么我会单独旋转图像并且没有尝试弄乱Graphics上下文:P

我所“想到的”是在缩放,旋转和操纵坐标(交换高度和宽度)之间,事情变得搞砸……但坦率地说,当我拥有它时,我不会乱用它更好的方案……

下面的例子使用了一堆个人库代码,所以有些可能有点复杂,但是我使用单独的function来处理其他事情,所以它很好地结合在一起……

因此,从7680×4800的图像开始,这将生成423×264的缩放图像

可打印

(红色边框仅为视觉指南,在将结果转储为PDF以节省纸张时使用;))

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.MediaPrintableArea; import javax.print.attribute.standard.OrientationRequested; import javax.print.attribute.standard.PrintQuality; import javax.print.attribute.standard.PrinterResolution; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class ImgPrinter implements Printable { BufferedImage img; BufferedImage scaled; @Override public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { int result = NO_SUCH_PAGE; Graphics2D g2d = (Graphics2D) graphics.create(); g2d.translate((int) (pageFormat.getImageableX()), (int) (pageFormat.getImageableY())); if (pageIndex == 0) { double pageWidth = pageFormat.getImageableWidth(); double pageHeight = pageFormat.getImageableHeight(); if (scaled == null) { // Swap the width and height to allow for the rotation... System.out.println(pageWidth + "x" + pageHeight); scaled = getScaledInstanceToFit( img, new Dimension((int)pageHeight, (int)pageWidth)); System.out.println("In " + img.getWidth() + "x" + img.getHeight()); System.out.println("Out " + scaled.getWidth() + "x" + scaled.getHeight()); } double imageWidth = scaled.getWidth(); double imageHeight = scaled.getHeight(); AffineTransform at = AffineTransform.getRotateInstance( Math.toRadians(90), pageWidth / 2d, pageHeight / 2d ); AffineTransform old = g2d.getTransform(); g2d.setTransform(at); double x = (pageHeight - imageWidth) / 2d; double y = (pageWidth - imageHeight) / 2d; g2d.drawImage( scaled, (int)x, (int)y, null); g2d.setTransform(old); // This is not affected by the previous changes, as those were made // to a different copy... g2d.setColor(Color.RED); g2d.drawRect(0, 0, (int)pageWidth - 1, (int)pageHeight - 1); result = PAGE_EXISTS; } g2d.dispose(); return result; } public void printPage(String file, String size) { try { img = ImageIO.read(new File(file)); PrintRequestAttributeSet aset = createAsetForMedia(size); PrinterJob pj = PrinterJob.getPrinterJob(); PageFormat pageFormat = pj.getPageFormat(aset); pj.setPrintable(this, pageFormat); if (pj.printDialog()) { pj.print(); } } catch (PrinterException ex) { ex.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private PrintRequestAttributeSet createAsetForMedia(String size) { PrintRequestAttributeSet aset = null; try { aset = new HashPrintRequestAttributeSet(); aset.add(PrintQuality.NORMAL); aset.add(OrientationRequested.PORTRAIT); /** * Suggesting the print DPI as 300 */ aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI)); /** * Setting the printable area and the margin as 0 */ if (size.equals("3r")) { aset.add(new MediaPrintableArea(1, 1, 3, 5, MediaPrintableArea.INCH)); } else if (size.equals("4r")) { aset.add(new MediaPrintableArea(1, 1, 4, 6, MediaPrintableArea.INCH)); } else if (size.equals("5r")) { aset.add(new MediaPrintableArea(1, 1, 5, 7, MediaPrintableArea.INCH)); } else if (size.equals("6r")) { aset.add(new MediaPrintableArea(1, 1, 6, 8, MediaPrintableArea.INCH)); } } catch (Exception e) { e.printStackTrace(); } return aset; } public static BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) { double scaleFactor = getScaleFactorToFit(img, size); return getScaledInstance(img, scaleFactor); } public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) { return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR); } public static double getScaleFactorToFit(BufferedImage img, Dimension size) { double dScale = 1; if (img != null) { int imageWidth = img.getWidth(); int imageHeight = img.getHeight(); dScale = getScaleFactorToFit(new Dimension(imageWidth, imageHeight), size); } return dScale; } public static double getScaleFactorToFit(Dimension original, Dimension toFit) { double dScale = 1d; if (original != null && toFit != null) { double dScaleWidth = getScaleFactor(original.width, toFit.width); double dScaleHeight = getScaleFactor(original.height, toFit.height); dScale = Math.min(dScaleHeight, dScaleWidth); } return dScale; } public static double getScaleFactor(int iMasterSize, int iTargetSize) { return (double) iTargetSize / (double) iMasterSize; } protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint) { BufferedImage imgScale = img; int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor); int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor); if (dScaleFactor <= 1.0d) { imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint); } else { imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint); } return imgScale; } protected static BufferedImage getScaledDownInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint) { // System.out.println("Scale down..."); int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; BufferedImage ret = (BufferedImage) img; if (targetHeight > 0 || targetWidth > 0) { int w = img.getWidth(); int h = img.getHeight(); do { if (w > targetWidth) { w /= 2; if (w < targetWidth) { w = targetWidth; } } if (h > targetHeight) { h /= 2; if (h < targetHeight) { h = targetHeight; } } BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type); Graphics2D g2 = tmp.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); g2.drawImage(ret, 0, 0, w, h, null); g2.dispose(); ret = tmp; } while (w != targetWidth || h != targetHeight); } else { ret = new BufferedImage(1, 1, type); } return ret; } protected static BufferedImage getScaledUpInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint) { int type = BufferedImage.TYPE_INT_ARGB; BufferedImage ret = (BufferedImage) img; int w = img.getWidth(); int h = img.getHeight(); do { if (w < targetWidth) { w *= 2; if (w > targetWidth) { w = targetWidth; } } if (h < targetHeight) { h *= 2; if (h > targetHeight) { h = targetHeight; } } BufferedImage tmp = new BufferedImage(w, h, type); Graphics2D g2 = tmp.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); g2.drawImage(ret, 0, 0, w, h, null); g2.dispose(); ret = tmp; tmp = null; } while (w != targetWidth || h != targetHeight); return ret; } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } new ImgPrinter().printPage("/Volumes/Disk02/Dropbox/Wallpapers/animepaper.net_wallpaper_art_anime_aria_duanwu_festival_205050_wonderngo_7680x4800-a8aecc9c.jpg", "4r"); } }); } } 

你知道什么会更容易,以横向模式打印页面开始:P

我会说你需要比例缩放。 喜欢这个

 double scaleX = pageWidth / imageWidth; double scaleY = pageHeight / imageHeight; double scale = Math.min(scaleX, scaleY); g2d.scale(scale, scale); 

更新:mKorbel提到的另一个建议是单独扩展。

尝试使用传递Image.SCALE_SMOOTH作为提示的BufferedImage public Image getScaledInstance(int width, int height, int hints)方法。