Java graphics random text

2019-02-26 11:38发布

问题:

I'm trying to draw random text with fontSize based on word frequency on the BufferedImage using the drawString(word, x, y) with random x,y values. Unfortunately it draws the random text that is overlaping. The image frame size is 1200 x 650 and the random numbers for x, y are between these values. Here's my code:

Random rand = new Random();
Font f = getRandomFont(fontSize);
FontMetrics metrics = graphics.getFontMetrics(f);
AffineTransform affinetransform = new AffineTransform();     
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);
int textwidth = (int)(f.getStringBounds(word, frc).getWidth());
int textheight = (int)(f.getStringBounds(word, frc).getHeight());
graphics.setColor(Color.red);
graphics.setFont(f);
int x = textwidth + rand.nextInt(800);
int y = -textheight + rand.nextInt(800);
graphics.drawString(word, x , y );

回答1:

First, fonts are drawn around the "base line", that is, the y position represents the baseline, so the text can grow above it and below it...

So, instead of using -textheight, you should be using FontMetrics#getAscent

Second, you need to keep track of all the previous locations text was painted, you could do this by using FontMetrics#getStringBounds, which returns a Rectangle2D and simply keep a list of this, which you can iterate over to check if the new text intersects any other, as an example...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

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

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage img;

        public TestPane() {
            img = new BufferedImage(1200, 650, BufferedImage.TYPE_INT_ARGB);
            try (BufferedReader br = new BufferedReader(new FileReader(new File("Words.txt")))) {
                List<String> words = new ArrayList<>(25);
                String text = null;
                System.out.println("Read");
                while ((text = br.readLine()) != null) {
                    words.add(text);
                }
                System.out.println("Loaded " + words.size());

                Collections.sort(words);

                Random rnd = new Random();
                Font font = getFont();
                Graphics2D g2d = img.createGraphics();
                g2d.setColor(Color.WHITE);
                g2d.fillRect(0, 0, 1200, 650);

                List<Rectangle2D> used = new ArrayList<>(25);
                for (String word : words) {
                    int size = rnd.nextInt(37) + 11;
                    Font drawFont = font.deriveFont((float) size);
                    FontMetrics fm = g2d.getFontMetrics(drawFont);
                    Rectangle2D bounds = fm.getStringBounds(word, g2d);
                    System.out.println("Positing " + word);
                    do {
                        int xPos = rnd.nextInt(1200 - (int)bounds.getWidth());
                        int yPos = rnd.nextInt(650 - (int)bounds.getHeight());

                        bounds.setFrame(xPos, yPos, bounds.getWidth(), bounds.getHeight());
                    } while (collision(used, bounds));
                    used.add(bounds);
                    g2d.setFont(drawFont);
                    g2d.setColor(Color.BLACK);
                    g2d.drawString(word, (float)bounds.getX(), (float)bounds.getY() + fm.getAscent());
                    g2d.setColor(Color.RED);
                    g2d.draw(bounds);
                }
                g2d.dispose();
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(1200, 650);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.drawImage(img, 0, 0, this);
            g2d.dispose();
        }

        protected boolean collision(List<Rectangle2D> used, Rectangle2D bounds) {
            boolean collides = false;
            for (Rectangle2D check : used) {
                if (bounds.intersects(check)) {
                    collides = true;
                    break;
                }
            }
            return collides;
        }

    }

}

The red rectangles are just for demonstration purposes and you can get rid of those.

This does, however, demonstrate a small problem, not all the text fills all of the rectangle.

A more complex solution would be to use a TextLayout to generate a Shape of the text, which would allow words to be grouped much closed together.

Have a look at Assigning a image to a String for a demonstration of how this can be generated



回答2:

  1. Get a Shape for the text as seen in this answer.
  2. Check if the shapes intersect as seen in this answer.


标签: java random