Print 1800 x 1200 image on 4 x 6 paper using Java

I need to print an image of 1800 x 1200 pixels, 300 dots on 4 "x 6" (also known as 4r)

What i tried

I created a PrintRequestAttributeSet that takes care of my PrintableArea (4 x 6), Printer print DPI , Orientation . I attached MCVE below.

Problem

While the code is working, and I get a PageFormat with the following attributes (for my printer):

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

The width and height are slightly smaller because my printer does not support Zero Margin . (This is what I reviewed. If anyone knows about this other than this, through which I can force a zero margin, please let me know)

I supply margin as 0 because these images will be printed using printers that support a zero marker (Photobooth Printers).

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

The print area, including stock, is approximately 4 x 6, if required. The problem occurs when I scale the image to print inside the print area. A.

Since the image is 1800 x 1200, it maintains an aspect ratio of 3: 2, which means that the image was created for printing on 4 x 6 paper (after rotation and scaling). For reference.

Now, since pageWidth and pageHeight PageFormat not exactly divided into ImageWidth and ImageHeight. I get problems with scaling.

Note I rotate the image because it should be printed on 4 x 6, not 6 x 4.

An image that should occupy 4 x 6 spaces occupies about 4 x 5. The image size also decreases sharply.

How do I solve this problem?

code

Please find here 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"); } } 

To start the program, simply set the image path 1800x1200 to the main program and print it by default.

+6
source share
2 answers

Things that concern me ...

  • Damage to the scale / rotation of the Graphics context without its first copy or reset after the fact. This may affect subsequent renderings, as printable can be called multiple times ...
  • Using Graphics2D#scale . This is really not the best quality and it is usually so fast. See Dangers of Image.getScaledInstance () . I also prefer to use AffineTransform , but it's just me ...
  • Not buffering the result. Ok, this refers to the previous comment, but your print method can be called several times to print one page, scaling the image every time is expensive, instead you have to scale it once and reuse the scaled result.
  • If you are not going to physically rotate the image, you probably want to rotate around the center of the page and not the image itself, this will affect where 0x0 .
  • Now remember, when you rotate the Graphics context, the start point changes, so instead of being in the top / left corner, in this case it will become the top / right corner. And now you know why I would rotate the image in isolation and not try to ruin the Graphics context: P

What I β€œthink” is happening is that between scaling, rotating and manipulating coordinates (replacing height and width) everything starts to twist ... but, to be honest, I was not going to get along with it when I have the best solutions...

The following example uses a bunch of personal library code, so some of them may be a bit confusing, but I use separate functions for other things, so it binds well together ...

So, starting with an image of 7680x4800, this creates a scaled image of 423x264

Printable

(red border - only visual guides used when dumping the result in PDF to save paper;))

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

You know that it would be a lot easier to print the page in landscape mode to start with: P

+1
source

I would say you need proportional scaling. Like this

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

UPDATE: Another suggestion mentioned by mKorbel will be separate scaling.

Try using the public Image getScaledInstance(int width, int height, int hints) method public Image getScaledInstance(int width, int height, int hints) BufferedImage passing Image.SCALE_SMOOTH as a hint.

+1
source

Source: https://habr.com/ru/post/979051/


All Articles