具有自动文本定位功能的Graphic2D生成图像

时间:2019-07-30 11:04:58

标签: 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 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()换行了,我也不知道宽度和高度如何,因为宽度必须保持不变,而高度可以根据需要进行任意更改,但是我怎么能仅凭消息知道需要多少?

1 个答案:

答案 0 :(得分:1)

假设这是XY-problem的示例。如果您的目标是生成带有特定文本的图像,并针对某些字体和换行符自动进行调整,则您可以可以自行完成此操作。您可以使用FontMetrics及其方法来计算图像的正确大小,以及通过drawString调用绘制的正确位置。

但这很复杂。而且, far far 比第一眼看上去要复杂得多。我什至没有在谈论النصالعربي(阿拉伯文字),甚至在谈论字体中看似最琐碎的元素。

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

意思是:

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

以下是MCVE,显示了如何实现此目标。

GenerateTextImage

核心是createTextImage方法,它使您可以从文本创建具有特定字体和颜色的图像。 (可选)您可以指定图像的宽度,并将艰辛的换行任务留给JTextArea

您可能会注意到顶部的“ HTML”复选框。启用后,输入将传递到createHtmlImage方法,这样您甚至可以输入类似

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

获取呈现的HTML输出的图像。

完整代码在这里:

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

编辑:其中一个注释的小附录。它使用https://stackoverflow.com/a/3213361/3182664中的代码段使文本水平居中。请注意,这没有经过测试。在某些时候,问题,评论和编辑归结为“为我编写一些代码”。我是自由职业者。你可以雇用我。

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