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. Unf
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 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 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 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