Java Graphic2D自动文本定位生成图像

Java Graphic2D自动文本定位生成图像,java,graphics,awt,graphics2d,Java,Graphics,Awt,Graphics2d,生成图像时,如何在图像内自动定位文本?例如,我使用此方法生成.png图像: public static void generatePNG(String message){ try { int width = 400, height = 400; // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed // into integer pixe

生成图像时,如何在图像内自动定位文本?例如,我使用此方法生成.png图像:

public static void generatePNG(String message){
        try {
            int width = 400, height = 400;
            // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
            // into integer pixels
            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            Graphics2D ig2 = bi.createGraphics();
            Font font = new Font("TimesRoman", Font.BOLD, 15);
            ig2.setFont(font);
            FontMetrics fontMetrics = ig2.getFontMetrics();
            int stringWidth = fontMetrics.stringWidth(message);
            int stringHeight = fontMetrics.getAscent();
            ig2.setPaint(Color.black);
            ig2.drawString(message, (width - stringWidth) / 2, height / 2 + stringHeight / 4);
            ImageIO.write(bi, "PNG", new File("myimg.png"));
        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }
但这将文本集中在我的img中,这对于测试来说还可以,但现在我想在图像中添加多行,并从上到下开始。我在方法中解析的用
StringBuilder
生成的消息被
System.lineSeparator()
以新行分隔,我也不知道它的宽度和高度如何,因为我的宽度必须保持不变,而高度可以随意改变,但是,我怎么知道仅仅通过消息就需要多少呢?

我假设这是一个示例。如果您的目标是生成一个带有特定文本的图像,并自动调整某些字体和换行符,那么您可以自己完成。您可以使用
FontMetrics
及其方法来计算图像的适当大小,以及使用
drawString
调用绘制的适当位置

但这很复杂。而且它远,远,比乍一看要复杂得多。我甚至不是在谈论阿拉伯文本,而是字体中看似最琐碎的元素

因此,最简单的解决方案可能是依靠该领域专家已经编写的数十万行经过时间测试的代码来解决这个问题

这意味着:

只需将文本放入
JTextArea
,然后从中创建一个图像

下面是一个例子,说明了如何实现这一点

核心是
createTextImage
方法,它允许您从文本中创建具有特定字体和颜色的图像。或者,您可以指定图像的宽度,并将执行换行的艰巨任务留给
JTextArea

您可能会注意到顶部的“HTML”复选框。启用时,输入被传递到
createHtmlImage
方法,以便您甚至可以输入类似的内容

<html>
This is <u>underlined</u> <br>
or in <i>italics</i>
</html>

我设法使这个解决方案工作,但由于某种原因,我无法更改字体大小。我不在乎我在createTextImage的字体中为“大小”输入了什么数字,但它不会改变任何东西。我用它直接打印到打印机,打印输出总是一样的。我可以在gui中看到,它发生了变化。@user3029612我不确定我是否正确理解了这一点。您正在更改字体大小(例如,从示例中的
15
更改为
25
左右),您会看到较大的字体用于创建图像,对吗?那么,打印机在哪里发挥作用呢?然后,您是打印生成的
PNG
文件,还是以某种方式使用Java打印API?我正在打印一个从方法(createTextImage)创建的BuffereImage转换而来的InputStream。对于打印,我使用的是javax.print中的“PrintService”。*在您创建的GUI中,我可以看到文本更大,但在打印时,文本的大小始终相同。@user3029612很难判断出哪里出了问题。我展示的方法创建了一个图像。您可以定义字体大小。字体越大,图像越大。如果图像总是以相同的大小打印,那么我想这与您打印图像的方式有关。你可能会把这个问题当作是一个专门的问题,但它不太可能被否决或关闭,因为“调试调试帮助”……我自己调试。我还有一个问题。如何将文本居中?我是否可以在createTextImage数组中解析具有两个属性(string和position)的对象,以便该方法将根据“Left”、“Center”或“Right”的位置附加obect的字符串?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;

import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class GenerateTextImage {

    public static void main(String[] args) {

        String text = "This is a text" + "\n" 
                + "with one line that is muuuuuuuuuuuuuuuuch longer than the others" + "\n" 
                + "and some empty lines" + "\n" 
                + "\n" 
                + "\n" 
                + "as a test.";

        SwingUtilities.invokeLater(() -> createAndShowGui(text));
    }

    private static void createAndShowGui(String initialText) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        f.getContentPane().setLayout(new BorderLayout());

        JPanel controlPanel = new JPanel();
        JCheckBox htmlCheckBox = new JCheckBox("HTML", false);
        controlPanel.add(htmlCheckBox);
        f.getContentPane().add(controlPanel, BorderLayout.NORTH);


        JPanel mainPanel = new JPanel(new GridLayout(1, 2));
        f.getContentPane().add(mainPanel, BorderLayout.CENTER);

        JTextArea inputTextArea = new JTextArea();
        JScrollPane sp0 = new JScrollPane(inputTextArea);
        sp0.setBorder(BorderFactory.createTitledBorder("Input:"));
        mainPanel.add(sp0);

        ImagePanel imagePanel = new ImagePanel();
        JScrollPane sp1 = new JScrollPane(imagePanel);
        sp1.setBorder(BorderFactory.createTitledBorder("Image:"));
        mainPanel.add(sp1);

        Runnable updateImage = () -> {
            if (!htmlCheckBox.isSelected()) {
                String text = inputTextArea.getText();
                BufferedImage image = createTextImage(text);
                imagePanel.setImage(image);
            } else {
                String text = inputTextArea.getText();
                BufferedImage image = createHtmlImage(text);
                imagePanel.setImage(image);
            }
        };

        inputTextArea.getDocument().addDocumentListener(new DocumentListener() {
            public void changedUpdate(DocumentEvent e) {
                updateImage();
            }

            public void insertUpdate(DocumentEvent e) {
                updateImage();
            }

            public void removeUpdate(DocumentEvent e) {
                updateImage();
            }

            private void updateImage() {
                updateImage.run();
            }
        });

        htmlCheckBox.addChangeListener(e -> {
            updateImage.run();
        });

        inputTextArea.setText(initialText);

        f.setSize(1200, 600);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static BufferedImage createTextImage(String text) {
        return createTextImage(text, -1, new Font("TimesRoman", Font.BOLD, 15), Color.BLACK, Color.WHITE);
    }

    /**
     * Creates an image with the given text, using the given font and foreground- and background color.<br>
     * <br>
     * If the given width is not positive, then the width of the image will be computed
     * to show the longest line that appears in the text. If the given width is positive,
     * then the lines of the given text will be wrapped (at word boundaries) if possible,
     * so that the whole text can be displayed.
     * 
     * @param text The text
     * @param width The image width
     * @param font The font
     * @param foreground The foreground color
     * @param background The background color
     * @return The image
     */
    private static BufferedImage createTextImage(String text, int width, Font font, Color foreground, Color background) {
        JTextArea textArea = new JTextArea(text);
        textArea.setFont(font);
        textArea.setForeground(foreground);
        textArea.setBackground(background);
        if (width > 0) 
        {
            textArea.setLineWrap(true);
            textArea.setWrapStyleWord(true);
            textArea.setSize(new Dimension(width, Short.MAX_VALUE));
        }
        Dimension size = textArea.getPreferredSize();
        int w = Math.max(1, size.width);
        if (width > 0)
        {
            w = width;
        }
        int h = Math.max(1, size.height);
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        SwingUtilities.paintComponent(g, textArea, new JPanel(), 0, 0, w, h);
        g.dispose();
        return image;
    }

    private static BufferedImage createHtmlImage(String text) {
        return createHtmlImage(text, new Font("TimesRoman", Font.BOLD, 15), Color.BLACK, Color.WHITE);
    }

    /**
     * Creates an image with the given HTML string, using the given font and foreground- and background color.<br>
     * 
     * @param html The HTML string
     * @param font The font
     * @param foreground The foreground color
     * @param background The background color
     * @return The image
     */
    private static BufferedImage createHtmlImage(String html, Font font, Color foreground, Color background) {
        JLabel label = new JLabel(html);
        label.setOpaque(true);
        label.setFont(font);
        label.setForeground(foreground);
        label.setBackground(background);
        Dimension size = label.getPreferredSize();
        int w = Math.max(1, size.width);
        int h = Math.max(1, size.height);
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        SwingUtilities.paintComponent(g, label, new JPanel(), 0, 0, w, h);
        g.dispose();
        return image;
    }


    static class ImagePanel extends JPanel {
        private static final long serialVersionUID = 1L;
        private BufferedImage image;

        public void setImage(BufferedImage image) {
            this.image = image;
            repaint();
        }

        @Override
        public Dimension getPreferredSize() {
            if (image == null || super.isPreferredSizeSet()) {
                return super.getPreferredSize();
            }
            return new Dimension(image.getWidth(), image.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (image != null) {
                g.drawImage(image, 0, 0, null);
            }
        }
    }
}
private static BufferedImage createTextImage(String text, int width, Font font, Color foreground, Color background) {
    JTextPane textPane = new JTextPane();
    textPane.setText(text);

    // See https://stackoverflow.com/a/3213361/3182664
    StyledDocument doc = textPane.getStyledDocument();
    SimpleAttributeSet center = new SimpleAttributeSet();
    StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
    doc.setParagraphAttributes(0, doc.getLength(), center, false);

    textPane.setFont(font);
    textPane.setForeground(foreground);
    textPane.setBackground(background);
    if (width > 0) 
    {
        //textPane.setLineWrap(true);
        //textPane.setWrapStyleWord(true);
        textPane.setSize(new Dimension(width, Short.MAX_VALUE));
    }
    Dimension size = textPane.getPreferredSize();
    int w = Math.max(1, size.width);
    if (width > 0)
    {
        w = width;
    }
    int h = Math.max(1, size.height);
    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = image.createGraphics();
    SwingUtilities.paintComponent(g, textPane, new JPanel(), 0, 0, w, h);
    g.dispose();
    return image;
}