Search code examples
javaimagetextregionsquare

Adding text to an image and make it fit into a rectangular region in Java


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?


Solution

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