I am trying to add some text to an image in Java with the following code:
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File("img.png"));
Graphics g = image.getGraphics();
Rectangle rectangle = new Rectangle(image.getWidth(), image.getHeight());
g.setFont(g.getFont().deriveFont(30f));
drawCenteredString(g, "TexvzgdsfadvcfkgsdASKJDFHJGgkdgfsakagjASGHDJStTexvzgdsfadvcfkgsdASKJDFHJGgkdgfsakagjASGHDJSt", rectangle, g.getFont());
g.dispose();
ImageIO.write(image, "png", new File("out.png"));
}
public static void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
FontMetrics metrics = g.getFontMetrics(font);
int x = rect.x (rect.width - metrics.stringWidth(text)) / 2;
int y = rect.y ((rect.height - metrics.getHeight()) / 2) metrics.getAscent();
g.setFont(font);
g.drawString(text, x, y);
}
But I have a problem: I would need to be able to scale the font size or send words to a new line so that the whole string fits in a rectangular region at the center of the image with a maximum size of x,y. How can i do it?
CodePudding user response:
I used an image I found on the web. Basically, you try with a large font size and reduce the font size until your text fits on the image.
Here's the image after I added the text.
I started with a font size of 96 and worked my way down to 46, which fit in the image.
Here's the complete runnable code.
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
public class AddTextToImage {
public static void main(String[] args) throws IOException {
URL url = new URL("https://clipartmag.com/images/car-cartoon-png-18.png");
BufferedImage image = ImageIO.read(url);
Graphics g = image.getGraphics();
Rectangle rectangle = new Rectangle(image.getWidth(), image.getHeight());
String text = "TexvzgdsfadvcfkgsdASKJDFHJGgkdgfsakagjASGHDJSt"
"TexvzgdsfadvcfkgsdASKJDFHJGgkdgfsakagjASGHDJSt";
int fontSize = 98;
FontMetrics metrics;
do {
fontSize -= 2;
g.setFont(g.getFont().deriveFont((float) fontSize));
metrics = g.getFontMetrics(g.getFont());
} while (metrics.stringWidth(text) > rectangle.width);
System.out.println("Font Size: " fontSize);
drawCenteredString(g, text, rectangle, g.getFont());
g.dispose();
ImageIO.write(image, "png", new File("output.png"));
}
public static void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
FontMetrics metrics = g.getFontMetrics(font);
int x = rect.x (rect.width - metrics.stringWidth(text)) / 2;
int y = rect.y ((rect.height - metrics.getHeight()) / 2) metrics.getAscent();
g.setFont(font);
g.drawString(text, x, y);
}
}
CodePudding user response:
To wrap the text, create TextLayout objects from a LineBreakMeasurer, and use TextLayout.draw instead of drawing with Graphics.drawString.
First, you need to create an AttributedCharacterIterator. You can do that by creating an AttributedString from your text:
AttributedString attrStr = new AttributedString(text);
AttributedCharacterIterator iter = attrStr.getIterator();
Now you can create a LineBreakMeasurer:
Graphics2D g2 = (Graphics2D) g;
LineBreakMeasurer measurer = new LineBreakMeasurer(iter,
g2.getFontRenderContext());
You then obtain the lines as TextLayouts, one at a time, from the LineBreakMeasurer:
List<TextLayout> lines = new ArrayList<>();
while (measurer.getPosition() < text.length()) {
lines.add(measurer.nextLayout(rect.width));
}
Once you have them, you can figure out the total height:
float textHeight = 0;
for (TextLayout line : lines) {
textHeight = line.getAscent() line.getDescent() line.getLeading();
}
Finally, you can draw the lines:
float y = (rect.height - textHeight) / 2;
for (TextLayout line : lines) {
Rectangle2D bounds = line.getBounds();
float x = (rect.width - (float) bounds.getWidth()) / 2;
line.draw(g2, x, y line.getAscent());
y = line.getAscent() line.getDescent() line.getLeading();
}