草庐IT

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

coder 2024-04-02 原文

我需要在 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

宽度和高度少了一点,因为我的打印机不支持零边距。 (这是我考虑过的。如果有人知道除此之外我可以强制零 margin 的方法,请告诉我)

我提供的 边距为 0,因为这些图像将通过支持零边距的打印机(Photobooth 打印机)打印。

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

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

由于图像是 1800 x 1200,它支持 3:2 的纵横比,这意味着创建图像以打印在 4 x 6 的纸张上(旋转和缩放后)。 For Reference.

现在,由于 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");
    }
}

要运行该程序,只需向主程序提供 1800x1200 图像路径,它就会打印到默认打印机。

最佳答案

让我担心的事情...

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

我“认为”正在发生的事情是在坐标的缩放、旋转和操作(交换高度和宽度)之间,事情变得一团糟……但坦率地说,我不会乱搞的当我有更好的解决方案时...

下面的例子使用了一堆个人库代码,所以有些可能有点复杂,但我对其他东西使用了单独的功能,所以它结合得很好......

因此,从 7680x4800 的图像开始,这会生成 423x264 的缩放图像

(红色边框仅为视觉指南,用于将结果转储为 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

关于java - 使用 Java 在 4 x 6 纸上打印 1800 x 1200 图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27273402/

有关java - 使用 Java 在 4 x 6 纸上打印 1800 x 1200 图像的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  3. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  6. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  7. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  8. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

  9. java - Ruby 相当于 Java 的 Collections.unmodifiableList 和 Collections.unmodifiableMap - 2

    Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur

  10. java - Java 的 StringReader 的 Ruby 等价物是什么? - 2

    在Java中,可以像这样从一个字符串创建一个IO流:Readerr=newStringReader("mytext");我希望能够在Ruby中做同样的事情,这样我就可以获取一个字符串并将其视为一个IO流。 最佳答案 r=StringIO.new("mytext")和here'sthedocumentation. 关于java-Java的StringReader的Ruby等价物是什么?,我们在StackOverflow上找到一个类似的问题: https://st

随机推荐