适应/缩放J组件以适配打印页面

3

我正在尝试缩放我的组件,以便它可以适应单个打印页面(纵向或横向)

 gDiagram.getComponent()

我正在尝试打印的组件是JPanel。

基于如何打印单个JPanel内容?,这是我目前的进展。

/**
 * Prints the diagram.
 */
public void printDiagram() {
    PrinterJob pj = PrinterJob.getPrinterJob();
    pj.setJobName(" Print Component ");

    pj.setPrintable(new Printable() {
        @Override
        public int print(Graphics g, PageFormat pf, int pageNumber)
                throws PrinterException {
            // TODO Auto-generated method stub
            if (pageNumber > 0) {
                return Printable.NO_SUCH_PAGE;
            }

            Graphics2D g2 = (Graphics2D) g;
            g2.translate(pf.getImageableX(), pf.getImageableY());

            double sx = pf.getImageableWidth() / gDiagram.getComponent().getWidth();
            double sy = pf.getImageableHeight() / gDiagram.getComponent().getHeight();

            gDiagram.getComponent().paint(g2);
            g2.scale(sx, sy);
            return Printable.PAGE_EXISTS;
        }
    });

    if (!pj.printDialog()) {
        return;
    }
    try {
        pj.print();
    } catch (PrinterException ex) {
        System.out.println(ex);
    }
}

我对图形不太熟悉,所以希望能得到一些帮助。


该组件被缩放后,是否适合页面大小? - MadProgrammer
@MadProgrammer 你好 - 抱歉我不明白你的问题。 - Sam Jarman
抱歉,应该是“不要”进行缩放。也就是说,如果你什么都不做,它能适合页面吗?将组件的大小更改为与页面的大小相匹配,是否可以将所有内容都放入其中... - MadProgrammer
@MadProgrammer 有时它可以适合于一张横向的A4纸。然而,通常情况下不行 - 它需要被缩小比例。 - Sam Jarman
@MadProgrammer 我还是卡住了,哈哈。 - Sam Jarman
显示剩余2条评论
1个回答

7
基本概念是使用AffineTransformation来提供输出的缩放。在我的测试中,我能够将一张7680x4800的图片缩小到595x842的页面上打印出来(大约缩小了93%)。
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
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 java.text.NumberFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class PrintTest {

    public static void main(String[] args) {
        new PrintTest();
    }

    public PrintTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                final TestPane imagePane = new TestPane();
                JButton print = new JButton("Print");
                print.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        printComponent(imagePane);
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(imagePane);
                frame.add(print, BorderLayout.SOUTH);
                frame.setSize(200, 200);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage bg;

        public TestPane() {
            try {
                bg = ImageIO.read(new File("path/to/a/image"));
            } catch (IOException ex) {
                Logger.getLogger(PrintTest.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return bg == null ? new Dimension(200, 200) : new Dimension(bg.getWidth(), bg.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            if (bg != null) {
                int x = (getWidth() - bg.getWidth()) / 2;
                int y = (getHeight() - bg.getHeight()) / 2;
                g2d.drawImage(bg, x, y, this);
            }
            g2d.dispose();
        }
    }

    public void printComponent(Component comp) {
        PrinterJob pj = PrinterJob.getPrinterJob();
        pj.setJobName(" Print Component ");

        pj.setPrintable(new ComponentPrintable(comp));

        if (!pj.printDialog()) {
            return;
        }
        try {
            pj.print();
        } catch (PrinterException ex) {
            System.out.println(ex);
        }
    }

    public class ComponentPrintable implements Printable {

        private Component comp;

        private ComponentPrintable(Component comp) {
            this.comp = comp;
        }

        @Override
        public int print(Graphics g, PageFormat pf, int pageNumber)
                throws PrinterException {
            // TODO Auto-generated method stub
            if (pageNumber > 0) {
                return Printable.NO_SUCH_PAGE;
            }

            // Get the preferred size ofthe component...
            Dimension compSize = comp.getPreferredSize();
            // Make sure we size to the preferred size
            comp.setSize(compSize);
            // Get the the print size
            Dimension printSize = new Dimension();
            printSize.setSize(pf.getImageableWidth(), pf.getImageableHeight());

            // Calculate the scale factor
            double scaleFactor = getScaleFactorToFit(compSize, printSize);
            // Don't want to scale up, only want to scale down
            if (scaleFactor > 1d) {
                scaleFactor = 1d;
            }

            // Calcaulte the scaled size...
            double scaleWidth = compSize.width * scaleFactor;
            double scaleHeight = compSize.height * scaleFactor;

            // Create a clone of the graphics context.  This allows us to manipulate
            // the graphics context without begin worried about what effects
            // it might have once we're finished
            Graphics2D g2 = (Graphics2D) g.create();
            // Calculate the x/y position of the component, this will center
            // the result on the page if it can
            double x = ((pf.getImageableWidth() - scaleWidth) / 2d) + pf.getImageableX();
            double y = ((pf.getImageableHeight() - scaleHeight) / 2d) + pf.getImageableY();
            // Create a new AffineTransformation
            AffineTransform at = new AffineTransform();
            // Translate the offset to out "center" of page
            at.translate(x, y);
            // Set the scaling
            at.scale(scaleFactor, scaleFactor);
            // Apply the transformation
            g2.transform(at);
            // Print the component
            comp.printAll(g2);
            // Dispose of the graphics context, freeing up memory and discarding
            // our changes
            g2.dispose();

            comp.revalidate();
            return Printable.PAGE_EXISTS;
        }
    }

    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) {

        double dScale = 1;
        if (iMasterSize > iTargetSize) {

            dScale = (double) iTargetSize / (double) iMasterSize;

        } else {

            dScale = (double) iTargetSize / (double) iMasterSize;

        }

        return dScale;

    }
}

有一些不同之处。首先,Swing组件是双缓冲的,这使得更新更加无缝,打印时不需要这种开销。此外,您还可以自定义打印输出(您可能并不关心)。此外,如果未连接到本机对等体(即未显示在屏幕上),我曾看到组件抛出异常。它是printAll,没有paintAll方法 ;) - MadProgrammer
好的。你是说两个都要用compSize.width吗? / 计算缩放后的尺寸... double scaleWidth = compSize.width * scaleFactor; double scaleHeight = compSize.width * scaleFactor; - Sam Jarman
还有,我们需要考虑纸张的方向吗?这会影响您正在进行的居中吗? - Sam Jarman
我不这么认为,在横向模式下,宽度和高度已经交换了(从记忆中)- 试试吧 ;) - MadProgrammer
compSize.width 是一个错误,是我的问题,我会修复它。 - MadProgrammer
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接