Printing a 1800 x 1200 image on 4 x 6 paper using Java
Asked Answered
R

2

6

I need to print a 1800 x 1200 pixels, 300 dpi image on 4" x 6" paper (also known as 4r)

What I have Tried

I have created a PrintRequestAttributeSet which takes care of my PrintableArea(4 x 6), Printer print DPI, Orientation. I have attached a MCVE at the bottom.

Problem

While the code works, 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 little less, because my printer doesn't support Zero Margin. (This is what I have considered. If anyone is aware of a way other than this through which I can force zero margin, please let me know)

I am supplying the margin as 0, because these images will be printed via printers which support zero margin(Photobooth Printers).

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

The printable area including the margin is roughly 4 x 6 as required. The problem occurs when I scale the image to print inside the printable area.

Since image is 1800 x 1200, it supports an aspect ratio of 3:2, which means the image is created to get printed on a 4 x 6 paper(after getting rotated and scaled). For Reference.

Now, since the pageWidth and pageHeight of the PageFormat are not exactly divisible by the ImageWidth and ImageHeight. I am getting scaling issues.

Note : I rotate the image because it has to be printed on 4 x 6 and not 6 x 4.

The image which is supposed to take 4 x 6 space is taking somewhere close to 4 x 5. The image size is also reduced drastically.

How do I overcome this issue?

Code

Please find the MCVE here :

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 run the program, just supply a 1800x1200 image path to the main program and it will print to the default printer.

Railhead answered 3/12, 2014 at 13:50 Comment(3)
load jpg FIle to BufferedImage, to scale to the desired resolution, ImageIO.write, then to print a new image directlyPetrillo
note as aside usage of scaledInstance is pretty asynchronous,Petrillo
I tried with resizing the image by converting it to BufferImage. I passed the new width as 4*72=288 and height as 6*72=432 (as everything is 72dpi in java). But the image gets distorted. Its prints a image of 3.7 x 4 and uses a printable area of 4 x 5.2. The print quality was also decreased drastically. The link to the edited code. I am not sure, if I am using the correct process to convert from and back to BufferedImage to Image.Railhead
D
1

Things that worry me...

  1. Changing the scale/rotation of the Graphics context without either first making a copy of it or resetting it after the fact. This could actually affect subsequent renderings, as the printable may be called multiple times...
  2. Using Graphics2D#scale. This really isn't the best quality nor is it generally that fast. See The Perils of Image.getScaledInstance(). I also prefer to use AffineTransform, but that's just me...
  3. Not buffering the result. Okay, this relates to the previous comment, but your print method may be called multiple times to print a single page, scaling the image each time is costly, instead, you should scale it once and re-use the scaled result.
  4. Unless you're going to physically rotate the image, you probably want to rotate about the center of the page, not the image itself, this will affect the location where 0x0 becomes.
  5. Now remember, when you rotate the Graphics context, the origin point changes, so instead of been in the top/left corner, in this case, it will become the top/right corner. And now you know why I would have rotated the image in isolation and not tried messing around with the Graphics context :P

What I "think" is happening is that between the scaling, rotating and manipulations of the coordinates (swapping the height and width), things are getting screwed...but frankly, I wasn't going to mess around with it when I have better solutions...

The following example makes use of a bunch of personal library code, so some of might be a little convoluted, but I use the separate functionality for other things, so it binds well together...

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

Printable

(the red border are visual guides only, used when dumping the result to 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 what would MUCH easier, printing the page in landscape mode to start with :P

Darees answered 8/12, 2014 at 11:23 Comment(8)
Hi, MadProgrammer. Thanks for the answer and apologies for the late reply. I tried your programs and it works great, the only problem faced is the Print Quality. The quality seems to be highly degraded, can you point out the reason and a possible solution to the same.Railhead
The major reason that I can think of is the scaling. You could take a look at imgscalr for an alternativeDarees
Thanks for the input, I will have a look at it. Thanks for the wonderful answer as well :)Railhead
I will try it out and it doesn't solve my issue, I'll be back to bug you ;)Railhead
A largely unknown or perhaps misunderstood feature of Java printing is that the subsystem is capable of doing scaling for you. This is because the Printable interface's graphics object is unlike any other as it is capable of drawing in units of 72nds of an inch defined by PageFormat with the graphics data passed without the need for intermediate resizing (with exception of proportion/cropping of course). So when someone complains about Print Quality, remember to let the print(Graphics g ...) graphics object do the rendering, it will always be better.Sheepskin
@QZSupport Except in my experience, unless you scale the image to fit within the prescribed area, it doesn't work, it simply allows the image to "overfill" the paper. I assume this is because there is a difference between DPI of the image and the Graphics subsystem. I wouldn't do this kind of thing for text though, only graphicsDarees
@Darees scaling should generally be done on the printable Graphics object because it will yield best results. drawImage can do the scaling for you, avoiding using an intermediate BufferedImage and reuse as many existing pixels as physically possible. Note, MacOS has an issue with nearest-neighbor scaling using the printable Graphics object, so an intermediate BufferedImage is preferred only on MacOS when image blurring (bicubic interpolation) is a concern (until the bug is fixed).Sheepskin
drawImage doesn't generate the best scaling output, nor is it the quickest, while this uses some personal library code I've put together over time, using something like imgscalr would probably be prefered solutionDarees
N
1

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: One more suggestions as mKorbel mentioned would be separate scaling.

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

Nancee answered 3/12, 2014 at 14:2 Comment(2)
Well I have already tried it. It doesn't create much of a difference.Railhead
@Railhead you can to painting an BufferedImage to AWT/Swing components with desired dimension in pixels, there is mutual relationship between pixel and DPI, problem is that you painting off_screen, to try, start with with printing already visible Object in screen (I'm sure thats similair questions with/about printing the visible image is a few times here)Petrillo
D
1

Things that worry me...

  1. Changing the scale/rotation of the Graphics context without either first making a copy of it or resetting it after the fact. This could actually affect subsequent renderings, as the printable may be called multiple times...
  2. Using Graphics2D#scale. This really isn't the best quality nor is it generally that fast. See The Perils of Image.getScaledInstance(). I also prefer to use AffineTransform, but that's just me...
  3. Not buffering the result. Okay, this relates to the previous comment, but your print method may be called multiple times to print a single page, scaling the image each time is costly, instead, you should scale it once and re-use the scaled result.
  4. Unless you're going to physically rotate the image, you probably want to rotate about the center of the page, not the image itself, this will affect the location where 0x0 becomes.
  5. Now remember, when you rotate the Graphics context, the origin point changes, so instead of been in the top/left corner, in this case, it will become the top/right corner. And now you know why I would have rotated the image in isolation and not tried messing around with the Graphics context :P

What I "think" is happening is that between the scaling, rotating and manipulations of the coordinates (swapping the height and width), things are getting screwed...but frankly, I wasn't going to mess around with it when I have better solutions...

The following example makes use of a bunch of personal library code, so some of might be a little convoluted, but I use the separate functionality for other things, so it binds well together...

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

Printable

(the red border are visual guides only, used when dumping the result to 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 what would MUCH easier, printing the page in landscape mode to start with :P

Darees answered 8/12, 2014 at 11:23 Comment(8)
Hi, MadProgrammer. Thanks for the answer and apologies for the late reply. I tried your programs and it works great, the only problem faced is the Print Quality. The quality seems to be highly degraded, can you point out the reason and a possible solution to the same.Railhead
The major reason that I can think of is the scaling. You could take a look at imgscalr for an alternativeDarees
Thanks for the input, I will have a look at it. Thanks for the wonderful answer as well :)Railhead
I will try it out and it doesn't solve my issue, I'll be back to bug you ;)Railhead
A largely unknown or perhaps misunderstood feature of Java printing is that the subsystem is capable of doing scaling for you. This is because the Printable interface's graphics object is unlike any other as it is capable of drawing in units of 72nds of an inch defined by PageFormat with the graphics data passed without the need for intermediate resizing (with exception of proportion/cropping of course). So when someone complains about Print Quality, remember to let the print(Graphics g ...) graphics object do the rendering, it will always be better.Sheepskin
@QZSupport Except in my experience, unless you scale the image to fit within the prescribed area, it doesn't work, it simply allows the image to "overfill" the paper. I assume this is because there is a difference between DPI of the image and the Graphics subsystem. I wouldn't do this kind of thing for text though, only graphicsDarees
@Darees scaling should generally be done on the printable Graphics object because it will yield best results. drawImage can do the scaling for you, avoiding using an intermediate BufferedImage and reuse as many existing pixels as physically possible. Note, MacOS has an issue with nearest-neighbor scaling using the printable Graphics object, so an intermediate BufferedImage is preferred only on MacOS when image blurring (bicubic interpolation) is a concern (until the bug is fixed).Sheepskin
drawImage doesn't generate the best scaling output, nor is it the quickest, while this uses some personal library code I've put together over time, using something like imgscalr would probably be prefered solutionDarees

© 2022 - 2024 — McMap. All rights reserved.